--- a/eric7/E5Gui/EricMapWidget.py Sat May 22 19:52:34 2021 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,352 +0,0 @@ -# -*- coding: utf-8 -*- - -# Copyright (c) 2014 - 2021 Detlev Offenbach <detlev@die-offenbachs.de> -# - -""" -Module implementing a base class for showing a document map. -""" - -from PyQt6.QtCore import Qt, QSize, QRect, QCoreApplication -from PyQt6.QtGui import QColor, QBrush, QPainter -from PyQt6.QtWidgets import QWidget, QAbstractScrollArea - - -class EricMapWidget(QWidget): - """ - Class implementing a base class for showing a document map. - """ - def __init__(self, parent=None): - """ - Constructor - - @param parent reference to the parent widget (QWidget) - """ - super().__init__(parent) - self.setAttribute(Qt.WidgetAttribute.WA_OpaquePaintEvent) - - self.__width = 14 - self.__lineBorder = 1 - self.__lineHeight = 2 - self.__backgroundColor = QColor("#e7e7e7") - self.__setSliderColor() - - self._master = None - self.__enabled = False - self.__rightSide = True - - if parent is not None and isinstance(parent, QAbstractScrollArea): - self.setMaster(parent) - - def __setSliderColor(self): - """ - Private method to set the slider color depending upon the background - color. - """ - if self.__backgroundColor.toHsv().value() < 128: - # dark background, use white slider - self.__sliderColor = Qt.GlobalColor.white - else: - # light background, use black slider - self.__sliderColor = Qt.GlobalColor.black - - def __updateMasterViewportWidth(self): - """ - Private method to update the master's viewport width. - """ - if self._master: - if self.__enabled: - width = self.__width - else: - width = 0 - if self.__rightSide: - self._master.setViewportMargins(0, 0, width, 0) - else: - self._master.setViewportMargins(width, 0, 0, 0) - - def setMaster(self, master): - """ - Public method to set the map master widget. - - @param master map master widget (QAbstractScrollArea) - """ - self._master = master - self._master.setVerticalScrollBarPolicy( - Qt.ScrollBarPolicy.ScrollBarAlwaysOn) - self._master.verticalScrollBar().valueChanged.connect(self.update) - self._master.verticalScrollBar().rangeChanged.connect(self.update) - self.__updateMasterViewportWidth() - - def setWidth(self, width): - """ - Public method to set the widget width. - - @param width widget width (integer) - """ - if width != self.__width: - self.__width = max(6, width) # minimum width 6 pixels - self.__updateMasterViewportWidth() - self.update() - - def width(self): - """ - Public method to get the widget's width. - - @return widget width (integer) - """ - return self.__width - - def setMapPosition(self, onRight): - """ - Public method to set, whether the map should be shown to the right or - left of the master widget. - - @param onRight flag indicating to show the map on the right side of - the master widget - @type bool - """ - if onRight != self.__rightSide: - self.__rightSide = onRight - self.__updateMasterViewportWidth() - self.update() - - def isOnRightSide(self): - """ - Public method to test, if the map is shown on the right side of the - master widget. - - @return flag indicating that the map is to the right of the master - widget - @rtype bool - """ - return self.__rightSide - - def setLineDimensions(self, border, height): - """ - Public method to set the line (indicator) dimensions. - - @param border border width on each side in x-direction (integer) - @param height height of the line in pixels (integer) - """ - if border != self.__lineBorder or height != self.__lineHeight: - self.__lineBorder = max(1, border) # min border 1 pixel - self.__lineHeight = max(1, height) # min height 1 pixel - self.update() - - def lineDimensions(self): - """ - Public method to get the line (indicator) dimensions. - - @return tuple with border width (integer) and line height (integer) - """ - return self.__lineBorder, self.__lineHeight - - def setEnabled(self, enable): - """ - Public method to set the enabled state. - - @param enable flag indicating the enabled state (boolean) - """ - if enable != self.__enabled: - self.__enabled = enable - self.setVisible(enable) - self.__updateMasterViewportWidth() - - def isEnabled(self): - """ - Public method to check the enabled state. - - @return flag indicating the enabled state (boolean) - """ - return self.__enabled - - def setBackgroundColor(self, color): - """ - Public method to set the widget background color. - - @param color color for the background (QColor) - """ - if color != self.__backgroundColor: - self.__backgroundColor = color - self.__setSliderColor() - self.update() - - def backgroundColor(self): - """ - Public method to get the background color. - - @return background color (QColor) - """ - return QColor(self.__backgroundColor) - - def sizeHint(self): - """ - Public method to give an indication about the preferred size. - - @return preferred size (QSize) - """ - return QSize(self.__width, 0) - - def paintEvent(self, event): - """ - Protected method to handle a paint event. - - @param event paint event (QPaintEvent) - """ - # step 1: fill the whole painting area - painter = QPainter(self) - painter.fillRect(event.rect(), self.__backgroundColor) - - # step 2: paint the indicators - self._paintIt(painter) - - # step 3: paint the slider - if self._master: - penColor = self.__sliderColor - painter.setPen(penColor) - brushColor = Qt.GlobalColor.transparent - painter.setBrush(QBrush(brushColor)) - painter.drawRect(self.__generateSliderRange( - self._master.verticalScrollBar())) - - def _paintIt(self, painter): - """ - Protected method for painting the widget's indicators. - - Note: This method should be implemented by subclasses. - - @param painter reference to the painter object (QPainter) - """ - pass - - def mousePressEvent(self, event): - """ - Protected method to handle a mouse button press. - - @param event reference to the mouse event (QMouseEvent) - """ - if event.button() == Qt.MouseButton.LeftButton and self._master: - vsb = self._master.verticalScrollBar() - value = self.position2Value(event.position().toPoint().y() - 1) - vsb.setValue(value - 0.5 * vsb.pageStep()) # center on page - self.__mousePressPos = None - - def mouseMoveEvent(self, event): - """ - Protected method to handle a mouse moves. - - @param event reference to the mouse event (QMouseEvent) - """ - if event.buttons() & Qt.MouseButton.LeftButton and self._master: - vsb = self._master.verticalScrollBar() - value = self.position2Value(event.position().toPoint().y() - 1) - vsb.setValue(value - 0.5 * vsb.pageStep()) # center on page - - def wheelEvent(self, event): - """ - Protected slot handling mouse wheel events. - - @param event reference to the wheel event (QWheelEvent) - """ - isVertical = event.angleDelta().x() == 0 - if ( - self._master and - event.modifiers() == Qt.KeyboardModifier.NoModifier and - isVertical - ): - QCoreApplication.sendEvent(self._master.verticalScrollBar(), event) - - def calculateGeometry(self): - """ - Public method to recalculate the map widget's geometry. - """ - if self._master: - cr = self._master.contentsRect() - vsb = self._master.verticalScrollBar() - if vsb.isVisible(): - vsbw = vsb.contentsRect().width() - else: - vsbw = 0 - margins = self._master.contentsMargins() - if margins.right() > vsbw: - vsbw = 0 - if self.__rightSide: - self.setGeometry( - QRect(cr.right() - self.__width - vsbw, cr.top(), - self.__width, cr.height())) - else: - self.setGeometry( - QRect(0, cr.top(), self.__width, cr.height())) - self.update() - - def scaleFactor(self, slider=False): - """ - Public method to determine the scrollbar's scale factor. - - @param slider flag indicating to calculate the result for the slider - (boolean) - @return scale factor (float) - """ - if self._master: - delta = 0 if slider else 2 - vsb = self._master.verticalScrollBar() - posHeight = vsb.height() - delta - 1 - valHeight = vsb.maximum() - vsb.minimum() + vsb.pageStep() - return float(posHeight) / valHeight - else: - return 1.0 - - def value2Position(self, value, slider=False): - """ - Public method to convert a scrollbar value into a position. - - @param value value to convert (integer) - @param slider flag indicating to calculate the result for the slider - (boolean) - @return position (integer) - """ - if self._master: - offset = 0 if slider else 1 - vsb = self._master.verticalScrollBar() - return (value - vsb.minimum()) * self.scaleFactor(slider) + offset - else: - return value - - def position2Value(self, position, slider=False): - """ - Public method to convert a position into a scrollbar value. - - @param position scrollbar position to convert (integer) - @param slider flag indicating to calculate the result for the slider - (boolean) - @return scrollbar value (integer) - """ - if self._master: - offset = 0 if slider else 1 - vsb = self._master.verticalScrollBar() - return vsb.minimum() + max( - 0, (position - offset) / self.scaleFactor(slider)) - else: - return position - - def generateIndicatorRect(self, position): - """ - Public method to generate an indicator rectangle. - - @param position indicator position (integer) - @return indicator rectangle (QRect) - """ - return QRect(self.__lineBorder, position - self.__lineHeight // 2, - self.__width - self.__lineBorder, self.__lineHeight) - - def __generateSliderRange(self, scrollbar): - """ - Private method to generate the slider rectangle. - - @param scrollbar reference to the vertical scrollbar (QScrollBar) - @return slider rectangle (QRect) - """ - pos1 = self.value2Position(scrollbar.value(), slider=True) - pos2 = self.value2Position(scrollbar.value() + scrollbar.pageStep(), - slider=True) - return QRect(0, pos1, self.__width - 1, pos2 - pos1)