mirror of
https://github.com/gitui-org/gitui
synced 2026-05-24 09:28:21 +00:00
205 lines
5.3 KiB
Rust
205 lines
5.3 KiB
Rust
use crate::{
|
|
components::CommandInfo, keys::SharedKeyConfig, strings,
|
|
ui::style::SharedTheme,
|
|
};
|
|
use std::borrow::Cow;
|
|
use tui::{
|
|
backend::Backend,
|
|
layout::{Alignment, Rect},
|
|
text::{Span, Spans},
|
|
widgets::Paragraph,
|
|
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: SharedTheme,
|
|
key_config: SharedKeyConfig,
|
|
lines: u16,
|
|
width: u16,
|
|
expandable: bool,
|
|
expanded: bool,
|
|
}
|
|
|
|
const MORE_WIDTH: u16 = 9;
|
|
|
|
impl CommandBar {
|
|
pub const fn new(
|
|
theme: SharedTheme,
|
|
key_config: SharedKeyConfig,
|
|
) -> Self {
|
|
Self {
|
|
draw_list: Vec::new(),
|
|
cmd_infos: Vec::new(),
|
|
theme,
|
|
key_config,
|
|
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.as_str());
|
|
|
|
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.as_str());
|
|
|
|
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 const 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) {
|
|
if r.width < MORE_WIDTH {
|
|
return;
|
|
}
|
|
let splitter = Span::raw(Cow::from(strings::cmd_splitter(
|
|
&self.key_config,
|
|
)));
|
|
|
|
let texts = self
|
|
.draw_list
|
|
.split(|c| matches!(c, DrawListEntry::LineBreak))
|
|
.map(|c_arr| {
|
|
Spans::from(
|
|
c_arr
|
|
.iter()
|
|
.map(|c| match c {
|
|
DrawListEntry::Command(c) => {
|
|
Span::styled(
|
|
Cow::from(c.txt.as_str()),
|
|
self.theme.commandbar(
|
|
c.enabled, c.line,
|
|
),
|
|
)
|
|
}
|
|
DrawListEntry::LineBreak => {
|
|
// Doesn't exist in split array
|
|
Span::raw("")
|
|
}
|
|
DrawListEntry::Splitter => {
|
|
splitter.clone()
|
|
}
|
|
})
|
|
.collect::<Vec<Span>>(),
|
|
)
|
|
})
|
|
.collect::<Vec<Spans>>();
|
|
|
|
f.render_widget(
|
|
Paragraph::new(texts).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.min(r.width),
|
|
1.min(r.height),
|
|
);
|
|
|
|
f.render_widget(
|
|
Paragraph::new(Spans::from(vec![Span::raw(
|
|
Cow::from(if self.expanded {
|
|
"less [.]"
|
|
} else {
|
|
"more [.]"
|
|
}),
|
|
)]))
|
|
.alignment(Alignment::Right),
|
|
r,
|
|
);
|
|
}
|
|
}
|
|
}
|