Skip to content

Commit

Permalink
Support more commands (#101)
Browse files Browse the repository at this point in the history
closes #83
  • Loading branch information
Stephan Dilly committed Jun 1, 2020
1 parent 91370f8 commit 4de0e52
Show file tree
Hide file tree
Showing 8 changed files with 209 additions and 53 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ backtrace = "0.3"
ron = "0.6"
serde = "1.0"
anyhow = "1.0.31"
unicode-width = "0.1"

[features]
default=[]
Expand Down
67 changes: 18 additions & 49 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
use crate::{
accessors,
cmdbar::CommandBar,
components::{
event_pump, CommandBlocking, CommandInfo, CommitComponent,
Component, DrawableComponent, HelpComponent, MsgComponent,
Expand All @@ -15,15 +16,13 @@ use anyhow::{anyhow, Result};
use asyncgit::{sync, AsyncNotification, CWD};
use crossbeam_channel::Sender;
use crossterm::event::Event;
use itertools::Itertools;
use std::borrow::Cow;
use strings::commands;
use tui::{
backend::Backend,
layout::{Alignment, Constraint, Direction, Layout, Rect},
layout::{Constraint, Direction, Layout, Rect},
style::Modifier,
style::Style,
widgets::{Block, Borders, Paragraph, Tabs, Text},
widgets::{Block, Borders, Tabs},
Frame,
};
///
Expand All @@ -34,7 +33,7 @@ pub struct App {
reset: ResetComponent,
commit: CommitComponent,
stashmsg_popup: StashMsgComponent,
current_commands: Vec<CommandInfo>,
cmdbar: CommandBar,
tab: usize,
revlog: Revlog,
status_tab: Status,
Expand All @@ -60,7 +59,7 @@ impl App {
&theme,
),
do_quit: false,
current_commands: Vec::new(),
cmdbar: CommandBar::new(&theme),
help: HelpComponent::new(&theme),
msg: MsgComponent::new(&theme),
tab: 0,
Expand All @@ -78,17 +77,23 @@ impl App {
&mut self,
f: &mut Frame<B>,
) -> Result<()> {
let fsize = f.size();

self.cmdbar.refresh_width(fsize.width);

let chunks_main = Layout::default()
.direction(Direction::Vertical)
.constraints(
[
Constraint::Length(2),
Constraint::Min(2),
Constraint::Length(1),
Constraint::Length(self.cmdbar.height()),
]
.as_ref(),
)
.split(f.size());
.split(fsize);

self.cmdbar.draw(f, chunks_main[2]);

self.draw_tabs(f, chunks_main[0]);

Expand All @@ -101,13 +106,6 @@ impl App {
_ => return Err(anyhow!("unknown tab")),
};

Self::draw_commands(
f,
chunks_main[2],
self.current_commands.as_slice(),
self.theme,
);

self.draw_popups(f)?;

Ok(())
Expand Down Expand Up @@ -135,6 +133,10 @@ impl App {
self.toggle_tabs(true)?;
NeedsUpdate::COMMANDS
}
keys::CMD_BAR_TOGGLE => {
self.cmdbar.toggle_more();
NeedsUpdate::empty()
}

_ => NeedsUpdate::empty(),
};
Expand Down Expand Up @@ -267,8 +269,7 @@ impl App {

fn update_commands(&mut self) {
self.help.set_cmds(self.commands(true));
self.current_commands = self.commands(false);
self.current_commands.sort_by_key(|e| e.order);
self.cmdbar.set_cmds(self.commands(false));
}

fn process_queue(&mut self) -> Result<NeedsUpdate> {
Expand Down Expand Up @@ -417,36 +418,4 @@ impl App {
r,
);
}

fn draw_commands<B: Backend>(
f: &mut Frame<B>,
r: Rect,
cmds: &[CommandInfo],
theme: Theme,
) {
let splitter = Text::Styled(
Cow::from(strings::CMD_SPLITTER),
Style::default(),
);

let texts = cmds
.iter()
.filter_map(|c| {
if c.show_in_quickbar() {
Some(Text::Styled(
Cow::from(c.text.name),
theme.toolbar(c.enabled),
))
} else {
None
}
})
.collect::<Vec<_>>();

f.render_widget(
Paragraph::new(texts.iter().intersperse(&splitter))
.alignment(Alignment::Left),
r,
);
}
}
176 changes: 176 additions & 0 deletions src/cmdbar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
use crate::{components::CommandInfo, strings, ui::style::Theme};
use std::borrow::Cow;
use tui::{
backend::Backend,
layout::{Alignment, Rect},
widgets::{Paragraph, Text},
Frame,
};
use unicode_width::UnicodeWidthStr;

enum DrawListEntry {
LineBreak,
Splitter,
Command(Command),
}

struct Command {
txt: String,
enabled: bool,
line: usize,
}

/// helper to be used while drawing
pub struct CommandBar {
draw_list: Vec<DrawListEntry>,
cmd_infos: Vec<CommandInfo>,
theme: Theme,
lines: u16,
width: u16,
expandable: bool,
expanded: bool,
}

const MORE_WIDTH: u16 = 11;

impl CommandBar {
pub const fn new(theme: &Theme) -> Self {
Self {
draw_list: Vec::new(),
cmd_infos: Vec::new(),
theme: *theme,
lines: 0,
width: 0,
expandable: false,
expanded: false,
}
}

pub fn refresh_width(&mut self, width: u16) {
if width != self.width {
self.refresh_list(width);
self.width = width;
}
}

fn is_multiline(&self, width: u16) -> bool {
let mut line_width = 0_usize;
for c in &self.cmd_infos {
let entry_w = UnicodeWidthStr::width(c.text.name);

if line_width + entry_w > width as usize {
return true;
}

line_width += entry_w + 1;
}

false
}

fn refresh_list(&mut self, width: u16) {
self.draw_list.clear();

let width = if self.is_multiline(width) {
width.saturating_sub(MORE_WIDTH)
} else {
width
};

let mut line_width = 0_usize;
let mut lines = 1_u16;

for c in &self.cmd_infos {
let entry_w = UnicodeWidthStr::width(c.text.name);

if line_width + entry_w > width as usize {
self.draw_list.push(DrawListEntry::LineBreak);
line_width = 0;
lines += 1;
} else if line_width > 0 {
self.draw_list.push(DrawListEntry::Splitter);
}

line_width += entry_w + 1;

self.draw_list.push(DrawListEntry::Command(Command {
txt: c.text.name.to_string(),
enabled: c.enabled,
line: lines.saturating_sub(1) as usize,
}));
}

self.expandable = lines > 1;

self.lines = lines;
}

pub fn set_cmds(&mut self, cmds: Vec<CommandInfo>) {
self.cmd_infos = cmds
.into_iter()
.filter(CommandInfo::show_in_quickbar)
.collect::<Vec<_>>();
self.cmd_infos.sort_by_key(|e| e.order);
self.refresh_list(self.width);
}

pub fn height(&self) -> u16 {
if self.expandable && self.expanded {
self.lines
} else {
1_u16
}
}

pub fn toggle_more(&mut self) {
if self.expandable {
self.expanded = !self.expanded;
}
}

pub fn draw<B: Backend>(&self, f: &mut Frame<B>, r: Rect) {
let splitter = Text::Raw(Cow::from(strings::CMD_SPLITTER));

let texts = self
.draw_list
.iter()
.map(|c| match c {
DrawListEntry::Command(c) => Text::Styled(
Cow::from(c.txt.as_str()),
self.theme.commandbar(c.enabled, c.line),
),
DrawListEntry::LineBreak => {
Text::Raw(Cow::from("\n"))
}
DrawListEntry::Splitter => splitter.clone(),
})
.collect::<Vec<_>>();

f.render_widget(
Paragraph::new(texts.iter()).alignment(Alignment::Left),
r,
);

if self.expandable {
let r = Rect::new(
r.width.saturating_sub(MORE_WIDTH),
r.y + r.height.saturating_sub(1),
MORE_WIDTH,
1,
);

f.render_widget(
Paragraph::new(
vec![Text::Raw(Cow::from(if self.expanded {
"less [.]"
} else {
"more [.]"
}))]
.iter(),
)
.alignment(Alignment::Right),
r,
);
}
}
}
1 change: 1 addition & 0 deletions src/keys.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,3 +50,4 @@ pub const STASHING_TOGGLE_INDEX: KeyEvent =
pub const STASH_APPLY: KeyEvent = no_mod(KeyCode::Enter);
pub const STASH_DROP: KeyEvent =
with_mod(KeyCode::Char('D'), KeyModifiers::SHIFT);
pub const CMD_BAR_TOGGLE: KeyEvent = no_mod(KeyCode::Char('.'));
1 change: 1 addition & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#![allow(clippy::module_name_repetitions)]

mod app;
mod cmdbar;
mod components;
mod keys;
mod poll;
Expand Down
4 changes: 2 additions & 2 deletions src/strings.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub mod commands {
);
///
pub static DIFF_HOME_END: CommandText = CommandText::new(
"Jump up/down [home,end,shift+up,shift+down]",
"Jump up/down [home,end,\u{11014}up,\u{2191}down]",
"scroll to top or bottom of diff",
CMD_GROUP_DIFF,
);
Expand Down Expand Up @@ -155,7 +155,7 @@ pub mod commands {
);
///
pub static QUIT: CommandText = CommandText::new(
"Quit [ctrl+c]",
"Quit [^c]",
"quit gitui application",
CMD_GROUP_GENERAL,
);
Expand Down

0 comments on commit 4de0e52

Please sign in to comment.