mirror of
https://github.com/extrawurst/gitui.git
synced 2024-12-24 17:45:00 +03:00
first test for a help popup
This commit is contained in:
parent
5d819ccd15
commit
adf925083d
@ -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
|
||||
|
154
src/app.rs
154
src/app.rs
@ -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
46
src/components/command.rs
Normal 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
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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
85
src/components/help.rs
Normal 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;
|
||||
}
|
||||
}
|
@ -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 {
|
||||
|
@ -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 {
|
||||
///
|
||||
|
@ -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'));
|
||||
|
@ -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";
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user