feat: make custom tab bar easier (#2782)

This commit is contained in:
三咲雅 misaki masa 2025-05-21 13:56:52 +08:00 committed by GitHub
parent 546920e049
commit c6fcb4f799
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
13 changed files with 66 additions and 53 deletions

View file

@ -13,5 +13,5 @@ Resolves #
<!-- <!--
A clear and concise description of the rationale of the changes, to help our reviewers understand your intent and why it is necessary. A clear and concise description of the rationale of the changes, to help our reviewers understand your intent and why it is necessary.
If it already been detailed in the associated issue, please skip this section. If it has already been detailed in the associated issue, please skip this section.
--> -->

View file

@ -38,7 +38,7 @@ end
function Modal:children_redraw() function Modal:children_redraw()
local elements = {} local elements = {}
for _, child in ipairs(self._children) do for _, child in ipairs(self._children) do
elements = ya.list_merge(elements, ya.redraw_with(child[1]:new(self._area))) elements = ya.list_merge(elements, ui.redraw(child[1]:new(self._area)))
end end
return elements return elements
end end

View file

@ -26,9 +26,7 @@ function Parent:redraw()
} }
end end
return { return ui.List(items):area(self._area)
ui.List(items):area(self._area),
}
end end
-- Mouse events -- Mouse events

View file

@ -40,9 +40,7 @@ function Progress:redraw()
end end
local left = progress.total - progress.succ local left = progress.total - progress.succ
return { return gauge
gauge :percent(percent)
:percent(percent) :label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(th.status.progress_label))
:label(ui.Span(string.format("%3d%%, %d left", percent, left)):style(th.status.progress_label)),
}
end end

View file

@ -24,7 +24,7 @@ function Rail:reflow() return {} end
function Rail:redraw() function Rail:redraw()
local elements = self._base or {} local elements = self._base or {}
for _, child in ipairs(self._children) do for _, child in ipairs(self._children) do
elements = ya.list_merge(elements, ya.redraw_with(child)) elements = ya.list_merge(elements, ui.redraw(child))
end end
return elements return elements
end end

View file

@ -15,7 +15,7 @@ function Root:layout()
:direction(ui.Layout.VERTICAL) :direction(ui.Layout.VERTICAL)
:constraints({ :constraints({
ui.Constraint.Length(1), ui.Constraint.Length(1),
ui.Constraint.Length(#cx.tabs > 1 and 1 or 0), ui.Constraint.Length(Tabs.height()),
ui.Constraint.Fill(1), ui.Constraint.Fill(1),
ui.Constraint.Length(1), ui.Constraint.Length(1),
}) })
@ -43,7 +43,7 @@ end
function Root:redraw() function Root:redraw()
local elements = self._base or {} local elements = self._base or {}
for _, child in ipairs(self._children) do for _, child in ipairs(self._children) do
elements = ya.list_merge(elements, ya.redraw_with(child)) elements = ya.list_merge(elements, ui.redraw(child))
end end
return elements return elements
end end

View file

@ -142,7 +142,7 @@ function Status:redraw()
ui.Text(""):area(self._area):style(th.status.overall), ui.Text(""):area(self._area):style(th.status.overall),
ui.Line(left):area(self._area), ui.Line(left):area(self._area),
ui.Line(right):area(self._area):align(ui.Line.RIGHT), ui.Line(right):area(self._area):align(ui.Line.RIGHT),
table.unpack(ya.redraw_with(Progress:new(self._area, right_width))), table.unpack(ui.redraw(Progress:new(self._area, right_width))),
} }
end end

View file

@ -41,7 +41,7 @@ end
function Tab:redraw() function Tab:redraw()
local elements = self._base or {} local elements = self._base or {}
for _, child in ipairs(self._children) do for _, child in ipairs(self._children) do
elements = ya.list_merge(elements, ya.redraw_with(child)) elements = ya.list_merge(elements, ui.redraw(child))
end end
return elements return elements
end end

View file

@ -38,9 +38,11 @@ function Tabs:redraw()
end end
lines[#lines + 1] = ui.Line(th.tabs.sep_outer.close):fg(th.tabs.inactive.bg) lines[#lines + 1] = ui.Line(th.tabs.sep_outer.close):fg(th.tabs.inactive.bg)
return { ui.Line(lines):area(self._area) } return ui.Line(lines):area(self._area)
end end
function Tabs.height() return #cx.tabs > 1 and 1 or 0 end
function Tabs:inner_width() function Tabs:inner_width()
local si, so = th.tabs.sep_inner, th.tabs.sep_outer local si, so = th.tabs.sep_inner, th.tabs.sep_outer
return math.max(0, self._area.w - ui.Line({ si.open, si.close, so.open, so.close }):width()) return math.max(0, self._area.w - ui.Line({ si.open, si.close, so.open, so.close }):width())

View file

@ -1,4 +1,4 @@
use mlua::{AnyUserData, IntoLua, Lua, Table, Value}; use mlua::{AnyUserData, IntoLua, Lua, Value};
use tracing::error; use tracing::error;
use super::Renderable; use super::Renderable;
@ -25,25 +25,35 @@ pub fn compose(lua: &Lua) -> mlua::Result<Value> {
b"Text" => super::Text::compose(lua)?, b"Text" => super::Text::compose(lua)?,
b"width" => super::Utils::width(lua)?, b"width" => super::Utils::width(lua)?,
b"redraw" => super::Utils::redraw(lua)?,
_ => return Ok(Value::Nil), _ => return Ok(Value::Nil),
} }
.into_lua(lua) .into_lua(lua)
}) })
} }
pub fn render_once<F>(widgets: Table, buf: &mut ratatui::buffer::Buffer, trans: F) pub fn render_once<F>(value: Value, buf: &mut ratatui::buffer::Buffer, trans: F)
where where
F: Fn(yazi_config::popup::Position) -> ratatui::layout::Rect + Copy, F: Fn(yazi_config::popup::Position) -> ratatui::layout::Rect + Copy,
{ {
for widget in widgets.sequence_values::<AnyUserData>() { match value {
let Ok(widget) = widget else { Value::Table(tbl) => {
error!("Failed to convert to renderable UserData: {}", widget.unwrap_err()); for widget in tbl.sequence_values::<AnyUserData>() {
continue; let Ok(widget) = widget else {
}; error!("Failed to convert to renderable UserData: {}", widget.unwrap_err());
continue;
};
match Renderable::try_from(widget) { match Renderable::try_from(widget) {
Ok(w) => w.render(buf, trans),
Err(e) => error!("{e}"),
}
}
}
Value::UserData(ud) => match Renderable::try_from(ud) {
Ok(w) => w.render(buf, trans), Ok(w) => w.render(buf, trans),
Err(e) => error!("{e}"), Err(e) => error!("{e}"),
} },
_ => error!("Expected a renderable UserData, or a table of them, got: {value:?}"),
} }
} }

View file

@ -1,5 +1,7 @@
use mlua::{ExternalError, IntoLua, Lua, Value}; use mlua::{ExternalError, IntoLua, Lua, ObjectLike, Table, Value};
use tracing::error;
use unicode_width::UnicodeWidthStr; use unicode_width::UnicodeWidthStr;
use yazi_config::LAYOUT;
use super::{Line, Span}; use super::{Line, Span};
@ -32,4 +34,33 @@ impl Utils {
f.into_lua(lua) f.into_lua(lua)
} }
pub(super) fn redraw(lua: &Lua) -> mlua::Result<Value> {
let f = lua.create_function(|lua, c: Table| {
let id: mlua::String = c.get("_id")?;
let mut layout = LAYOUT.get();
match id.as_bytes().as_ref() {
b"current" => layout.current = *c.raw_get::<crate::elements::Rect>("_area")?,
b"preview" => layout.preview = *c.raw_get::<crate::elements::Rect>("_area")?,
b"progress" => layout.progress = *c.raw_get::<crate::elements::Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Value>("redraw", ())? {
Value::Table(tbl) => Ok(tbl),
Value::UserData(ud) => lua.create_sequence_from([ud]),
_ => {
error!(
"Failed to `redraw()` the `{}` component: expected a table or UserData",
id.display(),
);
lua.create_table()
}
}
})?;
f.into_lua(lua)
}
} }

View file

@ -1,6 +1,4 @@
use mlua::{Function, Lua, ObjectLike, Table}; use mlua::{Function, Lua, Table};
use tracing::error;
use yazi_config::LAYOUT;
use yazi_dds::Sendable; use yazi_dds::Sendable;
use yazi_macro::{emit, render}; use yazi_macro::{emit, render};
use yazi_shared::{Layer, event::Cmd}; use yazi_shared::{Layer, event::Cmd};
@ -15,29 +13,6 @@ impl Utils {
}) })
} }
pub(super) fn redraw_with(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|lua, c: Table| {
let id: mlua::String = c.get("_id")?;
let mut layout = LAYOUT.get();
match id.as_bytes().as_ref() {
b"current" => layout.current = *c.raw_get::<crate::elements::Rect>("_area")?,
b"preview" => layout.preview = *c.raw_get::<crate::elements::Rect>("_area")?,
b"progress" => layout.progress = *c.raw_get::<crate::elements::Rect>("_area")?,
_ => {}
}
LAYOUT.set(layout);
match c.call_method::<Table>("redraw", ()) {
Err(e) => {
error!("Failed to `redraw()` the `{}` component:\n{e}", id.display());
lua.create_table()
}
ok => ok,
}
})
}
pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> { pub(super) fn emit(lua: &Lua) -> mlua::Result<Function> {
lua.create_function(|_, (name, args): (mlua::String, Table)| { lua.create_function(|_, (name, args): (mlua::String, Table)| {
let mut cmd = Cmd::new_or(&name.to_str()?, Layer::Mgr)?; let mut cmd = Cmd::new_or(&name.to_str()?, Layer::Mgr)?;

View file

@ -16,7 +16,6 @@ pub fn compose(lua: &Lua, isolate: bool) -> mlua::Result<Value> {
// Call // Call
b"render" => Utils::render(lua)?, b"render" => Utils::render(lua)?,
b"redraw_with" => Utils::redraw_with(lua)?,
b"emit" => Utils::emit(lua)?, b"emit" => Utils::emit(lua)?,
b"app_emit" => Utils::app_emit(lua)?, b"app_emit" => Utils::app_emit(lua)?,
b"mgr_emit" => Utils::mgr_emit(lua)?, b"mgr_emit" => Utils::mgr_emit(lua)?,