collective

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

diff --git a/crates/ui/src/docking/native_tests.rs b/crates/ui/src/docking/native_tests.rs--- a/crates/ui/src/docking/native_tests.rs+++ b/crates/ui/src/docking/native_tests.rs@@ -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() {diff --git a/crates/ui/src/docking/render/native.rs b/crates/ui/src/docking/render/native.rs--- a/crates/ui/src/docking/render/native.rs+++ b/crates/ui/src/docking/render/native.rs@@ -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 {diff --git a/docs/ui-docking.md b/docs/ui-docking.md--- a/docs/ui-docking.md+++ b/docs/ui-docking.md@@ -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