diff --git a/Cargo.lock b/Cargo.lock index 598bbc55..a5f31868 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -363,6 +363,7 @@ dependencies = [ "crossbeam-channel", "crossterm 0.19.0", "dirs-next", + "easy-cast", "itertools", "log", "pprof", diff --git a/Cargo.toml b/Cargo.toml index 8644cf39..47ab8d53 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,6 +41,7 @@ anyhow = "1.0" unicode-width = "0.1" textwrap = "0.13" unicode-truncate = "0.2.0" +easy-cast = "0.4" [target.'cfg(all(target_family="unix",not(target_os="macos")))'.dependencies] which = "4.1" diff --git a/src/components/branchlist.rs b/src/components/branchlist.rs index 73fe1982..de41295c 100644 --- a/src/components/branchlist.rs +++ b/src/components/branchlist.rs @@ -505,6 +505,7 @@ impl BranchListComponent { r: Rect, ) -> Result<()> { let height_in_lines = r.height as usize; + self.current_height.set(height_in_lines.try_into()?); self.scroll_top.set(calc_scroll_top( self.scroll_top.get(), @@ -524,17 +525,17 @@ impl BranchListComponent { let mut r = r; r.width += 1; + r.height += 2; + r.y = r.y.saturating_sub(1); ui::draw_scrollbar( f, r, &self.theme, - self.branches.len(), + self.branches.len().saturating_sub(height_in_lines), self.scroll_top.get(), ); - self.current_height.set(height_in_lines.try_into()?); - Ok(()) } } diff --git a/src/components/diff.rs b/src/components/diff.rs index 8e894310..de449f6e 100644 --- a/src/components/diff.rs +++ b/src/components/diff.rs @@ -237,16 +237,12 @@ impl DiffComponent { } fn lines_count(&self) -> usize { - self.diff - .as_ref() - .map_or(0, |diff| diff.lines.saturating_sub(1)) + self.diff.as_ref().map_or(0, |diff| diff.lines) } fn modify_selection(&mut self, direction: Direction) { - if let Some(diff) = &self.diff { - let max = diff.lines.saturating_sub(1); - - self.selection.modify(direction, max); + if self.diff.is_some() { + self.selection.modify(direction, self.lines_count()); } } @@ -610,9 +606,11 @@ impl DrawableComponent for DiffComponent { r.height.saturating_sub(2), )); + let current_height = self.current_size.get().1; + self.scroll_top.set(calc_scroll_top( self.scroll_top.get(), - self.current_size.get().1 as usize, + current_height as usize, self.selection.get_end(), )); @@ -628,7 +626,7 @@ impl DrawableComponent for DiffComponent { self.theme.text(false, false), )])] } else { - self.get_text(r.width, self.current_size.get().1) + self.get_text(r.width, current_height) }; f.render_widget( @@ -648,7 +646,8 @@ impl DrawableComponent for DiffComponent { f, r, &self.theme, - self.lines_count(), + self.lines_count() + .saturating_sub(usize::from(current_height)), self.scroll_top.get(), ); } diff --git a/src/ui/scrollbar.rs b/src/ui/scrollbar.rs index 73f2ab9b..a77d3023 100644 --- a/src/ui/scrollbar.rs +++ b/src/ui/scrollbar.rs @@ -1,4 +1,5 @@ use super::style::SharedTheme; +use easy_cast::CastFloat; use std::convert::TryFrom; use tui::{ backend::Backend, @@ -12,16 +13,16 @@ use tui::{ /// struct Scrollbar { - lines: u16, + max: u16, pos: u16, style_bar: Style, style_pos: Style, } impl Scrollbar { - fn new(lines: usize, pos: usize) -> Self { + fn new(max: usize, pos: usize) -> Self { Self { - lines: u16::try_from(lines).unwrap_or_default(), + max: u16::try_from(max).unwrap_or_default(), pos: u16::try_from(pos).unwrap_or_default(), style_pos: Style::default(), style_bar: Style::default(), @@ -31,39 +32,40 @@ impl Scrollbar { impl Widget for Scrollbar { fn render(self, area: Rect, buf: &mut Buffer) { + if area.height <= 2 { + return; + } + + if self.max == 0 { + return; + } + let right = area.right().saturating_sub(1); if right <= area.left() { return; }; - let area = area.inner(&Margin { - horizontal: 0, - vertical: 1, - }); + let (bar_top, bar_height) = { + let scrollbar_area = area.inner(&Margin { + horizontal: 0, + vertical: 1, + }); - if area.height == 0 { - return; - } + (scrollbar_area.top(), scrollbar_area.height) + }; - if area.height >= self.lines { - return; - } - - for y in area.top()..area.bottom() { + for y in bar_top..(bar_top + bar_height) { buf.set_string(right, y, DOUBLE_VERTICAL, self.style_bar); } - let max_pos = self.lines.saturating_sub(area.height); - let progress = f32::from(self.pos) / f32::from(max_pos); + let progress = f32::from(self.pos) / f32::from(self.max); let progress = if progress > 1.0 { 1.0 } else { progress }; - let pos = f32::from(area.height) * progress; + let pos = f32::from(bar_height) * progress; - //TODO: any better way for this? - #[allow(clippy::cast_sign_loss)] - #[allow(clippy::cast_possible_truncation)] - let pos = (pos as u16).saturating_sub(1); + let pos: u16 = pos.cast_nearest(); + let pos = pos.saturating_sub(1); - buf.set_string(right, area.top() + pos, FULL, self.style_pos); + buf.set_string(right, bar_top + pos, FULL, self.style_pos); } } @@ -71,10 +73,10 @@ pub fn draw_scrollbar( f: &mut Frame, r: Rect, theme: &SharedTheme, - lines: usize, + max: usize, pos: usize, ) { - let mut widget = Scrollbar::new(lines, pos); + let mut widget = Scrollbar::new(max, pos); widget.style_pos = theme.scroll_bar_pos(); f.render_widget(widget, r) }