From 64058ae6c8a1a84f0e05d345584842230cf1e3da Mon Sep 17 00:00:00 2001 From: Dawn Date: Wed, 19 Aug 2026 16:56:53 +0200 Subject: [PATCH] feat: fold expanded help cheatsheet when mouse leaves the box Click is no longer the only way back to the "?" badge: moving the pointer off the expanded box, or out of the widget entirely, folds it. Checked before the interaction gate so it works in viewing mode too. Co-Authored-By: Claude Fable 5 --- src/aare/gui/widgets/camera_image.py | 18 ++++++++++++++++++ tests/unit/gui/test_camera_image.py | 23 +++++++++++++++++++++++ 2 files changed, 41 insertions(+) diff --git a/src/aare/gui/widgets/camera_image.py b/src/aare/gui/widgets/camera_image.py index d02a0285..c13ff300 100644 --- a/src/aare/gui/widgets/camera_image.py +++ b/src/aare/gui/widgets/camera_image.py @@ -585,6 +585,11 @@ class SampleCameraImageLabel(QGraphicsView): if self._hover_pos is not None: self._hover_pos = None self.update() + if self._help_expanded: + # Pointer left the widget with the cheatsheet open — fold it, + # same as wandering off the box below. + self._help_expanded = False + self.update() super().leaveEvent(event) def mouseMoveEvent(self, event): @@ -593,6 +598,19 @@ class SampleCameraImageLabel(QGraphicsView): self._hover_pos = self.mapToScene(event.pos()) self.update() + # Expanded help folds when the pointer wanders off the box — click + # is not the only way back to the "?" badge. Before the gate: the + # badge is a pure UI affordance, live even in viewing mode. + if ( + self._help_expanded + and self._help_hit_rect is not None + and not self._help_hit_rect.contains( + QPointF(self.viewport().mapFrom(self, event.pos())) + ) + ): + self._help_expanded = False + self.update() + # Badge hover feedback must run BEFORE the interaction gate: the # badge is visible precisely when interaction is disabled. hovered = self._session_badge_rect is not None and self._session_badge_rect.contains( diff --git a/tests/unit/gui/test_camera_image.py b/tests/unit/gui/test_camera_image.py index 17b6fd23..68045ff5 100644 --- a/tests/unit/gui/test_camera_image.py +++ b/tests/unit/gui/test_camera_image.py @@ -114,6 +114,29 @@ def test_help_badge_click_toggles_cheatsheet(camera, qtbot): assert not camera._help_expanded +def test_help_overlay_folds_when_mouse_leaves_box(camera, qtbot): + camera.grab() # paint records the collapsed "?" badge hit rect + badge = camera._help_hit_rect + qtbot.mouseClick(camera.viewport(), Qt.MouseButton.LeftButton, pos=badge.center().toPoint()) + assert camera._help_expanded + camera.grab() # expanded paint records the full box rect + box = camera._help_hit_rect + + _mouse_move(camera, box.center().toPoint()) # inside the box: stays open + assert camera._help_expanded + + _mouse_move(camera, QPoint(int(box.left()) - 40, int(box.bottom()) + 40)) + assert not camera._help_expanded, "moving off the box must fold it back to the badge" + + # Leaving the widget entirely folds it too. + camera.grab() + badge = camera._help_hit_rect + qtbot.mouseClick(camera.viewport(), Qt.MouseButton.LeftButton, pos=badge.center().toPoint()) + assert camera._help_expanded + camera.leaveEvent(QEvent(QEvent.Type.Leave)) + assert not camera._help_expanded + + def test_camera_error_message_rewords_and_draws(camera): camera.set_camera_available(False) camera.set_camera_error_message("Sample camera feed unavailable: cable unplugged")