feat: add a new Bar component, and make border styles customizable (#278)

This commit is contained in:
三咲雅 · Misaki Masa 2023-10-16 01:03:07 +08:00 committed by GitHub
parent 56cd3dc940
commit c9d9418920
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 182 additions and 15 deletions

View file

@ -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);
}
}

View file

@ -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" }
]

View file

@ -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,
}

View file

@ -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)

View file

@ -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<AnyUserData>, buf: &mut ratatui::prelude::Buffer) -> mlua::Result<()> {
@ -9,6 +9,8 @@ fn layout(values: Vec<AnyUserData>, buf: &mut ratatui::prelude::Buffer) -> mlua:
c.render(buf)
} else if let Ok(c) = value.take::<List>() {
c.render(buf)
} else if let Ok(c) = value.take::<Bar>() {
c.render(buf)
} else if let Ok(c) = value.take::<Gauge>() {
c.render(buf)
}

141
plugin/src/layout/bar.rs Normal file
View file

@ -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<ratatui::style::Style>,
}
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<Self> {
match value {
Value::UserData(ud) => Ok(ud.borrow::<Self>()?.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::<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(tbl) => me.style = Some(Style::from(tbl).0),
Value::UserData(ud) => me.style = Some(ud.borrow::<Style>()?.0),
_ => return Err(mlua::Error::external("expected a Style or Table or nil")),
}
}
Ok(ud)
});
}
}

View file

@ -70,11 +70,11 @@ impl UserData for Layout {
let me = ud.borrow::<Self>()?;
let mut layout = ratatui::layout::Layout::new()
.direction(if me.direction {
ratatui::layout::Direction::Vertical
} else {
ratatui::layout::Direction::Horizontal
})
.direction(
me.direction
.then_some(ratatui::layout::Direction::Vertical)
.unwrap_or(ratatui::layout::Direction::Horizontal),
)
.constraints(me.constraints.as_slice());
if let Some(margin) = me.margin {

View file

@ -1,5 +1,6 @@
#![allow(clippy::module_inception)]
mod bar;
mod constraint;
mod gauge;
mod layout;
@ -10,6 +11,7 @@ mod rect;
mod span;
mod style;
pub use bar::*;
pub(super) use constraint::*;
pub(super) use gauge::*;
pub(super) use layout::*;

View file

@ -3,7 +3,7 @@
mod bindings;
mod components;
mod config;
mod layout;
pub mod layout;
mod plugin;
mod scope;
mod utils;

View file

@ -31,6 +31,7 @@ pub fn init() {
// Install
crate::Config.install()?;
layout::Bar::install()?;
layout::Constraint::install()?;
layout::Gauge::install()?;
layout::Layout::install()?;