// SPDX-License-Identifier: MIT // SPDX-FileCopyrightText: 2026 Martino Ferrari #pragma once #include #include namespace plot::qt { /// The cached pointer position a move event's movement is measured from, in /// **logical** widget pixels. /// /// Logical, not device, pixels on purpose. Qt reports every widget position in /// logical pixels, and the device-pixel ratio can change between two move events /// -- the window being dragged onto a monitor with another scale factor is the /// case -- so an anchor cached in device pixels would compare a position computed /// at the old ratio with one computed at the new one. At 1.0 -> 1.25 a logical /// position of 500 would then look like a jump of 125 device pixels, and /// `Controller::pan` applies a delta as a fraction of the range span (and clears /// auto scale), so a scale change mid-drag would pan the plot by 125 pixels' worth /// of data. Keeping the anchor logical and scaling the *difference* by the ratio /// in effect now makes the same gesture map to the same device distance whatever /// the window's scale is. /// /// A plain value with no toolkit dependency: the widget owns one, and this is the /// part of the adapter's translation a test can drive directly (see /// tests/test_qt_pointer.cpp). struct PointerAnchor { bool have = false; ///< false until the first sample, which has no movement f32 x = 0.0f; ///< last logical position, widget pixels f32 y = 0.0f; /// Movement from the cached position to `logical`, in device pixels at `dpr`. /// Zero before the first sample, so the first motion of a drag never includes /// the jump to where the drag began. Vec2 device_delta(Vec2 logical, f64 dpr) const noexcept { if (!have) return Vec2{0.0f, 0.0f}; return Vec2{static_cast((static_cast(logical.x) - static_cast(x)) * dpr), static_cast((static_cast(logical.y) - static_cast(y)) * dpr)}; } /// Caches `logical` as the position the next movement is measured from. Always /// logical pixels, whatever the ratio is: see the type's comment. void set(Vec2 logical) noexcept { x = logical.x; y = logical.y; have = true; } }; } // namespace plot::qt