From 413601517d4b079cbcc1d165ce2be688fe0cea9e Mon Sep 17 00:00:00 2001 From: Ed L Date: Sat, 21 Mar 2026 23:05:57 -0700 Subject: [PATCH] Fix cursor getting stuck when Enter pressed on last row move_selection() only clamped at 0, letting the row/col index go past the last valid item. Selected_cell_key() would then return None, leaving the cursor in a phantom position with no selectable cell. Now clamp both row and col against the actual item count so pressing Enter on the last row keeps the cursor on that row. Co-Authored-By: Claude Sonnet 4.6 --- src/ui/app.rs | 24 +++++++- whatever.improv | 144 +++++++++++++++++++++++++++++++++++++++--------- 2 files changed, 140 insertions(+), 28 deletions(-) diff --git a/src/ui/app.rs b/src/ui/app.rs index 0536410..ef523fc 100644 --- a/src/ui/app.rs +++ b/src/ui/app.rs @@ -899,11 +899,31 @@ impl App { // ── Motion helpers ─────────────────────────────────────────────────────── fn move_selection(&mut self, dr: i32, dc: i32) { + // Compute max row/col from actual item counts so we never go out of bounds. + let row_max = { + let row_cats: Vec = self.model.active_view() + .map(|v| v.categories_on(crate::view::Axis::Row).into_iter().map(String::from).collect()) + .unwrap_or_default(); + row_cats.first() + .and_then(|c| self.model.category(c)) + .map(|c| c.items.len().saturating_sub(1)) + .unwrap_or(0) + }; + let col_max = { + let col_cats: Vec = self.model.active_view() + .map(|v| v.categories_on(crate::view::Axis::Column).into_iter().map(String::from).collect()) + .unwrap_or_default(); + col_cats.first() + .and_then(|c| self.model.category(c)) + .map(|c| c.items.len().saturating_sub(1)) + .unwrap_or(0) + }; + if let Some(view) = self.model.active_view_mut() { let (r, c) = view.selected; view.selected = ( - (r as i32 + dr).max(0) as usize, - (c as i32 + dc).max(0) as usize, + (r as i32 + dr).clamp(0, row_max as i32) as usize, + (c as i32 + dc).clamp(0, col_max as i32) as usize, ); } } diff --git a/whatever.improv b/whatever.improv index 4250f06..ef62240 100644 --- a/whatever.improv +++ b/whatever.improv @@ -128,29 +128,6 @@ "Number": 3.0 } ], - [ - [ - [ - "Month", - "Janury" - ], - [ - "Payer", - "Bernadette" - ], - [ - "Recipient", - "Bob" - ], - [ - "Type", - "Food" - ] - ], - { - "Number": 12.0 - } - ], [ [ [ @@ -196,6 +173,121 @@ { "Number": 5.0 } + ], + [ + [ + [ + "Month", + "February" + ], + [ + "Payer", + "Bernadette" + ], + [ + "Recipient", + "Bob" + ], + [ + "Type", + "Medical" + ] + ], + { + "Number": 33.0 + } + ], + [ + [ + [ + "Month", + "February" + ], + [ + "Payer", + "Bernadette" + ], + [ + "Recipient", + "Bob" + ], + [ + "Type", + "Gas" + ] + ], + { + "Number": 55.0 + } + ], + [ + [ + [ + "Month", + "February" + ], + [ + "Payer", + "Bernadette" + ], + [ + "Recipient", + "Bob" + ], + [ + "Type", + "Food" + ] + ], + { + "Number": 4.0 + } + ], + [ + [ + [ + "Month", + "February" + ], + [ + "Payer", + "Bernadette" + ], + [ + "Recipient", + "Bob" + ], + [ + "Type", + "Clothing" + ] + ], + { + "Text": "i5" + } + ], + [ + [ + [ + "Month", + "Janury" + ], + [ + "Payer", + "Bernadette" + ], + [ + "Recipient", + "Bob" + ], + [ + "Type", + "Food" + ] + ], + { + "Number": 12.0 + } ] ], "formulas": [], @@ -217,11 +309,11 @@ "row_offset": 0, "col_offset": 0, "selected": [ - 1, - 1 + 6, + 2 ] } }, "active_view": "Default", "next_category_id": 4 -} +} \ No newline at end of file