Remember last selection when navigating back
Pushed the selected index onto a stack on every drill-down (library → series → season → episode) and pop it on back so the cursor lands on the row the user just opened, instead of snapping to row 0. Clamps the restored index to the current list length to survive a shrunk list. Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
1 parent
af43b21f5d
commit
79f3a2e13b
1 file changed
+23
-7
+23
-7
@@ -35,6 +35,10 @@ pub struct App {
|
|||||||
pub list_state: ListState,
|
pub list_state: ListState,
|
||||||
pub current_title: Option<String>,
|
pub current_title: Option<String>,
|
||||||
pub breadcrumbs: Vec<(String, String)>, // (id, name)
|
pub breadcrumbs: Vec<(String, String)>, // (id, name)
|
||||||
|
/// Selected index at each level we navigated away from, so `back` can restore
|
||||||
|
/// the cursor where the user left it (e.g. Series → Season → Episode → back
|
||||||
|
/// brings you to the same season/series row, not row 0).
|
||||||
|
pub selection_stack: Vec<usize>,
|
||||||
pub status: String,
|
pub status: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,6 +70,7 @@ impl App {
|
|||||||
list_state,
|
list_state,
|
||||||
current_title: None,
|
current_title: None,
|
||||||
breadcrumbs: vec![],
|
breadcrumbs: vec![],
|
||||||
|
selection_stack: vec![],
|
||||||
status,
|
status,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -122,6 +127,9 @@ impl App {
|
|||||||
let Some(entry) = self.home_entries.get(i).cloned() else {
|
let Some(entry) = self.home_entries.get(i).cloned() else {
|
||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
|
// Remember which library/virtual entry was selected for restore on back.
|
||||||
|
self.selection_stack.clear();
|
||||||
|
self.selection_stack.push(i);
|
||||||
if entry.id == RESUME_VIRTUAL_ID {
|
if entry.id == RESUME_VIRTUAL_ID {
|
||||||
self.items = self.resume.clone();
|
self.items = self.resume.clone();
|
||||||
self.view = View::Items;
|
self.view = View::Items;
|
||||||
@@ -151,6 +159,8 @@ impl App {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
};
|
};
|
||||||
if item.is_folder {
|
if item.is_folder {
|
||||||
|
// Remember position in this folder before drilling down.
|
||||||
|
self.selection_stack.push(i);
|
||||||
self.status = format!("Loading {}...", item.name);
|
self.status = format!("Loading {}...", item.name);
|
||||||
self.items = self.client.items(&item.id).await?;
|
self.items = self.client.items(&item.id).await?;
|
||||||
self.breadcrumbs.push((item.id.clone(), item.name.clone()));
|
self.breadcrumbs.push((item.id.clone(), item.name.clone()));
|
||||||
@@ -182,6 +192,7 @@ impl App {
|
|||||||
}
|
}
|
||||||
// Pop the current breadcrumb (current folder)
|
// Pop the current breadcrumb (current folder)
|
||||||
self.breadcrumbs.pop();
|
self.breadcrumbs.pop();
|
||||||
|
let restore = self.selection_stack.pop();
|
||||||
if let Some((parent_id, parent_name)) = self.breadcrumbs.last().cloned() {
|
if let Some((parent_id, parent_name)) = self.breadcrumbs.last().cloned() {
|
||||||
if parent_id == RESUME_VIRTUAL_ID {
|
if parent_id == RESUME_VIRTUAL_ID {
|
||||||
self.items = self.resume.clone();
|
self.items = self.resume.clone();
|
||||||
@@ -189,18 +200,14 @@ impl App {
|
|||||||
self.items = self.client.items(&parent_id).await?;
|
self.items = self.client.items(&parent_id).await?;
|
||||||
}
|
}
|
||||||
self.current_title = Some(parent_name.clone());
|
self.current_title = Some(parent_name.clone());
|
||||||
self.list_state
|
self.list_state.select(restore_selection(restore, self.items.len()));
|
||||||
.select(if self.items.is_empty() { None } else { Some(0) });
|
|
||||||
self.status = format!("{}: {} items", parent_name, self.items.len());
|
self.status = format!("{}: {} items", parent_name, self.items.len());
|
||||||
} else {
|
} else {
|
||||||
self.view = View::Libraries;
|
self.view = View::Libraries;
|
||||||
self.current_title = None;
|
self.current_title = None;
|
||||||
self.items.clear();
|
self.items.clear();
|
||||||
self.list_state.select(if self.home_entries.is_empty() {
|
self.list_state
|
||||||
None
|
.select(restore_selection(restore, self.home_entries.len()));
|
||||||
} else {
|
|
||||||
Some(0)
|
|
||||||
});
|
|
||||||
self.status = format!(
|
self.status = format!(
|
||||||
"{} libraries · {} in progress",
|
"{} libraries · {} in progress",
|
||||||
self.libraries.len(),
|
self.libraries.len(),
|
||||||
@@ -245,6 +252,15 @@ impl App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Clamp the remembered selection to the current list size. Returns None if
|
||||||
|
/// the list is empty (so no row is highlighted).
|
||||||
|
fn restore_selection(saved: Option<usize>, len: usize) -> Option<usize> {
|
||||||
|
if len == 0 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(saved.unwrap_or(0).min(len - 1))
|
||||||
|
}
|
||||||
|
|
||||||
fn build_home_entries(libraries: &[Item], resume: &[Item]) -> Vec<Item> {
|
fn build_home_entries(libraries: &[Item], resume: &[Item]) -> Vec<Item> {
|
||||||
let mut entries: Vec<Item> = Vec::with_capacity(libraries.len() + 1);
|
let mut entries: Vec<Item> = Vec::with_capacity(libraries.len() + 1);
|
||||||
if !resume.is_empty() {
|
if !resume.is_empty() {
|
||||||
|
|||||||
Reference in new issue
Block a user