mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
indicate file status (color coded)
This commit is contained in:
parent
46b4e37ccf
commit
571f45e464
7 changed files with 124 additions and 17 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -143,6 +143,7 @@ dependencies = [
|
|||
"git2",
|
||||
"itertools 0.9.0",
|
||||
"tui",
|
||||
"unicode-width",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
|
|
|||
|
|
@ -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'] }
|
||||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
74
src/tui_scrolllist.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue