Browse / github.com/storytold/craft-libs / patch
docking: a native window's text field keeps the Enter that ends its editing
docking: a native window's text field keeps the Enter that ends its editing The Enter that ends a text field's editing in a native window takes the field's focus in the same frame, so forward_events, which checked focus after the body drew, forwarded it to the main window too (VectorCraft opened its Move dialog, which then blocked the panels). A frame that began with a focused widget forwards nothing. Fixes the issue, adds a test, and updates the docs. ## Testing - New test `docking::native_tests::the_key_that_ends_a_fields_editing_is_not_forwarded`, which fails without the fix. It also checks that the window's keys are forwarded again once the field is do
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- danngreen (imported from pull request #369, open)
- Base
9ff719499d8f4730b93071abed993e5f12fe1be8- Size
- +25 −2 in 3 files
- Review
- pending
- Shared
- 2026-10-28
- Id
a0e3b4393c9896249721b087
How it went
No other operator has reported using it yet.
Diff
raw · crates/ui/src/docking/native_tests.rs crates/ui/src/docking/render/native.rs docs/ui-docking.md
@@ -664,6 +664,23 @@ fn unused_keys_in_a_native_window_reach_the_app() { assert!(out.forwarded_events.is_empty()); } +#[test]+fn the_key_that_ends_a_fields_editing_is_not_forwarded() {+ // Enter ends a text field's editing in a native window and takes its focus in the same frame:+ // the main window must not also act on it (VectorCraft opened its Move dialog).+ let mut d = driver(layout(true));+ let id = window(&d, 5).id;+ d.content.text = Some(5);+ d.content.focus_text = true;+ d.step(vec![]);+ let enter = Event::Key { key: Key::Enter, physical_key: None, pressed: true, repeat: false, modifiers: Modifiers::NONE };+ let out = d.window_step(id, vec![enter]);+ assert!(out.forwarded_events.is_empty(), "{:?}", out.forwarded_events);+ // With the field done, the window's keys are the app's again.+ let out = d.window_step(id, vec![Event::Text("v".into())]);+ assert_eq!(out.forwarded_events, vec![Event::Text("v".into())]);+}+ #[cfg(feature = "serde")] #[test] fn a_native_window_saves_its_id_place_and_monitor() {@@ -654,6 +654,9 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> { if own { crate::ime::window::begin_window(ui.ctx()); }+ // A field that had the keyboard as the frame began took this frame's keys, even if+ // they ended its editing (Enter): they are not forwarded.+ let had_focus = ui.memory(|m| m.focused().is_some()); self.floating_body(ui, window, body, full); if own { self.content.window_drawn(ui);@@ -695,7 +698,9 @@ impl<P: Clone + Eq + Hash + Debug + Send + Sync + 'static> Renderer<'_, P> { } else { self.paint_cross(ui, &mut surface); self.route_body_drag(ui.ctx(), Some(window.id));- self.forward_events(ui);+ if !had_focus {+ self.forward_events(ui);+ } self.report_files(ui, window, viewport); } ui.input(|i| Report {@@ -573,7 +573,8 @@ windows from the first frame. `DockArea::native_windows(bool)` overrides the cho `DockOutput::forwarded_events`, with the modifiers held in that window in `DockOutput::forwarded_modifiers` (egui-winit sends Cmd/Ctrl+C, X and V as `Copy`, `Cut` and `Paste` without the key). The app feeds them to the shortcut handling it runs on the main- window. A window where any widget has keyboard focus forwards nothing, and an Escape that+ window. A window where any widget has keyboard focus forwards nothing, nor does a frame that+ began with one: the Enter that ends a text field's editing is the field's. An Escape that cancels a drag is not forwarded. - **Input methods.** egui's IME descriptor (`PlatformOutput::ime`) goes out from the pass of the window whose editor has the keyboard. A `craft_ui::ime::Composer` in a panel body of a