From 43b5ae0e6cc5c8ee96462651f01d78a0d98077fc 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: Mon, 14 Oct 2024 01:54:03 +0800 Subject: [PATCH] feat!: add `ui.Text`, `ui.Table`, remove `ui.Paragraph` and `ui.ListItem` (#1776) --- yazi-core/src/manager/commands/hover.rs | 2 +- yazi-core/src/manager/commands/mod.rs | 4 +- yazi-core/src/manager/commands/tab_create.rs | 2 +- .../src/manager/commands/update_files.rs | 4 +- .../{update_mimetype.rs => update_mimes.rs} | 4 +- .../{update_task.rs => update_tasks.rs} | 8 +- yazi-core/src/manager/manager.rs | 2 +- yazi-core/src/manager/watcher.rs | 9 +- yazi-core/src/tab/commands/enter.rs | 2 +- yazi-core/src/tab/commands/escape.rs | 2 +- yazi-core/src/tab/commands/filter_do.rs | 4 +- yazi-core/src/tab/commands/hidden.rs | 6 +- yazi-core/src/tab/commands/mod.rs | 4 +- yazi-core/src/tab/commands/spot.rs | 20 +++ .../commands/{preview.rs => update_peeked.rs} | 4 +- yazi-core/src/tab/commands/update_spotted.rs | 22 +++ yazi-core/src/tab/tab.rs | 13 +- yazi-fm/src/executor.rs | 8 +- yazi-fm/src/lives/file.rs | 2 +- yazi-plugin/preset/components/current.lua | 10 +- yazi-plugin/preset/components/entity.lua | 2 +- yazi-plugin/preset/components/header.lua | 4 +- yazi-plugin/preset/components/parent.lua | 5 +- yazi-plugin/preset/components/progress.lua | 2 +- yazi-plugin/preset/components/status.lua | 7 +- yazi-plugin/preset/plugins/archive.lua | 11 +- yazi-plugin/preset/plugins/code.lua | 2 +- yazi-plugin/preset/plugins/empty.lua | 9 +- yazi-plugin/preset/plugins/file.lua | 17 ++- yazi-plugin/preset/plugins/folder.lua | 8 +- yazi-plugin/preset/plugins/json.lua | 2 +- yazi-plugin/preset/plugins/mime.lua | 2 +- yazi-plugin/src/cast.rs | 4 +- yazi-plugin/src/elements/bar.rs | 4 +- yazi-plugin/src/elements/border.rs | 4 +- yazi-plugin/src/elements/constraint.rs | 4 + yazi-plugin/src/elements/elements.rs | 5 +- yazi-plugin/src/elements/gauge.rs | 8 +- yazi-plugin/src/elements/layout.rs | 12 +- yazi-plugin/src/elements/line.rs | 2 +- yazi-plugin/src/elements/list.rs | 56 ++----- yazi-plugin/src/elements/mod.rs | 5 +- yazi-plugin/src/elements/paragraph.rs | 96 ------------ yazi-plugin/src/elements/style.rs | 2 +- yazi-plugin/src/elements/table.rs | 133 +++++++++++++++++ yazi-plugin/src/elements/text.rs | 137 ++++++++++++++++++ yazi-plugin/src/isolate/isolate.rs | 2 +- yazi-plugin/src/macros.rs | 28 ++-- yazi-plugin/src/process/command.rs | 14 +- yazi-plugin/src/utils/preview.rs | 12 +- yazi-proxy/src/manager.rs | 4 +- yazi-scheduler/src/scheduler.rs | 4 +- 52 files changed, 466 insertions(+), 273 deletions(-) rename yazi-core/src/manager/commands/{update_mimetype.rs => update_mimes.rs} (90%) rename yazi-core/src/manager/commands/{update_task.rs => update_tasks.rs} (62%) create mode 100644 yazi-core/src/tab/commands/spot.rs rename yazi-core/src/tab/commands/{preview.rs => update_peeked.rs} (81%) create mode 100644 yazi-core/src/tab/commands/update_spotted.rs delete mode 100644 yazi-plugin/src/elements/paragraph.rs create mode 100644 yazi-plugin/src/elements/table.rs create mode 100644 yazi-plugin/src/elements/text.rs diff --git a/yazi-core/src/manager/commands/hover.rs b/yazi-core/src/manager/commands/hover.rs index f31fd802..632e41f2 100644 --- a/yazi-core/src/manager/commands/hover.rs +++ b/yazi-core/src/manager/commands/hover.rs @@ -42,7 +42,7 @@ impl Manager { if let Some(ref p) = tab.parent { to_watch.insert(&p.url); } - if let Some(h) = tab.current.hovered().filter(|&h| h.is_dir()) { + if let Some(h) = tab.hovered().filter(|&h| h.is_dir()) { to_watch.insert(&h.url); } } diff --git a/yazi-core/src/manager/commands/mod.rs b/yazi-core/src/manager/commands/mod.rs index aba8b14b..cf1f8b73 100644 --- a/yazi-core/src/manager/commands/mod.rs +++ b/yazi-core/src/manager/commands/mod.rs @@ -20,9 +20,9 @@ yazi_macro::mod_flat!( tab_switch unyank update_files - update_mimetype + update_mimes update_paged - update_task + update_tasks update_yanked yank ); diff --git a/yazi-core/src/manager/commands/tab_create.rs b/yazi-core/src/manager/commands/tab_create.rs index b8ec0dde..b104aba0 100644 --- a/yazi-core/src/manager/commands/tab_create.rs +++ b/yazi-core/src/manager/commands/tab_create.rs @@ -40,7 +40,7 @@ impl Tabs { if !opt.current { tab.cd(opt.url); - } else if let Some(h) = self.active().current.hovered() { + } else if let Some(h) = self.active().hovered() { tab.conf = self.active().conf.clone(); tab.apply_files_attrs(); tab.reveal(h.url.to_regular()); diff --git a/yazi-core/src/manager/commands/update_files.rs b/yazi-core/src/manager/commands/update_files.rs index 71b39e85..2be38e4d 100644 --- a/yazi-core/src/manager/commands/update_files.rs +++ b/yazi-core/src/manager/commands/update_files.rs @@ -52,7 +52,7 @@ impl Manager { Self::update_current(tab, op, tasks); } else if matches!(&tab.parent, Some(p) if *url == p.url) { Self::update_parent(tab, op); - } else if matches!(tab.current.hovered(), Some(h) if *url == h.url) { + } else if matches!(tab.hovered(), Some(h) if *url == h.url) { Self::update_hovered(tab, op); } else { Self::update_history(tab, op); @@ -74,7 +74,7 @@ impl Manager { } fn update_current(tab: &mut Tab, op: Cow, tasks: &Tasks) { - let hovered = tab.current.hovered().filter(|_| tab.current.tracing).map(|h| h.urn_owned()); + let hovered = tab.hovered().filter(|_| tab.current.tracing).map(|h| h.urn_owned()); let calc = !matches!(*op, FilesOp::Size(..) | FilesOp::Deleting(..)); let foreign = matches!(op, Cow::Borrowed(_)); diff --git a/yazi-core/src/manager/commands/update_mimetype.rs b/yazi-core/src/manager/commands/update_mimes.rs similarity index 90% rename from yazi-core/src/manager/commands/update_mimetype.rs rename to yazi-core/src/manager/commands/update_mimes.rs index 3c8400f0..dfa2948d 100644 --- a/yazi-core/src/manager/commands/update_mimetype.rs +++ b/yazi-core/src/manager/commands/update_mimes.rs @@ -19,9 +19,9 @@ impl TryFrom for Opt { } impl Manager { - pub fn update_mimetype(&mut self, opt: impl TryInto, tasks: &Tasks) { + pub fn update_mimes(&mut self, opt: impl TryInto, tasks: &Tasks) { let Ok(opt) = opt.try_into() else { - return error!("invalid arguments for update_mimetype"); + return error!("invalid arguments for update_mimes"); }; let linked = LINKED.read(); diff --git a/yazi-core/src/manager/commands/update_task.rs b/yazi-core/src/manager/commands/update_tasks.rs similarity index 62% rename from yazi-core/src/manager/commands/update_task.rs rename to yazi-core/src/manager/commands/update_tasks.rs index fa2a8df4..caed5e4d 100644 --- a/yazi-core/src/manager/commands/update_task.rs +++ b/yazi-core/src/manager/commands/update_tasks.rs @@ -3,23 +3,23 @@ use yazi_shared::{event::Cmd, fs::Url}; use crate::manager::Manager; pub struct Opt { - url: Url, + urls: Vec, } impl TryFrom for Opt { type Error = (); fn try_from(mut c: Cmd) -> Result { - Ok(Self { url: c.take_any("url").ok_or(())? }) + Ok(Self { urls: c.take_any("urls").ok_or(())? }) } } impl Manager { - pub fn update_task(&mut self, opt: impl TryInto) { + pub fn update_tasks(&mut self, opt: impl TryInto) { let Ok(opt) = opt.try_into() else { return; }; - self.watcher.push_file(opt.url); + self.watcher.push_files(opt.urls); } } diff --git a/yazi-core/src/manager/manager.rs b/yazi-core/src/manager/manager.rs index 5d3bb11d..b262fc11 100644 --- a/yazi-core/src/manager/manager.rs +++ b/yazi-core/src/manager/manager.rs @@ -71,7 +71,7 @@ impl Manager { pub fn parent(&self) -> Option<&Folder> { self.active().parent.as_ref() } #[inline] - pub fn hovered(&self) -> Option<&File> { self.active().current.hovered() } + pub fn hovered(&self) -> Option<&File> { self.active().hovered() } #[inline] pub fn hovered_folder(&self) -> Option<&Folder> { self.active().hovered_folder() } diff --git a/yazi-core/src/manager/watcher.rs b/yazi-core/src/manager/watcher.rs index dc094338..1e6f4172 100644 --- a/yazi-core/src/manager/watcher.rs +++ b/yazi-core/src/manager/watcher.rs @@ -53,9 +53,12 @@ impl Watcher { self.in_tx.send(new.into_iter().cloned().collect()).ok(); } - pub(super) fn push_file(&self, url: Url) { - if url.parent_url().is_some_and(|p| WATCHED.read().contains(&p)) { - self.out_tx.send(url).ok(); + pub(super) fn push_files(&self, url: Vec) { + let watched = WATCHED.read(); + for u in url { + if u.parent_url().is_some_and(|p| watched.contains(&p)) { + self.out_tx.send(u).ok(); + } } } diff --git a/yazi-core/src/tab/commands/enter.rs b/yazi-core/src/tab/commands/enter.rs index 87e44cab..57aba094 100644 --- a/yazi-core/src/tab/commands/enter.rs +++ b/yazi-core/src/tab/commands/enter.rs @@ -4,6 +4,6 @@ use crate::tab::Tab; impl Tab { pub fn enter(&mut self, _: Cmd) { - self.current.hovered().filter(|h| h.is_dir()).map(|h| h.url.to_regular()).map(|u| self.cd(u)); + self.hovered().filter(|h| h.is_dir()).map(|h| h.url.to_regular()).map(|u| self.cd(u)); } } diff --git a/yazi-core/src/tab/commands/escape.rs b/yazi-core/src/tab/commands/escape.rs index 1ca6396a..ce43851a 100644 --- a/yazi-core/src/tab/commands/escape.rs +++ b/yazi-core/src/tab/commands/escape.rs @@ -86,7 +86,7 @@ impl Tab { } self.selected.clear(); - if self.current.hovered().is_some_and(|h| h.is_dir()) { + if self.hovered().is_some_and(|h| h.is_dir()) { ManagerProxy::peek(true); } render_and!(true) diff --git a/yazi-core/src/tab/commands/filter_do.rs b/yazi-core/src/tab/commands/filter_do.rs index 14cb8801..d7e1ded5 100644 --- a/yazi-core/src/tab/commands/filter_do.rs +++ b/yazi-core/src/tab/commands/filter_do.rs @@ -20,13 +20,13 @@ impl Tab { ManagerProxy::update_paged(); // Update for paged files in next loop } - let hovered = self.current.hovered().map(|f| f.urn_owned()); + let hovered = self.hovered().map(|f| f.urn_owned()); if !self.current.files.set_filter(filter) { return; } self.current.repos(hovered.as_ref().map(|u| u.as_urn())); - if self.current.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) { + if self.hovered().map(|f| f.urn()) != hovered.as_ref().map(|u| u.as_urn()) { ManagerProxy::hover(None, self.idx); } diff --git a/yazi-core/src/tab/commands/hidden.rs b/yazi-core/src/tab/commands/hidden.rs index b8a00d8d..527f6886 100644 --- a/yazi-core/src/tab/commands/hidden.rs +++ b/yazi-core/src/tab/commands/hidden.rs @@ -11,12 +11,12 @@ impl Tab { _ => !self.conf.show_hidden, }; - let hovered = self.current.hovered().map(|f| f.url_owned()); + let hovered = self.hovered().map(|f| f.url_owned()); self.apply_files_attrs(); - if hovered.as_ref() != self.current.hovered().map(|f| &f.url) { + if hovered.as_ref() != self.hovered().map(|f| &f.url) { ManagerProxy::hover(hovered, self.idx); - } else if self.current.hovered().is_some_and(|f| f.is_dir()) { + } else if self.hovered().is_some_and(|f| f.is_dir()) { ManagerProxy::peek(true); } ManagerProxy::update_paged(); diff --git a/yazi-core/src/tab/commands/mod.rs b/yazi-core/src/tab/commands/mod.rs index 6ef89e27..5021dcb0 100644 --- a/yazi-core/src/tab/commands/mod.rs +++ b/yazi-core/src/tab/commands/mod.rs @@ -14,14 +14,16 @@ yazi_macro::mod_flat!( hidden leave linemode - preview reveal search select select_all shell sort + spot toggle toggle_all + update_peeked + update_spotted visual_mode ); diff --git a/yazi-core/src/tab/commands/spot.rs b/yazi-core/src/tab/commands/spot.rs new file mode 100644 index 00000000..9623f90d --- /dev/null +++ b/yazi-core/src/tab/commands/spot.rs @@ -0,0 +1,20 @@ +use yazi_shared::event::{Cmd, Data}; + +use crate::tab::Tab; + +struct Opt { + skip: Option, +} + +impl From for Opt { + fn from(c: Cmd) -> Self { Self { skip: c.first().and_then(Data::as_usize) } } +} + +impl Tab { + #[yazi_codegen::command] + pub fn spot(&mut self, c: Cmd) { + let Some(hovered) = self.hovered().cloned() else { + return self.preview.reset(); + }; + } +} diff --git a/yazi-core/src/tab/commands/preview.rs b/yazi-core/src/tab/commands/update_peeked.rs similarity index 81% rename from yazi-core/src/tab/commands/preview.rs rename to yazi-core/src/tab/commands/update_peeked.rs index 75837b38..ebe58f82 100644 --- a/yazi-core/src/tab/commands/preview.rs +++ b/yazi-core/src/tab/commands/update_peeked.rs @@ -17,8 +17,8 @@ impl TryFrom for Opt { } impl Tab { - pub fn preview(&mut self, opt: impl TryInto) { - let Some(hovered) = self.current.hovered().map(|h| &h.url) else { + pub fn update_peeked(&mut self, opt: impl TryInto) { + let Some(hovered) = self.hovered().map(|h| &h.url) else { return self.preview.reset(); }; diff --git a/yazi-core/src/tab/commands/update_spotted.rs b/yazi-core/src/tab/commands/update_spotted.rs new file mode 100644 index 00000000..04c16eb4 --- /dev/null +++ b/yazi-core/src/tab/commands/update_spotted.rs @@ -0,0 +1,22 @@ +use yazi_plugin::utils::PreviewLock; +use yazi_shared::event::Cmd; + +use crate::tab::Tab; + +pub struct Opt { + lock: PreviewLock, +} + +impl TryFrom for Opt { + type Error = (); + + fn try_from(mut c: Cmd) -> Result { + Ok(Self { lock: c.take_any("lock").ok_or(())? }) + } +} + +impl Tab { + pub fn update_spotted(&mut self, opt: impl TryInto) { + todo!(); + } +} diff --git a/yazi-core/src/tab/tab.rs b/yazi-core/src/tab/tab.rs index 4ee6250b..345425de 100644 --- a/yazi-core/src/tab/tab.rs +++ b/yazi-core/src/tab/tab.rs @@ -7,7 +7,7 @@ use yazi_adapter::Dimension; use yazi_config::{LAYOUT, popup::{Origin, Position}}; use yazi_fs::{Folder, FolderStage}; use yazi_macro::render; -use yazi_shared::fs::Url; +use yazi_shared::fs::{File, Url}; use super::{Backstack, Config, Finder, History, Mode, Preview}; use crate::tab::Selected; @@ -42,8 +42,11 @@ impl Tab { #[inline] pub fn cwd(&self) -> &Url { &self.current.url } + #[inline] + pub fn hovered(&self) -> Option<&File> { self.current.hovered() } + pub fn hovered_rect(&self) -> Option { - let y = self.current.files.position(self.current.hovered()?.urn())? - self.current.offset; + let y = self.current.files.position(self.hovered()?.urn())? - self.current.offset; let mut rect = LAYOUT.load().current; rect.y = rect.y.saturating_sub(1) + y as u16; @@ -62,7 +65,7 @@ impl Tab { pub fn selected_or_hovered(&self, reorder: bool) -> Box + '_> { if self.selected.is_empty() { - Box::new(self.current.hovered().map(|h| vec![&h.url]).unwrap_or_default().into_iter()) + Box::new(self.hovered().map(|h| vec![&h.url]).unwrap_or_default().into_iter()) } else if !reorder { Box::new(self.selected.keys()) } else { @@ -73,7 +76,7 @@ impl Tab { } pub fn hovered_and_selected(&self, reorder: bool) -> Box + '_> { - let Some(h) = self.current.hovered() else { return Box::new(iter::empty()) }; + let Some(h) = self.hovered() else { return Box::new(iter::empty()) }; if self.selected.is_empty() { Box::new([&h.url, &h.url].into_iter()) @@ -89,7 +92,7 @@ impl Tab { // --- History #[inline] pub fn hovered_folder(&self) -> Option<&Folder> { - self.current.hovered().filter(|&h| h.is_dir()).and_then(|h| self.history.get(&h.url)) + self.hovered().filter(|&h| h.is_dir()).and_then(|h| self.history.get(&h.url)) } pub fn apply_files_attrs(&mut self) { diff --git a/yazi-fm/src/executor.rs b/yazi-fm/src/executor.rs index 278f5e23..c75069fd 100644 --- a/yazi-fm/src/executor.rs +++ b/yazi-fm/src/executor.rs @@ -65,20 +65,22 @@ impl<'a> Executor<'a> { }; } - on!(MANAGER, update_task); + on!(MANAGER, update_tasks); on!(MANAGER, update_files, &self.app.cx.tasks); - on!(MANAGER, update_mimetype, &self.app.cx.tasks); + on!(MANAGER, update_mimes, &self.app.cx.tasks); on!(MANAGER, update_paged, &self.app.cx.tasks); on!(MANAGER, update_yanked); on!(MANAGER, hover); on!(MANAGER, peek); on!(MANAGER, seek); + on!(ACTIVE, spot); on!(MANAGER, refresh, &self.app.cx.tasks); on!(MANAGER, quit, &self.app.cx.tasks); on!(MANAGER, close, &self.app.cx.tasks); on!(MANAGER, suspend); on!(ACTIVE, escape); - on!(ACTIVE, preview); + on!(ACTIVE, update_peeked); + on!(ACTIVE, update_spotted); // Navigation on!(ACTIVE, arrow); diff --git a/yazi-fm/src/lives/file.rs b/yazi-fm/src/lives/file.rs index c62878dd..a0e28776 100644 --- a/yazi-fm/src/lives/file.rs +++ b/yazi-fm/src/lives/file.rs @@ -90,7 +90,7 @@ impl File { }); reg.add_method("in_current", |_, me, ()| Ok(me.folder().url == me.tab().current.url)); reg.add_method("in_preview", |_, me, ()| { - Ok(me.tab().current.hovered().is_some_and(|f| f.url == me.folder().url)) + Ok(me.tab().hovered().is_some_and(|f| f.url == me.folder().url)) }); reg.add_method("found", |lua, me, ()| { let cx = lua.named_registry_value::("cx")?; diff --git a/yazi-plugin/preset/components/current.lua b/yazi-plugin/preset/components/current.lua index 1e5add7d..f472783d 100644 --- a/yazi-plugin/preset/components/current.lua +++ b/yazi-plugin/preset/components/current.lua @@ -19,7 +19,7 @@ function Current:empty() end return { - ui.Paragraph(self._area, { line }):align(ui.Paragraph.CENTER), + ui.Text(line):area(self._area):align(ui.Text.CENTER), } end @@ -32,14 +32,12 @@ function Current:render() local entities, linemodes = {}, {} for _, f in ipairs(files) do linemodes[#linemodes + 1] = Linemode:new(f):render() - - local entity = Entity:new(f) - entities[#entities + 1] = ui.ListItem(entity:render()):style(entity:style()) + entities[#entities + 1] = Entity:new(f):render() end return { - ui.List(self._area, entities), - ui.Paragraph(self._area, linemodes):align(ui.Paragraph.RIGHT), + ui.List(entities):area(self._area), + ui.Text(linemodes):area(self._area):align(ui.Text.RIGHT), } end diff --git a/yazi-plugin/preset/components/entity.lua b/yazi-plugin/preset/components/entity.lua index 0cf2e951..5da34f02 100644 --- a/yazi-plugin/preset/components/entity.lua +++ b/yazi-plugin/preset/components/entity.lua @@ -81,7 +81,7 @@ function Entity:render() for _, c in ipairs(self._children) do lines[#lines + 1] = (type(c[1]) == "string" and self[c[1]] or c[1])(self) end - return ui.Line(lines) + return ui.Line(lines):style(self:style()) end function Entity:style() diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index ac456073..d8340808 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -96,8 +96,8 @@ function Header:render() local left = self:children_render(self.LEFT) return { - ui.Paragraph(self._area, { left }), - ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), + ui.Text(left):area(self._area), + ui.Text(right):area(self._area):align(ui.Text.RIGHT), } end diff --git a/yazi-plugin/preset/components/parent.lua b/yazi-plugin/preset/components/parent.lua index 3caa5cec..c9ecdd60 100644 --- a/yazi-plugin/preset/components/parent.lua +++ b/yazi-plugin/preset/components/parent.lua @@ -17,12 +17,11 @@ function Parent:render() local items = {} for _, f in ipairs(self._folder.window) do - local entity = Entity:new(f) - items[#items + 1] = ui.ListItem(entity:render()):style(entity:style()) + items[#items + 1] = Entity:new(f):render() end return { - ui.List(self._area, items), + ui.List(items):area(self._area), } end diff --git a/yazi-plugin/preset/components/progress.lua b/yazi-plugin/preset/components/progress.lua index 4ed8da98..463f8935 100644 --- a/yazi-plugin/preset/components/progress.lua +++ b/yazi-plugin/preset/components/progress.lua @@ -18,7 +18,7 @@ end function Progress:partial_render() local progress = cx.tasks.progress if progress.total == 0 then - return { ui.Paragraph(self._area, {}) } + return { ui.Text {} } end local gauge = ui.Gauge(self._area) diff --git a/yazi-plugin/preset/components/status.lua b/yazi-plugin/preset/components/status.lua index 8686a1d5..1c64e40c 100644 --- a/yazi-plugin/preset/components/status.lua +++ b/yazi-plugin/preset/components/status.lua @@ -133,10 +133,11 @@ end function Status:render() local left = self:children_render(self.LEFT) local right = self:children_render(self.RIGHT) + local right_width = right:width() return { - ui.Paragraph(self._area, { left }), - ui.Paragraph(self._area, { right }):align(ui.Paragraph.RIGHT), - table.unpack(Progress:render(self._area, right:width())), + ui.Text(left):area(self._area), + ui.Text(right):area(self._area):align(ui.Text.RIGHT), + table.unpack(Progress:render(self._area, right_width)), } end diff --git a/yazi-plugin/preset/plugins/archive.lua b/yazi-plugin/preset/plugins/archive.lua index 5f621d50..e992d630 100644 --- a/yazi-plugin/preset/plugins/archive.lua +++ b/yazi-plugin/preset/plugins/archive.lua @@ -7,9 +7,10 @@ function M:peek() local files, bound, code = self.list_files({ "-p", tostring(self.file.url) }, self.skip, limit) if code ~= 0 then return ya.preview_widgets(self, { - ui.Paragraph(self.area, { - ui.Line(code == 2 and "File list in this archive is encrypted" or "Spawn `7z` and `7zz` both commands failed"), - }), + ui.Text( + ui.Line(code == 2 and "File list in this archive is encrypted" or "Spawn `7z` and `7zz` both commands failed") + ) + :area(self.area), }) end @@ -36,8 +37,8 @@ function M:peek() ya.manager_emit("peek", { math.max(0, bound - limit), only_if = self.file.url, upper_bound = true }) else ya.preview_widgets(self, { - ui.Paragraph(self.area, paths), - ui.Paragraph(self.area, sizes):align(ui.Paragraph.RIGHT), + ui.Text(paths):area(self.area), + ui.Text(sizes):area(self.area):align(ui.Text.RIGHT), }) end end diff --git a/yazi-plugin/preset/plugins/code.lua b/yazi-plugin/preset/plugins/code.lua index cdd92299..8396dc34 100644 --- a/yazi-plugin/preset/plugins/code.lua +++ b/yazi-plugin/preset/plugins/code.lua @@ -6,7 +6,7 @@ function M:peek() ya.manager_emit("peek", { bound, only_if = self.file.url, upper_bound = true }) elseif err and not err:find("cancelled", 1, true) then ya.preview_widgets(self, { - ui.Paragraph(self.area, { ui.Line(err):reverse() }), + ui.Text(ui.Line(err):reverse()):area(self.area), }) end end diff --git a/yazi-plugin/preset/plugins/empty.lua b/yazi-plugin/preset/plugins/empty.lua index 499a3b35..bc664a8b 100644 --- a/yazi-plugin/preset/plugins/empty.lua +++ b/yazi-plugin/preset/plugins/empty.lua @@ -1,11 +1,6 @@ local M = {} -function M:msg(s) - local p = ui.Paragraph(self.area, { - ui.Line(s):reverse(), - }) - ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) }) -end +function M:msg(s) ya.preview_widgets(self, { ui.Text(ui.Line(s):reverse()):area(self.area):wrap(ui.Text.WRAP) }) end function M:peek() local path = tostring(self.file.url) @@ -31,7 +26,7 @@ function M:peek() elseif self.skip > 0 and i < self.skip + limit then ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true }) else - ya.preview_widgets(self, { ui.Paragraph(self.area, lines) }) + ya.preview_widgets(self, { ui.Text(lines):area(self.area) }) end end diff --git a/yazi-plugin/preset/plugins/file.lua b/yazi-plugin/preset/plugins/file.lua index 6f331b4b..cc8b8255 100644 --- a/yazi-plugin/preset/plugins/file.lua +++ b/yazi-plugin/preset/plugins/file.lua @@ -6,16 +6,23 @@ function M:peek() local p if output then - p = ui.Paragraph.parse(self.area, "----- File Type Classification -----\n\n" .. output.stdout) + p = ui.Text.parse("----- File Type Classification -----\n\n" .. output.stdout):area(self.area) else - p = ui.Paragraph(self.area, { - ui.Line(string.format("Spawn `%s` command returns %s", cmd, code)), - }) + p = ui.Text(string.format("Spawn `%s` command returns %s", cmd, code)):area(self.area) end - ya.preview_widgets(self, { p:wrap(ui.Paragraph.WRAP) }) + ya.preview_widgets(self, { p:wrap(ui.Text.WRAP) }) end function M:seek() end +function M:spot(skip) + local rect = ui.Rect { x = 10, y = 10, w = 20, h = 20 } + + ya.spot_widgets(self, { + ui.Clear(rect), + ui.Table(rect), + }) +end + return M diff --git a/yazi-plugin/preset/plugins/folder.lua b/yazi-plugin/preset/plugins/folder.lua index 14913eee..61fcc8e8 100644 --- a/yazi-plugin/preset/plugins/folder.lua +++ b/yazi-plugin/preset/plugins/folder.lua @@ -13,19 +13,17 @@ function M:peek() if #folder.files == 0 then return ya.preview_widgets(self, { - ui.Paragraph(self.area, { ui.Line(folder.stage.is_loading and "Loading..." or "No items") }) - :align(ui.Paragraph.CENTER), + ui.Text(ui.Line(folder.stage.is_loading and "Loading..." or "No items")):area(self.area):align(ui.Text.CENTER), }) end local items = {} for _, f in ipairs(folder.window) do - local entity = Entity:new(f) - items[#items + 1] = ui.ListItem(entity:render()):style(entity:style()) + items[#items + 1] = Entity:new(f):render() end ya.preview_widgets(self, { - ui.List(self.area, items), + ui.List(items):area(self.area), table.unpack(Marker:new(self.area, folder):render()), }) end diff --git a/yazi-plugin/preset/plugins/json.lua b/yazi-plugin/preset/plugins/json.lua index 78cc04e1..85e649a1 100644 --- a/yazi-plugin/preset/plugins/json.lua +++ b/yazi-plugin/preset/plugins/json.lua @@ -37,7 +37,7 @@ function M:peek() ya.manager_emit("peek", { math.max(0, i - limit), only_if = self.file.url, upper_bound = true }) else lines = lines:gsub("\t", string.rep(" ", PREVIEW.tab_size)) - ya.preview_widgets(self, { ui.Paragraph.parse(self.area, lines) }) + ya.preview_widgets(self, { ui.Text.parse(lines):area(self.area) }) end end diff --git a/yazi-plugin/preset/plugins/mime.lua b/yazi-plugin/preset/plugins/mime.lua index 4403d663..b0613c06 100644 --- a/yazi-plugin/preset/plugins/mime.lua +++ b/yazi-plugin/preset/plugins/mime.lua @@ -28,7 +28,7 @@ function M:fetch() return end if next(updates) then - ya.manager_emit("update_mimetype", { updates = updates }) + ya.manager_emit("update_mimes", { updates = updates }) updates, last = {}, ya.time() end end diff --git a/yazi-plugin/src/cast.rs b/yazi-plugin/src/cast.rs index 9acf8b94..87a45aaf 100644 --- a/yazi-plugin/src/cast.rs +++ b/yazi-plugin/src/cast.rs @@ -3,7 +3,7 @@ use mlua::AnyUserData; use crate::elements::Renderable; pub fn cast_to_renderable(ud: &AnyUserData) -> Option> { - if let Ok(c) = ud.take::() { + if let Ok(c) = ud.take::() { Some(Box::new(c)) } else if let Ok(c) = ud.take::() { Some(Box::new(c)) @@ -15,6 +15,8 @@ pub fn cast_to_renderable(ud: &AnyUserData) -> Option Some(Box::new(c)) } else if let Ok(c) = ud.take::() { Some(Box::new(c)) + } else if let Ok(c) = ud.take::() { + Some(Box::new(c)) } else { None } diff --git a/yazi-plugin/src/elements/bar.rs b/yazi-plugin/src/elements/bar.rs index ad5bfa09..769a3b24 100644 --- a/yazi-plugin/src/elements/bar.rs +++ b/yazi-plugin/src/elements/bar.rs @@ -45,11 +45,11 @@ impl UserData for Bar { crate::impl_area_method!(methods); crate::impl_style_method!(methods, style); - methods.add_function("direction", |_, (ud, symbol): (AnyUserData, u8)| { + methods.add_function_mut("direction", |_, (ud, symbol): (AnyUserData, u8)| { ud.borrow_mut::()?.direction = Borders::from_bits_truncate(symbol); Ok(ud) }); - methods.add_function("symbol", |_, (ud, symbol): (AnyUserData, String)| { + methods.add_function_mut("symbol", |_, (ud, symbol): (AnyUserData, String)| { ud.borrow_mut::()?.symbol = symbol; Ok(ud) }); diff --git a/yazi-plugin/src/elements/border.rs b/yazi-plugin/src/elements/border.rs index 1414cf9b..d25c25a0 100644 --- a/yazi-plugin/src/elements/border.rs +++ b/yazi-plugin/src/elements/border.rs @@ -58,11 +58,11 @@ impl UserData for Border { crate::impl_area_method!(methods); crate::impl_style_method!(methods, style); - methods.add_function("position", |_, (ud, position): (AnyUserData, u8)| { + methods.add_function_mut("position", |_, (ud, position): (AnyUserData, u8)| { ud.borrow_mut::()?.position = ratatui::widgets::Borders::from_bits_truncate(position); Ok(ud) }); - methods.add_function("type", |_, (ud, value): (AnyUserData, u8)| { + methods.add_function_mut("type", |_, (ud, value): (AnyUserData, u8)| { ud.borrow_mut::()?.type_ = match value { ROUNDED => ratatui::widgets::BorderType::Rounded, DOUBLE => ratatui::widgets::BorderType::Double, diff --git a/yazi-plugin/src/elements/constraint.rs b/yazi-plugin/src/elements/constraint.rs index 6bb86da5..777455b7 100644 --- a/yazi-plugin/src/elements/constraint.rs +++ b/yazi-plugin/src/elements/constraint.rs @@ -40,4 +40,8 @@ impl Constraint { } } +impl From for ratatui::layout::Constraint { + fn from(value: Constraint) -> Self { value.0 } +} + impl UserData for Constraint {} diff --git a/yazi-plugin/src/elements/elements.rs b/yazi-plugin/src/elements/elements.rs index 1fe406e7..aa8c6be7 100644 --- a/yazi-plugin/src/elements/elements.rs +++ b/yazi-plugin/src/elements/elements.rs @@ -15,13 +15,14 @@ pub fn pour(lua: &Lua) -> mlua::Result<()> { super::Layout::install(lua, &ui)?; super::Line::install(lua, &ui)?; super::List::install(lua, &ui)?; - super::ListItem::install(lua, &ui)?; super::Padding::install(lua, &ui)?; - super::Paragraph::install(lua, &ui)?; super::Position::install(lua, &ui)?; super::Rect::install(lua, &ui)?; super::Span::install(lua, &ui)?; super::Style::install(lua, &ui)?; + super::Table::install(lua, &ui)?; + super::TableRow::install(lua, &ui)?; + super::Text::install(lua, &ui)?; lua.globals().raw_set("ui", ui) } diff --git a/yazi-plugin/src/elements/gauge.rs b/yazi-plugin/src/elements/gauge.rs index 4443e2b6..9561db68 100644 --- a/yazi-plugin/src/elements/gauge.rs +++ b/yazi-plugin/src/elements/gauge.rs @@ -28,7 +28,7 @@ impl UserData for Gauge { crate::impl_area_method!(methods); crate::impl_style_method!(methods, style); - methods.add_function("percent", |_, (ud, percent): (AnyUserData, u8)| { + methods.add_function_mut("percent", |_, (ud, percent): (AnyUserData, u8)| { if percent > 100 { return Err("percent must be between 0 and 100".into_lua_err()); } @@ -37,7 +37,7 @@ impl UserData for Gauge { Ok(ud) }); - methods.add_function("ratio", |_, (ud, ratio): (AnyUserData, f64)| { + methods.add_function_mut("ratio", |_, (ud, ratio): (AnyUserData, f64)| { if !(0.0..1.0).contains(&ratio) { return Err("ratio must be between 0 and 1".into_lua_err()); } @@ -46,12 +46,12 @@ impl UserData for Gauge { Ok(ud) }); - methods.add_function("label", |_, (ud, label): (AnyUserData, Span)| { + methods.add_function_mut("label", |_, (ud, label): (AnyUserData, Span)| { ud.borrow_mut::()?.label = Some(label.0); Ok(ud) }); - methods.add_function("gauge_style", |_, (ud, value): (AnyUserData, Value)| { + methods.add_function_mut("gauge_style", |_, (ud, value): (AnyUserData, Value)| { ud.borrow_mut::()?.gauge_style = Style::try_from(value)?.0; Ok(ud) }); diff --git a/yazi-plugin/src/elements/layout.rs b/yazi-plugin/src/elements/layout.rs index f3957885..b56bd12c 100644 --- a/yazi-plugin/src/elements/layout.rs +++ b/yazi-plugin/src/elements/layout.rs @@ -26,15 +26,15 @@ impl Layout { impl UserData for Layout { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { - methods.add_function("direction", |_, (ud, value): (AnyUserData, bool)| { + methods.add_function_mut("direction", |_, (ud, value): (AnyUserData, bool)| { ud.borrow_mut::()?.direction = value; Ok(ud) }); - methods.add_function("margin", |_, (ud, value): (AnyUserData, u16)| { + methods.add_function_mut("margin", |_, (ud, value): (AnyUserData, u16)| { ud.borrow_mut::()?.margin = Some(ratatui::layout::Margin::new(value, value)); Ok(ud) }); - methods.add_function("margin_h", |_, (ud, value): (AnyUserData, u16)| { + methods.add_function_mut("margin_h", |_, (ud, value): (AnyUserData, u16)| { { let mut me = ud.borrow_mut::()?; if let Some(margin) = &mut me.margin { @@ -45,7 +45,7 @@ impl UserData for Layout { } Ok(ud) }); - methods.add_function("margin_v", |_, (ud, value): (AnyUserData, u16)| { + methods.add_function_mut("margin_v", |_, (ud, value): (AnyUserData, u16)| { { let mut me = ud.borrow_mut::()?; if let Some(margin) = &mut me.margin { @@ -56,8 +56,8 @@ impl UserData for Layout { } Ok(ud) }); - methods.add_function("constraints", |_, (ud, value): (AnyUserData, Vec)| { - ud.borrow_mut::()?.constraints = value.into_iter().map(|c| c.0).collect(); + methods.add_function_mut("constraints", |_, (ud, value): (AnyUserData, Vec)| { + ud.borrow_mut::()?.constraints = value.into_iter().map(Into::into).collect(); Ok(ud) }); methods.add_method("split", |lua, me, value: Rect| { diff --git a/yazi-plugin/src/elements/line.rs b/yazi-plugin/src/elements/line.rs index 356305d3..d025e005 100644 --- a/yazi-plugin/src/elements/line.rs +++ b/yazi-plugin/src/elements/line.rs @@ -87,7 +87,7 @@ impl UserData for Line { crate::impl_style_shorthands!(methods, 0.style); methods.add_method("width", |_, me, ()| Ok(me.0.width())); - methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| { + methods.add_function_mut("align", |_, (ud, align): (AnyUserData, u8)| { ud.borrow_mut::()?.0.alignment = Some(match align { CENTER => ratatui::layout::Alignment::Center, RIGHT => ratatui::layout::Alignment::Right, diff --git a/yazi-plugin/src/elements/list.rs b/yazi-plugin/src/elements/list.rs index 20f91932..3e7c31b5 100644 --- a/yazi-plugin/src/elements/list.rs +++ b/yazi-plugin/src/elements/list.rs @@ -1,10 +1,10 @@ -use mlua::{ExternalError, FromLua, Lua, Table, UserData, Value}; +use mlua::{Lua, Table, UserData, Value}; use ratatui::widgets::Widget; -use super::{Line, Rect, Renderable, Span}; +use super::{Rect, Renderable, Text}; // --- List -#[derive(Clone)] +#[derive(Clone, Default)] pub struct List { area: Rect, @@ -15,8 +15,13 @@ impl List { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { ui.raw_set( "List", - lua.create_function(|_, (area, items): (Rect, Vec)| { - Ok(Self { area, inner: ratatui::widgets::List::new(items) }) + lua.create_function(|_, values: Vec| { + let mut items = Vec::with_capacity(values.len()); + for value in values { + items.push(ratatui::widgets::ListItem::new(Text::try_from(value)?)); + } + + Ok(Self { inner: ratatui::widgets::List::new(items), ..Default::default() }) })?, ) } @@ -37,44 +42,3 @@ impl Renderable for List { fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) } } - -// --- ListItem -#[derive(Clone, Default, FromLua)] -pub struct ListItem { - content: ratatui::text::Text<'static>, - style: ratatui::style::Style, -} - -impl ListItem { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { - ui.raw_set( - "ListItem", - lua.create_function(|_, value: Value| match value { - Value::String(s) => { - Ok(Self { content: s.to_string_lossy().into_owned().into(), ..Default::default() }) - } - Value::UserData(ud) => { - let content: ratatui::text::Text = if let Ok(line) = ud.take::() { - line.0.into() - } else if let Ok(span) = ud.take::() { - span.0.into() - } else { - return Err("expected a String, Line or Span".into_lua_err()); - }; - Ok(Self { content, ..Default::default() }) - } - _ => Err("expected a String, Line or Span".into_lua_err()), - })?, - ) - } -} - -impl From for ratatui::widgets::ListItem<'static> { - fn from(value: ListItem) -> Self { Self::new(value.content).style(value.style) } -} - -impl UserData for ListItem { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { - crate::impl_style_method!(methods, style); - } -} diff --git a/yazi-plugin/src/elements/mod.rs b/yazi-plugin/src/elements/mod.rs index b9c3d144..57ccc3bc 100644 --- a/yazi-plugin/src/elements/mod.rs +++ b/yazi-plugin/src/elements/mod.rs @@ -1,6 +1,3 @@ #![allow(clippy::module_inception)] -yazi_macro::mod_flat!( - bar border clear constraint elements gauge layout line list padding paragraph - position rect span style -); +yazi_macro::mod_flat!(bar border clear constraint elements gauge layout line list padding position rect span style table text); diff --git a/yazi-plugin/src/elements/paragraph.rs b/yazi-plugin/src/elements/paragraph.rs deleted file mode 100644 index 884c691c..00000000 --- a/yazi-plugin/src/elements/paragraph.rs +++ /dev/null @@ -1,96 +0,0 @@ -use ansi_to_tui::IntoText; -use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, Table, UserData}; -use ratatui::widgets::Widget; - -use super::{Line, Rect, Renderable}; - -// Alignment -const LEFT: u8 = 0; -const CENTER: u8 = 1; -const RIGHT: u8 = 2; - -// Wrap -pub const WRAP_NO: u8 = 0; -pub const WRAP: u8 = 1; -pub const WRAP_TRIM: u8 = 2; - -#[derive(Clone, Default)] -pub struct Paragraph { - pub area: Rect, - - pub text: ratatui::text::Text<'static>, - pub style: ratatui::style::Style, - pub alignment: ratatui::layout::Alignment, - pub wrap: u8, -} - -impl Paragraph { - pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { - let new = lua.create_function(|_, (_, area, lines): (Table, Rect, Vec)| { - Ok(Paragraph { area, text: lines.into_iter().map(|s| s.0).collect(), ..Default::default() }) - })?; - - let parse = lua.create_function(|_, (area, code): (Rect, mlua::String)| { - Ok(Paragraph { area, text: code.into_text().into_lua_err()?, ..Default::default() }) - })?; - - let paragraph = lua.create_table_from([ - ("parse", parse.into_lua(lua)?), - // Alignment - ("LEFT", LEFT.into_lua(lua)?), - ("CENTER", CENTER.into_lua(lua)?), - ("RIGHT", RIGHT.into_lua(lua)?), - // Wrap - ("WRAP_NO", WRAP_NO.into_lua(lua)?), - ("WRAP", WRAP.into_lua(lua)?), - ("WRAP_TRIM", WRAP_TRIM.into_lua(lua)?), - ])?; - - paragraph.set_metatable(Some(lua.create_table_from([("__call", new)])?)); - - ui.raw_set("Paragraph", paragraph) - } -} - -impl UserData for Paragraph { - fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { - crate::impl_area_method!(methods); - crate::impl_style_method!(methods, style); - crate::impl_style_shorthands!(methods, style); - - methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| { - ud.borrow_mut::()?.alignment = match align { - CENTER => ratatui::layout::Alignment::Center, - RIGHT => ratatui::layout::Alignment::Right, - _ => ratatui::layout::Alignment::Left, - }; - Ok(ud) - }); - methods.add_function("wrap", |_, (ud, wrap): (AnyUserData, u8)| { - ud.borrow_mut::()?.wrap = match wrap { - w @ (WRAP | WRAP_TRIM | WRAP_NO) => w, - _ => return Err("expected a WRAP or WRAP_TRIM or WRAP_OFF".into_lua_err()), - }; - Ok(ud) - }); - methods.add_method("max_width", |_, me, ()| { - Ok(me.text.lines.iter().take(me.area.height as usize).map(|l| l.width()).max()) - }); - } -} - -impl Renderable for Paragraph { - fn area(&self) -> ratatui::layout::Rect { *self.area } - - fn render(self: Box, buf: &mut ratatui::buffer::Buffer) { - let mut p = ratatui::widgets::Paragraph::new(self.text).style(self.style); - - if self.wrap != WRAP_NO { - p = p.wrap(ratatui::widgets::Wrap { trim: self.wrap == WRAP_TRIM }); - } - - p.alignment(self.alignment).render(*self.area, buf); - } - - fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) } -} diff --git a/yazi-plugin/src/elements/style.rs b/yazi-plugin/src/elements/style.rs index 58b29efd..caef4089 100644 --- a/yazi-plugin/src/elements/style.rs +++ b/yazi-plugin/src/elements/style.rs @@ -55,7 +55,7 @@ impl UserData for Style { fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) { crate::impl_style_shorthands!(methods, 0); - methods.add_function("patch", |_, (ud, value): (AnyUserData, Value)| { + methods.add_function_mut("patch", |_, (ud, value): (AnyUserData, Value)| { { let mut me = ud.borrow_mut::()?; me.0 = me.0.patch(Self::try_from(value)?.0); diff --git a/yazi-plugin/src/elements/table.rs b/yazi-plugin/src/elements/table.rs new file mode 100644 index 00000000..ab3016ed --- /dev/null +++ b/yazi-plugin/src/elements/table.rs @@ -0,0 +1,133 @@ +use mlua::{AnyUserData, FromLua, Lua, UserData}; +use ratatui::widgets::StatefulWidget; + +use super::{Rect, Renderable, Text}; +use crate::elements::Constraint; + +// --- Table +#[derive(Clone, Default)] +pub struct Table { + area: Rect, + + rows: Vec>, + header: Option>, + footer: Option>, + widths: Vec, + column_spacing: u16, + block: Option>, + style: ratatui::style::Style, + highlight_style: ratatui::style::Style, + highlight_symbol: ratatui::text::Text<'static>, + highlight_spacing: ratatui::widgets::HighlightSpacing, + flex: ratatui::layout::Flex, + + state: ratatui::widgets::TableState, +} + +impl Table { + pub fn install(lua: &Lua, ui: &mlua::Table) -> mlua::Result<()> { + ui.raw_set( + "Table", + lua.create_function(|_, (area, rows): (Rect, Vec)| { + Ok(Self { area, rows: rows.into_iter().map(Into::into).collect(), ..Default::default() }) + })?, + ) + } +} + +impl UserData for Table { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + crate::impl_area_method!(methods); + + methods.add_function_mut("header", |_, (ud, row): (AnyUserData, TableRow)| { + ud.borrow_mut::()?.header = Some(row.into()); + Ok(ud) + }); + methods.add_function_mut("footer", |_, (ud, row): (AnyUserData, TableRow)| { + ud.borrow_mut::()?.footer = Some(row.into()); + Ok(ud) + }); + methods.add_function_mut("widths", |_, (ud, widths): (AnyUserData, Vec)| { + ud.borrow_mut::()?.widths = widths.into_iter().map(Into::into).collect(); + Ok(ud) + }); + } +} + +impl Renderable for Table { + fn area(&self) -> ratatui::layout::Rect { *self.area } + + fn render(mut self: Box, buf: &mut ratatui::buffer::Buffer) { + let mut table = ratatui::widgets::Table::new(self.rows, self.widths) + .column_spacing(self.column_spacing) + .style(self.style) + .highlight_style(self.highlight_style) + .highlight_symbol(self.highlight_symbol) + .highlight_spacing(self.highlight_spacing) + .flex(self.flex); + + if let Some(header) = self.header { + table = table.header(header); + } + if let Some(footer) = self.footer { + table = table.footer(footer); + } + if let Some(block) = self.block { + table = table.block(block); + } + + table.render(*self.area, buf, &mut self.state); + } + + fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) } +} + +// --- TableRow +#[derive(Clone, Default, FromLua)] +pub struct TableRow { + cells: Vec>, + height: u16, + top_margin: u16, + bottom_margin: u16, + style: ratatui::style::Style, +} + +impl TableRow { + pub fn install(lua: &Lua, ui: &mlua::Table) -> mlua::Result<()> { + ui.raw_set( + "TableRow", + lua.create_function(|_, cols: Vec| { + Ok(Self { cells: cols.into_iter().map(Into::into).collect(), ..Default::default() }) + })?, + ) + } +} + +impl From for ratatui::widgets::Row<'static> { + fn from(value: TableRow) -> Self { + Self::new(value.cells) + .height(value.height) + .top_margin(value.top_margin) + .bottom_margin(value.bottom_margin) + .style(value.style) + } +} + +impl UserData for TableRow { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + crate::impl_style_method!(methods, style); + + methods.add_function_mut("height", |_, (ud, value): (AnyUserData, u16)| { + ud.borrow_mut::()?.height = value; + Ok(ud) + }); + methods.add_function_mut("margin_t", |_, (ud, value): (AnyUserData, u16)| { + ud.borrow_mut::()?.top_margin = value; + Ok(ud) + }); + methods.add_function_mut("margin_b", |_, (ud, value): (AnyUserData, u16)| { + ud.borrow_mut::()?.bottom_margin = value; + Ok(ud) + }); + } +} diff --git a/yazi-plugin/src/elements/text.rs b/yazi-plugin/src/elements/text.rs new file mode 100644 index 00000000..25a7d777 --- /dev/null +++ b/yazi-plugin/src/elements/text.rs @@ -0,0 +1,137 @@ +use ansi_to_tui::IntoText; +use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, Table, UserData, Value}; +use ratatui::widgets::Widget; + +use super::{Line, Rect, Renderable, Span}; + +// Alignment +pub(super) const LEFT: u8 = 0; +pub(super) const CENTER: u8 = 1; +pub(super) const RIGHT: u8 = 2; + +// Wrap +pub const WRAP_NO: u8 = 0; +pub const WRAP: u8 = 1; +pub const WRAP_TRIM: u8 = 2; + +#[derive(Clone, Default, FromLua)] +pub struct Text { + pub area: Rect, + + // TODO: block + pub inner: ratatui::text::Text<'static>, + pub wrap: u8, + // TODO: scroll +} + +impl Text { + pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { + let new = lua.create_function(|_, (_, value): (Table, Value)| Text::try_from(value))?; + + let parse = lua.create_function(|_, code: mlua::String| { + Ok(Text { inner: code.into_text().into_lua_err()?, ..Default::default() }) + })?; + + let text = lua.create_table_from([ + ("parse", parse.into_lua(lua)?), + // Alignment + ("LEFT", LEFT.into_lua(lua)?), + ("CENTER", CENTER.into_lua(lua)?), + ("RIGHT", RIGHT.into_lua(lua)?), + // Wrap + ("WRAP_NO", WRAP_NO.into_lua(lua)?), + ("WRAP", WRAP.into_lua(lua)?), + ("WRAP_TRIM", WRAP_TRIM.into_lua(lua)?), + ])?; + + text.set_metatable(Some(lua.create_table_from([("__call", new)])?)); + + ui.raw_set("Text", text) + } +} + +impl TryFrom> for Text { + type Error = mlua::Error; + + fn try_from(value: Value) -> mlua::Result { + match value { + Value::String(s) => { + Ok(Self { inner: s.to_string_lossy().into_owned().into(), ..Default::default() }) + } + Value::UserData(ud) => { + let inner: ratatui::text::Text = if let Ok(line) = ud.take::() { + line.0.into() + } else if let Ok(span) = ud.take::() { + span.0.into() + } else { + return Err("expected a String, Line or Span".into_lua_err()); + }; + Ok(Self { inner, ..Default::default() }) + } + Value::Table(tb) => { + let mut lines = Vec::with_capacity(tb.raw_len()); + for v in tb.sequence_values::() { + lines.extend(Self::try_from(v?)?.inner.lines); + } + Ok(Self { inner: lines.into(), ..Default::default() }) + } + _ => Err("expected a String, Line, Span or a Table of them".into_lua_err()), + } + } +} + +impl From for ratatui::text::Text<'static> { + fn from(value: Text) -> Self { value.inner } +} + +impl From for ratatui::widgets::Paragraph<'static> { + fn from(value: Text) -> Self { + let align = value.inner.alignment.unwrap_or(ratatui::layout::Alignment::Left); + let mut p = ratatui::widgets::Paragraph::new(value.inner); + + if value.wrap != WRAP_NO { + p = p.wrap(ratatui::widgets::Wrap { trim: value.wrap == WRAP_TRIM }); + } + + p.alignment(align) + } +} + +impl UserData for Text { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + crate::impl_area_method!(methods); + crate::impl_style_method!(methods, inner.style); + crate::impl_style_shorthands!(methods, inner.style); + + methods.add_function_mut("align", |_, (ud, align): (AnyUserData, u8)| { + ud.borrow_mut::()?.inner.alignment = Some(match align { + CENTER => ratatui::layout::Alignment::Center, + RIGHT => ratatui::layout::Alignment::Right, + _ => ratatui::layout::Alignment::Left, + }); + Ok(ud) + }); + methods.add_function_mut("wrap", |_, (ud, wrap): (AnyUserData, u8)| { + ud.borrow_mut::()?.wrap = match wrap { + w @ (WRAP | WRAP_TRIM | WRAP_NO) => w, + _ => return Err("expected a WRAP or WRAP_TRIM or WRAP_OFF".into_lua_err()), + }; + Ok(ud) + }); + methods.add_method("max_width", |_, me, ()| { + Ok(me.inner.lines.iter().take(me.area.height as usize).map(|l| l.width()).max()) + }); + } +} + +impl Renderable for Text { + fn area(&self) -> ratatui::layout::Rect { *self.area } + + fn render(self: Box, buf: &mut ratatui::buffer::Buffer) { + let area = *self.area; + let p: ratatui::widgets::Paragraph = (*self).into(); + p.render(area, buf); + } + + fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) } +} diff --git a/yazi-plugin/src/isolate/isolate.rs b/yazi-plugin/src/isolate/isolate.rs index f509d802..7152de27 100644 --- a/yazi-plugin/src/isolate/isolate.rs +++ b/yazi-plugin/src/isolate/isolate.rs @@ -23,9 +23,9 @@ pub fn slim_lua(name: &str) -> mlua::Result { // Elements let ui = lua.create_table()?; elements::Line::install(&lua, &ui)?; - elements::Paragraph::install(&lua, &ui)?; elements::Rect::install(&lua, &ui)?; elements::Span::install(&lua, &ui)?; + elements::Text::install(&lua, &ui)?; lua.globals().raw_set("ui", ui)?; Ok(lua) diff --git a/yazi-plugin/src/macros.rs b/yazi-plugin/src/macros.rs index 790121cf..6da7d302 100644 --- a/yazi-plugin/src/macros.rs +++ b/yazi-plugin/src/macros.rs @@ -1,7 +1,7 @@ #[macro_export] macro_rules! impl_style_method { ($methods:ident, $($field:tt).+) => { - $methods.add_function("style", |_, (ud, value): (mlua::AnyUserData, mlua::Value)| { + $methods.add_function_mut("style", |_, (ud, value): (mlua::AnyUserData, mlua::Value)| { ud.borrow_mut::()?.$($field).+ = $crate::elements::Style::try_from(value)?.0; Ok(ud) }); @@ -14,7 +14,7 @@ macro_rules! impl_area_method { use mlua::IntoLua; use $crate::elements::Rect; - $methods.add_function("area", |lua, (ud, area): (mlua::AnyUserData, Option)| { + $methods.add_function_mut("area", |lua, (ud, area): (mlua::AnyUserData, Option)| { if let Some(r) = area { ud.borrow_mut::()?.area = r; ud.into_lua(lua) @@ -28,51 +28,51 @@ macro_rules! impl_area_method { #[macro_export] macro_rules! impl_style_shorthands { ($methods:ident, $($field:tt).+) => { - $methods.add_function("fg", |_, (ud, color): (mlua::AnyUserData, String)| { + $methods.add_function_mut("fg", |_, (ud, color): (mlua::AnyUserData, String)| { ud.borrow_mut::()?.$($field).+.fg = yazi_shared::theme::Color::try_from(color).ok().map(Into::into); Ok(ud) }); - $methods.add_function("bg", |_, (ud, color): (mlua::AnyUserData, String)| { + $methods.add_function_mut("bg", |_, (ud, color): (mlua::AnyUserData, String)| { ud.borrow_mut::()?.$($field).+.bg = yazi_shared::theme::Color::try_from(color).ok().map(Into::into); Ok(ud) }); - $methods.add_function("bold", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("bold", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::BOLD; Ok(ud) }); - $methods.add_function("dim", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("dim", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::DIM; Ok(ud) }); - $methods.add_function("italic", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("italic", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::ITALIC; Ok(ud) }); - $methods.add_function("underline", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("underline", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::UNDERLINED; Ok(ud) }); - $methods.add_function("blink", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("blink", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::SLOW_BLINK; Ok(ud) }); - $methods.add_function("blink_rapid", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("blink_rapid", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::RAPID_BLINK; Ok(ud) }); - $methods.add_function("reverse", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("reverse", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::REVERSED; Ok(ud) }); - $methods.add_function("hidden", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("hidden", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::HIDDEN; Ok(ud) }); - $methods.add_function("crossed", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("crossed", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier |= ratatui::style::Modifier::CROSSED_OUT; Ok(ud) }); - $methods.add_function("reset", |_, ud: mlua::AnyUserData| { + $methods.add_function_mut("reset", |_, ud: mlua::AnyUserData| { ud.borrow_mut::()?.$($field).+.add_modifier = ratatui::style::Modifier::empty(); Ok(ud) }); diff --git a/yazi-plugin/src/process/command.rs b/yazi-plugin/src/process/command.rs index e502c0ba..f3ff918e 100644 --- a/yazi-plugin/src/process/command.rs +++ b/yazi-plugin/src/process/command.rs @@ -65,11 +65,11 @@ impl UserData for Command { ) } - methods.add_function("arg", |_, (ud, arg): (AnyUserData, mlua::String)| { + methods.add_function_mut("arg", |_, (ud, arg): (AnyUserData, mlua::String)| { ud.borrow_mut::()?.inner.arg(arg.to_string_lossy().as_ref()); Ok(ud) }); - methods.add_function("args", |_, (ud, args): (AnyUserData, Vec)| { + methods.add_function_mut("args", |_, (ud, args): (AnyUserData, Vec)| { { let mut me = ud.borrow_mut::()?; for arg in args { @@ -78,11 +78,11 @@ impl UserData for Command { } Ok(ud) }); - methods.add_function("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| { + methods.add_function_mut("cwd", |_, (ud, dir): (AnyUserData, mlua::String)| { ud.borrow_mut::()?.inner.current_dir(dir.to_str()?); Ok(ud) }); - methods.add_function( + methods.add_function_mut( "env", |_, (ud, key, value): (AnyUserData, mlua::String, mlua::String)| { ud.borrow_mut::()? @@ -91,15 +91,15 @@ impl UserData for Command { Ok(ud) }, ); - methods.add_function("stdin", |_, (ud, stdio): (AnyUserData, Value)| { + methods.add_function_mut("stdin", |_, (ud, stdio): (AnyUserData, Value)| { ud.borrow_mut::()?.inner.stdin(make_stdio(stdio)?); Ok(ud) }); - methods.add_function("stdout", |_, (ud, stdio): (AnyUserData, Value)| { + methods.add_function_mut("stdout", |_, (ud, stdio): (AnyUserData, Value)| { ud.borrow_mut::()?.inner.stdout(make_stdio(stdio)?); Ok(ud) }); - methods.add_function("stderr", |_, (ud, stdio): (AnyUserData, Value)| { + methods.add_function_mut("stderr", |_, (ud, stdio): (AnyUserData, Value)| { ud.borrow_mut::()?.inner.stderr(make_stdio(stdio)?); Ok(ud) }); diff --git a/yazi-plugin/src/utils/preview.rs b/yazi-plugin/src/utils/preview.rs index 752439ee..1c407894 100644 --- a/yazi-plugin/src/utils/preview.rs +++ b/yazi-plugin/src/utils/preview.rs @@ -4,7 +4,7 @@ use yazi_macro::emit; use yazi_shared::{Layer, errors::PeekError, event::Cmd}; use super::Utils; -use crate::{bindings::Window, cast_to_renderable, elements::{Paragraph, Rect, Renderable, WRAP, WRAP_NO}, external::Highlighter, file::FileRef}; +use crate::{bindings::Window, cast_to_renderable, elements::{Rect, Renderable, Text, WRAP, WRAP_NO}, external::Highlighter, file::FileRef}; pub struct PreviewLock { pub url: yazi_shared::fs::Url, @@ -41,7 +41,7 @@ impl Utils { let area: Rect = t.raw_get("area")?; let mut lock = PreviewLock::try_from(t)?; - let text = match Highlighter::new(&lock.url).highlight(lock.skip, *area).await { + let inner = match Highlighter::new(&lock.url).highlight(lock.skip, *area).await { Ok(text) => text, Err(e @ PeekError::Exceed(max)) => return (e.to_string(), max).into_lua_multi(lua), Err(e @ PeekError::Unexpected(_)) => { @@ -49,14 +49,14 @@ impl Utils { } }; - lock.data = vec![Box::new(Paragraph { + lock.data = vec![Box::new(Text { area, - text, + inner, wrap: if PREVIEW.wrap == PreviewWrap::Yes { WRAP } else { WRAP_NO }, ..Default::default() })]; - emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager)); + emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); (Value::Nil, Value::Nil).into_lua_multi(lua) })?, )?; @@ -67,7 +67,7 @@ impl Utils { let mut lock = PreviewLock::try_from(t)?; lock.data = widgets.into_iter().filter_map(|ud| cast_to_renderable(&ud)).collect(); - emit!(Call(Cmd::new("preview").with_any("lock", lock), Layer::Manager)); + emit!(Call(Cmd::new("update_peeked").with_any("lock", lock), Layer::Manager)); Ok(()) })?, )?; diff --git a/yazi-proxy/src/manager.rs b/yazi-proxy/src/manager.rs index 4ea14fe4..e3a1b171 100644 --- a/yazi-proxy/src/manager.rs +++ b/yazi-proxy/src/manager.rs @@ -38,8 +38,8 @@ impl ManagerProxy { } #[inline] - pub fn update_task(url: &Url) { - emit!(Call(Cmd::new("update_task").with_any("url", url.clone()), Layer::Manager)); + pub fn update_tasks(url: &Url) { + emit!(Call(Cmd::new("update_tasks").with_any("urls", vec![url.clone()]), Layer::Manager)); } #[inline] diff --git a/yazi-scheduler/src/scheduler.rs b/yazi-scheduler/src/scheduler.rs index be8ac3da..027a3e17 100644 --- a/yazi-scheduler/src/scheduler.rs +++ b/yazi-scheduler/src/scheduler.rs @@ -163,7 +163,7 @@ impl Scheduler { async move { if !canceled { fs::remove_dir_all(&target).await.ok(); - ManagerProxy::update_task(&target); + ManagerProxy::update_tasks(&target); Pump::push_delete(target); } ongoing.lock().try_remove(id, TaskStage::Hooked); @@ -191,7 +191,7 @@ impl Scheduler { Box::new(move |canceled: bool| { async move { if !canceled { - ManagerProxy::update_task(&target); + ManagerProxy::update_tasks(&target); Pump::push_trash(target); } ongoing.lock().try_remove(id, TaskStage::Hooked);