// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari // The Qt adapter's pointer translation, driven directly. The widget's cached // pointer anchor is a plain value with no toolkit dependency (see // adapters/qt6/qt_pointer.hpp) precisely so the one thing a screenshot cannot show // -- which pixel space a drag delta is in -- is testable without a QApplication, a // window or a display. #include "harness.hpp" #include #include // The defect this pins: the anchor used to be cached in *device* pixels while the // next event's delta was a raw difference of device positions, so a DPR change // between two move events -- the window dragged onto a monitor with a different // scale factor mid-drag -- mixed two pixel spaces. At 1.0 -> 1.25 a logical // position of 500 produced a spurious 125 device-pixel delta, which // `Controller::pan` applies as a fraction of the range span and which also clears // auto scale: a scale change mid-drag would move the plot by 125 pixels' worth of // data and silently disarm a live axis. PLOT_TEST(pointer_anchor_survives_a_dpr_change_between_move_events) { plot::qt::PointerAnchor anchor; // The press caches a logical position while the window is on the 1.0 display. anchor.set({500.0f, 300.0f}); // The next move arrives after the window moved to the 1.25 display, at the same // logical position: the pointer has not moved, so the delta must be zero -- not // 500 * 1.25 - 500 * 1.0 = 125 device pixels, which is what comparing two device // positions across the scale change reported. const plot::Vec2 still = anchor.device_delta({500.0f, 300.0f}, 1.25); PLOT_CHECK_NEAR(still.x, 0.0, 1e-6); PLOT_CHECK_NEAR(still.y, 0.0, 1e-6); // A real movement is the logical delta scaled by the ratio in effect now, in both // directions. const plot::Vec2 moved = anchor.device_delta({503.0f, 295.0f}, 1.25); PLOT_CHECK_NEAR(moved.x, 3.0 * 1.25, 1e-6); PLOT_CHECK_NEAR(moved.y, -5.0 * 1.25, 1e-6); // And the anchor follows the sample it just reported: the following move is // measured from there, at whatever ratio that event carries. anchor.set({503.0f, 295.0f}); const plot::Vec2 unchanged = anchor.device_delta({503.0f, 295.0f}, 1.0); PLOT_CHECK_NEAR(unchanged.x, 0.0, 1e-6); PLOT_CHECK_NEAR(unchanged.y, 0.0, 1e-6); } // The first sample of a gesture has no anchor to measure from, so its delta is // zero however far the position is from the origin: the first motion of a drag // must never include the jump to where the drag began. PLOT_TEST(pointer_anchor_reports_no_movement_before_its_first_sample) { plot::qt::PointerAnchor anchor; const plot::Vec2 delta = anchor.device_delta({500.0f, 300.0f}, 1.25); PLOT_CHECK_NEAR(delta.x, 0.0, 1e-9); PLOT_CHECK_NEAR(delta.y, 0.0, 1e-9); }