diff --git a/CHANGELOG.md b/CHANGELOG.md index ccecb23f..08843e64 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -55,6 +55,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): ### Improved - Reduce memory allocations by using Lua 5.5 external strings ([#3634]) +- Reuse previewed and spotted widgets when possible ([#3765]) ## [v26.1.22] @@ -1682,3 +1683,4 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/): [#3744]: https://github.com/sxyazi/yazi/pull/3744 [#3748]: https://github.com/sxyazi/yazi/pull/3748 [#3757]: https://github.com/sxyazi/yazi/pull/3757 +[#3765]: https://github.com/sxyazi/yazi/pull/3765 diff --git a/yazi-binding/src/elements/bar.rs b/yazi-binding/src/elements/bar.rs index b079ba74..5d24a774 100644 --- a/yazi-binding/src/elements/bar.rs +++ b/yazi-binding/src/elements/bar.rs @@ -1,5 +1,5 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, Table, UserData, Value}; -use ratatui::widgets::Borders; +use ratatui::widgets::{Borders, Widget}; use super::{Area, Edge}; @@ -21,8 +21,22 @@ impl Bar { bar.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; bar.into_lua(lua) } +} - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { +impl Widget for Bar { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &Bar { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { if rect.area() == 0 { return; } diff --git a/yazi-binding/src/elements/border.rs b/yazi-binding/src/elements/border.rs index 43dad5ed..d4b1f940 100644 --- a/yazi-binding/src/elements/border.rs +++ b/yazi-binding/src/elements/border.rs @@ -42,8 +42,13 @@ impl Border { border.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; border.into_lua(lua) } +} - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { +impl Widget for Border { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { let mut block = ratatui::widgets::Block::default() .borders(self.edge.0) .border_type(self.r#type) @@ -51,8 +56,8 @@ impl Border { for title in self.titles { block = match title { - (ratatui::widgets::TitlePosition::Top, line) => block.title(line), - (ratatui::widgets::TitlePosition::Bottom, line) => block.title(line), + (ratatui::widgets::TitlePosition::Top, line) => block.title_top(line), + (ratatui::widgets::TitlePosition::Bottom, line) => block.title_bottom(line), }; } @@ -60,6 +65,15 @@ impl Border { } } +impl Widget for &Border { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + self.clone().render(rect, buf); + } +} + impl UserData for Border { fn add_methods>(methods: &mut M) { crate::impl_area_method!(methods); diff --git a/yazi-binding/src/elements/clear.rs b/yazi-binding/src/elements/clear.rs index d1a25aba..3fb2c8e0 100644 --- a/yazi-binding/src/elements/clear.rs +++ b/yazi-binding/src/elements/clear.rs @@ -17,8 +17,22 @@ impl Clear { clear.into_lua(lua) } +} - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { +impl Widget for Clear { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &Clear { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { yazi_widgets::Clear.render(rect, buf); } } diff --git a/yazi-binding/src/elements/gauge.rs b/yazi-binding/src/elements/gauge.rs index 3b4ec40d..c3543a5c 100644 --- a/yazi-binding/src/elements/gauge.rs +++ b/yazi-binding/src/elements/gauge.rs @@ -23,8 +23,13 @@ impl Gauge { gauge.into_lua(lua) } +} - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { +impl Widget for Gauge { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { let mut gauge = ratatui::widgets::Gauge::default() .ratio(self.ratio) .style(self.style) @@ -38,6 +43,15 @@ impl Gauge { } } +impl Widget for &Gauge { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + self.clone().render(rect, buf); + } +} + impl UserData for Gauge { fn add_methods>(methods: &mut M) { crate::impl_area_method!(methods); diff --git a/yazi-binding/src/elements/line.rs b/yazi-binding/src/elements/line.rs index 2700f0e5..bd675daf 100644 --- a/yazi-binding/src/elements/line.rs +++ b/yazi-binding/src/elements/line.rs @@ -49,10 +49,6 @@ impl Line { line.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; line.into_lua(lua) } - - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - self.inner.render(rect, buf); - } } impl TryFrom for Line { @@ -84,6 +80,24 @@ impl From for ratatui::text::Line<'static> { fn from(value: Line) -> Self { value.inner } } +impl Widget for Line { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &Line { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self.inner).render(rect, buf); + } +} + impl FromLua for Line { fn from_lua(value: Value, _: &Lua) -> mlua::Result { Ok(Self { diff --git a/yazi-binding/src/elements/list.rs b/yazi-binding/src/elements/list.rs index d713df94..2af2d0d2 100644 --- a/yazi-binding/src/elements/list.rs +++ b/yazi-binding/src/elements/list.rs @@ -22,9 +22,23 @@ impl List { list.into_lua(lua) } +} - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - self.inner.render(rect, buf); +impl Widget for List { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self).render(rect, buf); + } +} + +impl Widget for &List { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + (&self.inner).render(rect, buf); } } diff --git a/yazi-binding/src/elements/renderable.rs b/yazi-binding/src/elements/renderable.rs index 2a33cdb1..7730edf5 100644 --- a/yazi-binding/src/elements/renderable.rs +++ b/yazi-binding/src/elements/renderable.rs @@ -1,6 +1,7 @@ use std::any::TypeId; use mlua::{AnyUserData, ExternalError, FromLua, Lua, Value}; +use ratatui::widgets::Widget; use super::{Bar, Border, Clear, Gauge, Line, List, Table, Text}; use crate::{Error, elements::Area}; @@ -46,19 +47,6 @@ impl Renderable { self } - pub fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - match self { - Self::Line(line) => line.render(rect, buf), - Self::Text(text) => text.render(rect, buf), - Self::List(list) => list.render(rect, buf), - Self::Bar(bar) => bar.render(rect, buf), - Self::Clear(clear) => clear.render(rect, buf), - Self::Border(border) => border.render(rect, buf), - Self::Gauge(gauge) => gauge.render(rect, buf), - Self::Table(table) => table.render(rect, buf), - } - } - pub fn render_with(self, buf: &mut ratatui::buffer::Buffer, trans: T) where T: FnOnce(yazi_config::popup::Position) -> ratatui::layout::Rect, @@ -96,6 +84,42 @@ impl From for Renderable { } } +impl Widget for Renderable { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + match self { + Self::Line(line) => line.render(rect, buf), + Self::Text(text) => text.render(rect, buf), + Self::List(list) => list.render(rect, buf), + Self::Bar(bar) => bar.render(rect, buf), + Self::Clear(clear) => clear.render(rect, buf), + Self::Border(border) => border.render(rect, buf), + Self::Gauge(gauge) => gauge.render(rect, buf), + Self::Table(table) => table.render(rect, buf), + } + } +} + +impl Widget for &Renderable { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + match self { + Renderable::Line(line) => line.render(rect, buf), + Renderable::Text(text) => text.render(rect, buf), + Renderable::List(list) => (&**list).render(rect, buf), + Renderable::Bar(bar) => bar.render(rect, buf), + Renderable::Clear(clear) => clear.render(rect, buf), + Renderable::Border(border) => border.render(rect, buf), + Renderable::Gauge(gauge) => (&**gauge).render(rect, buf), + Renderable::Table(table) => (&**table).render(rect, buf), + } + } +} + impl FromLua for Renderable { fn from_lua(value: Value, _: &Lua) -> mlua::Result { match value { diff --git a/yazi-binding/src/elements/table.rs b/yazi-binding/src/elements/table.rs index 6bedaa95..37a8b0e4 100644 --- a/yazi-binding/src/elements/table.rs +++ b/yazi-binding/src/elements/table.rs @@ -1,5 +1,5 @@ use mlua::{AnyUserData, IntoLua, Lua, MetaMethod, UserData, Value}; -use ratatui::widgets::StatefulWidget; +use ratatui::widgets::{StatefulWidget, Widget}; use super::{Area, Row}; use crate::{Style, elements::Constraint}; @@ -47,7 +47,30 @@ impl Table { if row.cells.is_empty() { None } else { Some(&row.cells[col.min(row.cells.len() - 1)].text) } } - pub(super) fn render(mut self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { + pub fn len(&self) -> usize { self.rows.len() } + + pub fn select(&mut self, idx: Option) { + self + .state + .select(idx.map(|i| if self.rows.is_empty() { 0 } else { i.min(self.rows.len() - 1) })); + } + + pub fn selected(&self) -> Option { + if self.rows.is_empty() { None } else { Some(self.state.selected()?.min(self.rows.len() - 1)) } + } +} + +impl TryFrom for Table { + type Error = mlua::Error; + + fn try_from(value: AnyUserData) -> Result { value.take() } +} + +impl Widget for Table { + fn render(mut self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { let mut table = ratatui::widgets::Table::new(self.rows, self.widths) .column_spacing(self.column_spacing) .style(self.style) @@ -68,26 +91,17 @@ impl Table { table = table.block(block); } - table.render(rect, buf, &mut self.state); - } - - pub fn len(&self) -> usize { self.rows.len() } - - pub fn select(&mut self, idx: Option) { - self - .state - .select(idx.map(|i| if self.rows.is_empty() { 0 } else { i.min(self.rows.len() - 1) })); - } - - pub fn selected(&self) -> Option { - if self.rows.is_empty() { None } else { Some(self.state.selected()?.min(self.rows.len() - 1)) } + StatefulWidget::render(table, rect, buf, &mut self.state); } } -impl TryFrom for Table { - type Error = mlua::Error; - - fn try_from(value: AnyUserData) -> Result { value.take() } +impl Widget for &Table { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + self.clone().render(rect, buf); + } } impl UserData for Table { diff --git a/yazi-binding/src/elements/text.rs b/yazi-binding/src/elements/text.rs index a1ed7f16..646d3291 100644 --- a/yazi-binding/src/elements/text.rs +++ b/yazi-binding/src/elements/text.rs @@ -31,14 +31,6 @@ impl Text { text.set_metatable(Some(lua.create_table_from([(MetaMethod::Call.name(), new)])?))?; text.into_lua(lua) } - - pub(super) fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) { - if self.wrap.is_none() && self.scroll == Default::default() { - self.inner.render(rect, buf); - } else { - ratatui::widgets::Paragraph::from(self).render(rect, buf); - } - } } impl TryFrom
for Text { @@ -82,6 +74,32 @@ impl From for ratatui::widgets::Paragraph<'static> { } } +impl Widget for Text { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + if self.wrap.is_none() && self.scroll == Default::default() { + self.inner.render(rect, buf); + } else { + ratatui::widgets::Paragraph::from(self).render(rect, buf); + } + } +} + +impl Widget for &Text { + fn render(self, rect: ratatui::layout::Rect, buf: &mut ratatui::buffer::Buffer) + where + Self: Sized, + { + if self.wrap.is_none() && self.scroll == Default::default() { + (&self.inner).render(rect, buf); + } else { + ratatui::widgets::Paragraph::from(self.clone()).render(rect, buf); + } + } +} + impl FromLua for Text { fn from_lua(value: Value, _: &Lua) -> mlua::Result { let inner = match value { diff --git a/yazi-fm/src/mgr/preview.rs b/yazi-fm/src/mgr/preview.rs index 1d2b51d7..7227edc3 100644 --- a/yazi-fm/src/mgr/preview.rs +++ b/yazi-fm/src/mgr/preview.rs @@ -24,7 +24,7 @@ impl Widget for Preview<'_> { for w in &lock.data { let rect = w.area().transform(|p| self.core.mgr.area(p)); if rect.intersection(win) == rect { - w.clone().render(rect, buf); + w.render(rect, buf); } } } diff --git a/yazi-fm/src/spot/spot.rs b/yazi-fm/src/spot/spot.rs index 355a9ca1..7dd5cc6f 100644 --- a/yazi-fm/src/spot/spot.rs +++ b/yazi-fm/src/spot/spot.rs @@ -18,7 +18,7 @@ impl Widget for Spot<'_> { for w in &lock.data { let rect = w.area().transform(|p| self.core.mgr.area(p)); if rect.intersection(win) == rect { - w.clone().render(rect, buf); + w.render(rect, buf); } } }