Skip to content

Commit 425963c

Browse files
author
Stephan Dilly
authored
Scrollbar in diff (#251)
closes #204
1 parent 2401293 commit 425963c

File tree

6 files changed

+103
-2
lines changed

6 files changed

+103
-2
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212
- fully **customizable key bindings** (see [KEY_CONFIG.md](KEY_CONFIG.md)) [[@yanganto](https://github.com/yanganto)] ([#109](https://github.com/extrawurst/gitui/issues/109)) ([#57](https://github.com/extrawurst/gitui/issues/57))
1313
- support scrolling in long commit messages [[@cruessler](https://github.com/cruessler)]([#208](https://github.com/extrawurst/gitui/issues/208))
1414
- copy lines from diffs to clipboard [[@cruessler](https://github.com/cruessler)]([#229](https://github.com/extrawurst/gitui/issues/229))
15+
- scrollbar in long diffs ([#204](https://github.com/extrawurst/gitui/issues/204))
16+
17+
![scrollbar](assets/scrollbar.gif)
1518

1619
### Fixed
1720

assets/scrollbar.gif

391 KB
Loading

src/components/diff.rs

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ use crate::{
66
keys::SharedKeyConfig,
77
queue::{Action, InternalEvent, NeedsUpdate, Queue, ResetItem},
88
strings, try_or_popup,
9-
ui::{calc_scroll_top, style::SharedTheme},
9+
ui::{self, calc_scroll_top, style::SharedTheme},
1010
};
1111
use asyncgit::{hash, sync, DiffLine, DiffLineType, FileDiff, CWD};
1212
use bytesize::ByteSize;
@@ -224,12 +224,18 @@ impl DiffComponent {
224224
Ok(())
225225
}
226226

227+
fn lines_count(&self) -> usize {
228+
self.diff
229+
.as_ref()
230+
.map_or(0, |diff| diff.lines.saturating_sub(1))
231+
}
232+
227233
fn modify_selection(
228234
&mut self,
229235
direction: Direction,
230236
) -> Result<()> {
231237
if let Some(diff) = &self.diff {
232-
let max = diff.lines.saturating_sub(1) as usize;
238+
let max = diff.lines.saturating_sub(1);
233239

234240
self.selection.modify(direction, max);
235241
}
@@ -583,6 +589,15 @@ impl DrawableComponent for DiffComponent {
583589
),
584590
r,
585591
);
592+
if self.focused {
593+
ui::draw_scrollbar(
594+
f,
595+
r,
596+
&self.theme,
597+
self.lines_count(),
598+
self.selection.get_end(),
599+
);
600+
}
586601

587602
Ok(())
588603
}

src/ui/mod.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
mod scrollbar;
12
mod scrolllist;
23
pub mod style;
34

5+
pub use scrollbar::draw_scrollbar;
46
pub use scrolllist::draw_list;
57
use tui::layout::{Constraint, Direction, Layout, Rect};
68

src/ui/scrollbar.rs

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
use super::style::SharedTheme;
2+
use std::convert::TryFrom;
3+
use tui::{
4+
backend::Backend,
5+
buffer::Buffer,
6+
layout::{Margin, Rect},
7+
style::Style,
8+
symbols::{block::FULL, line::THICK_VERTICAL},
9+
widgets::Widget,
10+
Frame,
11+
};
12+
13+
///
14+
struct Scrollbar {
15+
max: u16,
16+
pos: u16,
17+
style_bar: Style,
18+
style_pos: Style,
19+
}
20+
21+
impl Scrollbar {
22+
fn new(max: usize, pos: usize) -> Self {
23+
Self {
24+
max: u16::try_from(max).unwrap_or_default(),
25+
pos: u16::try_from(pos).unwrap_or_default(),
26+
style_pos: Style::default(),
27+
style_bar: Style::default(),
28+
}
29+
}
30+
}
31+
32+
impl Widget for Scrollbar {
33+
fn render(self, area: Rect, buf: &mut Buffer) {
34+
let right = area.right().saturating_sub(1);
35+
if right <= area.left() {
36+
return;
37+
};
38+
39+
let area = area.inner(&Margin {
40+
horizontal: 0,
41+
vertical: 1,
42+
});
43+
44+
if area.height <= 4 {
45+
return;
46+
}
47+
48+
if area.height > self.max {
49+
return;
50+
}
51+
52+
for y in area.top()..area.bottom() {
53+
buf.set_string(right, y, THICK_VERTICAL, self.style_bar);
54+
}
55+
56+
let progress = f32::from(self.pos) / f32::from(self.max);
57+
let pos = f32::from(area.height.saturating_sub(1)) * progress;
58+
//TODO: any better way for this?
59+
#[allow(clippy::cast_sign_loss)]
60+
#[allow(clippy::cast_possible_truncation)]
61+
let pos = pos as u16;
62+
63+
buf.set_string(right, area.top() + pos, FULL, self.style_pos);
64+
}
65+
}
66+
67+
pub fn draw_scrollbar<B: Backend>(
68+
f: &mut Frame<B>,
69+
r: Rect,
70+
theme: &SharedTheme,
71+
max: usize,
72+
pos: usize,
73+
) {
74+
let mut widget = Scrollbar::new(max, pos);
75+
widget.style_pos = theme.scroll_bar_pos();
76+
f.render_widget(widget, r)
77+
}

src/ui/style.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ pub struct Theme {
5151
}
5252

5353
impl Theme {
54+
pub fn scroll_bar_pos(&self) -> Style {
55+
Style::default().fg(self.selection_bg)
56+
}
57+
5458
pub fn block(&self, focus: bool) -> Style {
5559
if focus {
5660
Style::default()

0 commit comments

Comments
 (0)