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 <noreply@anthropic.com>
This commit is contained in:
duan_jandClaude Fable 5 committed 2026-08-08 15:09:30 +02:00
1 parent d5c9ed7467
commit 90ba13ad23
2 files changed
+44

No files matched your search

+43
View File
@@ -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
+1
View File
@@ -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