From 590d5e449275729569826ffa4598cb2bfa0af23a Mon Sep 17 00:00:00 2001 From: prxnvt <53838599+prxnvt@users.noreply.github.com> Date: Sat, 31 Oct 2026 07:03:16 -0400 Subject: [PATCH] UI: Insert Function: Enter on the category list doesn't confirm the dialog egui clicks a focused control on Enter as on Space, so Enter on the focused category list opened it, and the dialog's OK handling took the same key press and inserted the selected function. Enter in the open list did the same while picking a category. The dialog now leaves Enter to the category list while the list has the focus or its menu is open (or was open as the frame started, since Enter on a row closes it in that frame). Enter in the search box or on the function list still confirms. Test: function_library::enter_on_the_category_list_does_not_confirm_insert_function tabs to the list, presses Enter (the list opens, the dialog stays open, nothing is inserted), presses Enter again in the open list (still open, nothing inserted), then Shift+Tab back to the search box, where Enter confirms. --- crates/ui-egui/src/dialogs/insert_function.rs | 13 +++++-- crates/ui-egui/tests/it/function_library.rs | 35 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/crates/ui-egui/src/dialogs/insert_function.rs b/crates/ui-egui/src/dialogs/insert_function.rs index 6a93530f7..38a695a80 100644 --- a/crates/ui-egui/src/dialogs/insert_function.rs +++ b/crates/ui-egui/src/dialogs/insert_function.rs @@ -29,7 +29,10 @@ pub(super) fn insert_function(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dia } else { lib::category_id(&saved).map(lib::category_label).unwrap_or(MOST_RECENTLY_USED).to_string() }; - ui.horizontal(|ui| { + // The category list is the dialog's only menu, so a menu open as the frame starts is its own + // (Enter on a row picks it and closes the menu in this frame). + let menu_was_open = egui::Popup::is_any_open(ui.ctx()); + let picker = ui.horizontal(|ui| { let label = ui.label(language.tr("Or select a category:")); craft_ui::ComboBox::from_id_salt("insertFunctionCategory") .width(220.0) @@ -40,8 +43,14 @@ pub(super) fn insert_function(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dia } }) .response - .labelled_by(label.id); + .labelled_by(label.id) }); + // Enter on the category list or in its menu opens it or picks a category, as Space does + // (egui clicks a focused control on either key); it does not also confirm the dialog. + let picker = picker.inner; + if menu_was_open || picker.has_focus() || craft_ui::ComboBox::is_open(ui.ctx(), picker.id) { + ui.input_mut(|i| i.consume_key(egui::Modifiers::NONE, egui::Key::Enter)); + } if category != saved { d.list_index = 0; } diff --git a/crates/ui-egui/tests/it/function_library.rs b/crates/ui-egui/tests/it/function_library.rs index dd86f4652..a1a7a1a37 100644 --- a/crates/ui-egui/tests/it/function_library.rs +++ b/crates/ui-egui/tests/it/function_library.rs @@ -263,6 +263,41 @@ fn insert_function_lets_tab_reach_the_category_list() { assert_eq!(listed(&h), before + 1, "the list is open"); } +#[test] +fn enter_on_the_category_list_does_not_confirm_insert_function() { + use egui::accesskit::Role; + let mut h = formulas_tab(1440.0); + click(&mut h, "Insert Function"); + h.run_steps(3); + let open = |h: &Harness| h.state().dialog.as_ref().is_some_and(|d| d.name == "insertFunction"); + let listed = |h: &Harness| h.get_all_by_label("Date & Time").count(); + let closed = listed(&h); + h.input_mut().events.extend([key_event(Key::Tab, true), key_event(Key::Tab, false)]); + h.run_steps(5); + assert!(h.get_by_role(Role::ComboBox).is_focused(), "Tab reaches the category list"); + // Enter on the focused list opens it, as Space does; the dialog stays open and nothing is + // inserted. + h.input_mut().events.extend([key_event(Key::Enter, true), key_event(Key::Enter, false)]); + h.run_steps(3); + assert!(open(&h), "Enter on the category list leaves the dialog open"); + assert_eq!(editor_text(&h), None, "no function is inserted"); + assert_eq!(listed(&h), closed + 1, "Enter opens the list"); + // Enter again, with the list open, closes it and still does not confirm. + h.input_mut().events.extend([key_event(Key::Enter, true), key_event(Key::Enter, false)]); + h.run_steps(3); + assert!(open(&h), "Enter in the open list leaves the dialog open"); + assert_eq!(editor_text(&h), None); + // Back in the search box (Shift+Tab), Enter confirms as before. + let shift_tab = |pressed| Event::Key { key: Key::Tab, physical_key: None, pressed, repeat: false, modifiers: Modifiers::SHIFT }; + h.input_mut().events.extend([shift_tab(true), shift_tab(false)]); + h.run_steps(3); + assert!(!h.get_by_role(Role::ComboBox).is_focused(), "Shift+Tab leaves the category list"); + h.input_mut().events.extend([key_event(Key::Enter, true), key_event(Key::Enter, false)]); + h.run_steps(3); + assert!(!open(&h), "Enter in the search box confirms"); + assert!(editor_text(&h).is_some_and(|t| t.starts_with('=') && t.ends_with('(')), "{:?}", editor_text(&h)); +} + /// The text shapes the last frame painted, with their left edge and width. fn painted_text(h: &Harness) -> Vec<(String, f32, f32)> { let mut out = Vec::new();