indicate file status (color coded)

This commit is contained in:
Stephan Dilly 2020-03-20 01:40:39 +01:00
parent 46b4e37ccf
commit 571f45e464
7 changed files with 124 additions and 17 deletions

1
Cargo.lock generated
View file

@ -143,6 +143,7 @@ dependencies = [
"git2",
"itertools 0.9.0",
"tui",
"unicode-width",
]
[[package]]

View file

@ -11,4 +11,5 @@ edition = "2018"
git2 = "0.10"
crossterm = "0.15"
itertools = "0.9"
unicode-width = "0.1"
tui = { version = "0.8", default-features = false, features = ['crossterm'] }

View file

@ -15,7 +15,6 @@ Over the last 2 years my go-to GUI tool for this was [fork](https://git-fork.com
* [x] inspect diffs
* [x] commit
* [x] [input polling in thread](assets/perf_compare.jpg)
* [ ] indicate file status (modified,added,removed)
* [ ] ←→ to switch between index/diff
* [ ] support non-root git folder wd
* [ ] show content of new files

View file

@ -6,8 +6,15 @@ use crate::{
};
use crossterm::event::{Event, KeyCode};
use git2::StatusShow;
use std::cmp;
use tui::{backend::Backend, layout::Rect, Frame};
use git_status::StatusItemType;
use std::{borrow::Cow, cmp};
use tui::{
backend::Backend,
layout::Rect,
style::{Color, Modifier, Style},
widgets::Text,
Frame,
};
///
pub struct IndexComponent {
@ -55,6 +62,7 @@ impl IndexComponent {
}
}
///
pub fn focus_select(&mut self, focus: bool) {
self.focus(focus);
self.show_selection = focus;
@ -77,15 +85,37 @@ impl IndexComponent {
impl Component for IndexComponent {
fn draw<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
let item_to_text = |idx: usize, i: &StatusItem| -> Text {
let selected = self.show_selection
&& self.selection.map_or(false, |e| e == idx);
let txt = if selected {
format!("> {}", i.path)
} else {
format!(" {}", i.path)
};
let mut style = Style::default().fg(
match i.status.unwrap_or(StatusItemType::Modified) {
StatusItemType::Modified => Color::LightYellow,
StatusItemType::New => Color::LightGreen,
StatusItemType::Deleted => Color::LightRed,
_ => Color::White,
},
);
if selected {
style = style.modifier(Modifier::BOLD); //.fg(Color::White);
}
Text::Styled(Cow::from(txt), style)
};
tui_utils::draw_list(
f,
r,
self.title.to_string(),
&self.title.to_string(),
self.items
.iter()
.map(|e| e.path.clone())
.collect::<Vec<_>>()
.as_slice(),
.enumerate()
.map(|(idx, e)| item_to_text(idx, e)),
if self.show_selection {
self.selection
} else {

View file

@ -6,6 +6,7 @@ mod git_utils;
mod keys;
mod poll;
mod strings;
mod tui_scrolllist;
mod tui_utils;
use crate::{app::App, poll::QueueEvent};

74
src/tui_scrolllist.rs Normal file
View file

@ -0,0 +1,74 @@
use std::iter::Iterator;
use tui::buffer::Buffer;
use tui::layout::Rect;
use tui::style::Style;
use tui::widgets::{Block, List, Text, Widget};
///
pub struct ScrollableList<'b, L>
where
L: Iterator<Item = Text<'b>>,
{
block: Option<Block<'b>>,
/// Items to be displayed
items: L,
/// Index of the scroll position
scroll: usize,
/// Base style of the widget
style: Style,
}
impl<'b, L> ScrollableList<'b, L>
where
L: Iterator<Item = Text<'b>>,
{
pub fn new(items: L) -> Self {
Self {
block: None,
items,
scroll: 0,
style: Default::default(),
}
}
pub fn block(mut self, block: Block<'b>) -> Self {
self.block = Some(block);
self
}
pub fn style(mut self, style: Style) -> Self {
self.style = style;
self
}
pub fn scroll(mut self, index: usize) -> Self {
self.scroll = index;
self
}
}
impl<'b, L> Widget for ScrollableList<'b, L>
where
L: Iterator<Item = Text<'b>>,
{
fn draw(&mut self, area: Rect, buf: &mut Buffer) {
let list_area = match self.block {
Some(ref mut b) => b.inner(area),
None => area,
};
let list_height = list_area.height as usize;
let offset = if self.scroll >= list_height {
self.scroll - list_height + 1
} else {
0
};
// Render items
List::new(self.items.by_ref().skip(offset as usize))
.block(self.block.unwrap_or_default())
.style(self.style)
.draw(area, buf);
}
}

View file

@ -1,10 +1,12 @@
use crate::tui_scrolllist;
use tui::backend::Backend;
use tui::layout::{Constraint, Direction, Layout, Rect};
use tui::{
style::{Color, Modifier, Style},
widgets::{Block, Borders, SelectableList, Widget},
widgets::{Block, Borders, Text, Widget},
Frame,
};
use tui_scrolllist::ScrollableList;
/// use layouts to create a rects that
/// centers inside `r` and sizes `percent_x`/`percent_x` of `r`
@ -38,21 +40,23 @@ pub fn centered_rect(
.split(popup_layout[1])[1]
}
pub fn draw_list<B: Backend, T: AsRef<str>>(
pub fn draw_list<'b, B: Backend, L>(
f: &mut Frame<B>,
r: Rect,
title: String,
items: &[T],
title: &'b String,
items: L,
select: Option<usize>,
selected: bool,
) {
) where
L: Iterator<Item = Text<'b>>,
{
let mut style_border = Style::default();
let mut style_title = Style::default();
if selected {
style_border = style_border.fg(Color::Green);
style_title = style_title.modifier(Modifier::BOLD);
}
SelectableList::default()
ScrollableList::new(items)
.block(
Block::default()
.title(title.as_str())
@ -60,10 +64,7 @@ pub fn draw_list<B: Backend, T: AsRef<str>>(
.title_style(style_title)
.border_style(style_border),
)
.items(items)
.select(select)
.scroll(select.unwrap_or_default())
.style(Style::default().fg(Color::White))
.highlight_style(Style::default().modifier(Modifier::BOLD))
.highlight_symbol(">")
.render(f, r);
}