diff --git a/src/app/content.rs b/src/app/content.rs index 42f61e3..2134877 100644 --- a/src/app/content.rs +++ b/src/app/content.rs @@ -54,7 +54,7 @@ impl App { pub(crate) fn reload(&mut self, ss: &SyntaxSet, themes: &ThemeSet) -> bool { self.reset_numkey_state(); - self.reset_toc_scroll_mode(); + self.clear_toc_scroll_state(); let path = match &self.filepath { Some(p) => p, None => return false, @@ -130,7 +130,7 @@ impl App { self.invalidate_theme_preview_cache(); self.store_current_theme_preview_from(&parsed.lines, &parsed.toc); self.replace_content(parsed); - self.reset_toc_scroll_mode(); + self.clear_toc_scroll_state(); true } diff --git a/src/app/mod.rs b/src/app/mod.rs index 7b84868..ee553b8 100644 --- a/src/app/mod.rs +++ b/src/app/mod.rs @@ -50,10 +50,18 @@ pub(crate) use theme_picker::ThemePickerState; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub(crate) enum TocScrollMode { - Auto, + Auto(usize), Manual(usize), } +impl TocScrollMode { + pub(crate) fn offset(self) -> usize { + match self { + TocScrollMode::Auto(o) | TocScrollMode::Manual(o) => o, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub(crate) struct StatusCacheKey { pct: u16, @@ -260,7 +268,7 @@ impl App { toc_active_idx: None, toc_active_display_idx: None, hovered_toc_idx: None, - toc_scroll_mode: TocScrollMode::Auto, + toc_scroll_mode: TocScrollMode::Auto(0), toc_scroll_hint_dismissed: false, toc_active_pin: None, status_line: Line::default(), @@ -539,6 +547,7 @@ impl App { pub(crate) fn refresh_toc_cache(&mut self) { let active_idx = self.active_toc_index(); if self.toc_active_idx == active_idx && !self.toc_display_lines.is_empty() { + self.adjust_auto_toc_offset(); return; } @@ -568,6 +577,30 @@ impl App { self.toc_display_lines = lines; self.toc_display_entries = entries; self.toc_active_display_idx = active_display_idx; + self.adjust_auto_toc_offset(); + } + + fn adjust_auto_toc_offset(&mut self) { + let TocScrollMode::Auto(current_offset) = self.toc_scroll_mode else { + return; + }; + let (Some(active), Some(area)) = (self.toc_active_display_idx, self.toc_list_area) else { + return; + }; + let list_height = area.height as usize; + if list_height == 0 { + return; + } + let max_offset = self.max_toc_scroll_offset(area.height); + + let new_offset = if active + 1 >= current_offset + list_height { + (active + 2).saturating_sub(list_height) + } else if active <= current_offset { + active.saturating_sub(1) + } else { + current_offset + }; + self.toc_scroll_mode = TocScrollMode::Auto(new_offset.min(max_offset)); } pub(crate) fn refresh_status_cache(&mut self, pct: u16) { diff --git a/src/app/navigation.rs b/src/app/navigation.rs index bcfe44a..2ad6f2e 100644 --- a/src/app/navigation.rs +++ b/src/app/navigation.rs @@ -177,31 +177,24 @@ impl App { .is_some_and(|area| self.toc_overflows(area.height)) } - fn max_toc_scroll_offset(&self, list_height: u16) -> usize { + pub(super) fn max_toc_scroll_offset(&self, list_height: u16) -> usize { (self.toc_display_lines.len() + 1).saturating_sub(list_height as usize) } pub(crate) fn toc_scroll_offset(&self, list_height: u16) -> usize { let max_offset = self.max_toc_scroll_offset(list_height); - let list_height = list_height as usize; - match self.toc_scroll_mode { - TocScrollMode::Manual(offset) => offset.min(max_offset), - TocScrollMode::Auto => { - let Some(active_display_idx) = self.toc_active_display_idx else { - return 0; - }; - let mut offset = 0usize; - if active_display_idx + 1 >= list_height { - offset = active_display_idx + 2 - list_height; - } - offset.min(max_offset) - } - } + self.toc_scroll_mode.offset().min(max_offset) } pub(crate) fn reset_toc_scroll_mode(&mut self) { - self.toc_scroll_mode = TocScrollMode::Auto; + self.toc_scroll_mode = TocScrollMode::Auto(self.toc_scroll_mode.offset()); + self.toc_active_pin = None; + } + + pub(crate) fn clear_toc_scroll_state(&mut self) { + self.toc_scroll_mode = TocScrollMode::Auto(0); self.toc_active_pin = None; + self.toc_scroll_hint_dismissed = false; } pub(crate) fn scroll_toc_down(&mut self, n: usize) { diff --git a/src/tests/toc.rs b/src/tests/toc.rs index c4c437b..94e05e9 100644 --- a/src/tests/toc.rs +++ b/src/tests/toc.rs @@ -220,7 +220,7 @@ fn make_app_with_overflowing_toc() -> App { #[test] fn toc_scroll_mode_default_is_auto() { let app = make_app_with_toc(10, 15, toc(&[(2, 0)])); - assert_eq!(app.toc_scroll_mode(), TocScrollMode::Auto); + assert_eq!(app.toc_scroll_mode(), TocScrollMode::Auto(0)); assert!(!app.is_toc_scroll_hint_dismissed()); } @@ -279,7 +279,7 @@ fn content_scroll_resets_toc_scroll_mode_to_auto() { app.scroll_toc_down(5); assert!(matches!(app.toc_scroll_mode(), TocScrollMode::Manual(_))); app.scroll_down(1); - assert_eq!(app.toc_scroll_mode(), TocScrollMode::Auto); + assert!(matches!(app.toc_scroll_mode(), TocScrollMode::Auto(_))); } #[test]