fix(input): cursors follow the pointer while dragging and snap on drop

This commit is contained in:
Martino Ferrari committed 2026-09-28 20:46:14 +02:00
1 parent ea70cf4fca
commit d2ba67921e
7 files changed
+292 -27

No files matched your search

+37
View File
@@ -552,6 +552,43 @@ PLOT_TEST(a_cursor_owns_only_the_coordinate_and_snap_of_its_orientation) {
PLOT_CHECK_NEAR(p.cursor(marker).x, 12.0, 1e-12);
}
/// The setter's two acts, and the explicit commit between them: `CursorPlacement::Free` stores
/// the coordinate verbatim even on a `NearestSample` cursor -- the value a drag writes under the
/// pointer -- and `snap_cursor()` is what later puts it on the grid. The default is the other
/// act (snapping now), so a programmatic placement is unchanged; a ruler's commit uses its own
/// rule, which is what makes the two orientations snap at the same moment of a gesture.
PLOT_TEST(free_placement_defers_the_snap_until_snap_cursor) {
plot::data::ArraySeries s(ramp(100), 0.0, 1.0); // samples at x = 0, 1, 2, ...
plot::Plot p;
p.set_viewport({0.0f, 0.0f, 800.0f, 400.0f});
const u32 ax = p.add_x_axis({"t", {0.0, 99.0}, false, plot::AxisPlace::Bottom, false});
const u32 ay = p.add_y_axis({"v", {0.0, 99.0}, false, plot::AxisPlace::Left, false});
p.add_trace({&s, plot::Color::hex(0xFFFFFF), 1.0f, ax, ay});
const u32 marker = p.add_cursor(12.0, plot::Color::hex(0xFFB020));
p.set_cursor_snap(marker, plot::SnapMode::NearestSample);
PLOT_CHECK_NEAR(p.cursor(marker).x, 12.0, 1e-12);
// Free: the value is kept, and it is on no sample of the grid.
p.set_cursor_x(marker, 12.6, plot::CursorPlacement::Free);
PLOT_CHECK_NEAR(p.cursor(marker).x, 12.6, 1e-12);
// The commit applies the cursor's own rule -- the nearest sample -- exactly once.
p.snap_cursor(marker);
PLOT_CHECK_NEAR(p.cursor(marker).x, 13.0, 1e-12);
// The default placement snaps at once, so the free act is an opt-in, not a new default.
p.set_cursor_x(marker, 20.6);
PLOT_CHECK_NEAR(p.cursor(marker).x, 21.0, 1e-12);
// A ruler's commit is its own rule: a Free y is kept, and `snap_cursor()` is a no-op on a
// `Free` ruler -- there is no sample grid a y coordinate could land on.
const u32 ruler = p.add_h_ruler(50.0, plot::Color::hex(0x35D0A8));
p.set_cursor_y(ruler, 33.37, plot::CursorPlacement::Free);
PLOT_CHECK_NEAR(p.cursor(ruler).y, 33.37, 1e-12);
p.snap_cursor(ruler);
PLOT_CHECK_NEAR(p.cursor(ruler).y, 33.37, 1e-12);
}
/// `NearestTick` lands on a tick of the ruler's own y axis, and `Free` stores the value it was
/// handed -- so the two modes are told apart by what the store holds, not by a flag. The tick set
/// is the one `layout()` refreshed: ticks are generated there and nowhere else, which is why a