From ecd900ab375cc75b533f29021a6e60c2181c8385 Mon Sep 17 00:00:00 2001 From: Trevor Brown Date: Sun, 1 Nov 2026 23:47:48 -0500 Subject: [PATCH] Control channel: ui.key arrows pan the view when nothing is selected, as the window does Co-Authored-By: Claude Fable 5.1 --- crates/ui-egui/src/control.rs | 5 ++++- crates/ui-egui/src/inspect_fields_tests.rs | 25 ++++++++++++++++++++++ 2 files changed, 29 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/control.rs b/crates/ui-egui/src/control.rs index fd5a2a4e9..aa79bec78 100644 --- a/crates/ui-egui/src/control.rs +++ b/crates/ui-egui/src/control.rs @@ -996,7 +996,10 @@ fn ui_key(app: &mut FigApp, ctx: &Context, p: &Value) -> Result { "ArrowUp" => (0, -1), _ => (0, 1), }; - let (cmd, params) = if m.cmd || m.ctrl { + // With nothing selected the arrows pan the view, as the window's own key path does. + let (cmd, params) = if app.session.selection().is_empty() { + ("view.pan", json!({"dx": dx, "dy": dy, "big": m.shift})) + } else if m.cmd || m.ctrl { ("transform.nudgeSize", json!({"dw": dx, "dh": dy, "big": m.shift})) } else { ("transform.nudge", json!({"dx": dx, "dy": dy, "big": m.shift})) diff --git a/crates/ui-egui/src/inspect_fields_tests.rs b/crates/ui-egui/src/inspect_fields_tests.rs index 102e232b3..9ea3223fd 100644 --- a/crates/ui-egui/src/inspect_fields_tests.rs +++ b/crates/ui-egui/src/inspect_fields_tests.rs @@ -46,3 +46,28 @@ fn control_inspection_rejects_invalid_options_consistently() { } } } + +/// `ui.key` with an arrow and nothing selected pans the view, as the window's own key path does +/// (before, it answered that nothing was selected); with a selection it nudges (Shift: farther). +#[test] +fn control_key_arrows_pan_with_nothing_selected() { + let mut u = Ui::new(Session::new()); + let r = + call(&mut u, "engine.execute", json!({"command": "shape.rectangle", "params": {"x": 0, "y": 0, "width": 40, "height": 40}})).unwrap()["id"] + .clone(); + u.app.run("select.set", json!({"ids": []})); + u.app.run("view.set", json!({"zoom": 2.0, "cx": 0, "cy": 0})); + let view = |u: &Ui| { + let v = &u.app.session.ds().unwrap().view; + (v.cx, v.cy) + }; + call(&mut u, "ui.key", json!({"key": "ArrowRight"})).unwrap(); + assert_eq!(view(&u), (10.0, 0.0), "20 screen pixels at 200%"); + call(&mut u, "ui.key", json!({"key": "ArrowUp", "shift": true})).unwrap(); + assert_eq!(view(&u), (10.0, -100.0), "Shift: ten times as far"); + u.app.run("select.set", json!({"ids": [r]})); + call(&mut u, "ui.key", json!({"key": "ArrowRight"})).unwrap(); + assert_eq!(view(&u), (10.0, -100.0), "with a selection the view stays"); + let id = figcraft_engine::params::id_of(&r).unwrap(); + assert_eq!(u.app.session.doc().unwrap().world_bounds(id).unwrap().x0, 1.0, "the selection moved instead"); +}