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.
This commit is contained in:
prxnvt
2026-10-31 07:03:16 -04:00
parent c5a80ff791
commit 590d5e4492
2 changed files with 46 additions and 2 deletions
+11 -2
View File
@@ -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;
}
@@ -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();