first test for a help popup

This commit is contained in:
Stephan Dilly 2020-03-24 14:39:00 +01:00
parent 5d819ccd15
commit adf925083d
11 changed files with 282 additions and 91 deletions

View File

@ -50,6 +50,8 @@ gitui
# todo for 0.1 (first release)
* [ ] fix: no diff of untracked file in a subdir
* [ ] fix: dont show scrol option when any popup open
* [ ] fix: diff is not updated when changed
* [ ] fix: diff is updated when some hunks of the same file where diffed in unstaged before
* [ ] help command

View File

@ -1,7 +1,7 @@
use crate::{
components::{
CommandInfo, CommitComponent, Component, DiffComponent,
IndexComponent,
HelpComponent, IndexComponent,
},
keys, strings,
};
@ -42,6 +42,7 @@ pub struct App {
diff_target: DiffTarget,
do_quit: bool,
commit: CommitComponent,
help: HelpComponent,
index: IndexComponent,
index_wd: IndexComponent,
diff: DiffComponent,
@ -58,6 +59,7 @@ impl App {
diff_target: DiffTarget::WorkingDir,
do_quit: false,
commit: CommitComponent::default(),
help: HelpComponent::default(),
index_wd: IndexComponent::new(
strings::TITLE_STATUS,
true,
@ -124,17 +126,10 @@ impl App {
self.index.draw(f, left_chunks[1]);
self.diff.draw(f, chunks[1]);
let mut cmds = self.commit.commands();
if !self.commit.is_visible() {
cmds.extend(self.index.commands());
cmds.extend(self.index_wd.commands());
cmds.extend(self.diff.commands());
}
cmds.extend(self.commands());
self.draw_commands(f, chunks_main[2], cmds);
self.draw_commands(f, chunks_main[2], self.commands());
self.commit.draw(f, f.size());
self.help.draw(f, f.size());
}
///
@ -146,6 +141,9 @@ impl App {
self.update();
}
return;
} else if self.help.is_visible() {
self.help.event(ev);
return;
}
if !self.commit.is_visible() {
@ -169,6 +167,7 @@ impl App {
keys::FOCUS_STATUS => {
self.switch_focus(Focus::Status)
}
keys::OPEN_HELP => self.help.show(),
keys::FOCUS_STAGE => {
self.switch_focus(Focus::Stage)
}
@ -222,6 +221,7 @@ impl App {
self.index.update(&status.stage);
self.index_wd.update(&status.work_dir);
self.update_diff();
self.help.set_cmds(self.commands());
}
///
@ -230,8 +230,9 @@ impl App {
}
}
// private impls
impl App {
pub fn update_diff(&mut self) {
fn update_diff(&mut self) {
let (idx, is_stage) = match self.diff_target {
DiffTarget::Stage => (&self.index, true),
DiffTarget::WorkingDir => (&self.index_wd, false),
@ -255,43 +256,88 @@ impl App {
}
fn commands(&self) -> Vec<CommandInfo> {
let mut res = Vec::new();
if !self.commit.is_visible() {
res.push(CommandInfo {
name: strings::COMMIT_CMD_OPEN.to_string(),
enabled: !self.index.is_empty(),
});
let mut res = self.commit.commands();
res.extend(self.help.commands());
res.extend(self.index.commands());
res.extend(self.index_wd.commands());
res.extend(self.diff.commands());
{
let main_cmds_available =
!self.commit.is_visible() && !self.help.is_visible();
res.push(CommandInfo::new(
strings::COMMIT_CMD_OPEN,
!self.index.is_empty(),
main_cmds_available,
));
{
let main_cmds_index_wd_focused_availale =
main_cmds_available && self.index_wd.focused();
if self.index_wd.focused() {
let some_selection =
self.index_wd.selection().is_some();
res.push(CommandInfo {
name: strings::CMD_STATUS_STAGE.to_string(),
enabled: some_selection,
});
res.push(CommandInfo {
name: strings::CMD_STATUS_RESET.to_string(),
enabled: some_selection,
});
} else if self.index.focused() {
res.push(CommandInfo {
name: strings::CMD_STATUS_UNSTAGE.to_string(),
enabled: self.index.selection().is_some(),
});
res.push(CommandInfo::new(
strings::CMD_STATUS_STAGE,
some_selection,
main_cmds_index_wd_focused_availale,
));
res.push(CommandInfo::new(
strings::CMD_STATUS_RESET,
some_selection,
main_cmds_index_wd_focused_availale,
));
res.push(CommandInfo::new(
strings::CMD_STATUS_UNSTAGE,
self.index.selection().is_some(),
main_cmds_available && self.index.focused(),
));
}
res.push(CommandInfo {
name: if self.focus == Focus::Diff {
strings::CMD_STATUS_LEFT.to_string()
} else {
strings::CMD_STATUS_RIGHT.to_string()
},
enabled: true,
});
res.push(CommandInfo {
name: strings::CMD_STATUS_QUIT.to_string(),
enabled: true,
});
{
let focus_on_stage = self.focus == Focus::Stage;
let focus_not_diff = self.focus != Focus::Diff;
res.push(CommandInfo::new_hidden(
strings::CMD_STATUS_FOCUS_UNSTAGED,
true,
main_cmds_available
&& focus_on_stage
&& !focus_not_diff,
));
res.push(CommandInfo::new_hidden(
strings::CMD_STATUS_FOCUS_STAGED,
true,
main_cmds_available
&& !focus_on_stage
&& !focus_not_diff,
));
}
{
let focus_on_diff = self.focus == Focus::Diff;
res.push(CommandInfo::new(
strings::CMD_STATUS_LEFT,
true,
main_cmds_available && focus_on_diff,
));
res.push(CommandInfo::new(
strings::CMD_STATUS_RIGHT,
true,
main_cmds_available && !focus_on_diff,
));
}
res.push(CommandInfo::new(
strings::CMD_STATUS_HELP,
true,
main_cmds_available,
));
res.push(CommandInfo::new(
strings::CMD_STATUS_QUIT,
true,
main_cmds_available,
));
}
res
@ -315,15 +361,19 @@ impl App {
Style::default().fg(Color::DarkGray).bg(Color::Blue);
let texts = cmds
.iter()
.map(|c| {
Text::Styled(
Cow::from(c.name.clone()),
if c.enabled {
style_enabled
} else {
style_disabled
},
)
.filter_map(|c| {
if c.show_in_quickbar() {
Some(Text::Styled(
Cow::from(c.name.clone()),
if c.enabled {
style_enabled
} else {
style_disabled
},
))
} else {
None
}
})
.collect::<Vec<_>>();

46
src/components/command.rs Normal file
View File

@ -0,0 +1,46 @@
///
pub struct CommandInfo {
///
pub name: String,
///
// pub keys:
/// available but not active in the context
pub enabled: bool,
/// will show up in the quick bar
pub quick_bar: bool,
/// available in current app state
pub available: bool,
}
impl CommandInfo {
///
pub fn new(name: &str, enabled: bool, available: bool) -> Self {
Self {
name: name.to_string(),
enabled,
quick_bar: true,
available,
}
}
///
pub fn new_hidden(
name: &str,
enabled: bool,
available: bool,
) -> Self {
Self {
name: name.to_string(),
enabled,
quick_bar: false,
available,
}
}
///
pub fn print(&self, out: &mut String) {
out.push_str(self.name.as_str());
}
///
pub fn show_in_quickbar(&self) -> bool {
self.quick_bar && self.available
}
}

View File

@ -44,20 +44,18 @@ impl Component for CommitComponent {
}
fn commands(&self) -> Vec<CommandInfo> {
if !self.visible {
vec![]
} else {
vec![
CommandInfo {
name: strings::COMMIT_CMD_ENTER.to_string(),
enabled: self.can_commit(),
},
CommandInfo {
name: strings::COMMIT_CMD_CLOSE.to_string(),
enabled: true,
},
]
}
vec![
CommandInfo::new(
strings::COMMIT_CMD_ENTER,
self.can_commit(),
self.visible,
),
CommandInfo::new(
strings::COMMIT_CMD_CLOSE,
true,
self.visible,
),
]
}
fn event(&mut self, ev: Event) -> bool {

View File

@ -108,14 +108,11 @@ impl Component for DiffComponent {
}
fn commands(&self) -> Vec<CommandInfo> {
if self.focused {
return vec![CommandInfo {
name: strings::DIFF_CMD_SCROLL.to_string(),
enabled: self.can_scroll(),
}];
}
Vec::new()
vec![CommandInfo::new(
strings::CMD_SCROLL,
self.can_scroll(),
self.focused,
)]
}
fn event(&mut self, ev: Event) -> bool {

85
src/components/help.rs Normal file
View File

@ -0,0 +1,85 @@
use super::{CommandInfo, Component};
use crate::{strings, ui};
use crossterm::event::{Event, KeyCode};
use std::borrow::Cow;
use tui::{
backend::Backend,
layout::{Alignment, Rect},
widgets::{Block, Borders, Paragraph, Text, Widget},
Frame,
};
#[derive(Default)]
pub struct HelpComponent {
cmds: Vec<CommandInfo>,
visible: bool,
}
impl Component for HelpComponent {
fn draw<B: Backend>(&self, f: &mut Frame<B>, _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::<Vec<_>>();
ui::Clear::new(
Paragraph::new(txt.iter())
.block(
Block::default()
.title(strings::HELP_TITLE)
.borders(Borders::ALL),
)
.alignment(Alignment::Left),
)
.render(f, ui::centered_rect_absolute(60, 20, f.size()));
}
}
fn commands(&self) -> Vec<CommandInfo> {
vec![CommandInfo::new(
strings::COMMIT_CMD_CLOSE,
true,
self.visible,
)]
}
fn event(&mut self, ev: Event) -> bool {
if let Event::Key(e) = ev {
return match e.code {
KeyCode::Esc => {
self.hide();
true
}
_ => false,
};
}
false
}
fn is_visible(&self) -> bool {
self.visible
}
fn hide(&mut self) {
self.visible = false
}
fn show(&mut self) {
self.visible = true
}
}
impl HelpComponent {
///
pub fn set_cmds(&mut self, cmds: Vec<CommandInfo>) {
self.cmds = cmds;
}
}

View File

@ -1,6 +1,6 @@
use crate::{
components::{CommandInfo, Component},
ui,
strings, ui,
};
use asyncgit::{hash, StatusItem, StatusItemType};
use crossterm::event::{Event, KeyCode};
@ -126,14 +126,11 @@ impl Component for IndexComponent {
}
fn commands(&self) -> Vec<CommandInfo> {
if self.focused {
return vec![CommandInfo {
name: "Scroll [↑↓]".to_string(),
enabled: self.items.len() > 1,
}];
}
Vec::new()
vec![CommandInfo::new(
strings::CMD_SCROLL,
self.items.len() > 1,
self.focused,
)]
}
fn event(&mut self, ev: Event) -> bool {

View File

@ -1,19 +1,17 @@
use crossterm::event::Event;
use tui::{backend::Backend, layout::Rect, Frame};
mod command;
mod commit;
mod diff;
mod help;
mod index;
pub use command::CommandInfo;
pub use commit::CommitComponent;
pub use diff::DiffComponent;
pub use help::HelpComponent;
pub use index::IndexComponent;
///
pub struct CommandInfo {
pub name: String,
pub enabled: bool,
}
///
pub trait Component {
///

View File

@ -16,3 +16,4 @@ pub const STATUS_STAGE_FILE: KeyEvent = no_mod(KeyCode::Enter);
pub const EXIT_1: KeyEvent = no_mod(KeyCode::Esc);
pub const EXIT_2: KeyEvent = no_mod(KeyCode::Char('q'));
pub const OPEN_COMMIT: KeyEvent = no_mod(KeyCode::Char('c'));
pub const OPEN_HELP: KeyEvent = no_mod(KeyCode::Char('h'));

View File

@ -5,18 +5,22 @@ pub static TITLE_INDEX: &str = "Staged Changes [2]";
pub static TAB_STATUS: &str = "Status";
pub static TAB_DIVIDER: &str = " | ";
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]";
pub static CMD_STATUS_UNSTAGE: &str = "Unstage File [enter]";
pub static CMD_STATUS_RESET: &str = "Reset File [D]";
pub static CMD_STATUS_QUIT: &str = "Quit [esc,q]";
pub static CMD_STATUS_HELP: &str = "Help [h]";
pub static CMD_STATUS_LEFT: &str = "Back [←]";
pub static CMD_STATUS_RIGHT: &str = "Diff [→]";
pub static CMD_SPLITTER: &str = " ";
pub static DIFF_CMD_SCROLL: &str = "Scroll [↑↓]";
pub static CMD_SCROLL: &str = "Scroll [↑↓]";
pub static COMMIT_TITLE: &str = "Commit";
pub static COMMIT_MSG: &str = "type commit message..";
pub static COMMIT_CMD_OPEN: &str = "Commit [c]";
pub static COMMIT_CMD_ENTER: &str = "Commit [enter]";
pub static COMMIT_CMD_CLOSE: &str = "Close [esc]";
pub static HELP_TITLE: &str = "Help";

View File

@ -43,6 +43,19 @@ pub fn centered_rect(
.split(popup_layout[1])[1]
}
pub fn centered_rect_absolute(
width: u16,
height: u16,
r: Rect,
) -> Rect {
Rect::new(
(r.width - width) / 2,
(r.height - height) / 2,
width,
height,
)
}
pub fn draw_list<'b, B: Backend, L>(
f: &mut Frame<B>,
r: Rect,