Skip to content

Commit d7e3e09

Browse files
authored
Merge pull request #3146 from ales-erjavec/control-area-shrink
[ENH] OWWidget: Collapse/expand the widget on control area toggle
2 parents 7504b9d + cbbc258 commit d7e3e09

File tree

2 files changed

+78
-37
lines changed

2 files changed

+78
-37
lines changed

Orange/widgets/tests/test_widget.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,8 +194,7 @@ class Widget(OWWidget):
194194
want_control_area = True
195195

196196
w = Widget()
197-
splitter = w._OWWidget__splitter # type: OWWidget._Splitter
198-
splitter.setControlAreaVisible(False)
197+
w._OWWidget__setControlAreaVisible(False)
199198
w.setGeometry(QRect(51, 52, 53, 54))
200199
state = w.saveGeometryAndLayoutState()
201200
w1 = Widget()

Orange/widgets/widget.py

Lines changed: 77 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,11 @@
99
from AnyQt.QtWidgets import (
1010
QWidget, QDialog, QVBoxLayout, QSizePolicy, QApplication, QStyle,
1111
QShortcut, QSplitter, QSplitterHandle, QPushButton, QStatusBar,
12-
QProgressBar, QAction
12+
QProgressBar, QAction, QWIDGETSIZE_MAX
1313
)
1414
from AnyQt.QtCore import (
15-
Qt, QByteArray, QDataStream, QBuffer, QSettings, QUrl, pyqtSignal as Signal
15+
Qt, QRect, QMargins, QByteArray, QDataStream, QBuffer, QSettings,
16+
QUrl, pyqtSignal as Signal
1617
)
1718
from AnyQt.QtGui import QIcon, QKeySequence, QDesktopServices
1819

@@ -235,12 +236,13 @@ def __new__(cls, *args, captionTitle=None, **kwargs):
235236
self.controlArea.setFocus(Qt.ActiveWindowFocusReason)
236237

237238
if self.__splitter is not None:
238-
self.__splitter.controlAreaVisibilityChanged.connect(
239-
self.storeControlAreaVisibility)
239+
self.__splitter.handleClicked.connect(
240+
self.__toggleControlArea
241+
)
240242
sc = QShortcut(
241243
QKeySequence(Qt.ControlModifier | Qt.ShiftModifier | Qt.Key_D),
242-
self)
243-
sc.activated.connect(self.__splitter.flip)
244+
self, autoRepeat=False)
245+
sc.activated.connect(self.__toggleControlArea)
244246
return self
245247

246248
# pylint: disable=super-init-not-called
@@ -268,8 +270,7 @@ def get_flags(cls):
268270
else Qt.Dialog | Qt.MSWindowsFixedSizeDialogHint)
269271

270272
class _Splitter(QSplitter):
271-
controlAreaVisibilityChanged = Signal(int)
272-
273+
handleClicked = Signal()
273274
def _adjusted_size(self, size_method):
274275
size = size_method(super())()
275276
height = max((size_method(self.widget(i))().height()
@@ -289,26 +290,11 @@ def createHandle(self):
289290
return self._Handle(
290291
self.orientation(), self, cursor=Qt.PointingHandCursor)
291292

292-
def flip(self):
293-
if self.count() == 1: # Prevent hiding control area by shortcut
294-
return
295-
self.setControlAreaVisible(not self.controlAreaVisible())
296-
297-
def setControlAreaVisible(self, visible):
298-
if self.controlAreaVisible() == visible:
299-
return
300-
self.setSizes([int(visible), 100000])
301-
self.controlAreaVisibilityChanged.emit(visible)
302-
self.updateGeometry()
303-
304-
def controlAreaVisible(self):
305-
return bool(self.sizes()[0])
306-
307293
class _Handle(QSplitterHandle):
308294
def mouseReleaseEvent(self, event):
309295
"""Resize on left button"""
310296
if event.button() == Qt.LeftButton:
311-
self.splitter().flip()
297+
self.splitter().handleClicked.emit()
312298
super().mouseReleaseEvent(event)
313299

314300
def mouseMoveEvent(self, event):
@@ -458,6 +444,71 @@ def _(val):
458444
margins.setBottom(sb.sizeHint().height())
459445
self.setContentsMargins(margins)
460446

447+
def __toggleControlArea(self):
448+
if self.__splitter is None or self.__splitter.count() < 2:
449+
return
450+
self.__setControlAreaVisible(not self.__splitter.sizes()[0])
451+
452+
def __setControlAreaVisible(self, visible):
453+
# type: (bool) -> None
454+
if self.__splitter is None or self.__splitter.count() < 2:
455+
return
456+
self.controlAreaVisible = visible
457+
splitter = self.__splitter # type: QSplitter
458+
w = splitter.widget(0)
459+
# Set minimum width to 1 (overrides minimumSizeHint) when control area
460+
# is not visible to allow the main area to shrink further. Reset the
461+
# minimum width with a 0 if control area is visible.
462+
w.setMinimumWidth(int(not visible))
463+
464+
sizes = splitter.sizes()
465+
current_size = sizes[0]
466+
if bool(current_size) == visible:
467+
return
468+
469+
current_width = w.width()
470+
geom = self.geometry()
471+
frame = self.frameGeometry()
472+
framemargins = QMargins(
473+
frame.left() - geom.left(),
474+
frame.top() - geom.top(),
475+
frame.right() - geom.right(),
476+
frame.bottom() - geom.bottom()
477+
)
478+
splitter.setSizes([int(visible), QWIDGETSIZE_MAX])
479+
if not self.isWindow() or \
480+
self.windowState() not in [Qt.WindowNoState, Qt.WindowActive]:
481+
# not a window or not in state where we can move move/resize
482+
return
483+
484+
# force immediate resize recalculation
485+
splitter.refresh()
486+
self.layout().invalidate()
487+
self.layout().activate()
488+
489+
if visible:
490+
# move left and expand by the exposing widget's width
491+
diffx = -w.width()
492+
diffw = w.width()
493+
else:
494+
# move right and shrink by the collapsing width
495+
diffx = current_width
496+
diffw = -current_width
497+
newgeom = QRect(
498+
geom.x() + diffx, geom.y(), geom.width() + diffw, geom.height()
499+
)
500+
# bound/move by available geometry
501+
bounds = QApplication.desktop().availableGeometry(self)
502+
bounds = bounds.adjusted(
503+
framemargins.left(), framemargins.top(),
504+
-framemargins.right(), -framemargins.bottom()
505+
)
506+
newsize = newgeom.size().boundedTo(bounds.size())
507+
newgeom = QRect(newgeom.topLeft(), newsize)
508+
newgeom.moveLeft(max(newgeom.left(), bounds.left()))
509+
newgeom.moveRight(min(newgeom.right(), bounds.right()))
510+
self.setGeometry(newgeom)
511+
461512
def save_graph(self):
462513
"""Save the graph with the name given in class attribute `graph_name`.
463514
@@ -476,15 +527,6 @@ def copy_to_clipboard(self):
476527
return
477528
ClipboardFormat.write_image(None, graph_obj)
478529

479-
def storeControlAreaVisibility(self, visible):
480-
self.controlAreaVisible = visible
481-
482-
# Set minimum width to 1 (overrides minimumSizeHint) when control area
483-
# is not visible to allow the main area to shrink further. Reset the minimum
484-
# width with a 0 if control area is visible.
485-
self.__splitter.widget(0).setMinimumWidth(int(not visible))
486-
self.updateGeometry()
487-
488530
def __restoreWidgetGeometry(self, geometry):
489531
# type: (bytes) -> bool
490532
def _fullscreen_to_maximized(geometry):
@@ -583,7 +625,7 @@ def showEvent(self, event):
583625
if self.save_position and not self.__was_restored:
584626
# Restore saved geometry on (first) show
585627
if self.__splitter is not None:
586-
self.__splitter.setControlAreaVisible(self.controlAreaVisible)
628+
self.__setControlAreaVisible(self.controlAreaVisible)
587629
if self.savedWidgetGeometry is not None:
588630
self.__restoreWidgetGeometry(bytes(self.savedWidgetGeometry))
589631
self.__was_restored = True
@@ -830,7 +872,7 @@ def restoreGeometryAndLayoutState(self, state):
830872
has_spliter = splitter_state & 0x2
831873
splitter_state = splitter_state & 0x1
832874
if has_spliter and self.__splitter is not None:
833-
self.__splitter.setControlAreaVisible(bool(splitter_state))
875+
self.__setControlAreaVisible(bool(splitter_state))
834876
geometry = QByteArray()
835877
stream >> geometry
836878
if stream.status() == QDataStream.Ok:

0 commit comments

Comments
 (0)