use mlua::{AnyUserData, ExternalError, FromLua, Lua, Table, UserData, Value}; use ratatui::widgets::Widget; use super::{Line, RectRef, Renderable, Span, Style}; // --- List #[derive(Clone)] pub struct List { area: ratatui::layout::Rect, inner: ratatui::widgets::List<'static>, } impl List { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { ui.set( "List", lua.create_function(|_, (area, items): (RectRef, Vec)| { Ok(Self { area: *area, inner: ratatui::widgets::List::new(items) }) })?, ) } } impl UserData for List {} impl Renderable for List { fn render(self: Box, buf: &mut ratatui::buffer::Buffer) { self.inner.render(self.area, buf); } fn clone_render(&self, buf: &mut ratatui::buffer::Buffer) { Box::new(self.clone()).render(buf) } } // --- ListItem #[derive(Clone, FromLua)] pub struct ListItem { content: ratatui::text::Text<'static>, style: Option, } impl ListItem { pub fn install(lua: &Lua, ui: &Table) -> mlua::Result<()> { ui.set( "ListItem", lua.create_function(|_, value: Value| { match value { Value::UserData(ud) => { let content: ratatui::text::Text = if let Ok(line) = ud.take::() { line.0.into() } else if let Ok(span) = ud.take::() { 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_str()?.to_string().into(), style: None }); } _ => {} } Err("expected a String, Line or Span".into_lua_err()) })?, ) } } impl From 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 } } 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::()?.style = match value { Value::Nil => None, Value::Table(tbl) => Some(Style::from(tbl).0), Value::UserData(ud) => Some(ud.borrow::