feat: complete and consistent support for the ui.Style() API (#1637)

This commit is contained in:
三咲雅 · Misaki Masa 2024-09-12 18:17:04 +08:00 committed by sxyazi
parent 61c0db8630
commit bc68d1eb01
No known key found for this signature in database
9 changed files with 101 additions and 182 deletions

View File

@ -1,7 +1,7 @@
use mlua::{AnyUserData, ExternalError, Lua, Table, UserData, Value};
use mlua::{AnyUserData, Lua, Table, UserData};
use ratatui::widgets::Borders;
use super::{RectRef, Renderable, Style};
use super::{RectRef, Renderable};
#[derive(Clone)]
pub struct Bar {
@ -9,7 +9,7 @@ pub struct Bar {
direction: ratatui::widgets::Borders,
symbol: String,
style: Option<ratatui::style::Style>,
style: ratatui::style::Style,
}
impl Bar {
@ -42,22 +42,12 @@ impl Bar {
impl UserData for Bar {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, style);
methods.add_function("symbol", |_, (ud, symbol): (AnyUserData, String)| {
ud.borrow_mut::<Self>()?.symbol = symbol;
Ok(ud)
});
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{
let mut me = ud.borrow_mut::<Self>()?;
match value {
Value::Nil => me.style = None,
Value::Table(tb) => me.style = Some(Style::try_from(tb)?.0),
Value::UserData(ud) => me.style = Some(ud.borrow::<Style>()?.0),
_ => return Err("expected a Style or Table or nil".into_lua_err()),
}
}
Ok(ud)
});
}
}
@ -81,36 +71,24 @@ impl Renderable for Bar {
if self.direction.contains(Borders::LEFT) {
for y in self.area.top()..self.area.bottom() {
let cell = buf[(self.area.left(), y)].set_symbol(symbol);
if let Some(style) = self.style {
cell.set_style(style);
}
buf[(self.area.left(), y)].set_style(self.style).set_symbol(symbol);
}
}
if self.direction.contains(Borders::TOP) {
for x in self.area.left()..self.area.right() {
let cell = buf[(x, self.area.top())].set_symbol(symbol);
if let Some(style) = self.style {
cell.set_style(style);
}
buf[(x, self.area.top())].set_style(self.style).set_symbol(symbol);
}
}
if self.direction.contains(Borders::RIGHT) {
let x = self.area.right() - 1;
for y in self.area.top()..self.area.bottom() {
let cell = buf[(x, y)].set_symbol(symbol);
if let Some(style) = self.style {
cell.set_style(style);
}
buf[(x, y)].set_style(self.style).set_symbol(symbol);
}
}
if self.direction.contains(Borders::BOTTOM) {
let y = self.area.bottom() - 1;
for x in self.area.left()..self.area.right() {
let cell = buf[(x, y)].set_symbol(symbol);
if let Some(style) = self.style {
cell.set_style(style);
}
buf[(x, y)].set_style(self.style).set_symbol(symbol);
}
}
}

View File

@ -1,7 +1,7 @@
use mlua::{AnyUserData, ExternalError, Lua, Table, UserData, Value};
use mlua::{AnyUserData, Lua, Table, UserData};
use ratatui::widgets::{Borders, Widget};
use super::{RectRef, Renderable, Style};
use super::{RectRef, Renderable};
// Type
const PLAIN: u8 = 0;
@ -17,7 +17,7 @@ pub struct Border {
position: ratatui::widgets::Borders,
type_: ratatui::widgets::BorderType,
style: Option<ratatui::style::Style>,
style: ratatui::style::Style,
}
impl Border {
@ -55,6 +55,8 @@ impl Border {
impl UserData for Border {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, style);
methods.add_function("type", |_, (ud, value): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.type_ = match value {
ROUNDED => ratatui::widgets::BorderType::Rounded,
@ -66,18 +68,6 @@ impl UserData for Border {
};
Ok(ud)
});
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{
let mut me = ud.borrow_mut::<Self>()?;
match value {
Value::Nil => me.style = None,
Value::Table(tb) => me.style = Some(Style::try_from(tb)?.0),
Value::UserData(ud) => me.style = Some(ud.borrow::<Style>()?.0),
_ => return Err("expected a Style or Table or nil".into_lua_err()),
}
}
Ok(ud)
});
}
}
@ -85,14 +75,11 @@ impl Renderable for Border {
fn area(&self) -> ratatui::layout::Rect { self.area }
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
let mut block =
ratatui::widgets::Block::default().borders(self.position).border_type(self.type_);
if let Some(style) = self.style {
block = block.border_style(style);
}
block.render(self.area, buf);
ratatui::widgets::Block::default()
.borders(self.position)
.border_type(self.type_)
.border_style(self.style)
.render(self.area, buf);
}
fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf); }

View File

@ -1,7 +1,8 @@
use mlua::{AnyUserData, ExternalError, Lua, Table, UserData, UserDataMethods, Value};
use ratatui::widgets::Widget;
use super::{RectRef, Renderable, Span, Style};
use super::{RectRef, Renderable, Span};
use crate::elements::Style;
#[derive(Clone, Default)]
pub struct Gauge {
@ -9,8 +10,8 @@ pub struct Gauge {
ratio: f64,
label: Option<ratatui::text::Span<'static>>,
style: Option<ratatui::style::Style>,
gauge_style: Option<ratatui::style::Style>,
style: ratatui::style::Style,
gauge_style: ratatui::style::Style,
}
impl Gauge {
@ -24,6 +25,8 @@ impl Gauge {
impl UserData for Gauge {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, style);
methods.add_function("percent", |_, (ud, percent): (AnyUserData, u8)| {
if percent > 100 {
return Err("percent must be between 0 and 100".into_lua_err());
@ -47,23 +50,8 @@ impl UserData for Gauge {
Ok(ud)
});
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.style = match value {
Value::Nil => None,
Value::Table(tb) => Some(Style::try_from(tb)?.0),
Value::UserData(ud) => Some(ud.borrow::<Style>()?.0),
_ => return Err("expected a Style or Table or nil".into_lua_err()),
};
Ok(ud)
});
methods.add_function("gauge_style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.gauge_style = match value {
Value::Nil => None,
Value::Table(tb) => Some(Style::try_from(tb)?.0),
Value::UserData(ud) => Some(ud.borrow::<Style>()?.0),
_ => return Err("expected a Style or Table or nil".into_lua_err()),
};
ud.borrow_mut::<Self>()?.gauge_style = Style::try_from(value)?.0;
Ok(ud)
});
}
@ -73,20 +61,16 @@ impl Renderable for Gauge {
fn area(&self) -> ratatui::layout::Rect { self.area }
fn render(self: Box<Self>, buf: &mut ratatui::buffer::Buffer) {
let mut gauge = ratatui::widgets::Gauge::default();
let mut gauge = ratatui::widgets::Gauge::default()
.ratio(self.ratio)
.style(self.style)
.gauge_style(self.gauge_style);
gauge = gauge.ratio(self.ratio);
if let Some(label) = self.label {
gauge = gauge.label(label);
}
if let Some(style) = self.style {
gauge = gauge.style(style);
}
if let Some(gauge_style) = self.gauge_style {
gauge = gauge.gauge_style(gauge_style);
if let Some(s) = self.label {
gauge = gauge.label(s)
}
gauge.render(self.area, buf)
gauge.render(self.area, buf);
}
fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) }

View File

@ -4,7 +4,7 @@ use ansi_to_tui::IntoText;
use mlua::{AnyUserData, ExternalError, ExternalResult, FromLua, IntoLua, Lua, Table, UserData, UserDataMethods, Value};
use unicode_width::UnicodeWidthChar;
use super::{Span, Style};
use super::Span;
const LEFT: u8 = 0;
const CENTER: u8 = 1;
@ -83,21 +83,10 @@ impl Line {
impl UserData for Line {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, 0.style);
crate::impl_style_shorthands!(methods, 0.style);
methods.add_method("width", |_, me, ()| Ok(me.0.width()));
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{
let mut me = ud.borrow_mut::<Self>()?;
me.0.style = match value {
Value::Nil => ratatui::style::Style::default(),
Value::Table(tb) => Style::try_from(tb)?.0,
Value::UserData(ud) => ud.borrow::<Style>()?.0,
_ => return Err("expected a Style or Table or nil".into_lua_err()),
};
}
Ok(ud)
});
methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.0.alignment = Some(match align {
CENTER => ratatui::layout::Alignment::Center,

View File

@ -1,7 +1,7 @@
use mlua::{AnyUserData, ExternalError, FromLua, Lua, Table, UserData, Value};
use mlua::{ExternalError, FromLua, Lua, Table, UserData, Value};
use ratatui::widgets::Widget;
use super::{Line, RectRef, Renderable, Span, Style};
use super::{Line, RectRef, Renderable, Span};
// --- List
#[derive(Clone)]
@ -35,59 +35,42 @@ impl Renderable for List {
}
// --- ListItem
#[derive(Clone, FromLua)]
#[derive(Clone, Default, FromLua)]
pub struct ListItem {
content: ratatui::text::Text<'static>,
style: Option<ratatui::style::Style>,
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::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());
};
return Ok(Self { content, style: None });
}
Value::String(s) => {
return Ok(Self { content: s.to_string_lossy().into_owned().into(), style: None });
}
_ => {}
lua.create_function(|_, value: Value| match value {
Value::String(s) => {
Ok(Self { content: s.to_string_lossy().into_owned().into(), ..Default::default() })
}
Err("expected a String, Line or Span".into_lua_err())
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 {
let mut item = Self::new(value.content);
if let Some(style) = value.style {
item = item.style(style)
}
item
}
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) {
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.style = match value {
Value::Nil => None,
Value::Table(tb) => Some(Style::try_from(tb)?.0),
Value::UserData(ud) => Some(ud.borrow::<Style>()?.0),
_ => return Err("expected a Style or Table or nil".into_lua_err()),
};
Ok(ud)
});
crate::impl_style_method!(methods, style);
}
}

View File

@ -1,8 +1,8 @@
use ansi_to_tui::IntoText;
use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, Table, UserData, Value};
use mlua::{AnyUserData, ExternalError, ExternalResult, IntoLua, Lua, Table, UserData};
use ratatui::widgets::Widget;
use super::{Line, RectRef, Renderable, Style};
use super::{Line, RectRef, Renderable};
// Alignment
const LEFT: u8 = 0;
@ -58,20 +58,9 @@ impl Paragraph {
impl UserData for Paragraph {
fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, style);
crate::impl_style_shorthands!(methods, style);
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
{
let mut me = ud.borrow_mut::<Self>()?;
match value {
Value::Nil => me.style = ratatui::style::Style::default(),
Value::Table(tb) => me.style = Style::try_from(tb)?.0,
Value::UserData(ud) => me.style = ud.borrow::<Style>()?.0,
_ => return Err("expected a Style or Table or nil".into_lua_err()),
}
}
Ok(ud)
});
methods.add_function("align", |_, (ud, align): (AnyUserData, u8)| {
ud.borrow_mut::<Self>()?.alignment = match align {
CENTER => ratatui::layout::Alignment::Center,

View File

@ -1,8 +1,6 @@
use mlua::{AnyUserData, ExternalError, FromLua, Lua, Table, UserData, UserDataMethods, Value};
use mlua::{FromLua, Lua, Table, UserData, UserDataMethods};
use unicode_width::UnicodeWidthChar;
use super::Style;
#[derive(Clone, FromLua)]
pub struct Span(pub(super) ratatui::text::Span<'static>);
@ -19,17 +17,9 @@ impl Span {
impl UserData for Span {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_method!(methods, 0.style);
crate::impl_style_shorthands!(methods, 0.style);
methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| {
ud.borrow_mut::<Self>()?.0.style = match value {
Value::Nil => ratatui::style::Style::default(),
Value::Table(tb) => Style::try_from(tb)?.0,
Value::UserData(ud) => ud.borrow::<Style>()?.0,
_ => return Err("expected a Style or Table or nil".into_lua_err()),
};
Ok(ud)
});
methods.add_method("visible", |_, me, ()| {
Ok(me.0.content.chars().any(|c| c.width().unwrap_or(0) > 0))
});

View File

@ -8,7 +8,7 @@ pub struct Style(pub(super) ratatui::style::Style);
impl Style {
pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> {
let new = lua.create_function(|_, ()| Ok(Self::default()))?;
let new = lua.create_function(|_, (_, value): (Table, Value)| Self::try_from(value))?;
let style = lua.create_table()?;
style.set_metatable(Some(lua.create_table_from([("__call", new)])?));
@ -17,10 +17,6 @@ impl Style {
}
}
impl From<yazi_shared::theme::Style> for Style {
fn from(value: yazi_shared::theme::Style) -> Self { Self(value.into()) }
}
impl<'a> TryFrom<Table<'a>> for Style {
type Error = mlua::Error;
@ -38,6 +34,23 @@ impl<'a> TryFrom<Table<'a>> for Style {
}
}
impl<'a> TryFrom<Value<'a>> for Style {
type Error = mlua::Error;
fn try_from(value: Value<'a>) -> Result<Self, Self::Error> {
Ok(Self(match value {
Value::Nil => Default::default(),
Value::Table(tb) => Self::try_from(tb)?.0,
Value::UserData(ud) => ud.borrow::<Self>()?.0,
_ => Err("expected a Style or Table or nil".into_lua_err())?,
}))
}
}
impl From<yazi_shared::theme::Style> for Style {
fn from(value: yazi_shared::theme::Style) -> Self { Self(value.into()) }
}
impl UserData for Style {
fn add_methods<'lua, M: UserDataMethods<'lua, Self>>(methods: &mut M) {
crate::impl_style_shorthands!(methods, 0);
@ -45,11 +58,7 @@ impl UserData for Style {
methods.add_function("patch", |_, (ud, value): (AnyUserData, Value)| {
{
let mut me = ud.borrow_mut::<Self>()?;
me.0 = me.0.patch(match value {
Value::Table(tb) => Style::try_from(tb)?.0,
Value::UserData(ud) => ud.borrow::<Style>()?.0,
_ => return Err("expected a Style or Table".into_lua_err()),
});
me.0 = me.0.patch(Self::try_from(value)?.0);
}
Ok(ud)
})

View File

@ -1,51 +1,61 @@
#[macro_export]
macro_rules! impl_style_method {
($methods:ident, $($field:tt).+) => {
$methods.add_function("style", |_, (ud, value): (mlua::AnyUserData, mlua::Value)| {
ud.borrow_mut::<Self>()?.$($field).+ = $crate::elements::Style::try_from(value)?.0;
Ok(ud)
});
};
}
#[macro_export]
macro_rules! impl_style_shorthands {
($methods:ident, $($field:tt).+) => {
$methods.add_function("fg", |_, (ud, color): (AnyUserData, String)| {
$methods.add_function("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): (AnyUserData, String)| {
$methods.add_function("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: AnyUserData| {
$methods.add_function("bold", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::BOLD;
Ok(ud)
});
$methods.add_function("dim", |_, ud: AnyUserData| {
$methods.add_function("dim", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::DIM;
Ok(ud)
});
$methods.add_function("italic", |_, ud: AnyUserData| {
$methods.add_function("italic", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::ITALIC;
Ok(ud)
});
$methods.add_function("underline", |_, ud: AnyUserData| {
$methods.add_function("underline", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::UNDERLINED;
Ok(ud)
});
$methods.add_function("blink", |_, ud: AnyUserData| {
$methods.add_function("blink", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::SLOW_BLINK;
Ok(ud)
});
$methods.add_function("blink_rapid", |_, ud: AnyUserData| {
$methods.add_function("blink_rapid", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::RAPID_BLINK;
Ok(ud)
});
$methods.add_function("reverse", |_, ud: AnyUserData| {
$methods.add_function("reverse", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::REVERSED;
Ok(ud)
});
$methods.add_function("hidden", |_, ud: AnyUserData| {
$methods.add_function("hidden", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::HIDDEN;
Ok(ud)
});
$methods.add_function("crossed", |_, ud: AnyUserData| {
$methods.add_function("crossed", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier |= ratatui::style::Modifier::CROSSED_OUT;
Ok(ud)
});
$methods.add_function("reset", |_, ud: AnyUserData| {
$methods.add_function("reset", |_, ud: mlua::AnyUserData| {
ud.borrow_mut::<Self>()?.$($field).+.add_modifier = ratatui::style::Modifier::empty();
Ok(ud)
});