From ed5668c4c8c94637c816ade0902bbfa6b3f7fc49 Mon Sep 17 00:00:00 2001 From: Stephan Dilly Date: Fri, 3 Apr 2020 14:48:41 +0200 Subject: [PATCH] cmd help grouping and long desc --- src/app.rs | 5 ++ src/components/changes.rs | 4 ++ src/components/command.rs | 19 +++++- src/components/commit.rs | 3 + src/components/diff.rs | 1 + src/components/help.rs | 119 +++++++++++++++++++++++++++++++++----- src/strings.rs | 5 ++ 7 files changed, 141 insertions(+), 15 deletions(-) diff --git a/src/app.rs b/src/app.rs index 1e243334..2ee16da2 100644 --- a/src/app.rs +++ b/src/app.rs @@ -273,6 +273,7 @@ impl App { res.push( CommandInfo::new( strings::CMD_STATUS_FOCUS_UNSTAGED, + strings::CMD_GROUP_GENERAL, true, main_cmds_available && focus_on_stage @@ -283,6 +284,7 @@ impl App { res.push( CommandInfo::new( strings::CMD_STATUS_FOCUS_STAGED, + strings::CMD_GROUP_GENERAL, true, main_cmds_available && !focus_on_stage @@ -295,11 +297,13 @@ impl App { let focus_on_diff = self.focus == Focus::Diff; res.push(CommandInfo::new( strings::CMD_STATUS_LEFT, + strings::CMD_GROUP_GENERAL, true, main_cmds_available && focus_on_diff, )); res.push(CommandInfo::new( strings::CMD_STATUS_RIGHT, + strings::CMD_GROUP_GENERAL, true, main_cmds_available && !focus_on_diff, )); @@ -308,6 +312,7 @@ impl App { res.push( CommandInfo::new( strings::CMD_STATUS_QUIT, + strings::CMD_GROUP_GENERAL, true, main_cmds_available, ) diff --git a/src/components/changes.rs b/src/components/changes.rs index 0454dfa4..78df9235 100644 --- a/src/components/changes.rs +++ b/src/components/changes.rs @@ -178,17 +178,20 @@ impl Component for ChangesComponent { if self.is_working_dir { out.push(CommandInfo::new( strings::CMD_STATUS_STAGE, + strings::CMD_GROUP_CHANGES, some_selection, self.focused, )); out.push(CommandInfo::new( strings::CMD_STATUS_RESET, + strings::CMD_GROUP_CHANGES, some_selection, self.focused, )); } else { out.push(CommandInfo::new( strings::CMD_STATUS_UNSTAGE, + strings::CMD_GROUP_CHANGES, some_selection, self.focused, )); @@ -196,6 +199,7 @@ impl Component for ChangesComponent { out.push(CommandInfo::new( strings::CMD_SCROLL, + strings::CMD_GROUP_GENERAL, self.items.len() > 1, self.focused, )); diff --git a/src/components/command.rs b/src/components/command.rs index 047a3609..44fa3394 100644 --- a/src/components/command.rs +++ b/src/components/command.rs @@ -3,6 +3,10 @@ pub struct CommandInfo { /// pub name: String, /// + pub group: String, + /// + pub desc: String, + /// // pub keys: /// available but not active in the context pub enabled: bool, @@ -16,9 +20,16 @@ pub struct CommandInfo { impl CommandInfo { /// - pub fn new(name: &str, enabled: bool, available: bool) -> Self { + pub fn new( + name: &str, + group: &str, + enabled: bool, + available: bool, + ) -> Self { Self { name: name.to_string(), + group: group.to_string(), + desc: String::default(), enabled, quick_bar: true, available, @@ -32,6 +43,12 @@ impl CommandInfo { res } /// + pub fn desc(self, txt: &str) -> Self { + let mut res = self; + res.desc = txt.to_string(); + res + } + /// pub fn hidden(self) -> Self { let mut res = self; res.quick_bar = false; diff --git a/src/components/commit.rs b/src/components/commit.rs index 08a4c92e..527328fa 100644 --- a/src/components/commit.rs +++ b/src/components/commit.rs @@ -55,16 +55,19 @@ impl Component for CommitComponent { ) -> CommandBlocking { out.push(CommandInfo::new( strings::COMMIT_CMD_OPEN, + strings::CMD_GROUP_COMMIT, !self.stage_empty, !self.visible, )); out.push(CommandInfo::new( strings::COMMIT_CMD_ENTER, + strings::CMD_GROUP_COMMIT, self.can_commit(), self.visible, )); out.push(CommandInfo::new( strings::COMMIT_CMD_CLOSE, + strings::CMD_GROUP_COMMIT, true, self.visible, )); diff --git a/src/components/diff.rs b/src/components/diff.rs index 4879453c..7160a25a 100644 --- a/src/components/diff.rs +++ b/src/components/diff.rs @@ -248,6 +248,7 @@ impl Component for DiffComponent { ) -> CommandBlocking { out.push(CommandInfo::new( strings::CMD_SCROLL, + strings::CMD_GROUP_DIFF, self.can_scroll(), self.focused, )); diff --git a/src/components/help.rs b/src/components/help.rs index 14f5b0c9..36884bb3 100644 --- a/src/components/help.rs +++ b/src/components/help.rs @@ -3,11 +3,14 @@ use super::{ DrawableComponent, EventUpdate, }; use crate::{keys, strings, ui}; +use asyncgit::hash; use crossterm::event::Event; -use std::borrow::Cow; +use itertools::Itertools; +use std::{borrow::Cow, cmp, convert::TryFrom}; use tui::{ backend::Backend, layout::{Alignment, Rect}, + style::{Color, Style}, widgets::{Block, Borders, Paragraph, Text, Widget}, Frame, }; @@ -16,21 +19,22 @@ use tui::{ pub struct HelpComponent { cmds: Vec, visible: bool, + selection: u16, } impl DrawableComponent for HelpComponent { fn draw(&self, f: &mut Frame, _rect: Rect) { if self.visible { - let txt = self - .cmds - .iter() - .map(|e| { - let mut out = String::new(); - e.print(&mut out); - out.push('\n'); - Text::Raw(Cow::from(out)) - }) - .collect::>(); + let (txt, selected_line) = self.get_text(); + + let height = 24; + let scroll_threshold = height / 3; + + let scroll = if selected_line > scroll_threshold { + self.selection - scroll_threshold + } else { + 0 + }; ui::Clear::new( Paragraph::new(txt.iter()) @@ -39,9 +43,13 @@ impl DrawableComponent for HelpComponent { .title(strings::HELP_TITLE) .borders(Borders::ALL), ) + .scroll(scroll) .alignment(Alignment::Left), ) - .render(f, ui::centered_rect_absolute(60, 20, f.size())); + .render( + f, + ui::centered_rect_absolute(65, height, f.size()), + ); } } } @@ -59,14 +67,24 @@ impl Component for HelpComponent { out.push( CommandInfo::new( strings::CMD_STATUS_HELP, + strings::CMD_GROUP_GENERAL, true, !self.visible, ) + .desc("open this help screen") .order(99), ); + out.push(CommandInfo::new( + strings::CMD_SCROLL, + strings::CMD_GROUP_GENERAL, + true, + self.visible, + )); + out.push(CommandInfo::new( strings::COMMIT_CMD_CLOSE, + strings::CMD_GROUP_GENERAL, true, self.visible, )); @@ -77,8 +95,11 @@ impl Component for HelpComponent { fn event(&mut self, ev: Event) -> Option { if self.visible { if let Event::Key(e) = ev { - if let keys::EXIT_POPUP = e { - self.hide(); + match e { + keys::EXIT_POPUP => self.hide(), + keys::MOVE_DOWN => self.move_selection(true), + keys::MOVE_UP => self.move_selection(false), + _ => (), } } @@ -108,5 +129,75 @@ impl HelpComponent { /// pub fn set_cmds(&mut self, cmds: Vec) { self.cmds = cmds; + self.cmds.sort_by_key(|e| hash(&e.group)); + } + + fn move_selection(&mut self, inc: bool) { + let mut new_selection = self.selection; + + new_selection = if inc { + new_selection.saturating_add(1) + } else { + new_selection.saturating_sub(1) + }; + new_selection = cmp::max(new_selection, 0); + + if let Ok(max) = u16::try_from(self.cmds.len() - 1) { + self.selection = cmp::min(new_selection, max); + } + } + + fn get_text<'a>(&self) -> (Vec>, u16) { + let mut txt = Vec::new(); + + let mut processed = 0_u16; + let mut selected_line = 0_u16; + + for (key, group) in + &self.cmds.iter().group_by(|e| e.group.clone()) + { + txt.push(Text::Styled( + Cow::from(format!(" {}\n", key)), + Style::default().fg(Color::Black).bg(Color::Gray), + )); + + txt.extend( + group + .into_iter() + .map(|e| { + let is_selected = self.selection == processed; + if is_selected { + selected_line = processed; + } + processed += 1; + + let mut out = String::from(if is_selected { + ">" + } else { + " " + }); + + e.print(&mut out); + out.push('\n'); + + if is_selected { + out.push_str( + format!(" {}\n", e.desc).as_str(), + ); + } + + let style = if is_selected { + Style::default().fg(Color::Yellow) + } else { + Style::default() + }; + + Text::Styled(Cow::from(out), style) + }) + .collect::>(), + ); + } + + (txt, selected_line) } } diff --git a/src/strings.rs b/src/strings.rs index 1f392093..9bd11a77 100644 --- a/src/strings.rs +++ b/src/strings.rs @@ -5,6 +5,11 @@ pub static TITLE_INDEX: &str = "Staged Changes [2]"; pub static TAB_STATUS: &str = "Status"; pub static TAB_DIVIDER: &str = " | "; +pub static CMD_GROUP_GENERAL: &str = "General"; +pub static CMD_GROUP_DIFF: &str = "Diff"; +pub static CMD_GROUP_CHANGES: &str = "Changes"; +pub static CMD_GROUP_COMMIT: &str = "Commit"; + pub static CMD_STATUS_FOCUS_UNSTAGED: &str = "Unstaged [1]"; pub static CMD_STATUS_FOCUS_STAGED: &str = "Staged [2]"; pub static CMD_STATUS_STAGE: &str = "Stage File [enter]";