From 90ba13ad2347ec8e3113947a17ea32a95a028e5a Mon Sep 17 00:00:00 2001 From: Dawn Date: Sat, 8 Aug 2026 15:09:30 +0200 Subject: [PATCH] feat: rest-to-reveal hint for dock separators Separators stay invisible until the mouse rests on one for ~1s (or a drag starts); the QSS ::separator:hover rule does the hit-testing and a dynamic separatorHint property gates it, so event() never needs to know where separators are. Co-Authored-By: Claude Fable 5 --- src/aare/gui/main_window.py | 43 +++++++++++++++++++++++++++++++++++++ src/aare/gui/styles.py | 1 + 2 files changed, 44 insertions(+) diff --git a/src/aare/gui/main_window.py b/src/aare/gui/main_window.py index 90e2521c..781f1b49 100644 --- a/src/aare/gui/main_window.py +++ b/src/aare/gui/main_window.py @@ -87,6 +87,7 @@ from aare.gui.styles import ( APP_BACKGROUND, DARK_TEXT, DOCK_CONTENT_LEFT_PAD, + SEPARATOR_HINT_DELAY_MS, THEME_FADE_MS, THEME_ORIGINAL, THEME_PORTRAIT, @@ -251,6 +252,16 @@ class MainWindow(QMainWindow): self.setStyleSheet(f"background-color: {APP_BACKGROUND};") + # Resize-line hint (see SEPARATOR_HINT in styles.py): WA_Hover gives + # us HoverMove without a button held; the single-shot timer implements + # the "rested for 1s" gate. The QSS ::separator:hover rule does the + # hit-testing, so event() never needs to know where separators are. + self.setAttribute(Qt.WidgetAttribute.WA_Hover, True) + self._separator_hint_timer = QTimer(self) + self._separator_hint_timer.setSingleShot(True) + self._separator_hint_timer.setInterval(SEPARATOR_HINT_DELAY_MS) + self._separator_hint_timer.timeout.connect(lambda: self._set_separator_hint(True)) + root_widget = QWidget(parent=self) root_widget.setObjectName("mainContentRoot") root_layout = QVBoxLayout(root_widget) @@ -2871,6 +2882,38 @@ class MainWindow(QMainWindow): if dx or dy: dock.move(geo.x() + dx, geo.y() + dy) + def _set_separator_hint(self, on: bool) -> None: + if self.property("separatorHint") == on: + return + self.setProperty("separatorHint", on) + # Property selectors are only re-evaluated on repolish. + self.style().unpolish(self) + self.style().polish(self) + self.update() + + def event(self, event): + # Separator press/drag never reaches mousePressEvent — QMainWindow + # eats it inside event() to start the separator move — so the hint + # gate must sit here, before super() dispatches. + t = event.type() + if t in (QEvent.Type.HoverEnter, QEvent.Type.HoverMove): + # Any move restarts the 1s countdown ("hover and rest"); during a + # drag (button held) the hint stays on instead. + if QApplication.mouseButtons() == Qt.MouseButton.NoButton: + self._set_separator_hint(False) + self._separator_hint_timer.start() + elif t == QEvent.Type.HoverLeave: + self._separator_hint_timer.stop() + self._set_separator_hint(False) + elif t == QEvent.Type.MouseButtonPress: + # Press shows the line immediately, no 1s wait. + self._separator_hint_timer.stop() + self._set_separator_hint(True) + elif t == QEvent.Type.MouseButtonRelease: + self._set_separator_hint(False) + self._separator_hint_timer.start() + return super().event(event) + def eventFilter(self, obj, event): # Closing a floated (popped-out) dock re-docks it instead of hiding — # otherwise the panel silently disappears and has to be restored via diff --git a/src/aare/gui/styles.py b/src/aare/gui/styles.py index fb1cd382..c139ad6f 100644 --- a/src/aare/gui/styles.py +++ b/src/aare/gui/styles.py @@ -98,6 +98,7 @@ DOCK_CONTENT_LEFT_PAD = 10 # separator under the cursor fills with SEPARATOR_HINT. The rest/drag gate # lives in MainWindow.event(); the QSS :hover part picks the one separator. SEPARATOR_HINT = "rgba(168, 178, 192, 50%)" # scrollbar-track grey @50% +SEPARATOR_HINT_DELAY_MS = 888 # int, used in code, not QSS # Theme-switch screenshot cross-fade duration (int ms, used in code). THEME_FADE_MS = 250