feat(ui): trackpad pan with two fingers, zoom with pinch (#386)
Two-finger scroll pans the model instead of zooming; pinch-to-zoom zooms toward the cursor and honors reverse zoom. Trackpad scrolls arrive bracketed (Start..End, momentum included) while mouse wheels send bare moves, so phases tell them apart: bracketed gestures pan, bare vertical input zooms and bare horizontal input pans (thumb wheel). Mouse line/page input keeps zooming, and ui.scroll now sends line input so scripts and agents are unaffected. New Navigation preference 'Two-finger scroll pans' (default on) restores the old behaviour when off.
This commit is contained in:
1 parent
829912c6c5
commit
3c94f59d43
5 files changed
+351
-42
No files matched your search
@@ -194,6 +194,8 @@ pub struct Prefs {
|
||||
pub zoom_to_cursor: bool,
|
||||
/// Spread discrete wheel input across frames (off: apply each event immediately).
|
||||
pub smooth_wheel_zoom: bool,
|
||||
/// Two-finger trackpad scroll pans (off: it zooms like the wheel); pinch always zooms.
|
||||
pub trackpad_pan: bool,
|
||||
/// View changes (axis view, Look At, Home) animate; `look_at_speed` scales how fast.
|
||||
pub look_at_anim: bool,
|
||||
pub look_at_speed: f32,
|
||||
@@ -221,6 +223,7 @@ impl Default for Prefs {
|
||||
ui_scale: 1.0,
|
||||
zoom_to_cursor: true,
|
||||
smooth_wheel_zoom: true,
|
||||
trackpad_pan: true,
|
||||
look_at_anim: true,
|
||||
look_at_speed: 1.0,
|
||||
up_axis: "z".into(),
|
||||
@@ -322,6 +325,7 @@ fn defaults(app: &mut SolveApp, page: usize) {
|
||||
p.zoom_reverse = d.zoom_reverse;
|
||||
p.zoom_to_cursor = d.zoom_to_cursor;
|
||||
p.smooth_wheel_zoom = d.smooth_wheel_zoom;
|
||||
p.trackpad_pan = d.trackpad_pan;
|
||||
p.orbit_cursor = d.orbit_cursor;
|
||||
p.space_mouse_enabled = d.space_mouse_enabled;
|
||||
p.space_mouse_speed = d.space_mouse_speed;
|
||||
@@ -815,6 +819,11 @@ fn page(app: &mut SolveApp, ui: &mut egui::Ui, t: &Tokens) {
|
||||
ui.checkbox(&mut app.preferences.smooth_wheel_zoom, "")
|
||||
.on_hover_text("Turn off to apply each wheel event immediately, without a smoothing tail");
|
||||
ui.end_row();
|
||||
ui.label("Two-finger scroll pans");
|
||||
ui.checkbox(&mut app.preferences.trackpad_pan, "").on_hover_text(
|
||||
"Trackpad gestures pan (off: they zoom like the wheel); mouse wheel zooms, thumb wheel pans sideways, pinch always zooms",
|
||||
);
|
||||
ui.end_row();
|
||||
ui.label("Orbit around");
|
||||
ui.horizontal(|ui| {
|
||||
ui.radio_value(&mut app.preferences.orbit_cursor, false, "View centre");
|
||||
@@ -1119,6 +1128,26 @@ mod tests {
|
||||
assert_eq!(other.preferences.nav, OPENSCAD_NAV);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn trackpad_pan_defaults_on_persists_and_follows_dialog_buttons() {
|
||||
assert!(serde_json::from_value::<Prefs>(json!({})).unwrap().trackpad_pan);
|
||||
assert!(Prefs::default().trackpad_pan);
|
||||
let mut app = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
control(&mut app, &json!({"open":"Navigation", "trackpad_pan":false})).unwrap();
|
||||
control(&mut app, &json!({"button":"cancel"})).unwrap();
|
||||
assert!(app.preferences.trackpad_pan);
|
||||
control(&mut app, &json!({"open":"Navigation", "trackpad_pan":false})).unwrap();
|
||||
control(&mut app, &json!({"button":"apply"})).unwrap();
|
||||
assert!(!app.preferences.trackpad_pan);
|
||||
let mut other = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
other.load_prefs(&app.prefs());
|
||||
assert!(!other.preferences.trackpad_pan);
|
||||
control(&mut app, &json!({"open":"Navigation", "button":"defaults"})).unwrap();
|
||||
assert!(app.preferences.trackpad_pan);
|
||||
control(&mut app, &json!({"button":"cancel"})).unwrap();
|
||||
assert!(!app.preferences.trackpad_pan);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn presets_map_the_mouse_and_prefs_round_trip() {
|
||||
let mut app = SolveApp::new(solvecraft_engine::Session::default(), Default::default());
|
||||
|
||||
Reference in new issue
Block a user