2024-09-28 20:05:19 +01:00
|
|
|
use std::borrow::Cow;
|
|
|
|
|
2024-09-06 22:36:12 +01:00
|
|
|
use ratatui::{
|
|
|
|
style::{Style, Stylize},
|
|
|
|
text::{Line, Span},
|
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
|
|
|
}
|
|
|
|
|
2024-09-19 19:18:55 +01:00
|
|
|
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())
|
|
|
|
}
|
|
|
|
|
2024-09-28 20:05:19 +01:00
|
|
|
pub fn create_shortcut_list(
|
|
|
|
shortcuts: impl IntoIterator<Item = Shortcut>,
|
|
|
|
render_width: u16,
|
|
|
|
) -> Box<[Line<'static>]> {
|
|
|
|
let hints = shortcuts.into_iter().collect::<Box<[Shortcut]>>();
|
2024-09-06 22:36:12 +01:00
|
|
|
|
2024-09-28 20:05:19 +01:00
|
|
|
let mut shortcut_spans: Vec<Vec<Span<'static>>> = hints.iter().map(|h| h.to_spans()).collect();
|
|
|
|
|
|
|
|
let mut lines: Vec<Line<'static>> = vec![];
|
|
|
|
|
|
|
|
loop {
|
|
|
|
let split_idx = shortcut_spans
|
|
|
|
.iter()
|
|
|
|
.scan(0usize, |total_len, s| {
|
|
|
|
// take at least one so that we guarantee that we drain the list
|
|
|
|
// otherwise, this might lock up if there's a shortcut that exceeds the window width
|
|
|
|
if *total_len == 0 {
|
|
|
|
*total_len += span_vec_len(s) + 4;
|
|
|
|
Some(())
|
|
|
|
} else {
|
2024-09-06 22:36:12 +01:00
|
|
|
*total_len += span_vec_len(s);
|
2024-09-28 20:05:19 +01:00
|
|
|
if *total_len > render_width as usize {
|
2024-09-06 22:36:12 +01:00
|
|
|
None
|
|
|
|
} else {
|
|
|
|
*total_len += 4;
|
2024-09-28 20:05:19 +01:00
|
|
|
Some(())
|
2024-09-06 22:36:12 +01:00
|
|
|
}
|
2024-09-28 20:05:19 +01:00
|
|
|
}
|
|
|
|
})
|
|
|
|
.count();
|
2024-09-19 19:18:55 +01:00
|
|
|
|
2024-09-28 20:05:19 +01:00
|
|
|
let rest = shortcut_spans.split_off(split_idx);
|
|
|
|
lines.push(add_spacing(shortcut_spans));
|
2024-09-06 22:36:12 +01:00
|
|
|
|
2024-09-28 20:05:19 +01:00
|
|
|
if rest.is_empty() {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
shortcut_spans = rest;
|
|
|
|
}
|
2024-09-06 22:36:12 +01:00
|
|
|
}
|
2024-09-28 20:05:19 +01:00
|
|
|
|
|
|
|
lines.into_boxed_slice()
|
2024-09-06 22:36:12 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Shortcut {
|
2024-09-28 20:05:19 +01:00
|
|
|
pub fn new<const N: usize>(desc: &'static str, key_sequences: [&'static str; N]) -> Self {
|
2024-09-06 22:36:12 +01:00
|
|
|
Self {
|
2024-09-14 16:30:03 +01:00
|
|
|
key_sequences: key_sequences
|
2024-09-06 22:36:12 +01:00
|
|
|
.iter()
|
2024-09-28 20:05:19 +01:00
|
|
|
.map(|s| Span::styled(Cow::<'static, str>::Borrowed(s), Style::default().bold()))
|
2024-09-06 22:36:12 +01:00
|
|
|
.collect(),
|
|
|
|
desc,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-09-28 20:05:19 +01:00
|
|
|
fn to_spans(&self) -> Vec<Span<'static>> {
|
2024-09-06 22:36:12 +01:00
|
|
|
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
|
|
|
|
}
|
|
|
|
}
|