mirror of
https://github.com/sxyazi/yazi.git
synced 2024-12-24 01:02:37 +03:00
feat!: add ui.Text
, ui.Table
, remove ui.Paragraph
and ui.ListItem
(#1776)
This commit is contained in:
parent
35c3c9e0f7
commit
43b5ae0e6c
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
);
|
||||
|
@ -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());
|
||||
|
@ -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<FilesOp>, 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(_));
|
||||
|
@ -19,9 +19,9 @@ impl TryFrom<Cmd> for Opt {
|
||||
}
|
||||
|
||||
impl Manager {
|
||||
pub fn update_mimetype(&mut self, opt: impl TryInto<Opt>, tasks: &Tasks) {
|
||||
pub fn update_mimes(&mut self, opt: impl TryInto<Opt>, 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();
|
@ -3,23 +3,23 @@ use yazi_shared::{event::Cmd, fs::Url};
|
||||
use crate::manager::Manager;
|
||||
|
||||
pub struct Opt {
|
||||
url: Url,
|
||||
urls: Vec<Url>,
|
||||
}
|
||||
|
||||
impl TryFrom<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
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<Opt>) {
|
||||
pub fn update_tasks(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Ok(opt) = opt.try_into() else {
|
||||
return;
|
||||
};
|
||||
|
||||
self.watcher.push_file(opt.url);
|
||||
self.watcher.push_files(opt.urls);
|
||||
}
|
||||
}
|
@ -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() }
|
||||
|
@ -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<Url>) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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
|
||||
);
|
||||
|
20
yazi-core/src/tab/commands/spot.rs
Normal file
20
yazi-core/src/tab/commands/spot.rs
Normal file
@ -0,0 +1,20 @@
|
||||
use yazi_shared::event::{Cmd, Data};
|
||||
|
||||
use crate::tab::Tab;
|
||||
|
||||
struct Opt {
|
||||
skip: Option<usize>,
|
||||
}
|
||||
|
||||
impl From<Cmd> 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();
|
||||
};
|
||||
}
|
||||
}
|
@ -17,8 +17,8 @@ impl TryFrom<Cmd> for Opt {
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn preview(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Some(hovered) = self.current.hovered().map(|h| &h.url) else {
|
||||
pub fn update_peeked(&mut self, opt: impl TryInto<Opt>) {
|
||||
let Some(hovered) = self.hovered().map(|h| &h.url) else {
|
||||
return self.preview.reset();
|
||||
};
|
||||
|
22
yazi-core/src/tab/commands/update_spotted.rs
Normal file
22
yazi-core/src/tab/commands/update_spotted.rs
Normal file
@ -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<Cmd> for Opt {
|
||||
type Error = ();
|
||||
|
||||
fn try_from(mut c: Cmd) -> Result<Self, Self::Error> {
|
||||
Ok(Self { lock: c.take_any("lock").ok_or(())? })
|
||||
}
|
||||
}
|
||||
|
||||
impl Tab {
|
||||
pub fn update_spotted(&mut self, opt: impl TryInto<Opt>) {
|
||||
todo!();
|
||||
}
|
||||
}
|
@ -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<Rect> {
|
||||
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<dyn Iterator<Item = &Url> + '_> {
|
||||
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<dyn Iterator<Item = &Url> + '_> {
|
||||
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) {
|
||||
|
@ -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);
|
||||
|
@ -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::<CtxRef>("cx")?;
|
||||
|
@ -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
|
||||
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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
|
||||
|
@ -3,7 +3,7 @@ use mlua::AnyUserData;
|
||||
use crate::elements::Renderable;
|
||||
|
||||
pub fn cast_to_renderable(ud: &AnyUserData) -> Option<Box<dyn Renderable + Send>> {
|
||||
if let Ok(c) = ud.take::<crate::elements::Paragraph>() {
|
||||
if let Ok(c) = ud.take::<crate::elements::Text>() {
|
||||
Some(Box::new(c))
|
||||
} else if let Ok(c) = ud.take::<crate::elements::List>() {
|
||||
Some(Box::new(c))
|
||||
@ -15,6 +15,8 @@ pub fn cast_to_renderable(ud: &AnyUserData) -> Option<Box<dyn Renderable + Send>
|
||||
Some(Box::new(c))
|
||||
} else if let Ok(c) = ud.take::<crate::elements::Gauge>() {
|
||||
Some(Box::new(c))
|
||||
} else if let Ok(c) = ud.take::<crate::elements::Table>() {
|
||||
Some(Box::new(c))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
|
@ -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::<Self>()?.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::<Self>()?.symbol = symbol;
|
||||
Ok(ud)
|
||||
});
|
||||
|
@ -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::<Self>()?.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::<Self>()?.type_ = match value {
|
||||
ROUNDED => ratatui::widgets::BorderType::Rounded,
|
||||
DOUBLE => ratatui::widgets::BorderType::Double,
|
||||
|
@ -40,4 +40,8 @@ impl Constraint {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Constraint> for ratatui::layout::Constraint {
|
||||
fn from(value: Constraint) -> Self { value.0 }
|
||||
}
|
||||
|
||||
impl UserData for Constraint {}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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::<Self>()?.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::<Self>()?.gauge_style = Style::try_from(value)?.0;
|
||||
Ok(ud)
|
||||
});
|
||||
|
@ -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::<Self>()?.direction = value;
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function("margin", |_, (ud, value): (AnyUserData, u16)| {
|
||||
methods.add_function_mut("margin", |_, (ud, value): (AnyUserData, u16)| {
|
||||
ud.borrow_mut::<Self>()?.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::<Self>()?;
|
||||
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::<Self>()?;
|
||||
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<Constraint>)| {
|
||||
ud.borrow_mut::<Self>()?.constraints = value.into_iter().map(|c| c.0).collect();
|
||||
methods.add_function_mut("constraints", |_, (ud, value): (AnyUserData, Vec<Constraint>)| {
|
||||
ud.borrow_mut::<Self>()?.constraints = value.into_iter().map(Into::into).collect();
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_method("split", |lua, me, value: Rect| {
|
||||
|
@ -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::<Self>()?.0.alignment = Some(match align {
|
||||
CENTER => ratatui::layout::Alignment::Center,
|
||||
RIGHT => ratatui::layout::Alignment::Right,
|
||||
|
@ -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<ListItem>)| {
|
||||
Ok(Self { area, inner: ratatui::widgets::List::new(items) })
|
||||
lua.create_function(|_, values: Vec<Value>| {
|
||||
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>() {
|
||||
line.0.into()
|
||||
} else if let Ok(span) = ud.take::<Span>() {
|
||||
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<ListItem> 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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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<Line>)| {
|
||||
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::<Self>()?.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::<Self>()?.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<Self>, 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) }
|
||||
}
|
@ -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::<Self>()?;
|
||||
me.0 = me.0.patch(Self::try_from(value)?.0);
|
||||
|
133
yazi-plugin/src/elements/table.rs
Normal file
133
yazi-plugin/src/elements/table.rs
Normal file
@ -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<ratatui::widgets::Row<'static>>,
|
||||
header: Option<ratatui::widgets::Row<'static>>,
|
||||
footer: Option<ratatui::widgets::Row<'static>>,
|
||||
widths: Vec<ratatui::layout::Constraint>,
|
||||
column_spacing: u16,
|
||||
block: Option<ratatui::widgets::Block<'static>>,
|
||||
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<TableRow>)| {
|
||||
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::<Self>()?.header = Some(row.into());
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function_mut("footer", |_, (ud, row): (AnyUserData, TableRow)| {
|
||||
ud.borrow_mut::<Self>()?.footer = Some(row.into());
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function_mut("widths", |_, (ud, widths): (AnyUserData, Vec<Constraint>)| {
|
||||
ud.borrow_mut::<Self>()?.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<Self>, 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<ratatui::widgets::Cell<'static>>,
|
||||
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<Text>| {
|
||||
Ok(Self { cells: cols.into_iter().map(Into::into).collect(), ..Default::default() })
|
||||
})?,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<TableRow> 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::<Self>()?.height = value;
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function_mut("margin_t", |_, (ud, value): (AnyUserData, u16)| {
|
||||
ud.borrow_mut::<Self>()?.top_margin = value;
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function_mut("margin_b", |_, (ud, value): (AnyUserData, u16)| {
|
||||
ud.borrow_mut::<Self>()?.bottom_margin = value;
|
||||
Ok(ud)
|
||||
});
|
||||
}
|
||||
}
|
137
yazi-plugin/src/elements/text.rs
Normal file
137
yazi-plugin/src/elements/text.rs
Normal file
@ -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<Value<'_>> for Text {
|
||||
type Error = mlua::Error;
|
||||
|
||||
fn try_from(value: Value) -> mlua::Result<Self> {
|
||||
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>() {
|
||||
line.0.into()
|
||||
} else if let Ok(span) = ud.take::<Span>() {
|
||||
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::<Value>() {
|
||||
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<Text> for ratatui::text::Text<'static> {
|
||||
fn from(value: Text) -> Self { value.inner }
|
||||
}
|
||||
|
||||
impl From<Text> 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::<Self>()?.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::<Self>()?.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<Self>, 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) }
|
||||
}
|
@ -23,9 +23,9 @@ pub fn slim_lua(name: &str) -> mlua::Result<Lua> {
|
||||
// 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)
|
||||
|
@ -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::<Self>()?.$($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<Rect>)| {
|
||||
$methods.add_function_mut("area", |lua, (ud, area): (mlua::AnyUserData, Option<Rect>)| {
|
||||
if let Some(r) = area {
|
||||
ud.borrow_mut::<Self>()?.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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($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::<Self>()?.$($field).+.add_modifier = ratatui::style::Modifier::empty();
|
||||
Ok(ud)
|
||||
});
|
||||
|
@ -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::<Self>()?.inner.arg(arg.to_string_lossy().as_ref());
|
||||
Ok(ud)
|
||||
});
|
||||
methods.add_function("args", |_, (ud, args): (AnyUserData, Vec<mlua::String>)| {
|
||||
methods.add_function_mut("args", |_, (ud, args): (AnyUserData, Vec<mlua::String>)| {
|
||||
{
|
||||
let mut me = ud.borrow_mut::<Self>()?;
|
||||
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::<Self>()?.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::<Self>()?
|
||||
@ -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::<Self>()?.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::<Self>()?.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::<Self>()?.inner.stderr(make_stdio(stdio)?);
|
||||
Ok(ud)
|
||||
});
|
||||
|
@ -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(())
|
||||
})?,
|
||||
)?;
|
||||
|
@ -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]
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user