Aleš Erjavec avatar Aleš Erjavec committed d8183bc

Changed the tab widget in a quick menu.

The tab widget is now displayed vertically beside the menu items
and changes the current tab on hover.

Comments (0)

Files changed (3)

Orange/OrangeCanvas/document/quickmenu.py

     QButtonGroup, QStackedWidget, QHBoxLayout, QVBoxLayout, QSizePolicy,
     QStandardItemModel, QSortFilterProxyModel, QStyleOptionToolButton,
     QStylePainter, QStyle, QApplication, QStyledItemDelegate,
-    QStyleOptionViewItemV4, QSizeGrip, QKeySequence
+    QStyleOptionViewItemV4, QSizeGrip
 )
 
 from PyQt4.QtCore import pyqtSignal as Signal
 from ..gui.framelesswindow import FramelessWindow
 from ..gui.lineedit import LineEdit
 from ..gui.tooltree import ToolTree, FlattenedTreeItemModel
+from ..gui.toolgrid import ToolButtonEventListener
+from ..gui.toolbox import create_tab_gradient
 from ..gui.utils import StyledWidget_paintEvent
 
 from ..registry.qt import QtWidgetRegistry
 log = logging.getLogger(__name__)
 
 
+class _MenuItemDelegate(QStyledItemDelegate):
+    def __init__(self, parent=None):
+        QStyledItemDelegate.__init__(self, parent)
+
+    def sizeHint(self, option, index):
+        option = QStyleOptionViewItemV4(option)
+        self.initStyleOption(option, index)
+        size = QStyledItemDelegate.sizeHint(self, option, index)
+
+        # TODO: get the default QMenu item height from the current style.
+        size.setHeight(max(size.height(), 25))
+        return size
+
+
 class MenuPage(ToolTree):
     """
     A menu page in a :class:`QuickMenu` widget, showing a list of actions.
         self.__title = title
         self.__icon = icon
 
+        self.view().setItemDelegate(_MenuItemDelegate(self.view()))
         # Make sure the initial model is wrapped in a ItemDisableFilter.
         self.setModel(self.model())
 
                      designable=True)
 
     def paintEvent(self, event):
+        opt = QStyleOptionToolButton()
+        self.initStyleOption(opt)
+        opt.features |= QStyleOptionToolButton.HasMenu
         if self.__flat:
             # Use default widget background/border styling.
             StyledWidget_paintEvent(self, event)
 
-            opt = QStyleOptionToolButton()
-            self.initStyleOption(opt)
             p = QStylePainter(self)
             p.drawControl(QStyle.CE_ToolButtonLabel, opt)
         else:
-            QToolButton.paintEvent(self, event)
+            p = QStylePainter(self)
+            p.drawComplexControl(QStyle.CC_ToolButton, opt)
 
+    def sizeHint(self):
+        opt = QStyleOptionToolButton()
+        self.initStyleOption(opt)
+        opt.features |= QStyleOptionToolButton.HasMenu
+        style = self.style()
+
+        hint = style.sizeFromContents(QStyle.CT_ToolButton, opt,
+                                      opt.iconSize, self)
+        return hint
 
 _Tab = \
     namedtuple(
          "palette"])
 
 
-# TODO: ..application.canvastooldock.QuickCategoryToolbar is very similar,
-#       to TobBarWidget. Maybe common functionality could factored our.
-
 class TabBarWidget(QWidget):
     """
     A tab bar widget using tool buttons as tabs.
 
     def __init__(self, parent=None, **kwargs):
         QWidget.__init__(self, parent, **kwargs)
-        layout = QHBoxLayout()
+        layout = QVBoxLayout()
         layout.setContentsMargins(0, 0, 0, 0)
         layout.setSpacing(0)
         self.setLayout(layout)
 
-        self.setSizePolicy(QSizePolicy.Expanding,
-                           QSizePolicy.Fixed)
+        self.setSizePolicy(QSizePolicy.Fixed,
+                           QSizePolicy.Expanding)
         self.__tabs = []
+
         self.__currentIndex = -1
+        self.__changeOnHover = False
+
+        self.__iconSize = QSize(26, 26)
+
         self.__group = QButtonGroup(self, exclusive=True)
         self.__group.buttonPressed[QAbstractButton].connect(
             self.__onButtonPressed
         )
 
+        self.__hoverListener = ToolButtonEventListener(self)
+
+    def setChangeOnHover(self, changeOnHover):
+        """
+        If set to ``True`` the tab widget will change the current index when
+        the mouse hovers over a tab button.
+
+        """
+        if self.__changeOnHover != changeOnHover:
+            self.__changeOnHover = changeOnHover
+
+            if changeOnHover:
+                self.__hoverListener.buttonEnter.connect(
+                    self.__onButtonEnter
+                )
+            else:
+                self.__hoverListener.buttonEnter.disconnect(
+                    self.__onButtonEnter
+                )
+
+    def changeOnHover(self):
+        """
+        Does the current tab index follow the mouse cursor.
+        """
+        return self.__changeOnHover
+
     def count(self):
         """
         Return the number of tabs in the widget.
         button = TabButton(self, objectName="tab-button")
         button.setSizePolicy(QSizePolicy.Expanding,
                              QSizePolicy.Expanding)
+        button.setIconSize(self.__iconSize)
 
         self.__group.addButton(button)
+
+        button.installEventFilter(self.__hoverListener)
+
         tab = _Tab(text, icon, toolTip, button, None, None)
         self.layout().insertWidget(index, button)
 
             self.layout().takeItem(index)
             tab = self.__tabs.pop(index)
             self.__group.removeButton(tab.button)
+
+            tab.button.removeEventFilter(self.__hoverListener)
+
             tab.button.deleteLater()
 
             if self.currentIndex() == index:
         """
         return self.__tabs[index].button
 
+    def setIconSize(self, size):
+        if self.__iconSize != size:
+            self.__iconSize = size
+            for tab in self.__tabs:
+                tab.button.setIconSize(self.__iconSize)
+
     def __updateTab(self, index):
         """
         Update the tab button.
                 self.setCurrentIndex(i)
                 break
 
+    def __onButtonEnter(self, button):
+        if self.__changeOnHover:
+            button.click()
+
 
 class PagedMenu(QWidget):
     """
         self.__pages = []
         self.__currentIndex = -1
 
-        layout = QVBoxLayout()
+        layout = QHBoxLayout()
         layout.setContentsMargins(0, 0, 0, 0)
         layout.setSpacing(0)
 
         self.__tab = TabBarWidget(self)
-        self.__tab.setFixedHeight(25)
         self.__tab.currentChanged.connect(self.setCurrentIndex)
+        self.__tab.setChangeOnHover(True)
 
         self.__stack = MenuStackWidget(self)
 
-        layout.addWidget(self.__tab)
+        layout.addWidget(self.__tab, alignment=Qt.AlignTop)
         layout.addWidget(self.__stack)
 
         self.setLayout(layout)
         self.setLayout(QVBoxLayout(self))
         self.layout().setContentsMargins(6, 6, 6, 6)
 
+        self.__search = SearchWidget(self, objectName="search-line")
+
+        self.__search.setPlaceholderText(
+            self.tr("Search for widget or select from the list.")
+        )
+
+        self.layout().addWidget(self.__search)
+
         self.__frame = QFrame(self, objectName="menu-frame")
         layout = QVBoxLayout()
-        layout.setContentsMargins(1, 1, 1, 1)
+        layout.setContentsMargins(0, 0, 0, 0)
         layout.setSpacing(2)
         self.__frame.setLayout(layout)
 
 
         self.__frame.layout().addWidget(self.__pages)
 
-        self.__search = SearchWidget(self, objectName="search-line")
-
-        self.__search.setPlaceholderText(
-            self.tr("Search for widget or select from the list.")
-        )
-
-        self.layout().addWidget(self.__search)
         self.setSizePolicy(QSizePolicy.Fixed,
                            QSizePolicy.Expanding)
 
         if sys.platform == "darwin":
             view = self.__suggestPage.view()
             view.verticalScrollBar().setAttribute(Qt.WA_MacMiniSize, True)
-            # Don't show the focus frame because it expands into the tab
-            # bar at the top.
+            # Don't show the focus frame because it expands into the tab bar.
             view.setAttribute(Qt.WA_MacShowFocusRect, False)
 
-        self.addPage(self.tr("Quick Search"), self.__suggestPage)
+        i = self.addPage(self.tr("Quick Search"), self.__suggestPage)
+        button = self.__pages.tabButton(i)
+        button.setObjectName("search-tab-button")
+        button.setStyleSheet(
+            "TabButton {\n"
+            "    qproperty-flat_: false;\n"
+            "    border: none;"
+            "}\n")
 
         self.__search.textEdited.connect(self.__on_textEdited)
 
 
         """
         page = MenuPage(self)
-        view = page.view()
-        delegate = WidgetItemDelegate(view)
-        view.setItemDelegate(delegate)
 
         page.setModel(index.model())
         page.setRootIndex(index)
 
             if brush.isValid():
                 brush = brush.toPyObject()
+                base_color = brush.color()
                 button = self.__pages.tabButton(i)
-                palette = button.palette()
                 button.setStyleSheet(
                     "TabButton {\n"
                     "    qproperty-flat_: false;\n"
-                    "    background-color: %s;\n"
+                    "    background: %s;\n"
                     "    border: none;\n"
+                    "    border-bottom: 1px solid palette(dark);\n"
                     "}\n"
                     "TabButton:checked {\n"
-                    "    border: 1px solid %s;\n"
-                    "}" % (brush.color().name(),
-                           palette.color(palette.Mid).name())
+                    "    background: %s\n"
+                    "}" % (create_css_gradient(base_color),
+                           create_css_gradient(base_color.darker(110)))
                 )
 
         self.__model = model
         return FramelessWindow.eventFilter(self, obj, event)
 
 
-class WidgetItemDelegate(QStyledItemDelegate):
-    def __init__(self, parent=None):
-        QStyledItemDelegate.__init__(self, parent)
-
-    def sizeHint(self, option, index):
-        option = QStyleOptionViewItemV4(option)
-        self.initStyleOption(option, index)
-        size = QStyledItemDelegate.sizeHint(self, option, index)
-        size.setHeight(max(size.height(), 25))
-        return size
-
-
 class ItemViewKeyNavigator(QObject):
     """
     A event filter class listening to key press events and responding
             y = window_size.height() - size.height()
 
         self.move(x, y)
+
+
+def create_css_gradient(base_color):
+    """
+    Create a Qt css linear gradient fragment based on the `base_color`.
+    """
+    grad = create_tab_gradient(base_color)
+    stops = grad.stops()
+    stops = "\n".join("    stop: {0:f} {1}".format(stop, color.name())
+                      for stop, color in stops)
+    return ("qlineargradient(\n"
+            "    x1: 0, y1: 0, x2: 0, y2: 1,\n"
+            "{0})").format(stops)
Add a comment to this file

Orange/OrangeCanvas/icons/arrow-right.svg

Added
New image
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<!-- Generator: Adobe Illustrator 16.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<svg xmlns="http://www.w3.org/2000/svg" enable-background="new 0 0 48 48" xml:space="preserve" viewBox="0 0 48 48" version="1.1" y="0px" x="0px">
+<polygon points="24.007,37.979,42,10.021,6,10.021" transform="matrix(0,-1,1,0,0,48)" fill="#3a3a3a"/>
+</svg>

Orange/OrangeCanvas/styles/orange.qss

 }
 
 QuickMenu ToolTree QTreeView::item {
-    height: 25px;
-    border-top: 1px solid #e9eff2;
-    border-bottom: 1px solid #e9eff2;
+	height: 25px;
+	border-bottom: 1px solid #e9eff2;
 }
 
 QuickMenu QTreeView::item:hover {
-    background-color: lightblue;
-    color: white;
+	background: qlineargradient(
+		x1: 0, y1: 0, x2: 0, y2: 1,
+		stop: 0 #688EF6,
+		stop: 0.5 #4047f4,
+		stop: 1.0 #2D68F3
+	);
+	color: white;
 }
 
 QuickMenu QTreeView::item:selected {
-    background-color: blue;
-    color: white;
+	background-color: blue;
+	color: white;
 }
 
-/* Quick Menu search line edit 
+QuickMenu TabBarWidget QToolButton {
+	width: 33px;
+	height: 25px;
+	border-bottom: 1px solid palette(dark);
+	padding-right: 5px;
+}
+
+QuickMenu TabBarWidget QToolButton#search-tab-button {
+	background-color: #9CACB4;
+}
+
+QuickMenu TabBarWidget QToolButton:menu-indicator {
+	image: url(canvas_icons:/arrow-right.svg);
+	subcontrol-position: center right;
+	height: 8px;
+	width: 8px;
+}
+
+/* Quick Menu search line edit
  */
 
 QuickMenu QLineEdit {
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.