diff --git a/src/input/controller.cpp b/src/input/controller.cpp index 05f4b37..9eaaa30 100644 --- a/src/input/controller.cpp +++ b/src/input/controller.cpp @@ -323,17 +323,27 @@ void Controller::on_key(const KeyEvent& e) { if (zooms) { const f64 notches = e.key == kKeyZoomIn ? kKeyZoomNotches : -kKeyZoomNotches; const f64 factor = zoom_factor(notches); - for (u32 i = 0; i < plot_->x_axis_count(); ++i) { - const Axis& a = plot_->x_axis(i); - const f64 lo = to_space(a.range.min, a.log); - const f64 hi = to_space(a.range.max, a.log); - zoom_x_axis(i, from_space(0.5 * (lo + hi), a.log), factor); + // Ctrl restricts the zoom to x, Alt to y; bare +/- zooms both. (Shift cannot be + // used: '+' is shift+'=' on common layouts, so key_from() folds them together and + // a bare zoom-in already arrives with Mod::Shift set.) Ctrl+Alt zooms neither, + // which is harmless and needs no special case. + const bool only_x = has_mod(e.mods, Mod::Ctrl); + const bool only_y = has_mod(e.mods, Mod::Alt); + if (!only_y) { + for (u32 i = 0; i < plot_->x_axis_count(); ++i) { + const Axis& a = plot_->x_axis(i); + const f64 lo = to_space(a.range.min, a.log); + const f64 hi = to_space(a.range.max, a.log); + zoom_x_axis(i, from_space(0.5 * (lo + hi), a.log), factor); + } } - for (u32 i = 0; i < plot_->y_axis_count(); ++i) { - const Axis& a = plot_->y_axis(i); - const f64 lo = to_space(a.range.min, a.log); - const f64 hi = to_space(a.range.max, a.log); - zoom_y_axis(i, from_space(0.5 * (lo + hi), a.log), factor); + if (!only_x) { + for (u32 i = 0; i < plot_->y_axis_count(); ++i) { + const Axis& a = plot_->y_axis(i); + const f64 lo = to_space(a.range.min, a.log); + const f64 hi = to_space(a.range.max, a.log); + zoom_y_axis(i, from_space(0.5 * (lo + hi), a.log), factor); + } } } else if (fits) { plot_->auto_fit_x(); diff --git a/tests/test_controller.cpp b/tests/test_controller.cpp index 804afd8..275f6c5 100644 --- a/tests/test_controller.cpp +++ b/tests/test_controller.cpp @@ -263,6 +263,27 @@ PLOT_TEST(key_minus_zooms_out) { PLOT_CHECK(f.plot.x_range(f.ax).span() > 100.0); } +PLOT_TEST(ctrl_plus_zooms_only_x) { + Fixture f; + f.ay = f.plot.add_y_axis({"v", {-1.0, 1.0}, false, plot::AxisPlace::Left, false}); + const f64 x_before = f.plot.x_range(f.ax).span(); + const f64 y_before = f.plot.y_range(f.ay).span(); + f.controller.on_key({plot::input::kKeyZoomIn, kCtrl, true}); + PLOT_CHECK(f.plot.x_range(f.ax).span() < x_before); + PLOT_CHECK_NEAR(f.plot.y_range(f.ay).span(), y_before, 1e-12); +} + +PLOT_TEST(alt_plus_zooms_only_y) { + Fixture f; + f.ay = f.plot.add_y_axis({"v", {-1.0, 1.0}, false, plot::AxisPlace::Left, false}); + constexpr plot::u32 kAlt = static_cast(plot::input::Mod::Alt); + const f64 x_before = f.plot.x_range(f.ax).span(); + const f64 y_before = f.plot.y_range(f.ay).span(); + f.controller.on_key({plot::input::kKeyZoomIn, kAlt, true}); + PLOT_CHECK_NEAR(f.plot.x_range(f.ax).span(), x_before, 1e-12); + PLOT_CHECK(f.plot.y_range(f.ay).span() < y_before); +} + /// A host can hand the controller a coordinate that is not a number: ImGui reports /// (-FLT_MAX, -FLT_MAX) for "no mouse position", and any host can produce a NaN out /// of a degenerate mapping of its own. The pan arithmetic runs through the axis