From d89535236b30909d4c948a7d6d83491ee252820f Mon Sep 17 00:00:00 2001 From: ykcheungab Date: Sun, 18 Oct 2026 21:54:11 +0800 Subject: [PATCH] Sync the replay chart cursor from every subplot, not only FN Each subplot carries its own copy of the shared time cursor, but the drag handler always read the first copy's value, so dragging FX or FY re-read FN's unchanged position and returned without moving the others. The handler now uses the line the signal names. Verified by dragging all three cursors in the offscreen UI test and by the full studio suite (569 tests, 0 failed). --- .../studio/src/orisys_studio/ui/force_plots.py | 18 +++++++++++++++--- apps/studio/tests/test_ui_smoke.py | 15 +++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/apps/studio/src/orisys_studio/ui/force_plots.py b/apps/studio/src/orisys_studio/ui/force_plots.py index 2b4c7a7..ec8e651 100644 --- a/apps/studio/src/orisys_studio/ui/force_plots.py +++ b/apps/studio/src/orisys_studio/ui/force_plots.py @@ -1027,11 +1027,23 @@ class ForcePlotsPanel(QFrame): for cursor in self.cursors: cursor.setMovable(bool(browsing)) - def _on_cursor_dragged(self) -> None: - """The shared cursor is a timeline control while browsing.""" + def _on_cursor_dragged(self, cursor=None) -> None: + """The shared cursor is a timeline control while browsing. + + Each subplot carries its own copy of the same cursor and any of them can be + dragged, so the signal names the one that moved. Reading ``cursors[0]`` instead + made only the first subplot work: dragging FX or FY re-read FN's already-current + value, judged it unchanged and returned, leaving the other two subplots behind. + """ if self._syncing or self._mode != "browse": return - value = float(self.cursors[0].value()) + # ``sigPositionChanged`` emits the line that moved; ``sender()`` covers a + # pyqtgraph that emits no argument. A direct call with neither falls back to the + # first subplot, which is the only cursor a caller could mean. + line = cursor if cursor is not None else self.sender() + if line is None: + line = self.cursors[0] + value = float(line.value()) if value == self._cursor_t: return self.pause_playback() diff --git a/apps/studio/tests/test_ui_smoke.py b/apps/studio/tests/test_ui_smoke.py index b63aae4..6016f23 100644 --- a/apps/studio/tests/test_ui_smoke.py +++ b/apps/studio/tests/test_ui_smoke.py @@ -1452,6 +1452,21 @@ def test_the_charts_zoom_and_pan_in_every_view(make_window): QApplication.processEvents() assert [round(cursor.value(), 2) for cursor in panel.cursors] == [1.25] * 3 assert moved and moved[-1] == 1.25, "the cursor drag did not notify the replay" + + # Every subplot carries the same cursor, so every one of them is the control. + # Dragging FX or FY used to re-read FN's already-current value, judge it unchanged and + # return — only the first subplot moved the other two. + for index in (1, 2): + moved.clear() + target = round(1.5 + index * 0.1, 2) + panel.cursors[index].setValue(target) + QApplication.processEvents() + assert [round(cursor.value(), 2) for cursor in panel.cursors] == [target] * 3, ( + f"dragging subplot {index}'s cursor did not move the other two" + ) + assert moved and round(moved[-1], 2) == target, ( + f"dragging subplot {index}'s cursor did not notify the replay" + ) _finish(window, stuck)