fix(navigator): zoom the document with the mouse wheel over the preview (#2724)

* fix(navigator): zoom with wheel over the preview (#2716)

* rustfmt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Nigel Ihza Firdausy
2026-10-10 16:43:57 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 Brandon Thomas
parent cfe031f2a6
commit a28d786638
+51
View File
@@ -1627,6 +1627,20 @@ fn navigator(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
app.ui.views[idx].center = [d.x.clamp(0.0, size.width as f32), d.y.clamp(0.0, size.height as f32)];
app.ui.views[idx].fit_pending = false;
}
// The Navigator has its own zoom target: an unmodified wheel over the preview
// should zoom even when the document canvas is configured to scroll on wheel.
// Use the canvas wheel classifier to honour one ×1.1 step per notch without
// applying egui's smoothed wheel tail a second time.
if resp.hovered()
&& let Some(crate::wheel_nav::Wheel::Zoom(factor)) = crate::wheel_nav::read(&ctx, true)
{
let view = &mut app.ui.views[idx];
let next = crate::zoom_levels::clamp(view.zoom * factor, view.doc_size);
if next != view.zoom {
view.zoom = next;
view.fit_pending = false;
}
}
// Visible-area rectangle.
let v = app.ui.views[idx].clone();
let canvas = app.last_canvas_rect;
@@ -1660,6 +1674,43 @@ fn navigator(app: &mut PhotocraftApp, ui: &mut egui::Ui) {
}
}
#[cfg(test)]
mod navigator_wheel_tests {
use super::*;
use egui::{Event, Modifiers, MouseWheelUnit, RawInput, TouchPhase};
fn frame(app: &mut PhotocraftApp, ctx: &egui::Context, pointer: Pos2, wheel: f32) {
let mut events = vec![Event::PointerMoved(pointer)];
if wheel != 0.0 {
events.push(Event::MouseWheel { unit: MouseWheelUnit::Line, delta: vec2(0.0, wheel), phase: TouchPhase::Move, modifiers: Modifiers::NONE });
}
let mut out = ctx
.run_ui(RawInput { screen_rect: Some(Rect::from_min_size(Pos2::ZERO, vec2(430.0, 460.0))), events, ..Default::default() }, |ui| navigator(app, ui));
out.textures_delta.clear();
}
#[test]
fn navigator_wheel_changes_zoom_only_when_over_preview() {
let mut app = PhotocraftApp::new(photocraft_engine::Session::new(), crate::Services::default());
app.run("file.new", json!({"width": 200, "height": 100})).unwrap();
app.sync_views();
app.ui.views[0].zoom = 1.0;
app.ui.views[0].fit_pending = false;
let ctx = egui::Context::default();
PhotocraftApp::setup_context(&ctx, crate::theme::ThemeKind::ALL[0]);
crate::wheel_nav::configure(&ctx);
frame(&mut app, &ctx, pos2(50.0, 55.0), 0.0);
let center = app.ui.views[0].center;
frame(&mut app, &ctx, pos2(50.0, 55.0), 1.0);
assert!((app.ui.views[0].zoom - 1.1).abs() < 1e-4, "one wheel notch zooms once");
assert_eq!(app.ui.views[0].center, center, "Navigator wheel must not pan");
frame(&mut app, &ctx, pos2(50.0, 55.0), -1.0);
assert!((app.ui.views[0].zoom - 1.0).abs() < 1e-4);
frame(&mut app, &ctx, pos2(50.0, 400.0), 1.0);
assert!((app.ui.views[0].zoom - 1.0).abs() < 1e-4, "wheel outside preview leaves zoom unchanged");
}
}
fn empty(ui: &mut egui::Ui, s: &str) {
let t = Tokens::get(ui.ctx());
ui.add_space(6.0);