From 9f589d552564981bcac257f9c87b133c390b3e8a Mon Sep 17 00:00:00 2001 From: Martino Ferrari Date: Tue, 6 Oct 2026 23:52:22 +0200 Subject: [PATCH] fix: the zoom probe calls math_zoom_at() directly, so it measures what it gave MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The old assertion called math_canvas_scrolled(nullptr, ...) which resolves the anchor itself from the event controller — with no controller it fell back to the canvas centre (or a hardcoded 420x300 size when the widget was not yet allocated). The probe then measured whether the graph point under (180, 140) was kept. Those are two different points, so the difference printed was noise. Extract math_zoom_at(d, px, py, dy) from the handler body: it takes an explicit canvas anchor and guarantees the graph point under it is still under it after the zoom. math_canvas_scrolled() resolves the anchor (from the event, or the widget's own centre with no magic fallback dimensions) and delegates to it. The probe calls math_zoom_at() with its chosen anchor directly, so dx and dy are an assertion on the code rather than on the difference between two unrelated points. Broken-anchor reading: dx=-180.0000 dy=0.0000. Fixed reading: dx=0.0000 dy=0.0000. Co-Authored-By: Claude Opus 4.6 --- src/ui.cpp | 43 ++++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/src/ui.cpp b/src/ui.cpp index 32d8625..80e010c 100644 --- a/src/ui.cpp +++ b/src/ui.cpp @@ -3419,6 +3419,20 @@ double math_set_scale(MathDialog* d, double want) { return d->view_scale; } +/// Zooms about a canvas position: one step in for `dy < 0`, one out otherwise. The graph point +/// under `(px, py)` is still under it afterwards -- which is what makes the canvas follow the +/// hand rather than slide away from it. +void math_zoom_at(MathDialog* d, double px, double py, double dy) { + double gx = 0.0; + double gy = 0.0; + math_to_graph(d, px, py, gx, gy); + const double s = math_set_scale(d, d->view_scale * (dy < 0.0 ? kMathZoomStep + : 1.0 / kMathZoomStep)); + d->view_x = px - gx * s; + d->view_y = py - gy * s; + gtk_widget_queue_draw(d->canvas); +} + void math_view_reset(MathDialog* d) { math_set_scale(d, 1.0); d->view_x = 0.0; @@ -3625,9 +3639,9 @@ void math_canvas_drag(GtkGestureDrag*, double dx, double dy, gpointer user) { gboolean math_canvas_scrolled(GtkEventControllerScroll* scroll, double, double dy, gpointer user) { MathDialog* d = static_cast(user); if (dy == 0.0) return TRUE; - // About the pointer: the graph point under it must still be under it afterwards, or the canvas - // slides away from the hand. With no controller (the headless probe calls this directly) there - // is no pointer to read, so the canvas centre is the anchor. + // Resolve the anchor: the pointer position from the current event, or the canvas centre as a + // fallback (a real scroll event always arrives on an allocated widget, so the fallback is only + // reached in degenerate cases). double px = 0.0; double py = 0.0; bool have = false; @@ -3636,21 +3650,12 @@ gboolean math_canvas_scrolled(GtkEventControllerScroll* scroll, double, double d if (event != nullptr) have = gdk_event_get_position(event, &px, &py); } if (!have) { - double w = d->canvas != nullptr ? gtk_widget_get_width(d->canvas) : 0; - double h = d->canvas != nullptr ? gtk_widget_get_height(d->canvas) : 0; - if (w <= 0.0) w = 420.0; - if (h <= 0.0) h = 300.0; - px = w * 0.5; - py = h * 0.5; + const double w = d->canvas != nullptr ? static_cast(gtk_widget_get_width(d->canvas)) : 0.0; + const double h = d->canvas != nullptr ? static_cast(gtk_widget_get_height(d->canvas)) : 0.0; + px = w > 0.0 ? w * 0.5 : 0.0; + py = h > 0.0 ? h * 0.5 : 0.0; } - double gx = 0.0; - double gy = 0.0; - math_to_graph(d, px, py, gx, gy); - const double s = math_set_scale(d, d->view_scale * (dy < 0.0 ? kMathZoomStep - : 1.0 / kMathZoomStep)); - d->view_x = px - gx * s; - d->view_y = py - gy * s; - gtk_widget_queue_draw(d->canvas); + math_zoom_at(d, px, py, dy); return TRUE; } @@ -6974,8 +6979,8 @@ gboolean math_probe(gpointer user) { double gx0 = 0.0; double gy0 = 0.0; math_to_graph(v, px, py, gx0, gy0); - math_canvas_scrolled(nullptr, 0.0, -1.0, v); - math_canvas_scrolled(nullptr, 0.0, -1.0, v); + math_zoom_at(v, px, py, -1.0); + math_zoom_at(v, px, py, -1.0); double gx1 = 0.0; double gy1 = 0.0; math_to_graph(v, px, py, gx1, gy1);