linutil/tui/src/hint.rs

171 lines
5.5 KiB
Rust
Raw Normal View History

2024-09-06 22:36:12 +01:00
use ratatui::{
layout::{Margin, Rect},
style::{Style, Stylize},
text::{Line, Span},
widgets::{Block, Borders, Paragraph},
Frame,
};
use crate::state::{AppState, Focus};
pub const SHORTCUT_LINES: usize = 2;
pub struct ShortcutList {
pub scope_name: &'static str,
pub hints: Vec<Shortcut>,
}
pub struct Shortcut {
2024-09-14 16:30:03 +01:00
pub key_sequences: Vec<Span<'static>>,
2024-09-06 22:36:12 +01:00
pub desc: &'static str,
}
fn add_spacing(list: Vec<Vec<Span>>) -> Line {
list.into_iter()
.flat_map(|mut s| {
s.push(Span::default().content(" "));
s
})
.collect()
}
2024-09-06 22:36:12 +01:00
pub fn span_vec_len(span_vec: &[Span]) -> usize {
span_vec.iter().rfold(0, |init, s| init + s.width())
}
impl ShortcutList {
pub fn draw(&self, frame: &mut Frame, area: Rect) {
let block = Block::default()
.title(self.scope_name)
.borders(Borders::all());
let inner_area = area.inner(Margin::new(1, 1));
let shortcut_spans: Vec<Vec<Span>> = self.hints.iter().map(|h| h.to_spans()).collect();
2024-09-06 22:36:12 +01:00
let mut lines: Vec<Line> = Vec::with_capacity(SHORTCUT_LINES);
2024-09-06 22:36:12 +01:00
let shortcut_list = (0..SHORTCUT_LINES - 1).fold(shortcut_spans, |mut acc, _| {
let split_idx = acc
2024-09-06 22:36:12 +01:00
.iter()
.scan(0_usize, |total_len, s| {
2024-09-06 22:36:12 +01:00
*total_len += span_vec_len(s);
if *total_len > inner_area.width as usize {
None
} else {
*total_len += 4;
Some(1)
}
})
.count();
let new_shortcut_list = acc.split_off(split_idx);
lines.push(add_spacing(acc));
new_shortcut_list
});
lines.push(add_spacing(shortcut_list));
2024-09-06 22:36:12 +01:00
let p = Paragraph::new(lines).block(block);
frame.render_widget(p, area);
}
}
impl Shortcut {
pub fn new(key_sequences: Vec<&'static str>, desc: &'static str) -> Self {
Self {
2024-09-14 16:30:03 +01:00
key_sequences: key_sequences
2024-09-06 22:36:12 +01:00
.iter()
.map(|s| Span::styled(*s, Style::default().bold()))
.collect(),
desc,
}
}
fn to_spans(&self) -> Vec<Span> {
let mut ret: Vec<_> = self
2024-09-14 16:30:03 +01:00
.key_sequences
2024-09-06 22:36:12 +01:00
.iter()
.flat_map(|seq| {
[
Span::default().content("["),
seq.clone(),
Span::default().content("] "),
]
})
.collect();
ret.push(Span::styled(self.desc, Style::default().italic()));
ret
}
}
fn get_list_item_shortcut(state: &AppState) -> Vec<Shortcut> {
2024-09-06 22:36:12 +01:00
if state.selected_item_is_dir() {
vec![Shortcut::new(
vec!["l", "Right", "Enter"],
"Go to selected dir",
)]
2024-09-06 22:36:12 +01:00
} else {
vec![
Shortcut::new(vec!["l", "Right", "Enter"], "Run selected command"),
Shortcut::new(vec!["p"], "Enable preview"),
Shortcut::new(vec!["d"], "Command Description"),
]
2024-09-06 22:36:12 +01:00
}
}
pub fn draw_shortcuts(state: &AppState, frame: &mut Frame, area: Rect) {
match state.focus {
Focus::Search => ShortcutList {
scope_name: "Search bar",
hints: vec![Shortcut::new(vec!["Enter"], "Finish search")],
},
2024-09-06 22:36:12 +01:00
Focus::List => {
let mut hints = Vec::new();
hints.push(Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"));
2024-09-06 22:36:12 +01:00
if state.at_root() {
hints.push(Shortcut::new(vec!["h", "Left"], "Focus tab list"));
hints.extend(get_list_item_shortcut(state));
2024-09-19 19:29:08 +01:00
} else if state.selected_item_is_up_dir() {
hints.push(Shortcut::new(
vec!["l", "Right", "Enter", "h", "Left"],
"Go to parent directory",
));
2024-09-06 22:36:12 +01:00
} else {
2024-09-19 19:29:08 +01:00
hints.push(Shortcut::new(vec!["h", "Left"], "Go to parent directory"));
hints.extend(get_list_item_shortcut(state));
}
2024-09-06 22:36:12 +01:00
hints.push(Shortcut::new(vec!["k", "Up"], "Select item above"));
hints.push(Shortcut::new(vec!["j", "Down"], "Select item below"));
hints.push(Shortcut::new(vec!["t"], "Next theme"));
hints.push(Shortcut::new(vec!["T"], "Previous theme"));
if state.is_current_tab_multi_selectable() {
hints.push(Shortcut::new(vec!["v"], "Toggle multi-selection mode"));
hints.push(Shortcut::new(vec!["Space"], "Select multiple commands"));
}
hints.push(Shortcut::new(vec!["Tab"], "Next tab"));
hints.push(Shortcut::new(vec!["Shift-Tab"], "Previous tab"));
2024-09-06 22:36:12 +01:00
ShortcutList {
scope_name: "Item list",
hints,
}
}
2024-09-06 22:36:12 +01:00
Focus::TabList => ShortcutList {
scope_name: "Tab list",
hints: vec![
Shortcut::new(vec!["q", "CTRL-c"], "Exit linutil"),
Shortcut::new(vec!["l", "Right", "Enter"], "Focus action list"),
2024-09-06 22:36:12 +01:00
Shortcut::new(vec!["k", "Up"], "Select item above"),
Shortcut::new(vec!["j", "Down"], "Select item below"),
Shortcut::new(vec!["t"], "Next theme"),
Shortcut::new(vec!["T"], "Previous theme"),
Shortcut::new(vec!["Tab"], "Next tab"),
Shortcut::new(vec!["Shift-Tab"], "Previous tab"),
2024-09-06 22:36:12 +01:00
],
},
2024-09-06 22:36:12 +01:00
Focus::FloatingWindow(ref float) => float.get_shortcut_list(),
}
.draw(frame, area);
}