diff --git a/app/src/manager/layout.rs b/app/src/manager/layout.rs index 80c3bf5c..0c470410 100644 --- a/app/src/manager/layout.rs +++ b/app/src/manager/layout.rs @@ -1,6 +1,7 @@ use core::Ctx; -use config::MANAGER; +use config::{MANAGER, THEME}; +use plugin::layout::Bar; use ratatui::{buffer::Buffer, layout::{self, Constraint, Direction, Rect}, widgets::{Block, Borders, Padding, Widget}}; use super::{Folder, Preview}; @@ -28,18 +29,23 @@ impl<'a> Widget for Layout<'a> { .split(area); // Parent - let block = Block::new().borders(Borders::RIGHT).padding(Padding::new(1, 0, 0, 0)); + Bar::new(chunks[0], Borders::RIGHT) + .symbol(&THEME.manager.border_symbol) + .style(THEME.manager.border_style.into()) + .render(buf); if manager.parent().is_some() { - Folder::Parent.render(block.inner(chunks[0]), buf); + Folder::Parent.render(Block::new().padding(Padding::new(1, 1, 0, 0)).inner(chunks[0]), buf); } - block.render(chunks[0], buf); // Current Folder::Current.render(chunks[1], buf); // Preview - let block = Block::new().borders(Borders::LEFT).padding(Padding::new(0, 1, 0, 0)); - Preview::new(self.cx).render(block.inner(chunks[2]), buf); - block.render(chunks[2], buf); + Bar::new(chunks[2], Borders::LEFT) + .symbol(&THEME.manager.border_symbol) + .style(THEME.manager.border_style.into()) + .render(buf); + Preview::new(self.cx) + .render(Block::new().padding(Padding::new(1, 1, 0, 0)).inner(chunks[2]), buf); } } diff --git a/config/preset/theme.toml b/config/preset/theme.toml index 31505407..92e6c505 100644 --- a/config/preset/theme.toml +++ b/config/preset/theme.toml @@ -23,6 +23,10 @@ tab_active = { fg = "black", bg = "blue" } tab_inactive = { bg = "darkgray" } tab_width = 1 +# Border +border_symbol = "│" +border_style = { fg = "gray" } + # Highlighting syntect_theme = "" @@ -134,7 +138,7 @@ rules = [ { mime = "application/x-rar", fg = "magenta" }, # Fallback - { name = "*", blink = true }, + # { name = "*", fg = "white" }, { name = "*/", fg = "blue" } ] diff --git a/config/src/theme/theme.rs b/config/src/theme/theme.rs index 74a2a387..9ca1ee28 100644 --- a/config/src/theme/theme.rs +++ b/config/src/theme/theme.rs @@ -30,6 +30,10 @@ pub struct Manager { #[validate(range(min = 1, message = "Must be greater than 0"))] tab_width: u8, + // Border + pub border_symbol: String, + pub border_style: Style, + // Highlighting pub syntect_theme: PathBuf, } diff --git a/plugin/preset/ui.lua b/plugin/preset/ui.lua index 6f208dce..13d9c410 100644 --- a/plugin/preset/ui.lua +++ b/plugin/preset/ui.lua @@ -8,6 +8,13 @@ ui = { HORIZONTAL = false, VERTICAL = true, }, + Position = { + NONE = 0, + TOP = 1, + RIGHT = 2, + BOTTOM = 3, + LEFT = 4, + }, } function ui.highlight_ranges(s, ranges) diff --git a/plugin/src/components.rs b/plugin/src/components.rs index e7e61068..f26eae27 100644 --- a/plugin/src/components.rs +++ b/plugin/src/components.rs @@ -1,6 +1,6 @@ use mlua::{AnyUserData, Table, TableExt}; -use crate::{layout::{Gauge, List, Paragraph, Rect}, GLOBALS, LUA}; +use crate::{layout::{Bar, Gauge, List, Paragraph, Rect}, GLOBALS, LUA}; #[inline] fn layout(values: Vec, buf: &mut ratatui::prelude::Buffer) -> mlua::Result<()> { @@ -9,6 +9,8 @@ fn layout(values: Vec, buf: &mut ratatui::prelude::Buffer) -> mlua: c.render(buf) } else if let Ok(c) = value.take::() { c.render(buf) + } else if let Ok(c) = value.take::() { + c.render(buf) } else if let Ok(c) = value.take::() { c.render(buf) } diff --git a/plugin/src/layout/bar.rs b/plugin/src/layout/bar.rs new file mode 100644 index 00000000..25b62d41 --- /dev/null +++ b/plugin/src/layout/bar.rs @@ -0,0 +1,141 @@ +use mlua::{AnyUserData, FromLua, Lua, Table, UserData, Value}; + +use super::{Rect, Style}; +use crate::{GLOBALS, LUA}; + +// --- Bar +#[derive(Clone)] +pub struct Bar { + area: ratatui::layout::Rect, + + position: ratatui::widgets::Borders, + symbol: String, + style: Option, +} + +impl Bar { + pub(crate) fn install() -> mlua::Result<()> { + let ui: Table = GLOBALS.get("ui")?; + ui.set( + "Bar", + LUA.create_function(|_, (area, direction): (Rect, u8)| { + Ok(Self { + area: area.0, + + position: match direction { + 1 => ratatui::widgets::Borders::TOP, + 2 => ratatui::widgets::Borders::RIGHT, + 3 => ratatui::widgets::Borders::BOTTOM, + 4 => ratatui::widgets::Borders::LEFT, + _ => ratatui::widgets::Borders::NONE, + }, + symbol: Default::default(), + style: Default::default(), + }) + })?, + ) + } + + #[inline] + pub fn new(area: ratatui::layout::Rect, position: ratatui::widgets::Borders) -> Self { + Self { area, position, symbol: Default::default(), style: Default::default() } + } + + #[inline] + pub fn symbol(mut self, symbol: &str) -> Self { + self.symbol = symbol.to_owned(); + self + } + + #[inline] + pub fn style(mut self, style: ratatui::style::Style) -> Self { + self.style = Some(style); + self + } + + pub fn render(self, buf: &mut ratatui::buffer::Buffer) { + use ratatui::widgets::Borders; + if self.area.area() == 0 { + return; + } + + let symbol = if !self.symbol.is_empty() { + &self.symbol + } else if self.position.intersects(Borders::TOP | Borders::BOTTOM) { + "─" + } else if self.position.intersects(Borders::LEFT | Borders::RIGHT) { + "│" + } else { + " " + }; + + if self.position.contains(Borders::LEFT) { + for y in self.area.top()..self.area.bottom() { + let cell = buf.get_mut(self.area.left(), y).set_symbol(symbol); + if let Some(style) = self.style { + cell.set_style(style); + } + } + } + if self.position.contains(Borders::TOP) { + for x in self.area.left()..self.area.right() { + let cell = buf.get_mut(x, self.area.top()).set_symbol(symbol); + if let Some(style) = self.style { + cell.set_style(style); + } + } + } + if self.position.contains(Borders::RIGHT) { + let x = self.area.right() - 1; + for y in self.area.top()..self.area.bottom() { + let cell = buf.get_mut(x, y).set_symbol(symbol); + if let Some(style) = self.style { + cell.set_style(style); + } + } + } + if self.position.contains(Borders::BOTTOM) { + let y = self.area.bottom() - 1; + for x in self.area.left()..self.area.right() { + let cell = buf.get_mut(x, y).set_symbol(symbol); + if let Some(style) = self.style { + cell.set_style(style); + } + } + } + } +} + +impl<'lua> FromLua<'lua> for Bar { + fn from_lua(value: Value<'lua>, _: &'lua Lua) -> mlua::Result { + match value { + Value::UserData(ud) => Ok(ud.borrow::()?.clone()), + _ => Err(mlua::Error::FromLuaConversionError { + from: value.type_name(), + to: "Bar", + message: Some("expected a Bar".to_string()), + }), + } + } +} + +impl UserData for Bar { + fn add_methods<'lua, M: mlua::UserDataMethods<'lua, Self>>(methods: &mut M) { + methods.add_function("symbol", |_, (ud, symbol): (AnyUserData, String)| { + ud.borrow_mut::()?.symbol = symbol; + Ok(ud) + }); + methods.add_function("style", |_, (ud, value): (AnyUserData, Value)| { + { + let mut me = ud.borrow_mut::()?; + match value { + Value::Nil => me.style = None, + Value::Table(tbl) => me.style = Some(Style::from(tbl).0), + Value::UserData(ud) => me.style = Some(ud.borrow::