From 97a007cf22f460d394b6c46177fbda458c46070d Mon Sep 17 00:00:00 2001 From: Ales Erjavec Date: Fri, 20 Jul 2018 14:45:11 +0200 Subject: [PATCH] OWWidget: Collapse/expand the widget on control area toggle --- Orange/widgets/tests/test_widget.py | 3 +- Orange/widgets/widget.py | 100 ++++++++++++++++++++-------- 2 files changed, 73 insertions(+), 30 deletions(-) diff --git a/Orange/widgets/tests/test_widget.py b/Orange/widgets/tests/test_widget.py index 8eb46947090..36f683fab9e 100644 --- a/Orange/widgets/tests/test_widget.py +++ b/Orange/widgets/tests/test_widget.py @@ -194,8 +194,7 @@ class Widget(OWWidget): want_control_area = True w = Widget() - splitter = w._OWWidget__splitter # type: OWWidget._Splitter - splitter.setControlAreaVisible(False) + w._OWWidget__setControlAreaVisible(False) w.setGeometry(QRect(51, 52, 53, 54)) state = w.saveGeometryAndLayoutState() w1 = Widget() diff --git a/Orange/widgets/widget.py b/Orange/widgets/widget.py index f8bef6a2343..0e295e35bf5 100644 --- a/Orange/widgets/widget.py +++ b/Orange/widgets/widget.py @@ -9,10 +9,11 @@ from AnyQt.QtWidgets import ( QWidget, QDialog, QVBoxLayout, QSizePolicy, QApplication, QStyle, QShortcut, QSplitter, QSplitterHandle, QPushButton, QStatusBar, - QProgressBar, QAction + QProgressBar, QAction, QWIDGETSIZE_MAX ) from AnyQt.QtCore import ( - Qt, QByteArray, QDataStream, QBuffer, QSettings, QUrl, pyqtSignal as Signal + Qt, QRect, QMargins, QByteArray, QDataStream, QBuffer, QSettings, + QUrl, pyqtSignal as Signal ) from AnyQt.QtGui import QIcon, QKeySequence, QDesktopServices @@ -235,12 +236,13 @@ def __new__(cls, *args, captionTitle=None, **kwargs): self.controlArea.setFocus(Qt.ActiveWindowFocusReason) if self.__splitter is not None: - self.__splitter.controlAreaVisibilityChanged.connect( - self.storeControlAreaVisibility) + self.__splitter.handleClicked.connect( + self.__toggleControlArea + ) sc = QShortcut( QKeySequence(Qt.ControlModifier | Qt.ShiftModifier | Qt.Key_D), self) - sc.activated.connect(self.__splitter.flip) + sc.activated.connect(self.__toggleControlArea) return self # pylint: disable=super-init-not-called @@ -268,8 +270,7 @@ def get_flags(cls): else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint) class _Splitter(QSplitter): - controlAreaVisibilityChanged = Signal(int) - + handleClicked = Signal() def _adjusted_size(self, size_method): size = size_method(super())() height = max((size_method(self.widget(i))().height() @@ -289,26 +290,11 @@ def createHandle(self): return self._Handle( self.orientation(), self, cursor=Qt.PointingHandCursor) - def flip(self): - if self.count() == 1: # Prevent hiding control area by shortcut - return - self.setControlAreaVisible(not self.controlAreaVisible()) - - def setControlAreaVisible(self, visible): - if self.controlAreaVisible() == visible: - return - self.setSizes([int(visible), 100000]) - self.controlAreaVisibilityChanged.emit(visible) - self.updateGeometry() - - def controlAreaVisible(self): - return bool(self.sizes()[0]) - class _Handle(QSplitterHandle): def mouseReleaseEvent(self, event): """Resize on left button""" if event.button() == Qt.LeftButton: - self.splitter().flip() + self.splitter().handleClicked.emit() super().mouseReleaseEvent(event) def mouseMoveEvent(self, event): @@ -458,6 +444,67 @@ def _(val): margins.setBottom(sb.sizeHint().height()) self.setContentsMargins(margins) + def __toggleControlArea(self): + if self.__splitter is None or self.__splitter.count() < 2: + return + self.__setControlAreaVisible(not self.__splitter.sizes()[0]) + + def __setControlAreaVisible(self, visible): + # type: (bool) -> None + if self.__splitter is None or self.__splitter.count() < 2: + return + self.controlAreaVisible = visible + splitter = self.__splitter # type: QSplitter + w = splitter.widget(0) + sizes = splitter.sizes() + current_size = sizes[0] + + if bool(current_size) == visible: + return + + current_width = w.width() + geom = self.geometry() + frame = self.frameGeometry() + framemargins = QMargins( + frame.left() - geom.left(), + frame.top() - geom.top(), + frame.right() - geom.right(), + frame.bottom() - geom.bottom() + ) + splitter.setSizes([int(visible), QWIDGETSIZE_MAX]) + if not self.isWindow() or \ + self.windowState() not in {Qt.WindowNoState, Qt.WindowActive}: + # not a window or not in state where we can move move/resize + return + + # force immediate resize recalculation + splitter.refresh() + self.layout().invalidate() + self.layout().activate() + + if visible: + # move left and expand by the exposing widget's width + diffx = -w.width() + diffw = w.width() + else: + # move right and shrink by the collapsing width + diffx = current_width + diffw = -current_width + newgeom = QRect( + geom.x() + diffx, geom.y(), geom.width() + diffw, geom.height() + ) + # bound/move by available geometry + bounds = QApplication.desktop().availableGeometry(self) + bounds = bounds.adjusted( + framemargins.left(), framemargins.top(), + -framemargins.right(), -framemargins.bottom() + ) + newsize = newgeom.size().boundedTo(bounds.size()) + newgeom = QRect(newgeom.topLeft(), newsize) + newgeom.moveLeft(max(newgeom.left(), bounds.left())) + newgeom.moveRight(min(newgeom.right(), bounds.right())) + self.setGeometry(newgeom) + def save_graph(self): """Save the graph with the name given in class attribute `graph_name`. @@ -476,9 +523,6 @@ def copy_to_clipboard(self): return ClipboardFormat.write_image(None, graph_obj) - def storeControlAreaVisibility(self, visible): - self.controlAreaVisible = visible - def __restoreWidgetGeometry(self, geometry): # type: (bytes) -> bool def _fullscreen_to_maximized(geometry): @@ -577,7 +621,7 @@ def showEvent(self, event): if self.save_position and not self.__was_restored: # Restore saved geometry on (first) show if self.__splitter is not None: - self.__splitter.setControlAreaVisible(self.controlAreaVisible) + self.__setControlAreaVisible(self.controlAreaVisible) if self.savedWidgetGeometry is not None: self.__restoreWidgetGeometry(bytes(self.savedWidgetGeometry)) self.__was_restored = True @@ -824,7 +868,7 @@ def restoreGeometryAndLayoutState(self, state): has_spliter = splitter_state & 0x2 splitter_state = splitter_state & 0x1 if has_spliter and self.__splitter is not None: - self.__splitter.setControlAreaVisible(bool(splitter_state)) + self.__setControlAreaVisible(bool(splitter_state)) geometry = QByteArray() stream >> geometry if stream.status() == QDataStream.Ok: