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
@@ -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; }@@ -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"))]