From ea90b0477ecc32af7fae9d7fa49fbfb9290875a9 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: Sun, 11 May 2025 12:11:29 +0800 Subject: [PATCH] feat!: redesign tabs (#2745) --- yazi-config/preset/theme-dark.toml | 18 +++++-- yazi-config/preset/theme-light.toml | 18 +++++-- yazi-config/src/theme/theme.rs | 25 ++++++--- yazi-plugin/preset/components/header.lua | 22 -------- yazi-plugin/preset/components/root.lua | 6 ++- yazi-plugin/preset/components/tabs.lua | 64 ++++++++++++++++++++++++ yazi-plugin/src/config/theme.rs | 1 + yazi-plugin/src/lua.rs | 1 + 8 files changed, 113 insertions(+), 42 deletions(-) create mode 100644 yazi-plugin/preset/components/tabs.lua diff --git a/yazi-config/preset/theme-dark.toml b/yazi-config/preset/theme-dark.toml index 997c3de1..0d28284a 100644 --- a/yazi-config/preset/theme-dark.toml +++ b/yazi-config/preset/theme-dark.toml @@ -38,11 +38,6 @@ marker_cut = { fg = "lightred", bg = "lightred" } marker_marked = { fg = "lightcyan", bg = "lightcyan" } marker_selected = { fg = "lightyellow", bg = "lightyellow" } -# Tab -tab_active = { reversed = true } -tab_inactive = {} -tab_width = 1 - # Count count_copied = { fg = "white", bg = "green" } count_cut = { fg = "white", bg = "red" } @@ -58,6 +53,19 @@ syntect_theme = "" # : }}} +# : Tabs {{{ + +[tabs] +active = { bg = "blue", bold = true } +inactive = { fg = "blue", bg = "gray" } + +# Separator +sep_inner = { open = "", close = "" } +sep_outer = { open = "", close = "" } + +# : }}} + + # : Mode {{{ [mode] diff --git a/yazi-config/preset/theme-light.toml b/yazi-config/preset/theme-light.toml index 4e2bf4a2..41b82a6a 100644 --- a/yazi-config/preset/theme-light.toml +++ b/yazi-config/preset/theme-light.toml @@ -38,11 +38,6 @@ marker_cut = { fg = "lightred", bg = "lightred" } marker_marked = { fg = "lightcyan", bg = "lightcyan" } marker_selected = { fg = "lightyellow", bg = "lightyellow" } -# Tab -tab_active = { reversed = true } -tab_inactive = {} -tab_width = 1 - # Count count_copied = { fg = "white", bg = "green" } count_cut = { fg = "white", bg = "red" } @@ -58,6 +53,19 @@ syntect_theme = "" # : }}} +# : Tabs {{{ + +[tabs] +active = { bg = "blue", bold = true } +inactive = { fg = "blue", bg = "gray" } + +# Separator +sep_inner = { open = "", close = "" } +sep_outer = { open = "", close = "" } + +# : }}} + + # : Mode {{{ [mode] diff --git a/yazi-config/src/theme/theme.rs b/yazi-config/src/theme/theme.rs index 1f66539a..5c5fa419 100644 --- a/yazi-config/src/theme/theme.rs +++ b/yazi-config/src/theme/theme.rs @@ -13,6 +13,7 @@ pub struct Theme { pub flavor: Flavor, #[serde(rename = "manager")] pub mgr: Mgr, // TODO: Remove `serde(rename)` + pub tabs: Tabs, pub mode: Mode, pub status: Status, pub which: Which, @@ -53,11 +54,6 @@ pub struct Mgr { marker_marked: Style, marker_selected: Style, - // Tab - tab_active: Style, - tab_inactive: Style, - tab_width: u8, - // Count count_copied: Style, count_cut: Style, @@ -71,6 +67,21 @@ pub struct Mgr { pub syntect_theme: PathBuf, } +#[derive(Deserialize, DeserializeOver2, Serialize)] +pub struct Tabs { + pub active: Style, + pub inactive: Style, + + pub sep_inner: TabsSep, + pub sep_outer: TabsSep, +} + +#[derive(Deserialize, DeserializeOver2, Serialize)] +pub struct TabsSep { + pub open: String, + pub close: String, +} + #[derive(Deserialize, DeserializeOver2, Serialize)] pub struct Mode { pub normal_main: Style, @@ -197,9 +208,7 @@ pub struct Help { impl Theme { pub(crate) fn reshape(mut self, light: bool) -> Result { - if self.mgr.tab_width < 1 { - bail!("[mgr].tab_width must be greater than 0"); - } else if self.which.cols < 1 || self.which.cols > 3 { + if self.which.cols < 1 || self.which.cols > 3 { bail!("[which].cols must be between 1 and 3"); } diff --git a/yazi-plugin/preset/components/header.lua b/yazi-plugin/preset/components/header.lua index 17a9acc8..ddb810c1 100644 --- a/yazi-plugin/preset/components/header.lua +++ b/yazi-plugin/preset/components/header.lua @@ -9,7 +9,6 @@ Header = { }, _right = { { "count", id = 1, order = 1000 }, - { "tabs", id = 2, order = 2000 }, }, } @@ -74,27 +73,6 @@ function Header:count() } end -function Header:tabs() - local tabs = #cx.tabs - if tabs == 1 then - return "" - end - - local spans = {} - for i = 1, tabs do - local text = i - if th.mgr.tab_width > 2 then - text = ya.truncate(text .. " " .. cx.tabs[i].name, { max = th.mgr.tab_width }) - end - if i == cx.tabs.idx then - spans[#spans + 1] = ui.Span(" " .. text .. " "):style(th.mgr.tab_active) - else - spans[#spans + 1] = ui.Span(" " .. text .. " "):style(th.mgr.tab_inactive) - end - end - return ui.Line(spans) -end - function Header:reflow() return { self } end function Header:redraw() diff --git a/yazi-plugin/preset/components/root.lua b/yazi-plugin/preset/components/root.lua index 8792e13f..5667dd01 100644 --- a/yazi-plugin/preset/components/root.lua +++ b/yazi-plugin/preset/components/root.lua @@ -15,6 +15,7 @@ function Root:layout() :direction(ui.Layout.VERTICAL) :constraints({ ui.Constraint.Length(1), + ui.Constraint.Length(#cx.tabs > 1 and 1 or 0), ui.Constraint.Fill(1), ui.Constraint.Length(1), }) @@ -24,8 +25,9 @@ end function Root:build() self._children = { Header:new(self._chunks[1], cx.active), - Tab:new(self._chunks[2], cx.active), - Status:new(self._chunks[3], cx.active), + Tabs:new(self._chunks[2]), + Tab:new(self._chunks[3], cx.active), + Status:new(self._chunks[4], cx.active), Modal:new(self._area), } end diff --git a/yazi-plugin/preset/components/tabs.lua b/yazi-plugin/preset/components/tabs.lua new file mode 100644 index 00000000..8ae7b08e --- /dev/null +++ b/yazi-plugin/preset/components/tabs.lua @@ -0,0 +1,64 @@ +Tabs = { + _id = "tabs", + _offsets = {}, +} + +function Tabs:new(area) + return setmetatable({ + _area = area, + }, { __index = self }) +end + +function Tabs:reflow() return { self } end + +function Tabs:redraw() + local len = #cx.tabs + if len < 2 then + return {} + end + + local lines = { + ui.Line(th.tabs.sep_outer.open):fg(th.tabs.inactive.bg), + } + + local pos = lines[1]:width() + local max = math.floor(self:inner_width() / len) + for i = 1, len do + local name = ya.truncate(string.format(" %d %s ", i, cx.tabs[i].name), { max = max }) + if i == cx.tabs.idx then + lines[#lines + 1] = ui.Line { + ui.Span(th.tabs.sep_inner.open):style(th.tabs.inactive), + ui.Span(name):style(th.tabs.active), + ui.Span(th.tabs.sep_inner.close):style(th.tabs.inactive), + } + else + lines[#lines + 1] = ui.Line(name):style(th.tabs.inactive) + end + self._offsets[i], pos = pos, pos + lines[#lines]:width() + end + + lines[#lines + 1] = ui.Line(th.tabs.sep_outer.close):fg(th.tabs.inactive.bg) + return { ui.Line(lines):area(self._area) } +end + +function Tabs:inner_width() + 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()) +end + +-- Mouse events +function Tabs:click(event, up) + if up or event.is_middle then + return + end + for i = #self._offsets, 1, -1 do + if event.x >= self._offsets[i] then + ya.emit("tab_switch", { i - 1 }) + break + end + end +end + +function Tabs:scroll(event, step) end + +function Tabs:touch(event, step) end diff --git a/yazi-plugin/src/config/theme.rs b/yazi-plugin/src/config/theme.rs index 06f5b91b..e144a7d7 100644 --- a/yazi-plugin/src/config/theme.rs +++ b/yazi-plugin/src/config/theme.rs @@ -11,6 +11,7 @@ impl Theme { Composer::make(lua, 15, |lua, key| { match key { b"mgr" => lua.to_value_with(&THEME.mgr, OPTS)?, + b"tabs" => lua.to_value_with(&THEME.tabs, OPTS)?, b"mode" => lua.to_value_with(&THEME.mode, OPTS)?, b"status" => lua.to_value_with(&THEME.status, OPTS)?, b"which" => lua.to_value_with(&THEME.which, OPTS)?, diff --git a/yazi-plugin/src/lua.rs b/yazi-plugin/src/lua.rs index ec66c456..22c78263 100644 --- a/yazi-plugin/src/lua.rs +++ b/yazi-plugin/src/lua.rs @@ -52,6 +52,7 @@ fn stage_1(lua: &'static Lua) -> Result<()> { lua.load(preset!("components/root")).set_name("root.lua").exec()?; lua.load(preset!("components/status")).set_name("status.lua").exec()?; lua.load(preset!("components/tab")).set_name("tab.lua").exec()?; + lua.load(preset!("components/tabs")).set_name("tabs.lua").exec()?; Ok(()) }