fix: page navigation works with multiple page-axis categories

[/] previously broke after the first page category due to a hard-coded
`break`. Replaced with odometer-style navigation: ] advances the last
page category, carrying into the previous when it wraps (like digit
incrementing). [ decrements the same way. Single-category behaviour is
unchanged except it now wraps around instead of clamping at the end.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ed L
2026-03-22 00:02:01 -07:00
parent 8063a484e1
commit 6ba7245338

View File

@ -1052,47 +1052,63 @@ impl App {
}
}
fn page_next(&mut self) {
/// Gather (cat_name, items, current_idx) for all non-empty page categories.
fn page_cat_data(&self) -> Vec<(String, Vec<String>, usize)> {
let page_cats: Vec<String> = self.model.active_view()
.map(|v| v.categories_on(Axis::Page).into_iter().map(String::from).collect())
.unwrap_or_default();
for cat_name in &page_cats {
let items: Vec<String> = self.model.category(cat_name)
page_cats.into_iter().filter_map(|cat| {
let items: Vec<String> = self.model.category(&cat)
.map(|c| c.ordered_item_names().into_iter().map(String::from).collect())
.unwrap_or_default();
if items.is_empty() { continue; }
if items.is_empty() { return None; }
let current = self.model.active_view()
.and_then(|v| v.page_selection(cat_name))
.and_then(|v| v.page_selection(&cat))
.map(String::from)
.unwrap_or_else(|| items[0].clone());
let idx = items.iter().position(|i| i == &current).unwrap_or(0);
let next_idx = (idx + 1).min(items.len() - 1);
if let Some(view) = self.model.active_view_mut() {
view.set_page_selection(cat_name, &items[next_idx]);
.or_else(|| items.first().cloned())
.unwrap_or_default();
let idx = items.iter().position(|i| *i == current).unwrap_or(0);
Some((cat, items, idx))
}).collect()
}
fn page_next(&mut self) {
let data = self.page_cat_data();
if data.is_empty() { return; }
// Odometer: advance from last category, carry propagates backward.
let mut indices: Vec<usize> = data.iter().map(|(_, _, i)| *i).collect();
let mut carry = true;
for i in (0..data.len()).rev() {
if !carry { break; }
indices[i] += 1;
if indices[i] >= data[i].1.len() { indices[i] = 0; } else { carry = false; }
}
if let Some(view) = self.model.active_view_mut() {
for (i, (cat, items, _)) in data.iter().enumerate() {
view.set_page_selection(cat, &items[indices[i]]);
}
break;
}
}
fn page_prev(&mut self) {
let page_cats: Vec<String> = self.model.active_view()
.map(|v| v.categories_on(Axis::Page).into_iter().map(String::from).collect())
.unwrap_or_default();
for cat_name in &page_cats {
let items: Vec<String> = self.model.category(cat_name)
.map(|c| c.ordered_item_names().into_iter().map(String::from).collect())
.unwrap_or_default();
if items.is_empty() { continue; }
let current = self.model.active_view()
.and_then(|v| v.page_selection(cat_name))
.map(String::from)
.unwrap_or_else(|| items[0].clone());
let idx = items.iter().position(|i| i == &current).unwrap_or(0);
let prev_idx = idx.saturating_sub(1);
if let Some(view) = self.model.active_view_mut() {
view.set_page_selection(cat_name, &items[prev_idx]);
let data = self.page_cat_data();
if data.is_empty() { return; }
// Odometer: decrement from last category, borrow propagates backward.
let mut indices: Vec<usize> = data.iter().map(|(_, _, i)| *i).collect();
let mut borrow = true;
for i in (0..data.len()).rev() {
if !borrow { break; }
if indices[i] == 0 {
indices[i] = data[i].1.len().saturating_sub(1);
} else {
indices[i] -= 1;
borrow = false;
}
}
if let Some(view) = self.model.active_view_mut() {
for (i, (cat, items, _)) in data.iter().enumerate() {
view.set_page_selection(cat, &items[indices[i]]);
}
break;
}
}