--- a/eric7/EricWidgets/EricSideBar.py Sun Sep 05 18:07:03 2021 +0200 +++ b/eric7/EricWidgets/EricSideBar.py Mon Sep 06 19:52:37 2021 +0200 @@ -10,14 +10,11 @@ import enum import json -from PyQt6.QtCore import QEvent, QSize, Qt, QTimer -from PyQt6.QtWidgets import ( - QTabBar, QWidget, QStackedWidget, QBoxLayout, QToolButton, QSizePolicy -) +from PyQt6.QtCore import pyqtSlot, Qt, QSize +from PyQt6.QtGui import QIcon +from PyQt6.QtWidgets import QWidget, QStackedWidget, QBoxLayout -from EricWidgets.EricApplication import ericApp - -import UI.PixmapCache +from .EricIconBar import EricIconBar class EricSideBarSide(enum.Enum): @@ -30,60 +27,37 @@ WEST = 3 -# TODO: change to used a QListWidget with icons (48px) instead of QTabBar and -# remove the auto hide/show feature (?) class EricSideBar(QWidget): """ Class implementing a sidebar with a widget area, that is hidden or shown, if the current tab is clicked again. """ - Version = 2 + Version = 3 - def __init__(self, orientation=None, delay=200, parent=None): + def __init__(self, orientation=None, parent=None): """ Constructor @param orientation orientation of the sidebar widget @type EricSideBarSide - @param delay value for the expand/shrink delay in milliseconds - @type int @param parent parent widget @type QWidget """ super().__init__(parent) - self.__tabBar = QTabBar() - self.__tabBar.setDrawBase(True) - self.__tabBar.setShape(QTabBar.Shape.RoundedNorth) - self.__tabBar.setUsesScrollButtons(True) - self.__tabBar.setDrawBase(False) + # initial layout is done for NORTH + self.__iconBar = EricIconBar(Qt.Orientation.Horizontal) + self.__stackedWidget = QStackedWidget(self) self.__stackedWidget.setContentsMargins(0, 0, 0, 0) - self.__autoHideButton = QToolButton() - self.__autoHideButton.setCheckable(True) - self.__autoHideButton.setIcon( - UI.PixmapCache.getIcon("autoHideOff")) - self.__autoHideButton.setChecked(True) - self.__autoHideButton.setToolTip( - self.tr("Deselect to activate automatic collapsing")) - self.barLayout = QBoxLayout(QBoxLayout.Direction.LeftToRight) - self.barLayout.setContentsMargins(0, 0, 0, 0) + self.layout = QBoxLayout(QBoxLayout.Direction.TopToBottom) self.layout.setContentsMargins(0, 0, 0, 0) self.layout.setSpacing(0) - self.barLayout.addWidget(self.__autoHideButton) - self.barLayout.addWidget(self.__tabBar) - self.layout.addLayout(self.barLayout) + self.layout.addWidget(self.__iconBar) self.layout.addWidget(self.__stackedWidget) self.setLayout(self.layout) - # initialize the delay timer - self.__actionMethod = None - self.__delayTimer = QTimer(self) - self.__delayTimer.setSingleShot(True) - self.__delayTimer.setInterval(delay) - self.__delayTimer.timeout.connect(self.__delayedAction) - self.__minimized = False self.__minSize = 0 self.__maxSize = 0 @@ -96,17 +70,17 @@ # flag storing if this widget or any child has the focus self.__autoHide = False - self.__tabBar.installEventFilter(self) - self.__orientation = EricSideBarSide.NORTH if orientation is None: orientation = EricSideBarSide.NORTH self.setOrientation(orientation) - self.__tabBar.currentChanged[int].connect( + self.__iconBar.currentChanged.connect( self.__stackedWidget.setCurrentIndex) - ericApp().focusChanged.connect(self.__appFocusChanged) - self.__autoHideButton.toggled[bool].connect(self.__autoHideToggled) + self.__iconBar.currentChanged.connect( + self.__currentIconChanged) + self.__iconBar.currentClicked.connect( + self.__currentIconClicked) def setSplitter(self, splitter): """ @@ -130,47 +104,6 @@ if self.splitter: self.splitterSizes = self.splitter.sizes() - def __delayedAction(self): - """ - Private slot to handle the firing of the delay timer. - """ - if self.__actionMethod is not None: - self.__actionMethod() - - def setDelay(self, delay): - """ - Public method to set the delay value for the expand/shrink delay in - milliseconds. - - @param delay value for the expand/shrink delay in milliseconds - (integer) - """ - self.__delayTimer.setInterval(delay) - - def delay(self): - """ - Public method to get the delay value for the expand/shrink delay in - milliseconds. - - @return value for the expand/shrink delay in milliseconds (integer) - """ - return self.__delayTimer.interval() - - def __cancelDelayTimer(self): - """ - Private method to cancel the current delay timer. - """ - self.__delayTimer.stop() - self.__actionMethod = None - - def shrink(self): - """ - Public method to record a shrink request. - """ - self.__delayTimer.stop() - self.__actionMethod = self.__shrinkIt - self.__delayTimer.start() - def __shrinkIt(self): """ Private method to shrink the sidebar. @@ -193,19 +126,9 @@ if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): - self.setFixedHeight(self.__tabBar.minimumSizeHint().height()) + self.setFixedHeight(self.__iconBar.minimumSizeHint().height()) else: - self.setFixedWidth(self.__tabBar.minimumSizeHint().width()) - - self.__actionMethod = None - - def expand(self): - """ - Public method to record a expand request. - """ - self.__delayTimer.stop() - self.__actionMethod = self.__expandIt - self.__delayTimer.start() + self.setFixedWidth(self.__iconBar.minimumSizeHint().width()) def __expandIt(self): """ @@ -226,8 +149,6 @@ self.setMaximumWidth(self.__maxSize) if self.splitter: self.splitter.setSizes(self.splitterSizes) - - self.__actionMethod = None def isMinimized(self): """ @@ -237,63 +158,44 @@ """ return self.__minimized - def isAutoHiding(self): - """ - Public method to check, if the auto hide function is active. - - @return flag indicating the state of auto hiding (boolean) + @pyqtSlot(int) + def __currentIconChanged(self, index): """ - return self.__autoHide - - def eventFilter(self, obj, evt): - """ - Public method to handle some events for the tabbar. + Private slot to handle a change of the current icon. - @param obj reference to the object (QObject) - @param evt reference to the event object (QEvent) - @return flag indicating, if the event was handled (boolean) + @param index index of the current icon + @type int """ - if obj == self.__tabBar: - if evt.type() == QEvent.Type.MouseButtonPress: - pos = evt.position().toPoint() - for i in range(self.__tabBar.count()): - if self.__tabBar.tabRect(i).contains(pos): - break - - if i == self.__tabBar.currentIndex(): - if self.isMinimized(): - self.expand() - else: - self.shrink() - return True - elif self.isMinimized(): - self.expand() - elif evt.type() == QEvent.Type.Wheel: - delta = evt.angleDelta().y() - if delta > 0: - self.prevTab() - else: - self.nextTab() - return True + if self.isMinimized(): + self.__expandIt() + + @pyqtSlot(int) + def __currentIconClicked(self, index): + """ + Private slot to handle a click of the current icon. - return QWidget.eventFilter(self, obj, evt) + @param index index of the clicked icon + @type int + """ + if self.isMinimized(): + self.__expandIt() + else: + self.__shrinkIt() - def addTab(self, widget, iconOrLabel, label=None): + def addTab(self, widget, icon, label=None): """ Public method to add a tab to the sidebar. - @param widget reference to the widget to add (QWidget) - @param iconOrLabel reference to the icon or the label text of the tab - (QIcon, string) - @param label the labeltext of the tab (string) (only to be - used, if the second parameter is a QIcon) + @param widget reference to the widget to add + @type QWidget + @param icon reference to the icon of the widget + @type QIcon or QPixmap + @param label the label text of the widget + @type str """ - if label: - index = self.__tabBar.addTab(iconOrLabel, label) - self.__tabBar.setTabToolTip(index, label) - else: - index = self.__tabBar.addTab(iconOrLabel) - self.__tabBar.setTabToolTip(index, iconOrLabel) + if isinstance(icon, QIcon): + icon = icon.pixmap(48, 48) + self.__iconBar.addIcon(icon, label) self.__stackedWidget.addWidget(widget) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH @@ -302,23 +204,23 @@ else: self.__minSize = self.minimumSizeHint().width() - def insertTab(self, index, widget, iconOrLabel, label=None): + def insertTab(self, index, widget, icon, label=None): """ Public method to insert a tab into the sidebar. - @param index the index to insert the tab at (integer) - @param widget reference to the widget to insert (QWidget) - @param iconOrLabel reference to the icon or the labeltext of the tab - (QIcon, string) - @param label the labeltext of the tab (string) (only to be - used, if the second parameter is a QIcon) + @param index the index to insert the tab at + @type int + @param widget reference to the widget to insert + @type QWidget + @param icon reference to the icon of the widget + @type QIcon or QPixmap + @param label the label text of the widget + @type str """ - if label: - index = self.__tabBar.insertTab(index, iconOrLabel, label) - self.__tabBar.setTabToolTip(index, label) - else: - index = self.__tabBar.insertTab(index, iconOrLabel) - self.__tabBar.setTabToolTip(index, iconOrLabel) + if isinstance(icon, QIcon): + icon = icon.pixmap(48, 48) + self.__iconBar.insertIcon(index, icon, label) + self.__stackedWidget.insertWidget(index, widget) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH @@ -331,10 +233,11 @@ """ Public method to remove a tab. - @param index the index of the tab to remove (integer) + @param index the index of the tab to remove + @type int """ self.__stackedWidget.removeWidget(self.__stackedWidget.widget(index)) - self.__tabBar.removeTab(index) + self.__iconBar.removeIcon(index) if self.__orientation in ( EricSideBarSide.NORTH, EricSideBarSide.SOUTH ): @@ -377,7 +280,7 @@ @return number of tabs in the sidebar (integer) """ - return self.__tabBar.count() + return self.__iconBar.count() def currentIndex(self): """ @@ -393,10 +296,10 @@ @param index the index to set as the current index (integer) """ - self.__tabBar.setCurrentIndex(index) + self.__iconBar.setCurrentIndex(index) self.__stackedWidget.setCurrentIndex(index) if self.isMinimized(): - self.expand() + self.__expandIt() def currentWidget(self): """ @@ -414,9 +317,9 @@ (QWidget) """ self.__stackedWidget.setCurrentWidget(widget) - self.__tabBar.setCurrentIndex(self.__stackedWidget.currentIndex()) + self.__iconBar.setCurrentIndex(self.__stackedWidget.currentIndex()) if self.isMinimized(): - self.expand() + self.__expandIt() def indexOf(self, widget): """ @@ -427,24 +330,6 @@ """ return self.__stackedWidget.indexOf(widget) - def isTabEnabled(self, index): - """ - Public method to check, if a tab is enabled. - - @param index index of the tab to check (integer) - @return flag indicating the enabled state (boolean) - """ - return self.__tabBar.isTabEnabled(index) - - def setTabEnabled(self, index, enabled): - """ - Public method to set the enabled state of a tab. - - @param index index of the tab to set (integer) - @param enabled enabled state to set (boolean) - """ - self.__tabBar.setTabEnabled(index, enabled) - def orientation(self): """ Public method to get the orientation of the sidebar. @@ -462,109 +347,19 @@ @type EricSideBarSide """ if orient == EricSideBarSide.NORTH: - self.__tabBar.setShape(QTabBar.Shape.RoundedNorth) - self.__tabBar.setSizePolicy( - QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) - self.barLayout.setDirection(QBoxLayout.Direction.LeftToRight) + self.__iconBar.setOrientation(Qt.Orientation.Horizontal) self.layout.setDirection(QBoxLayout.Direction.TopToBottom) - self.layout.setAlignment(self.barLayout, - Qt.AlignmentFlag.AlignLeft) elif orient == EricSideBarSide.EAST: - self.__tabBar.setShape(QTabBar.Shape.RoundedEast) - self.__tabBar.setSizePolicy( - QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding) - self.barLayout.setDirection(QBoxLayout.Direction.TopToBottom) + self.__iconBar.setOrientation(Qt.Orientation.Vertical) self.layout.setDirection(QBoxLayout.Direction.RightToLeft) - self.layout.setAlignment(self.barLayout, Qt.AlignmentFlag.AlignTop) elif orient == EricSideBarSide.SOUTH: - self.__tabBar.setShape(QTabBar.Shape.RoundedSouth) - self.__tabBar.setSizePolicy( - QSizePolicy.Policy.Expanding, QSizePolicy.Policy.Preferred) - self.barLayout.setDirection(QBoxLayout.Direction.LeftToRight) + self.__iconBar.setOrientation(Qt.Orientation.Horizontal) self.layout.setDirection(QBoxLayout.Direction.BottomToTop) - self.layout.setAlignment(self.barLayout, - Qt.AlignmentFlag.AlignLeft) elif orient == EricSideBarSide.WEST: - self.__tabBar.setShape(QTabBar.Shape.RoundedWest) - self.__tabBar.setSizePolicy( - QSizePolicy.Policy.Preferred, QSizePolicy.Policy.Expanding) - self.barLayout.setDirection(QBoxLayout.Direction.TopToBottom) + self.__iconBar.setOrientation(Qt.Orientation.Vertical) self.layout.setDirection(QBoxLayout.Direction.LeftToRight) - self.layout.setAlignment(self.barLayout, Qt.AlignmentFlag.AlignTop) self.__orientation = orient - def tabIcon(self, index): - """ - Public method to get the icon of a tab. - - @param index index of the tab (integer) - @return icon of the tab (QIcon) - """ - return self.__tabBar.tabIcon(index) - - def setTabIcon(self, index, icon): - """ - Public method to set the icon of a tab. - - @param index index of the tab (integer) - @param icon icon to be set (QIcon) - """ - self.__tabBar.setTabIcon(index, icon) - - def tabText(self, index): - """ - Public method to get the text of a tab. - - @param index index of the tab (integer) - @return text of the tab (string) - """ - return self.__tabBar.tabText(index) - - def setTabText(self, index, text): - """ - Public method to set the text of a tab. - - @param index index of the tab (integer) - @param text text to set (string) - """ - self.__tabBar.setTabText(index, text) - - def tabToolTip(self, index): - """ - Public method to get the tooltip text of a tab. - - @param index index of the tab (integer) - @return tooltip text of the tab (string) - """ - return self.__tabBar.tabToolTip(index) - - def setTabToolTip(self, index, tip): - """ - Public method to set the tooltip text of a tab. - - @param index index of the tab (integer) - @param tip tooltip text to set (string) - """ - self.__tabBar.setTabToolTip(index, tip) - - def tabWhatsThis(self, index): - """ - Public method to get the WhatsThis text of a tab. - - @param index index of the tab (integer) - @return WhatsThis text of the tab (string) - """ - return self.__tabBar.tabWhatsThis(index) - - def setTabWhatsThis(self, index, text): - """ - Public method to set the WhatsThis text of a tab. - - @param index index of the tab (integer) - @param text WhatsThis text to set (string) - """ - self.__tabBar.setTabWhatsThis(index, text) - def widget(self, index): """ Public method to get a reference to the widget associated with a tab. @@ -574,6 +369,24 @@ """ return self.__stackedWidget.widget(index) + def setIconBarColor(self, color): + """ + Public method to set the icon bar color. + + @param color icon bar color + @type QColor + """ + self.__iconBar.setColor(color) + + def iconBarColor(self): + """ + Public method to get the icon bar color. + + @return icon bar color + @rtype QColor + """ + return self.__iconBar.color() + def saveState(self): """ Public method to save the state of the sidebar. @@ -600,7 +413,6 @@ "min_size": self.__minSize, "max_size": self.__maxSize, "splitter_sizes": self.splitterSizes, - "auto_hide": self.__autoHide } data = json.dumps(dataDict) @@ -633,90 +445,18 @@ minSize = self.layout.minimumSize().width() maxSize = self.maximumWidth() - if stateDict["version"] == 2: + if stateDict["version"] in (2, 3): if stateDict["minimized"] and not self.__minimized: - self.shrink() + self.__shrinkIt() self.__bigSize = QSize(*stateDict["big_size"]) self.__minSize = max(stateDict["min_size"], minSize) self.__maxSize = max(stateDict["max_size"], maxSize) self.splitterSizes = stateDict["splitter_sizes"] - self.__autoHide = stateDict["auto_hide"] - self.__autoHideButton.setChecked(not self.__autoHide) - if not stateDict["minimized"]: - self.expand() + self.__expandIt() return True return False - - ####################################################################### - ## methods below implement the autohide functionality - ####################################################################### - - def __autoHideToggled(self, checked): - """ - Private slot to handle the toggling of the autohide button. - - @param checked flag indicating the checked state of the button - (boolean) - """ - self.__autoHide = not checked - if self.__autoHide: - self.__autoHideButton.setIcon( - UI.PixmapCache.getIcon("autoHideOn")) - else: - self.__autoHideButton.setIcon( - UI.PixmapCache.getIcon("autoHideOff")) - - def __appFocusChanged(self, old, now): - """ - Private slot to handle a change of the focus. - - @param old reference to the widget, that lost focus (QWidget or None) - @param now reference to the widget having the focus (QWidget or None) - """ - if isinstance(now, QWidget): - self.__hasFocus = self.isAncestorOf(now) - if ( - self.__autoHide and - not self.__hasFocus and - not self.isMinimized() - ): - self.shrink() - elif self.__autoHide and self.__hasFocus and self.isMinimized(): - self.expand() - - def enterEvent(self, event): - """ - Protected method to handle the mouse entering this widget. - - @param event reference to the event (QEvent) - """ - if self.__autoHide and self.isMinimized(): - self.expand() - else: - self.__cancelDelayTimer() - - def leaveEvent(self, event): - """ - Protected method to handle the mouse leaving this widget. - - @param event reference to the event (QEvent) - """ - if self.__autoHide and not self.__hasFocus and not self.isMinimized(): - self.shrink() - else: - self.__cancelDelayTimer() - - def shutdown(self): - """ - Public method to shut down the object. - - This method does some preparations so the object can be deleted - properly. It disconnects from the focusChanged signal in order to - avoid trouble later on. - """ - ericApp().focusChanged.disconnect(self.__appFocusChanged)