From 4bfd0bc5a108c37e60e6a67f06bd8377973a70bc Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 15 Oct 2026 03:23:16 +0200 Subject: [PATCH 1/2] Improve Command Palette keyboard navigation and layout --- crates/ui-egui/src/dialogs.rs | 67 ++++++++++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 8 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index fb899c702..b4e12817f 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -40,6 +40,7 @@ pub struct Dialog { picture_target: Option<(u64, usize)>, /// The dialog this one was opened from (Name Manager › New…): it comes back when this closes. back: Option>, + focus_initialized: bool, } impl Dialog { @@ -73,6 +74,7 @@ impl Dialog { picture_inbox: None, picture_target: None, back: None, + focus_initialized: false, } } @@ -91,6 +93,7 @@ impl Dialog { picture_inbox: None, picture_target: None, back: None, + focus_initialized: false, } } @@ -2239,8 +2242,24 @@ fn manage_rules(app: &mut SheetApp, ui: &mut egui::Ui) { } fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm: &mut bool) { - let r = ui.add(egui::TextEdit::singleline(&mut d.search).desired_width(480.0).hint_text("Type a command, e.g. freeze, chart, bold…")); - r.request_focus(); + let (down, up, home, end, pgup, pgdn) = ui.input_mut(|i| { + ( + i.count_and_consume_key(egui::Modifiers::NONE, Key::ArrowDown), + i.count_and_consume_key(egui::Modifiers::NONE, Key::ArrowUp), + i.consume_key(egui::Modifiers::CTRL, Key::Home), + i.consume_key(egui::Modifiers::CTRL, Key::End), + i.consume_key(egui::Modifiers::NONE, Key::PageUp), + i.consume_key(egui::Modifiers::NONE, Key::PageDown), + ) + }); + let width = ui.available_width(); + let r = ui.add(egui::TextEdit::singleline(&mut d.search).desired_width(width).hint_text("Type a command, e.g. freeze, chart, bold…")); + + if !d.focus_initialized { + r.request_focus(); + d.focus_initialized = true; + } + let q = d.search.to_ascii_lowercase(); let cmds: Vec<(String, String, String)> = app .session @@ -2251,19 +2270,51 @@ fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm .map(|c| (c.id.to_string(), c.label.to_string(), c.menu.join(" › "))) .take(40) .collect(); - if ui.input(|i| i.key_pressed(Key::ArrowDown)) { - d.list_index = (d.list_index + 1).min(cmds.len().saturating_sub(1)); + + let last = cmds.len().saturating_sub(1); + // let page_size = 10; + + let row_h = ui.spacing().interact_size.y.max(20.0); + let row_step = row_h + ui.spacing().item_spacing.y; + + const LIST_HEIGHT: f32 = 320.0; + + let page_size = ((LIST_HEIGHT / row_step).floor() as usize).saturating_sub(1).max(1); + + if down > 0 { + d.list_index = d.list_index.saturating_add(down).min(last); } - if ui.input(|i| i.key_pressed(Key::ArrowUp)) { - d.list_index = d.list_index.saturating_sub(1); + if up > 0 { + d.list_index = d.list_index.saturating_sub(up); } - egui::ScrollArea::vertical().max_height(320.0).show(ui, |ui| { + if pgdn { + d.list_index = (d.list_index + page_size).min(last); + } + if pgup { + d.list_index = d.list_index.saturating_sub(page_size); + } + if home { + d.list_index = 0; + } + if end { + d.list_index = last; + } + + d.list_index = d.list_index.min(last); + + egui::ScrollArea::vertical().max_height(LIST_HEIGHT).auto_shrink([true, false]).show(ui, |ui| { for (i, (id, label, menu)) in cmds.iter().enumerate() { - let r = ui.add(egui::Button::selectable(i == d.list_index, format!("{label} {menu}")).min_size(vec2(480.0, 20.0))); + let r = ui.add(egui::Button::selectable(i == d.list_index, format!("{label} {menu}")).min_size(vec2(width, 20.0))); + + if (down > 0 || up > 0 || home || end || pgup || pgdn) && i == d.list_index && !ui.clip_rect().contains_rect(r.rect) { + r.scroll_to_me(Some(if down > 0 || end || pgdn { egui::Align::Max } else { egui::Align::Min })); + } + if r.clicked() { d.list_index = i; *confirm = true; } + r.on_hover_text(id); } }); From f065f9168a68849c2341a0feea5ada2e5dcd6eef Mon Sep 17 00:00:00 2001 From: Mario Date: Thu, 15 Oct 2026 05:18:08 +0200 Subject: [PATCH 2/2] Clean up Command Palette code --- crates/ui-egui/src/dialogs.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/ui-egui/src/dialogs.rs b/crates/ui-egui/src/dialogs.rs index b4e12817f..0d9d02f2e 100644 --- a/crates/ui-egui/src/dialogs.rs +++ b/crates/ui-egui/src/dialogs.rs @@ -2244,8 +2244,8 @@ fn manage_rules(app: &mut SheetApp, ui: &mut egui::Ui) { fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm: &mut bool) { let (down, up, home, end, pgup, pgdn) = ui.input_mut(|i| { ( - i.count_and_consume_key(egui::Modifiers::NONE, Key::ArrowDown), - i.count_and_consume_key(egui::Modifiers::NONE, Key::ArrowUp), + i.consume_key(egui::Modifiers::NONE, Key::ArrowDown), + i.consume_key(egui::Modifiers::NONE, Key::ArrowUp), i.consume_key(egui::Modifiers::CTRL, Key::Home), i.consume_key(egui::Modifiers::CTRL, Key::End), i.consume_key(egui::Modifiers::NONE, Key::PageUp), @@ -2272,7 +2272,6 @@ fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm .collect(); let last = cmds.len().saturating_sub(1); - // let page_size = 10; let row_h = ui.spacing().interact_size.y.max(20.0); let row_step = row_h + ui.spacing().item_spacing.y; @@ -2281,11 +2280,11 @@ fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm let page_size = ((LIST_HEIGHT / row_step).floor() as usize).saturating_sub(1).max(1); - if down > 0 { - d.list_index = d.list_index.saturating_add(down).min(last); + if down { + d.list_index = (d.list_index + 1).min(last); } - if up > 0 { - d.list_index = d.list_index.saturating_sub(up); + if up { + d.list_index = d.list_index.saturating_sub(1); } if pgdn { d.list_index = (d.list_index + page_size).min(last); @@ -2303,11 +2302,12 @@ fn command_search(app: &mut SheetApp, ui: &mut egui::Ui, d: &mut Dialog, confirm d.list_index = d.list_index.min(last); egui::ScrollArea::vertical().max_height(LIST_HEIGHT).auto_shrink([true, false]).show(ui, |ui| { + let scroll_area_width = ui.available_width(); for (i, (id, label, menu)) in cmds.iter().enumerate() { - let r = ui.add(egui::Button::selectable(i == d.list_index, format!("{label} {menu}")).min_size(vec2(width, 20.0))); + let r = ui.add(egui::Button::selectable(i == d.list_index, format!("{label} {menu}")).min_size(vec2(scroll_area_width, 20.0))); - if (down > 0 || up > 0 || home || end || pgup || pgdn) && i == d.list_index && !ui.clip_rect().contains_rect(r.rect) { - r.scroll_to_me(Some(if down > 0 || end || pgdn { egui::Align::Max } else { egui::Align::Min })); + if (down || up || home || end || pgup || pgdn) && i == d.list_index && !ui.clip_rect().contains_rect(r.rect) { + r.scroll_to_me(Some(if down || end || pgdn { egui::Align::Max } else { egui::Align::Min })); } if r.clicked() {