feat: customizing the number of columns of "which key" component (#571)

This commit is contained in:
三咲雅 · Misaki Masa 2024-01-24 16:18:40 +08:00 committed by GitHub
parent a19e263242
commit 28870ad26e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
7 changed files with 84 additions and 71 deletions

View file

@ -113,6 +113,7 @@ hovered = { underline = true }
# : Which {{{
[which]
cols = 3
mask = { bg = "black" }
cand = { fg = "lightcyan" }
rest = { fg = "darkgray" }

View file

@ -95,8 +95,10 @@ pub struct Tasks {
pub hovered: Style,
}
#[derive(Deserialize, Serialize)]
#[derive(Deserialize, Serialize, Validate)]
pub struct Which {
#[validate(range(min = 1, max = 3, message = "Must be between 1 and 3"))]
pub cols: u8,
pub mask: Style,
pub cand: Style,
pub rest: Style,
@ -139,6 +141,7 @@ impl Default for Theme {
let mut theme: Self = toml::from_str(&MERGED_THEME).unwrap();
check_validation(theme.manager.validate());
check_validation(theme.which.validate());
theme.manager.syntect_theme = expand_path(&theme.manager.syntect_theme);

39
yazi-fm/src/which/cand.rs Normal file
View file

@ -0,0 +1,39 @@
use ratatui::{buffer::Buffer, layout::Rect, text::{Line, Span}, widgets::{Paragraph, Widget}};
use yazi_config::{keymap::Control, THEME};
pub(super) struct Cand<'a> {
cand: &'a Control,
times: usize,
}
impl<'a> Cand<'a> {
pub(super) fn new(cand: &'a Control, times: usize) -> Self { Self { times, cand } }
fn keys(&self) -> Vec<String> {
self.cand.on[self.times..].iter().map(ToString::to_string).collect()
}
}
impl Widget for Cand<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let keys = self.keys();
let mut spans = Vec::with_capacity(10);
// Padding
spans.push(Span::raw(" ".repeat(10usize.saturating_sub(keys.join("").len()))));
// First key
spans.push(Span::styled(keys[0].clone(), THEME.which.cand.into()));
// Rest keys
spans.extend(keys.iter().skip(1).map(|k| Span::styled(k, THEME.which.rest.into())));
// Separator
spans.push(Span::styled(&THEME.which.separator, THEME.which.separator_style.into()));
// Description
spans.push(Span::styled(self.cand.desc_or_exec(), THEME.which.desc.into()));
Paragraph::new(Line::from(spans)).render(area, buf);
}
}

View file

@ -1,9 +1,12 @@
use ratatui::{buffer::Buffer, layout, layout::{Constraint, Direction, Rect}, widgets::{Block, Widget}};
use yazi_config::THEME;
use super::Side;
use super::Cand;
use crate::{widgets, Ctx};
const PADDING_X: u16 = 1;
const PADDING_Y: u16 = 1;
pub(crate) struct Which<'a> {
cx: &'a Ctx,
}
@ -15,31 +18,42 @@ impl<'a> Which<'a> {
impl Widget for Which<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let which = &self.cx.which;
let mut cands: (Vec<_>, Vec<_>, Vec<_>) = Default::default();
for (i, &c) in which.cands.iter().enumerate() {
match i % 3 {
0 => cands.0.push(c),
1 => cands.1.push(c),
2 => cands.2.push(c),
_ => unreachable!(),
}
let cols = THEME.which.cols as usize;
let height = area.height.min(which.cands.len().div_ceil(cols) as u16 + PADDING_Y * 2);
let area = Rect {
x: PADDING_X.min(area.width),
y: area.height.saturating_sub(height + PADDING_Y * 2),
width: area.width.saturating_sub(PADDING_X * 2),
height,
};
// Don't render if there's no space
if area.height <= PADDING_Y * 2 {
return;
}
let height = area.height.min(cands.0.len() as u16 + 2);
let y = area.height.saturating_sub(height + 2);
let area = Rect { x: area.width.min(1), y, width: area.width.saturating_sub(2), height };
let chunks = layout::Layout::new(Direction::Horizontal, [
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
Constraint::Ratio(1, 3),
])
.split(area);
let chunks = {
use Constraint::*;
layout::Layout::new(Direction::Horizontal, match cols {
1 => &[Ratio(1, 1)] as &[Constraint],
2 => &[Ratio(1, 2), Ratio(1, 2)],
_ => &[Ratio(1, 3), Ratio(1, 3), Ratio(1, 3)],
})
.split(area)
};
widgets::Clear.render(area, buf);
Block::new().style(THEME.which.mask.into()).render(area, buf);
Side::new(which.times, cands.0).render(chunks[0], buf);
Side::new(which.times, cands.1).render(chunks[1], buf);
Side::new(which.times, cands.2).render(chunks[2], buf);
for y in 0..area.height {
for (x, chunk) in chunks.iter().enumerate() {
let Some(&cand) = which.cands.get(y as usize * cols + x) else {
break;
};
Cand::new(cand, which.times).render(Rect { y: chunk.y + y + 1, height: 1, ..*chunk }, buf);
}
}
}
}

View file

@ -1,5 +1,5 @@
mod cand;
mod layout;
mod side;
use cand::*;
pub(super) use layout::*;
use side::*;

View file

@ -1,44 +0,0 @@
use ratatui::{buffer::Buffer, layout::Rect, text::{Line, Span}, widgets::{Block, List, ListItem, Padding, Widget}};
use yazi_config::{keymap::Control, THEME};
pub(super) struct Side<'a> {
times: usize,
cands: Vec<&'a Control>,
}
impl<'a> Side<'a> {
pub(super) fn new(times: usize, cands: Vec<&'a Control>) -> Self { Self { times, cands } }
}
impl Widget for Side<'_> {
fn render(self, area: Rect, buf: &mut Buffer) {
let items = self
.cands
.into_iter()
.map(|c| {
let mut spans = Vec::with_capacity(10);
// Keys
let keys = c.on[self.times..].iter().map(ToString::to_string).collect::<Vec<_>>();
spans.push(Span::raw(" ".repeat(10usize.saturating_sub(keys.join("").len()))));
spans.push(Span::styled(keys[0].clone(), THEME.which.cand.into()));
spans.extend(
keys.iter().skip(1).map(|k| Span::styled(k.to_string(), THEME.which.rest.into())),
);
// Separator
spans.push(Span::styled(
THEME.which.separator.to_string(),
THEME.which.separator_style.into(),
));
// Desc / Exec
spans.push(Span::styled(c.desc_or_exec(), THEME.which.desc.into()));
ListItem::new(Line::from(spans))
})
.collect::<Vec<_>>();
List::new(items).block(Block::new().padding(Padding::new(0, 1, 1, 1))).render(area, buf);
}
}

View file

@ -6,8 +6,8 @@ function Progress:render(area, offset)
self.area = ui.Rect {
x = math.max(0, area.w - offset - 21),
y = area.y,
w = math.max(0, math.min(20, area.w - offset - 1)),
h = 1,
w = ya.clamp(0, area.w - offset - 1, 20),
h = math.min(1, area.h),
}
return self:partial_render()
end