diff --git a/src/components/revision_files.rs b/src/components/revision_files.rs index f0f86f01..6ec05654 100644 --- a/src/components/revision_files.rs +++ b/src/components/revision_files.rs @@ -193,7 +193,8 @@ impl DrawableComponent for RevisionFilesComponent { ) .split(area); - let tree_height = usize::from(chunks[0].height); + let tree_height = + usize::from(chunks[0].height.saturating_sub(2)); let selection = self.tree.visual_selection(); selection.map_or_else( @@ -264,16 +265,21 @@ impl Component for RevisionFilesComponent { .order(1), ); - out.push( - CommandInfo::new( - strings::commands::blame_file(&self.key_config), - self.tree.selected_file().is_some(), - true, - ) - .order(order::NAV), - ); - - tree_nav_cmds(&self.tree, &self.key_config, out); + if matches!(self.focus, Focus::Tree) || force_all { + out.push( + CommandInfo::new( + strings::commands::blame_file( + &self.key_config, + ), + self.tree.selected_file().is_some(), + true, + ) + .order(order::NAV), + ); + tree_nav_cmds(&self.tree, &self.key_config, out); + } else { + self.current_file.commands(out, force_all); + } } visibility_blocking(self) diff --git a/src/components/syntax_text.rs b/src/components/syntax_text.rs index 55c22bd0..46fc960e 100644 --- a/src/components/syntax_text.rs +++ b/src/components/syntax_text.rs @@ -4,6 +4,7 @@ use super::{ }; use crate::{ keys::SharedKeyConfig, + strings, ui::{ self, style::SharedTheme, AsyncSyntaxJob, ParagraphState, ScrollPos, StatefulParagraph, @@ -32,7 +33,7 @@ pub struct SyntaxTextComponent { async_highlighting: AsyncSingleJob, key_config: SharedKeyConfig, - scroll_top: Cell, + paragraph_state: Cell, focused: bool, theme: SharedTheme, } @@ -50,7 +51,7 @@ impl SyntaxTextComponent { AsyncNotification::SyntaxHighlighting, ), current_file: None, - scroll_top: Cell::new(0), + paragraph_state: Cell::new(ParagraphState::default()), focused: false, key_config, theme, @@ -118,6 +119,36 @@ impl SyntaxTextComponent { } } } + + fn scroll(&self, down: Option) { + let mut state = self.paragraph_state.get(); + let new_scroll_pos = down.map_or_else( + || state.scroll().y, + |down| { + if down { + state.scroll().y.saturating_add(1) + } else { + state.scroll().y.saturating_sub(1) + } + }, + ); + + let new_scroll_pos = new_scroll_pos.min( + state + .lines() + .saturating_sub(state.height().saturating_sub(2)), + ); + + if new_scroll_pos == state.scroll().y { + return; + } + + state.set_scroll(ScrollPos { + x: 0, + y: new_scroll_pos, + }); + self.paragraph_state.set(state); + } } impl DrawableComponent for SyntaxTextComponent { @@ -148,16 +179,13 @@ impl DrawableComponent for SyntaxTextComponent { .border_style(self.theme.title(self.focused())), ); - let mut state = ParagraphState::default(); - state.set_scroll(ScrollPos::new(0, self.scroll_top.get())); + let mut state = self.paragraph_state.get(); f.render_stateful_widget(content, area, &mut state); - self.scroll_top.set( - self.scroll_top - .get() - .min(state.lines().saturating_sub(area.height)), - ); + self.paragraph_state.set(state); + + self.scroll(None); Ok(()) } @@ -166,10 +194,19 @@ impl DrawableComponent for SyntaxTextComponent { impl Component for SyntaxTextComponent { fn commands( &self, - _out: &mut Vec, - _force_all: bool, + out: &mut Vec, + force_all: bool, ) -> CommandBlocking { - //TODO: scrolling + if self.focused() || force_all { + out.push( + CommandInfo::new( + strings::commands::scroll(&self.key_config), + true, + true, + ) + .order(strings::order::NAV), + ); + } CommandBlocking::PassingOn } @@ -179,11 +216,9 @@ impl Component for SyntaxTextComponent { ) -> Result { if let Event::Key(key) = event { if key == self.key_config.move_down { - self.scroll_top - .set(self.scroll_top.get().saturating_add(1)); + self.scroll(Some(true)); } else if key == self.key_config.move_up { - self.scroll_top - .set(self.scroll_top.get().saturating_sub(1)); + self.scroll(Some(false)); } } diff --git a/src/ui/stateful_paragraph.rs b/src/ui/stateful_paragraph.rs index 089a7c4e..f785c60b 100644 --- a/src/ui/stateful_paragraph.rs +++ b/src/ui/stateful_paragraph.rs @@ -45,8 +45,8 @@ pub struct StatefulParagraph<'a> { #[derive(Debug, Default, Clone, Copy)] pub struct ScrollPos { - x: u16, - y: u16, + pub x: u16, + pub y: u16, } impl ScrollPos { @@ -61,6 +61,8 @@ pub struct ParagraphState { scroll: ScrollPos, /// after all wrapping this is the amount of lines lines: u16, + /// last visible height + height: u16, } impl ParagraphState { @@ -68,6 +70,10 @@ impl ParagraphState { self.lines } + pub const fn height(self) -> u16 { + self.height + } + pub const fn scroll(self) -> ScrollPos { self.scroll } @@ -202,5 +208,6 @@ impl<'a> StatefulWidget for StatefulParagraph<'a> { } state.lines = y; + state.height = area.height; } }