Fix select branch list scrolling (#380)

Closes #368
This commit is contained in:
Richard Menzies 2020-10-31 11:44:51 +00:00 committed by GitHub
parent 7ba62b82df
commit bbc0488d22
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -3,9 +3,11 @@ use super::{
DrawableComponent, DrawableComponent,
}; };
use crate::{ use crate::{
components::ScrollType,
keys::SharedKeyConfig, keys::SharedKeyConfig,
queue::{Action, InternalEvent, NeedsUpdate, Queue}, queue::{Action, InternalEvent, NeedsUpdate, Queue},
strings, ui, strings,
ui::{self, calc_scroll_top},
}; };
use asyncgit::{ use asyncgit::{
sync::{ sync::{
@ -14,7 +16,7 @@ use asyncgit::{
CWD, CWD,
}; };
use crossterm::event::Event; use crossterm::event::Event;
use std::{cmp, convert::TryFrom}; use std::{cell::Cell, convert::TryInto};
use tui::{ use tui::{
backend::Backend, backend::Backend,
layout::{Alignment, Rect}, layout::{Alignment, Rect},
@ -32,6 +34,7 @@ pub struct SelectBranchComponent {
branch_names: Vec<BranchForDisplay>, branch_names: Vec<BranchForDisplay>,
visible: bool, visible: bool,
selection: u16, selection: u16,
scroll_top: Cell<usize>,
queue: Queue, queue: Queue,
theme: SharedTheme, theme: SharedTheme,
key_config: SharedKeyConfig, key_config: SharedKeyConfig,
@ -56,22 +59,28 @@ impl DrawableComponent for SelectBranchComponent {
ui::rect_inside(MIN_SIZE, f.size().into(), area); ui::rect_inside(MIN_SIZE, f.size().into(), area);
let area = area.intersection(rect); let area = area.intersection(rect);
let scroll_threshold = area.height / 3; let height_in_lines =
let scroll = (area.height as usize).saturating_sub(2);
self.selection.saturating_sub(scroll_threshold);
self.scroll_top.set(calc_scroll_top(
self.scroll_top.get(),
height_in_lines,
self.selection as usize,
));
f.render_widget(Clear, area); f.render_widget(Clear, area);
f.render_widget( f.render_widget(
Paragraph::new( Paragraph::new(self.get_text(
self.get_text(&self.theme, area.width)?, &self.theme,
) area.width,
height_in_lines,
)?)
.block( .block(
Block::default() Block::default()
.title(strings::SELECT_BRANCH_POPUP_MSG) .title(strings::SELECT_BRANCH_POPUP_MSG)
.borders(Borders::ALL) .borders(Borders::ALL)
.border_type(BorderType::Thick), .border_type(BorderType::Thick),
) )
.scroll((scroll, 0))
.alignment(Alignment::Left), .alignment(Alignment::Left),
area, area,
); );
@ -135,9 +144,9 @@ impl Component for SelectBranchComponent {
if e == self.key_config.exit_popup { if e == self.key_config.exit_popup {
self.hide() self.hide()
} else if e == self.key_config.move_down { } else if e == self.key_config.move_down {
self.move_selection(true) return self.move_selection(ScrollType::Up);
} else if e == self.key_config.move_up { } else if e == self.key_config.move_up {
self.move_selection(false) return self.move_selection(ScrollType::Down);
} else if e == self.key_config.enter { } else if e == self.key_config.enter {
if let Err(e) = self.switch_to_selected_branch() { if let Err(e) = self.switch_to_selected_branch() {
log::error!("switch branch error: {}", e); log::error!("switch branch error: {}", e);
@ -211,6 +220,7 @@ impl SelectBranchComponent {
branch_names: Vec::new(), branch_names: Vec::new(),
visible: false, visible: false,
selection: 0, selection: 0,
scroll_top: Cell::new(0),
queue, queue,
theme, theme,
key_config, key_config,
@ -248,21 +258,23 @@ impl SelectBranchComponent {
} }
/// ///
fn move_selection(&mut self, inc: bool) { fn move_selection(&mut self, scroll: ScrollType) -> Result<bool> {
let mut new_selection = self.selection; let num_branches: u16 = self.branch_names.len().try_into()?;
let num_branches = num_branches.saturating_sub(1);
new_selection = if inc { let mut new_selection = match scroll {
new_selection.saturating_add(1) ScrollType::Up => self.selection.saturating_add(1),
} else { ScrollType::Down => self.selection.saturating_sub(1),
new_selection.saturating_sub(1) _ => self.selection,
}; };
new_selection = cmp::max(new_selection, 0);
if let Ok(max) = if new_selection > num_branches {
u16::try_from(self.branch_names.len().saturating_sub(1)) new_selection = num_branches;
{
self.selection = cmp::min(new_selection, max);
} }
self.selection = new_selection;
Ok(true)
} }
/// Get branches to display /// Get branches to display
@ -270,6 +282,7 @@ impl SelectBranchComponent {
&self, &self,
theme: &SharedTheme, theme: &SharedTheme,
width_available: u16, width_available: u16,
height: usize,
) -> Result<Text> { ) -> Result<Text> {
const COMMIT_HASH_LENGTH: usize = 8; const COMMIT_HASH_LENGTH: usize = 8;
const IS_HEAD_STAR_LENGTH: usize = 3; // "* " const IS_HEAD_STAR_LENGTH: usize = 3; // "* "
@ -286,7 +299,12 @@ impl SelectBranchComponent {
.saturating_sub(THREE_DOTS_LENGTH); .saturating_sub(THREE_DOTS_LENGTH);
let mut txt = Vec::new(); let mut txt = Vec::new();
for (i, displaybranch) in self.branch_names.iter().enumerate() for (i, displaybranch) in self
.branch_names
.iter()
.skip(self.scroll_top.get())
.take(height)
.enumerate()
{ {
let mut commit_message = let mut commit_message =
displaybranch.top_commit_message.clone(); displaybranch.top_commit_message.clone();
@ -310,63 +328,67 @@ impl SelectBranchComponent {
let is_head_str = let is_head_str =
if displaybranch.is_head { "*" } else { " " }; if displaybranch.is_head { "*" } else { " " };
txt.push(Spans::from(if self.selection as usize == i { txt.push(Spans::from(
vec![ if self.selection as usize - self.scroll_top.get()
Span::styled( == i
format!("{} ", is_head_str), {
theme.commit_author(true), vec![
), Span::styled(
Span::styled( format!("{} ", is_head_str),
format!( theme.commit_author(true),
">{:w$} ",
branch_name,
w = branch_name_length
), ),
theme.commit_author(true), Span::styled(
), format!(
Span::styled( ">{:w$} ",
format!( branch_name,
"{} ", w = branch_name_length
displaybranch ),
.top_commit theme.commit_author(true),
.get_short_string()
), ),
theme.commit_hash(true), Span::styled(
), format!(
Span::styled( "{} ",
commit_message.to_string(), displaybranch
theme.text(true, true), .top_commit
), .get_short_string()
] ),
} else { theme.commit_hash(true),
vec![
Span::styled(
format!("{} ", is_head_str),
theme.commit_author(false),
),
Span::styled(
format!(
" {:w$} ",
branch_name,
w = branch_name_length
), ),
theme.commit_author(false), Span::styled(
), commit_message.to_string(),
Span::styled( theme.text(true, true),
format!(
"{} ",
displaybranch
.top_commit
.get_short_string()
), ),
theme.commit_hash(false), ]
), } else {
Span::styled( vec![
commit_message.to_string(), Span::styled(
theme.text(true, false), format!("{} ", is_head_str),
), theme.commit_author(false),
] ),
})); Span::styled(
format!(
" {:w$} ",
branch_name,
w = branch_name_length
),
theme.commit_author(false),
),
Span::styled(
format!(
"{} ",
displaybranch
.top_commit
.get_short_string()
),
theme.commit_hash(false),
),
Span::styled(
commit_message.to_string(),
theme.text(true, false),
),
]
},
));
} }
Ok(Text::from(txt)) Ok(Text::from(txt))