auto-scroll status selection
[?]
Jul 17, 2025, 10:13 AM
RDRBP7AL74NBFNZSQFTU7VQCMWTGJO5RZWGPCWVVS5WRTXJ77DFACDependencies
- [2]
WT3GA27Padd cursor with selection - [3]
L6KSEFQImove cursor related stuff into its module - [4]
BFN2VHZSrefactor file stuff into sub-mod - [5]
3BK22XE5add a test for hover btn and more refactors - [6]
WW36JYLRadd iced_nav_scrollable widget crate - [7]
K5YUSV2Wauto-scroll to last offset - [8]
KWTBNTO3diffs selection and scrolling - [9]
5MUEECMJsmooth scrolling nav - [10]
3TLPJ57Balt scroll via context and couple fixes - [11]
KQABQCCZupdate rust to 1.88 - [12]
AI3IMKC3refactor stairs - [13]
PTWZYQFRuse nav-scrollable for repo status - [14]
4PNWU55Oreplace the circular hor navigation - [*]
6YZAVBWUInitial commit
Change contents
- replacement in inflorescence/src/main.rs at line 271
state.repo.as_ref().map(|repo| &repo.state),state.repo.as_mut(), - edit in inflorescence/src/cursor.rs at line 1
use inflorescence_view::app; - replacement in inflorescence/src/cursor.rs at line 21
repo: Option<&repo::State>,repo: Option<&mut app::Repo>, - replacement in inflorescence/src/cursor.rs at line 60
Dir::Left => select_left(state, repo),Dir::Right => select_right(state, repo, files_diffs, log_diffs),Dir::Left => select_left(state, repo.as_deref()),Dir::Right => {select_right(state, repo.as_deref(), files_diffs, log_diffs)} - replacement in inflorescence/src/cursor.rs at line 102
Msg::Select(select) => {select_exact(select, state, files, repo, files_diffs, log_diffs)}Msg::Select(select) => select_exact(select,state,files,repo.as_deref(),files_diffs,log_diffs,), - replacement in inflorescence/src/cursor.rs at line 180
repo: Option<&repo::State>,repo: Option<&mut app::Repo>, - replacement in inflorescence/src/cursor.rs at line 185
let Some(repo) = repo.as_ref() else {let Some(app::Repo {state: repo_state,status_nav,}) = repoelse { - replacement in inflorescence/src/cursor.rs at line 215
} else if repo.untracked_files.len().saturating_sub(1) == ix {if !repo.changed_files.is_empty() {} else if repo_state.untracked_files.len().saturating_sub(1) == ix {// Last untracked file selectedif !repo_state.changed_files.is_empty() { - replacement in inflorescence/src/cursor.rs at line 219
let selection = changed_file_selection(repo, ix, files);(selection, Task::none())} else if !repo.log.is_empty() {let selection =changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task)} else if !repo_state.log.is_empty() { - replacement in inflorescence/src/cursor.rs at line 232
(log_selection(repo, ix), Task::none())let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(log_selection(repo_state, ix), task) - replacement in inflorescence/src/cursor.rs at line 243
let selection = untracked_file_selection(repo, ix, files);(selection, Task::none())let selection =untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 257
let selection = untracked_file_selection(repo, ix, files);(selection, Task::none())let selection = untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 292
} else if repo.changed_files.len().saturating_sub(1) == ix {if !repo.log.is_empty() {} else if repo_state.changed_files.len().saturating_sub(1) == ix {// Last changed file selectedif !repo_state.log.is_empty() { - replacement in inflorescence/src/cursor.rs at line 296
(log_selection(repo, ix), Task::none())} else if !repo.untracked_files.is_empty() {let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(log_selection(repo_state, ix), task)} else if !repo_state.untracked_files.is_empty() { - replacement in inflorescence/src/cursor.rs at line 307
let selection = untracked_file_selection(repo, ix, files);(selection, Task::none())let selection =untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 320
let selection = changed_file_selection(repo, ix, files);(selection, Task::none())let selection =changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 334
let selection = changed_file_selection(repo, ix, files);(selection, Task::none())let selection = changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 383
let log_entry = repo.log.get(log_ix).unwrap();let log_entry = repo_state.log.get(log_ix).unwrap(); - replacement in inflorescence/src/cursor.rs at line 408[12.5418]→[12.5418:5897](∅→∅),[12.5897]→[8.29597:29634](∅→∅),[8.29597]→[8.29597:29634](∅→∅),[8.29634]→[12.5898:5942](∅→∅),[12.5942]→[8.29687:29743](∅→∅),[8.29687]→[8.29687:29743](∅→∅),[8.29743]→[12.5943:5973](∅→∅)
let selection =if repo.log.len().saturating_sub(1) == log_ix {if !repo.untracked_files.is_empty() {let ix = 0;untracked_file_selection(repo, ix, files)} else if !repo.changed_files.is_empty() {let ix = 0;changed_file_selection(repo, ix, files)} else {let ix = 0;log_selection(repo, ix)}if repo_state.log.len().saturating_sub(1) == log_ix {// Last log selectedif !repo_state.untracked_files.is_empty() {let ix = 0;let task =iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(untracked_file_selection(repo_state, ix, files),task,)} else if !repo_state.changed_files.is_empty() {let ix = 0;let task =iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(changed_file_selection(repo_state, ix, files),task,) - replacement in inflorescence/src/cursor.rs at line 441
let ix = log_ix + 1;log_selection(repo, ix)};(selection, Task::none())let ix = 0;let task =iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(log_selection(repo_state, ix), task)}} else {let ix = log_ix + 1;let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(log_selection(repo_state, ix), task)} - replacement in inflorescence/src/cursor.rs at line 470
let (selection, task) = if !repo.untracked_files.is_empty() {if !repo_state.untracked_files.is_empty() { - replacement in inflorescence/src/cursor.rs at line 472
let selection = Some(untracked_file_selection(repo, ix, files));(selection, Task::none())} else if !repo.changed_files.is_empty() {let selection =Some(untracked_file_selection(repo_state, ix, files));let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task)} else if !repo_state.changed_files.is_empty() { - replacement in inflorescence/src/cursor.rs at line 485
let selection = Some(changed_file_selection(repo, ix, files));(selection, Task::none())} else if !repo.log.is_empty() {let ix = repo.log.len() - 1;let selection = log_selection(repo, ix);(Some(selection), Task::none())let selection =Some(changed_file_selection(repo_state, ix, files));let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task)} else if !repo_state.log.is_empty() {let ix = repo_state.log.len() - 1;let selection = log_selection(repo_state, ix);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state, ix, StatusSectionKind::Log),status_nav,);(Some(selection), task) - replacement in inflorescence/src/cursor.rs at line 506
};(selection, task)} - replacement in inflorescence/src/cursor.rs at line 516
repo: Option<&repo::State>,repo: Option<&mut app::Repo>, - replacement in inflorescence/src/cursor.rs at line 521
let Some(repo) = repo.as_ref() else {let Some(app::Repo {state: repo_state,status_nav,}) = repoelse { - replacement in inflorescence/src/cursor.rs at line 552
if !repo.log.is_empty() {let ix = repo.log.len() - 1;(log_selection(repo, ix), Task::none())} else if !repo.changed_files.is_empty() {let ix = repo.changed_files.len() - 1;let selection = changed_file_selection(repo, ix, files);(selection, Task::none())// First untracked file selectedif !repo_state.log.is_empty() {let ix = repo_state.log.len() - 1;let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(log_selection(repo_state, ix), task)} else if !repo_state.changed_files.is_empty() {let ix = repo_state.changed_files.len() - 1;let selection =changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 578
let ix = repo.untracked_files.len() - 1;let selection = untracked_file_selection(repo, ix, files);(selection, Task::none())let ix = repo_state.untracked_files.len() - 1;let selection =untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 593
let selection = untracked_file_selection(repo, ix, files);(selection, Task::none())let selection = untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 629
let selection = if !repo.untracked_files.is_empty() {let ix = repo.untracked_files.len() - 1;untracked_file_selection(repo, ix, files)} else if !repo.log.is_empty() {let ix = repo.log.len() - 1;log_selection(repo, ix)// First changed file selectedif !repo_state.untracked_files.is_empty() {let ix = repo_state.untracked_files.len() - 1;let selection =untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task)} else if !repo_state.log.is_empty() {let ix = repo_state.log.len() - 1;let selection = log_selection(repo_state, ix);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 656[8.34503]→[12.9779:9898](∅→∅),[12.9898]→[8.34663:34682](∅→∅),[8.34663]→[8.34663:34682](∅→∅),[8.34682]→[12.9899:9941](∅→∅)
let ix = repo.changed_files.len() - 1;changed_file_selection(repo, ix, files)};(selection, Task::none())let ix = repo_state.changed_files.len() - 1;let selection =changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task)} - replacement in inflorescence/src/cursor.rs at line 671
let selection = changed_file_selection(repo, ix, files);(selection, Task::none())let selection = changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 720
let log_entry = repo.log.get(log_ix).unwrap();let log_entry = repo_state.log.get(log_ix).unwrap(); - replacement in inflorescence/src/cursor.rs at line 741
let selection = if 0 == log_ix {if !repo.changed_files.is_empty() {let ix = repo.changed_files.len() - 1;changed_file_selection(repo, ix, files)} else if !repo.untracked_files.is_empty() {let ix = repo.untracked_files.len() - 1;untracked_file_selection(repo, ix, files)if 0 == log_ix {// First log selectedif !repo_state.changed_files.is_empty() {let ix = repo_state.changed_files.len() - 1;let selection =changed_file_selection(repo_state, ix, files);let task =iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Changed,),status_nav,);(selection, task)} else if !repo_state.untracked_files.is_empty() {let ix = repo_state.untracked_files.len() - 1;let selection =untracked_file_selection(repo_state, ix, files);let task =iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 772
let ix = repo.log.len() - 1;log_selection(repo, ix)let ix = repo_state.log.len() - 1;let selection = log_selection(repo_state, ix);let task =iced_nav_scrollable::scroll_down_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(selection, task) - replacement in inflorescence/src/cursor.rs at line 787
log_selection(repo, ix)};(selection, Task::none())let selection = log_selection(repo_state, ix);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Log,),status_nav,);(selection, task)} - replacement in inflorescence/src/cursor.rs at line 803
let (selection, task) = if !repo.log.is_empty() {let ix = repo.log.len() - 1;let selection = log_selection(repo, ix);(Some(selection), Task::none())} else if !repo.changed_files.is_empty() {let ix = repo.changed_files.len() - 1;let selection = changed_file_selection(repo, ix, files);(Some(selection), Task::none())} else if !repo.untracked_files.is_empty() {let ix = repo.untracked_files.len() - 1;let selection = untracked_file_selection(repo, ix, files);(Some(selection), Task::none())if !repo_state.log.is_empty() {let ix = repo_state.log.len() - 1;let selection = log_selection(repo_state, ix);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state, ix, StatusSectionKind::Log),status_nav,);(Some(selection), task)} else if !repo_state.changed_files.is_empty() {let ix = repo_state.changed_files.len() - 1;let selection = changed_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(Some(selection), task)} else if !repo_state.untracked_files.is_empty() {let ix = repo_state.untracked_files.len() - 1;let selection = untracked_file_selection(repo_state, ix, files);let task = iced_nav_scrollable::scroll_up_to_section(status_section_ix(repo_state,ix,StatusSectionKind::Untracked,),status_nav,);(Some(selection), task) - replacement in inflorescence/src/cursor.rs at line 837
};(selection, task)} - replacement in inflorescence/src/cursor.rs at line 977
fn select_left<M>(state: &mut State, repo: Option<&repo::State>) -> Task<M> {fn select_left<M>(state: &mut State, repo: Option<&app::Repo>) -> Task<M> { - replacement in inflorescence/src/cursor.rs at line 1042
repo: Option<&repo::State>,repo: Option<&app::Repo>, - replacement in inflorescence/src/cursor.rs at line 1046
let Some(repo) = repo.as_ref() else {let Some(app::Repo {state: repo_state,status_nav: _,}) = repo.as_ref()else { - replacement in inflorescence/src/cursor.rs at line 1157
let log_entry = repo.log.get(ix).unwrap();let log_entry = repo_state.log.get(ix).unwrap(); - replacement in inflorescence/src/cursor.rs at line 1260
repo: Option<&repo::State>,repo: Option<&app::Repo>, - edit in inflorescence/src/cursor.rs at line 1264
let Some(app::Repo {state: repo_state,status_nav: _,}) = repoelse {return Task::none();}; - replacement in inflorescence/src/cursor.rs at line 1307
if let Some(diffs) =repo.as_ref().and_then(|repo| repo.changed_files.get(&path))if let Some(diffs) = repo_state.changed_files.get(&path) - edit in inflorescence/src/cursor.rs at line 1387
}enum StatusSectionKind {Untracked,Changed,Log, - edit in inflorescence/src/cursor.rs at line 1394[3.27247]
fn status_section_ix(repo: &repo::State,ix: usize,kind: StatusSectionKind,) -> usize {// The literals are for section headersmatch kind {StatusSectionKind::Untracked => 1 + ix,StatusSectionKind::Changed => 2 + repo.untracked_files.len() + ix,StatusSectionKind::Log => {3 + repo.untracked_files.len() + repo.changed_files.len() + ix}}} - edit in iced_nav_scrollable/src/lib.rs at line 259
}enum Delay {Start,Apply, - edit in iced_nav_scrollable/src/lib.rs at line 452
}pub fn scroll_down_to_section<M>(section_ix: usize,nav: &mut NavScrollable,) -> Task<M> {// If the given section is below the bottom frame, scroll down to align// their bottom edgeslet (offset, height) = nav.section_offsets.iter().zip(nav.section_heights.values()).nth(section_ix).unwrap();if let Some(y) = {// dbg!(ix, offset, height, nav.offset);let bottom_frame =saturating_sub(nav.offset + nav.height, VISIBLE_CONTEXT_HEIGHT);let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;if offset + height > bottom_frame {Some(saturating_sub(offset + height + VISIBLE_CONTEXT_HEIGHT,nav.height,))} else if *offset < top_frame {Some(saturating_sub(*offset, VISIBLE_CONTEXT_HEIGHT))} else {None}} {return task::scroll_to(nav.id.clone(),scrollable::AbsoluteOffset { x: 0.0, y },);}Task::none() - edit in iced_nav_scrollable/src/lib.rs at line 490
pub fn scroll_up_to_section<M>(section_ix: usize,nav: &mut NavScrollable,) -> Task<M> {// If the given section is above the top frame, scroll up to align their top// edgeslet (offset, height) = nav.section_offsets.iter().zip(nav.section_heights.values()).nth(section_ix).unwrap();if let Some(y) = {// dbg!(ix, offset, height, nav.offset);let bottom_frame =saturating_sub(nav.offset + nav.height, VISIBLE_CONTEXT_HEIGHT);let top_frame = nav.offset + VISIBLE_CONTEXT_HEIGHT;if *offset < top_frame {Some(saturating_sub(*offset, VISIBLE_CONTEXT_HEIGHT))} else if offset + height > bottom_frame {Some(saturating_sub(offset + height + VISIBLE_CONTEXT_HEIGHT,nav.height,))} else {None}} {return task::scroll_to(nav.id.clone(),scrollable::AbsoluteOffset { x: 0.0, y },);}Task::none()}enum Delay {Start,Apply,}