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);