From 571f45e46450a51d8d432e7207e9b337c5c56c06 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Fri, 20 Mar 2020 01:40:39 +0100 Subject: [PATCH] indicate file status (color coded) --- Cargo.lock | 1 + Cargo.toml | 1 + README.md | 1 - src/components/index.rs | 42 +++++++++++++++++++---- src/main.rs | 1 + src/tui_scrolllist.rs | 74 +++++++++++++++++++++++++++++++++++++++++ src/tui_utils.rs | 21 ++++++------ 7 files changed, 124 insertions(+), 17 deletions(-) create mode 100644 src/tui_scrolllist.rs diff --git a/Cargo.lock b/Cargo.lock index f40f06ed..cdd549bc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -143,6 +143,7 @@ dependencies = [ "git2", "itertools 0.9.0", "tui", + "unicode-width", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 6c1e92ed..3cbf571b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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'] } \ No newline at end of file diff --git a/README.md b/README.md index ea184e08..3276cdd6 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/src/components/index.rs b/src/components/index.rs index c3d4513b..93e907d1 100644 --- a/src/components/index.rs +++ b/src/components/index.rs @@ -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(&self, f: &mut Frame, 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::>() - .as_slice(), + .enumerate() + .map(|(idx, e)| item_to_text(idx, e)), if self.show_selection { self.selection } else { diff --git a/src/main.rs b/src/main.rs index 6497fbe3..615323fb 100644 --- a/src/main.rs +++ b/src/main.rs @@ -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}; diff --git a/src/tui_scrolllist.rs b/src/tui_scrolllist.rs new file mode 100644 index 00000000..65c7feaf --- /dev/null +++ b/src/tui_scrolllist.rs @@ -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>, +{ + block: Option>, + /// 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>, +{ + 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>, +{ + 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); + } +} diff --git a/src/tui_utils.rs b/src/tui_utils.rs index e9cae262..c83fb7b6 100644 --- a/src/tui_utils.rs +++ b/src/tui_utils.rs @@ -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>( +pub fn draw_list<'b, B: Backend, L>( f: &mut Frame, r: Rect, - title: String, - items: &[T], + title: &'b String, + items: L, select: Option, selected: bool, -) { +) where + L: Iterator>, +{ 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>( .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); }