From f300321e282d5986d40d0f5e4507504406ea5a4d Mon Sep 17 00:00:00 2001 From: bflatastic Date: Sat, 10 Oct 2026 00:51:18 +0700 Subject: [PATCH] Part of #793: Artboard Options lists Name, Width, Height; one-key shortcuts follow the key on any layout MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Artboard Options listed its fields by key (Height, Name, Width); it now lists Name, then Width and Height. - One-key tool and command shortcuts (V, P, X, D…) matched the character typed, so with a layout without Latin letters (Persian, Arabic, Russian, Greek…) they did nothing. When the text typed holds no ASCII character, the key's position stands in: the V key selects the Selection tool whatever it types. Latin layouts (AZERTY…) keep the letter typed. Co-Authored-By: Claude Opus 5.5 --- crates/ui-egui/src/dialogs/form.rs | 1 + crates/ui-egui/src/dialogs/tests.rs | 11 ++++++++ crates/ui-egui/src/shortcuts.rs | 41 +++++++++++++++++++++++++++-- 3 files changed, 51 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/dialogs/form.rs b/crates/ui-egui/src/dialogs/form.rs index cc5e106e..73ee6cad 100644 --- a/crates/ui-egui/src/dialogs/form.rs +++ b/crates/ui-egui/src/dialogs/form.rs @@ -67,6 +67,7 @@ fn order(kind: &str) -> &'static [&'static str] { match kind { "offsetPath" => &["offset", "joins", "miterLimit"], "splitIntoGrid" => &["rows", "columns", "gutter"], + "artboardOptions" => &["name", "width", "height"], _ => &[], } } diff --git a/crates/ui-egui/src/dialogs/tests.rs b/crates/ui-egui/src/dialogs/tests.rs index 52015f88..2ca0495a 100644 --- a/crates/ui-egui/src/dialogs/tests.rs +++ b/crates/ui-egui/src/dialogs/tests.rs @@ -370,3 +370,14 @@ fn a_dialog_opens_with_its_first_field_focused_so_typing_and_enter_apply() { let after = bounds(&app); assert!((after.x0 - before.x0 - 15.0).abs() < 1e-6 && (after.y0 - before.y0).abs() < 1e-6, "{before:?} → {after:?}"); } + +/// #793: Artboard Options lists its fields as Name, Width, Height, not by key. +#[test] +fn artboard_options_lists_name_then_width_and_height() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 612, "height": 792})).unwrap(); + open(&mut app, "artboardOptions", json!({"index": 0, "name": "Artboard 1", "width": 612, "height": 792, "x": 0, "y": 0})); + let text = crate::tests_labels::painted_text(&mut app, |app, ui| show(app, ui.ctx())); + let at = |s: &str| text.find(s).unwrap_or_else(|| panic!("{s} in {text}")); + assert!(at("Name") < at("Width") && at("Width") < at("Height"), "{text}"); +} diff --git a/crates/ui-egui/src/shortcuts.rs b/crates/ui-egui/src/shortcuts.rs index dbdb941d..dc25ceb6 100644 --- a/crates/ui-egui/src/shortcuts.rs +++ b/crates/ui-egui/src/shortcuts.rs @@ -414,12 +414,18 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { { let _ = app.run("edit.clear", json!({})); } - // Single-key tool shortcuts (no Cmd/Ctrl/Alt). + // Single-key tool shortcuts (no Cmd/Ctrl/Alt). With a layout without Latin letters (Persian, + // Arabic, Russian, Greek…) the key's position stands in for what it types (#793). let events: Vec<(String, Modifiers)> = ctx.input(|i| { + let mut physical = None; i.events .iter() .filter_map(|e| match e { - egui::Event::Text(t) => Some((t.clone(), i.modifiers)), + egui::Event::Key { physical_key, pressed: true, .. } => { + physical = *physical_key; + None + } + egui::Event::Text(t) => Some((latin(t, physical), i.modifiers)), _ => None, }) .collect() @@ -445,6 +451,16 @@ pub fn handle(app: &mut VectorcraftApp, ctx: &egui::Context) { } } +/// What a single-key shortcut reads for `text`, typed by the key at `physical`: the text itself, +/// or, when it holds no ASCII character (a layout without Latin letters), what that key types on a +/// US layout, so V selects the Selection tool whatever the V key types. +fn latin(text: &str, physical: Option) -> String { + match physical { + Some(k) if !text.is_ascii() => k.symbol_or_name().to_lowercase(), + _ => text.to_string(), + } +} + /// The digit a number-row or keypad key types. fn digit_of(k: Key) -> Option { const DIGITS: [Key; 10] = [Key::Num0, Key::Num1, Key::Num2, Key::Num3, Key::Num4, Key::Num5, Key::Num6, Key::Num7, Key::Num8, Key::Num9]; @@ -678,6 +694,27 @@ mod tests { assert_eq!(app.session.tool_id(), "curvature"); } + /// #793: with a layout without Latin letters the one-key shortcuts go by the key's position: + /// the P key typing Persian `ح` chooses the Pen, the V key typing Russian `м` the Selection + /// tool. A Latin layout keeps the letter typed: AZERTY's A (the Q key's place) is A. + #[test] + fn one_key_shortcuts_follow_the_key_on_layouts_without_latin_letters() { + let mut app = VectorcraftApp::new(vectorcraft_engine::Session::new(), Default::default()); + app.session.execute("file.new", &json!({"width": 200, "height": 200})).unwrap(); + let typed = |key, physical, text: &str| { + vec![ + egui::Event::Key { key, physical_key: Some(physical), pressed: true, repeat: false, modifiers: Modifiers::NONE }, + egui::Event::Text(text.into()), + ] + }; + frame(&mut app, typed(Key::P, Key::P, "ح")); + assert_eq!(app.session.tool_id(), "pen"); + frame(&mut app, typed(Key::V, Key::V, "м")); + assert_eq!(app.session.tool_id(), "selection"); + frame(&mut app, typed(Key::A, Key::Q, "a")); + assert_eq!(app.session.tool_id(), "directSelection", "AZERTY: the letter typed"); + } + #[test] fn plus_typed_any_way_zooms_in() { assert_eq!(parse("Cmd++"), parse("Cmd+="), "`+` and `=` are one chord key");