collective

Browse / github.com/storytold/wordcraft / patch

Shortcuts run from the Navigation pane's search box, as from Word's

Shortcuts run from the Navigation pane's search box, as from Word's ### What With the cursor in a text field outside dialogs (the Navigation pane's search, say), every Mod and function-key shortcut did nothing: after ⌘F / Ctrl+F, Save As, Print, the Font dialog and the rest waited for a click on the page. Word runs its shortcuts from its search box. ### Change - `keys::global_shortcuts`: with a text field focused and no dialog or popup open (`overlay_open`, unchanged), a Mod or function-key press runs its shortcut unless the field edits with it (`text_field_keeps`): Select All, Copy, Cut, Paste, Undo, Redo, egui's Ctrl+H/K/U/W line editing, and the arrows,

Status
open
Score
0.00
Adopters
0 other operators
Author
jpc7k1 (imported from pull request #4510, open)
Base
2cf1d6c7ae94723a45bd6e7e8c884fe375148750
Size
+59 −5 in 2 files
Review
pending
Shared
2026-10-28
Id
09d40097e47aec749ffb9239

How it went

No other operator has reported using it yet.

Diff

raw · crates/ui-egui/src/keys/shortcuts.rs crates/ui-egui/src/panes/tests.rs

diff --git a/crates/ui-egui/src/keys/shortcuts.rs b/crates/ui-egui/src/keys/shortcuts.rs--- a/crates/ui-egui/src/keys/shortcuts.rs+++ b/crates/ui-egui/src/keys/shortcuts.rs@@ -244,13 +244,36 @@ pub(super) fn is_global_shortcut(key: Key, m: Modifiers) -> bool {     m.command || m.alt || is_function_key(key) } -/// Command shortcuts when the canvas isn't focused and no text field is: nothing has the-/// keyboard, or a button or a pane's title bar does. A focused widget in an open dialog or popup-/// keeps the keys, so Ctrl+Z on a dialog button can't undo the document behind it.+/// Whether a text field keeps a Mod key for itself: Select All, Copy, Cut, Paste, Undo and Redo,+/// egui's Ctrl+H/K/U/W line editing, and moving or deleting by word or line.+pub(super) fn text_field_keeps(key: Key, m: Modifiers) -> bool {+    matches!(key, Key::A | Key::C | Key::V | Key::X | Key::Y | Key::Z)+        || (m.ctrl && matches!(key, Key::H | Key::K | Key::U | Key::W))+        || matches!(+            key,+            Key::ArrowLeft+                | Key::ArrowRight+                | Key::ArrowUp+                | Key::ArrowDown+                | Key::Home+                | Key::End+                | Key::Backspace+                | Key::Delete+                | Key::Enter+                | Key::Tab+        )+}++/// Command shortcuts when the canvas isn't focused: nothing has the keyboard, or a button, a+/// pane's title bar or a text field outside dialogs does. A text field (the Navigation pane's+/// search, say) keeps the keys it edits with and Alt (Option types characters on macOS); its+/// other Mod and function-key shortcuts run, as Save As and Find do from Word's search box. A+/// focused widget in an open dialog or popup keeps the keys, so Ctrl+Z on a dialog button can't+/// undo the document behind it. pub fn global_shortcuts(app: &mut WordApp, ctx: &egui::Context) {+    let field = ctx.text_edit_focused();     // Customize Keyboard records the keys pressed; they never run commands meanwhile.     if app.canvas.focused-        || ctx.text_edit_focused()         || (ctx.egui_wants_keyboard_input() && crate::panes::overlay_open(app, ctx))         || matches!(app.dialog, Some(crate::dialogs::Dialog::CustomizeKeyboard { .. }))     {@@ -264,6 +287,12 @@ pub fn global_shortcuts(app: &mut WordApp, ctx: &egui::Context) {             break;         }         if let egui::Event::Key { key, pressed: true, modifiers, .. } = e {+            if field {+                if (modifiers.command || is_function_key(key)) && !text_field_keeps(key, modifiers) {+                    dispatch(app, key, modifiers);+                }+                continue;+            }             if leave_full_screen(app, ctx, key, modifiers, false) {                 continue;             }diff --git a/crates/ui-egui/src/panes/tests.rs b/crates/ui-egui/src/panes/tests.rs--- a/crates/ui-egui/src/panes/tests.rs+++ b/crates/ui-egui/src/panes/tests.rs@@ -119,10 +119,35 @@ fn shortcuts_run_while_a_pane_title_bar_has_focus() {     h.state_mut().nav_search_focus = true;     h.run_steps(2);     assert!(h.ctx.text_edit_focused());-    press(&mut h, egui::Key::H, egui::Modifiers::COMMAND);+    // Ctrl itself: on macOS ⌘H isn't the field's, and runs from it as other ⌘ shortcuts do.+    press(&mut h, egui::Key::H, egui::Modifiers { ctrl: true, command: !cfg!(target_os = "macos"), ..Default::default() });     assert!(h.state().dialog.is_none(), "the search field keeps Ctrl+H"); } +/// Word's search box doesn't swallow the window's shortcuts: with the Navigation pane's search+/// focused, Mod+D opens the Font dialog, while Mod+A, the arrows and Mod+Z stay with the field.+#[test]+#[cfg(not(target_arch = "wasm32"))]+fn shortcuts_run_from_the_navigation_search_box() {+    let mut app = crate::WordApp::new(wordcraft_engine::Session::new(wordcraft_doc::Document::from_text("kept")), crate::Services::default());+    app.run("view.navigationPane", serde_json::json!({"value": true})).unwrap();+    let mut h = pane_harness(app);+    h.state_mut().nav_search_focus = true;+    h.run_steps(2);+    assert!(h.ctx.text_edit_focused() && !h.state().canvas.focused, "the search box has the keyboard");+    let sel = h.state().session.sel.clone();+    for (key, m) in+        [(egui::Key::A, egui::Modifiers::COMMAND), (egui::Key::ArrowLeft, egui::Modifiers::COMMAND), (egui::Key::Z, egui::Modifiers::COMMAND)]+    {+        press(&mut h, key, m);+        assert!(h.state().dialog.is_none() && h.ctx.text_edit_focused(), "{key:?} stays with the field");+        assert_eq!(h.state().session.sel, sel, "{key:?} left the document's selection alone");+    }+    assert_eq!(h.state().session.doc.plain_text(wordcraft_doc::StoryRef::Body).trim(), "kept", "Mod+Z left the document alone");+    press(&mut h, egui::Key::D, egui::Modifiers::COMMAND);+    assert_eq!(h.state().dialog.as_ref().map(|d| d.name()), Some("font"), "Mod+D opened Font from the search box");+}+ /// A dialog button with the keyboard keeps Ctrl+Z away from the document behind the dialog. #[test] #[cfg(not(target_arch = "wasm32"))]