From 0514e537ac5608fafad3579345b002f01fb39109 Mon Sep 17 00:00:00 2001 From: derechopiano <257026466+derechopiano@users.noreply.github.com> Date: Mon, 12 Oct 2026 09:56:52 +0100 Subject: [PATCH] #441: Open AutoFilter header menus with Alt+Down --- crates/ui-egui/src/keymap.rs | 3 + crates/ui-egui/tests/keyboard_shortcuts.rs | 65 ++++++++++++++++++++++ docs/ui-parity.md | 4 +- 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/crates/ui-egui/src/keymap.rs b/crates/ui-egui/src/keymap.rs index 21bf41f..94f6d30 100644 --- a/crates/ui-egui/src/keymap.rs +++ b/crates/ui-egui/src/keymap.rs @@ -218,6 +218,9 @@ pub(crate) fn dispatch(app: &mut SheetApp, ctx: &egui::Context, key: Key, physic Shortcut::DropDown => { if sh.validations.iter().any(|d| d.in_cell_dropdown && d.ranges.iter().any(|r| r.contains(active))) { app.grid.list_picker = Some(active); + } else if sh.has_filter_button(active) { + let at = geo.mirror_pos(crate::grid::filter_button_rect(geo, sh, active).center()); + app.grid.filter_menu = Some((active.col, at)); } } Shortcut::Zoom(step) => { diff --git a/crates/ui-egui/tests/keyboard_shortcuts.rs b/crates/ui-egui/tests/keyboard_shortcuts.rs index 82d3f2f..ae0ce5c 100644 --- a/crates/ui-egui/tests/keyboard_shortcuts.rs +++ b/crates/ui-egui/tests/keyboard_shortcuts.rs @@ -149,3 +149,68 @@ fn space_shortcuts_per_platform() { // Cmd+Space is Spotlight on macOS. assert_eq!(space(mac_cmd, true), None); } + +#[test] +fn alt_down_opens_autofilter_headers_without_changing_the_workbook() { + for rtl in [false, true] { + for cell in ["A1", "B1"] { + let mut session = book(cell); + session.execute("data.filter", json!({"range": "A1:B3"})).unwrap(); + session.execute("view.rightToLeft", json!({"on": rtl})).unwrap(); + let mut h = harness(session); + let before = h.state().session.active().unwrap().wb.clone(); + key(&mut h, Key::ArrowDown, Modifiers::ALT); + let (column, at) = h.state().grid.filter_menu.expect("Alt+Down must open the filter header menu"); + let d = h.state().session.active().unwrap(); + let active = CellRef::parse(cell).unwrap(); + assert_eq!(column, active.col); + assert_eq!(d.selection.active, active); + assert_eq!(d.wb, before); + let sh = d.wb.active().unwrap(); + let geo = gridcraft_ui_egui::grid::Geo::new(sh, h.state().grid.rect.unwrap(), h.state().view().scroll); + assert_eq!(at, geo.mirror_pos(gridcraft_ui_egui::grid::filter_button_rect(&geo, sh, active).center())); + // Opening focuses the existing search box; typing must not edit the header. + h.input_mut().events.push(Event::Text("1".into())); + h.run_steps(2); + assert_eq!(h.ctx.data(|data| data.get_temp::(egui::Id::new(("filter_search", column)))).as_deref(), Some("1")); + assert!(h.state().editor.is_none()); + assert_eq!(h.state().session.active().unwrap().wb, before); + key(&mut h, Key::Escape, Modifiers::NONE); + assert!(h.state().grid.filter_menu.is_none()); + } + } +} + +#[test] +fn alt_down_keeps_validation_priority_and_plain_down_navigation() { + let mut session = book("A1"); + session.execute("data.filter", json!({"range": "A1:B3"})).unwrap(); + session.execute("data.validation", json!({"range": "A1", "type": "list", "formula1": "\"First,Second\""})).unwrap(); + let mut h = harness(session); + key(&mut h, Key::ArrowDown, Modifiers::ALT); + assert_eq!(h.state().grid.list_picker, Some(CellRef::parse("A1").unwrap())); + assert!(h.state().grid.filter_menu.is_none()); + key(&mut h, Key::Escape, Modifiers::NONE); + key(&mut h, Key::ArrowDown, Modifiers::NONE); + assert_eq!(h.state().session.active().unwrap().selection.active.a1(), "A2"); + assert!(h.state().grid.filter_menu.is_none()); + key(&mut h, Key::ArrowDown, Modifiers::ALT); + assert!(h.state().grid.filter_menu.is_none(), "a data cell has no filter button"); + assert_eq!(h.state().session.active().unwrap().selection.active.a1(), "A2"); +} + +#[test] +fn alt_down_only_opens_enabled_table_filter_buttons() { + for enabled in [false, true] { + let mut session = book("B1"); + session.execute("insert.table", json!({"range": "A1:B3"})).unwrap(); + if !enabled { + session.execute("table.headerRow", json!({"on": false})).unwrap(); + } + session.execute("selection.set", json!({"cell": "B1"})).unwrap(); + let mut h = harness(session); + key(&mut h, Key::ArrowDown, Modifiers::ALT); + assert_eq!(h.state().grid.filter_menu.map(|(column, _)| column), enabled.then_some(1)); + assert_eq!(h.state().session.active().unwrap().selection.active.a1(), "B1"); + } +} diff --git a/docs/ui-parity.md b/docs/ui-parity.md index 10abcbc..940305f 100644 --- a/docs/ui-parity.md +++ b/docs/ui-parity.md @@ -1,6 +1,6 @@ # UI parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-11 · **Change:** minor (AutoFit reserves filter dropdown space, observed against Windows Excel) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4 +> **Last reviewed:** 2026-10-12 · **Last updated:** 2026-10-12 · **Change:** minor (Alt+Down opens AutoFilter header menus) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4 Interaction, controls and feel, against Excel. Part of [target-app-parity.md](target-app-parity.md). **UI/UX fidelity: ~55% ready, 50–80 h** (estimated). Ribbon command coverage is measured @@ -117,6 +117,7 @@ otherwise. Number-format and border keys match the physical key (Ctrl+Shift+4, w | Ctrl+Alt+V | Cmd+Control+V, Cmd+Option+V | Paste Special | **added** | | Ctrl+Alt+= / Ctrl+Alt+- | — | zoom in / out (10% steps) | **added** | | Alt+=, Alt+Down, Alt+Page Down / Up | Option+Down, Option+Page Down / Up | AutoSum, drop-down list, one screen right / left | **added** | +| Alt+Down on a filter header | Option+Down | Open the existing AutoFilter popup; validation dropdowns keep priority | **added** (AutoFilter portion of #441; Ribbon dropdowns and column value picker are separate work) | | Alt+F1, Alt+Shift+F1, Shift+F11 | Shift+F11 | embedded chart, new sheet | **added** | | Shift+F2, Ctrl+Shift+F2 | Shift+F2, Cmd+Shift+F2 | note, threaded comment | **added** | | Shift+F3, Ctrl+F3, Ctrl+Shift+F3 | Shift+F3, Cmd+F3 (Define Name), Cmd+Shift+F3 | Insert Function, Name Manager, Create from Selection | **added** | @@ -160,6 +161,7 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12. | Date | Change | Summary | |---|---|---| +| 2026-10-12 | minor | Alt+Down opens the active filter header's existing popup, with validation priority and mirrored anchoring on right-to-left sheets (part of #441) | | 2026-10-11 | minor | AutoFit accounts for table and ordinary filter dropdowns, including low zoom; header text excludes the button area | | 2026-10-11 | minor | Outline band for grouped rows and columns, Show / Hide Detail on the Data tab, numpad navigation keys in shortcuts (#373) | | 2026-10-11 | minor | Shift+Space / Ctrl+Space no longer start an edit; Control+Space selects the column on Mac (#338) |