Browse / github.com/storytold/gridcraft / patch
fix(keyboard): Ctrl with the numpad's + inserts rows, columns or cells, as in Excel
fix(keyboard): Ctrl with the numpad's + inserts rows, columns or cells, as in Excel ## Change In Excel, Ctrl with the numpad's + does the same as Ctrl+Shift+=: with whole rows or columns selected it inserts them, otherwise it opens the Insert dialog. In GridCraft it did nothing. The cause is that egui-winit reports NumpadAdd as `Key::Plus` without Shift, and `ribbon::shortcut` only matched `Key::Plus | Key::Equals` when Shift was held. Now `Key::Plus` matches with or without Shift. `Key::Equals` still needs Shift, so Ctrl+= stays unbound. Reading the code, this probably also reaches layouts whose main block types + without Shift (Nordic, or a German keyboard with Cmd
- Status
- open
- Score
- 0.00
- Adopters
- 0 other operators
- Author
- RomanLis91 (imported from pull request #2769, open)
- Base
11bb67386c9bb19b0717e21e98adc608162ae2e5- Size
- +22 −4 in 3 files
- Review
- pending
- Shared
- 2026-10-28
- Id
c3130a2ff5e5ec00b1eeba71
How it went
No other operator has reported using it yet.
Diff
raw · crates/ui-egui/src/ribbon/shortcuts.rs crates/ui-egui/tests/it/whole_line_shortcuts.rs docs/ui-parity.md
@@ -61,7 +61,8 @@ pub fn shortcut(app: &mut SheetApp, key: Key, m: Modifiers) { } }, // Insert works on one area at a time, so several areas still go through the dialog.- Key::Plus | Key::Equals if shift => match whole_lines(app).filter(|_| single_area(app)) {+ // The numpad's + arrives as Plus without Shift; Excel takes Ctrl with it as well.+ Key::Plus | Key::Equals if shift || key == Key::Plus => match whole_lines(app).filter(|_| single_area(app)) { Some(true) => Some(("home.insertRows", json!({}))), Some(false) => Some(("home.insertColumns", json!({}))), None => {@@ -1,4 +1,4 @@-//! Ctrl+Shift+= and Ctrl+- on whole rows or columns insert or delete them at once, as in Excel;+//! Ctrl+Shift+= (or Ctrl with the numpad's +) and Ctrl+- on whole rows or columns insert or delete them at once, as in Excel; //! any other selection opens the Insert or Delete dialog. use egui::{Key, Modifiers};@@ -57,6 +57,22 @@ fn whole_columns_are_inserted_and_deleted_at_once() { assert_eq!(value(&h, "A1"), Value::from("a1")); } +#[test]+fn numpad_plus_inserts_without_shift() {+ let mut h = harness("2:2");+ press(&mut h, Key::Plus, false);+ assert!(h.state().dialog.is_none(), "no dialog for whole rows");+ assert!(value(&h, "A2").is_empty(), "a row was inserted above row 2");+ assert_eq!(value(&h, "A3"), Value::from("a2"));+}++#[test]+fn unshifted_equals_inserts_nothing() {+ let mut h = harness("2:2");+ press(&mut h, Key::Equals, false);+ assert_eq!(value(&h, "A2"), Value::from("a2"));+}+ #[test] fn other_selections_still_ask_which_way_to_shift() { let mut h = harness("A2:B2");@@ -1,6 +1,6 @@ # UI parity -> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-25 · **Change:** minor (AutoSum over a selected row puts its total to the right) · **Target:** Microsoft Excel (Microsoft 365), Excel for Mac 16.113.4+> **Last reviewed:** 2026-10-10 · **Last updated:** 2026-10-27 · **Change:** minor (Ctrl with the numpad's + inserts, as Ctrl+Shift+= does) · **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@@ -106,7 +106,7 @@ otherwise. Number-format and border keys match the physical key (Ctrl+Shift+4, w | Ctrl+S, Ctrl+Shift+S, Ctrl+O, Ctrl+N, Ctrl+W | Cmd | save, save as, open, new, close | present | | Ctrl+F, Ctrl+H, Ctrl+G, Ctrl+K, Ctrl+T | Cmd (Ctrl+H) | find, replace, go to, hyperlink, table | present | | Ctrl+D, Ctrl+R, Ctrl+E, Ctrl+1, Ctrl+9 / 0, Ctrl+Shift+9 / 0 | Cmd (Ctrl+E on Mac: see below) | fill down/right, Flash Fill, Format Cells, hide/unhide | present |-| Ctrl+- , Ctrl+Shift+= | Cmd | Delete / Insert cells dialog | present |+| Ctrl+- , Ctrl+Shift+= , Ctrl+numpad + | Cmd | Delete / Insert cells dialog | present | | Ctrl+; , Ctrl+` | Control+; | current date, show formulas | present (Mac Control+; added) | | Arrows, Shift / Ctrl / Ctrl+Shift+arrows, Home, Ctrl+Home / End, Page Up / Down, Ctrl+Page Up / Down | Cmd+arrows | navigation and extension | present | | Tab, Shift+Tab, Enter, Shift+Enter, Ctrl+Space, Shift+Space, Delete, Esc | same | entry and selection | present |@@ -187,6 +187,7 @@ scrollbar bugs 2–4 · pixel/feel pass against observed Excel 7–12. | Date | Change | Summary | |---|---|---|+| 2026-10-27 | minor | Ctrl with the numpad's + inserts rows, columns or cells as Ctrl+Shift+= does, as in Excel; it did nothing because the numpad key arrives without Shift | | 2026-10-25 | minor | AutoSum (Alt+=, Home › AutoSum) over a selected row puts its total to the right of the row, or in its last cell when that is empty, as in Excel; it wrote a one-cell total under each cell. A selection's empty last column takes each row's total and its empty last row each column's, with the block's total in the corner when both are empty | | 2026-10-25 | minor | Paste Name lays out only the names in view, so a workbook with thousands of names opens it at once; the arrow keys still scroll the chosen name into view. Go To matches a `Sheet2!Name` reference and hides a workbook name behind the active sheet's own name in any case and script (`Größe` = `GRÖSSE`), as formulas do. The two Page Layout view rows are one; the 0.7 in side margin used when Align with Page Margins is off is marked unverified | | 2026-10-24 | minor | Go To lists the named ranges on every sheet, as Excel's does ("The Go to popup window shows named ranges on every worksheet in your workbook"): a name scoped to another sheet appears as `Sheet2!Name`, and hidden names, constants and formulas are left out. It keeps the references entered (newest first, ten at most), and a double-click on an entry goes there. The Reference box and `edit.goTo` take several names or references separated by commas (`Price, Type`, `B14:C22,F19:G30,H21:H29`) and select them all; `edit.goTo {list: true}` returns the list |