From 28870ad26e9bf7831b8f267d09d0ea76c752c99e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E4=B8=89=E5=92=B2=E9=9B=85=20=C2=B7=20Misaki=20Masa?= Date: Wed, 24 Jan 2024 16:18:40 +0800 Subject: [PATCH] feat: customizing the number of columns of "which key" component (#571) --- yazi-config/preset/theme.toml | 1 + yazi-config/src/theme/theme.rs | 5 +- yazi-fm/src/which/cand.rs | 39 +++++++++++++++ yazi-fm/src/which/layout.rs | 58 ++++++++++++++-------- yazi-fm/src/which/mod.rs | 4 +- yazi-fm/src/which/side.rs | 44 ---------------- yazi-plugin/preset/components/progress.lua | 4 +- 7 files changed, 84 insertions(+), 71 deletions(-) create mode 100644 yazi-fm/src/which/cand.rs delete mode 100644 yazi-fm/src/which/side.rs diff --git a/yazi-config/preset/theme.toml b/yazi-config/preset/theme.toml index dd8cae0b..2e1379be 100644 --- a/yazi-config/preset/theme.toml +++ b/yazi-config/preset/theme.toml @@ -113,6 +113,7 @@ hovered = { underline = true } # : Which {{{ [which] +cols = 3 mask = { bg = "black" } cand = { fg = "lightcyan" } rest = { fg = "darkgray" } diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index f7b58c6f..089f02f3 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -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); diff --git a/yazi-fm/src/which/cand.rs b/yazi-fm/src/which/cand.rs new file mode 100644 index 00000000..75f9ced1 --- /dev/null +++ b/yazi-fm/src/which/cand.rs @@ -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 { + 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); + } +} diff --git a/yazi-fm/src/which/layout.rs b/yazi-fm/src/which/layout.rs index e7cdca36..31b0d9a1 100644 --- a/yazi-fm/src/which/layout.rs +++ b/yazi-fm/src/which/layout.rs @@ -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); + } + } } } diff --git a/yazi-fm/src/which/mod.rs b/yazi-fm/src/which/mod.rs index 2ed246ae..e456039b 100644 --- a/yazi-fm/src/which/mod.rs +++ b/yazi-fm/src/which/mod.rs @@ -1,5 +1,5 @@ +mod cand; mod layout; -mod side; +use cand::*; pub(super) use layout::*; -use side::*; diff --git a/yazi-fm/src/which/side.rs b/yazi-fm/src/which/side.rs deleted file mode 100644 index 666954a4..00000000 --- a/yazi-fm/src/which/side.rs +++ /dev/null @@ -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::>(); - 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::>(); - - List::new(items).block(Block::new().padding(Padding::new(0, 1, 1, 1))).render(area, buf); - } -} diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua index eca652df..079a445e 100644 --- a/yazi-plugin/preset/components/progress.lua +++ b/yazi-plugin/preset/components/progress.lua @@ -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