diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/app.rs | 30 | ||||
| -rw-r--r-- | src/config/constants.rs | 23 | ||||
| -rw-r--r-- | src/config/mod.rs | 37 | ||||
| -rw-r--r-- | src/config/theme/colorpair.rs | 106 | ||||
| -rw-r--r-- | src/config/theme/mod.rs | 30 | ||||
| -rw-r--r-- | src/config/theme/sheetview.rs | 69 | ||||
| -rw-r--r-- | src/lua/mod.rs | 85 | ||||
| -rw-r--r-- | src/lua/runtime.rs | 52 | ||||
| -rw-r--r-- | src/main.rs | 1 | ||||
| -rw-r--r-- | src/sheet/eval.rs | 147 | ||||
| -rw-r--r-- | src/widgets/sheetview/mod.rs (renamed from src/widgets/sheetview.rs) | 144 |
11 files changed, 479 insertions, 245 deletions
@@ -1,6 +1,11 @@ use std::{io, time::Duration}; -use crate::{sheet::register::Register, tui, widgets::{logview::LogView, sheetview::SheetView}}; +use crate::{ + config, lua, + sheet::register::Register, + tui, + widgets::{logview::LogView, sheetview::SheetView}, +}; use ratatui::{ crossterm::event::{self, Event, KeyCode, KeyEvent, KeyEventKind, KeyModifiers}, @@ -29,12 +34,22 @@ impl App<'_> { } } - let view = SheetView::new(sheet_id).block(Block::bordered().title("Sheet")); + let view = SheetView::new(sheet_id); - Self { exit: false, view, logview: None } + Self { + exit: false, + view, + logview: None, + } } pub fn run(&mut self, terminal: &mut tui::Tui) -> io::Result<()> { + if let Err(e) = lua::source(&config::constants::USER_RC_PATH) { + self.exit = true; + tui::restore()?; + println!("{}", e.to_string()); + } + while !self.exit { terminal.draw(|frame| self.render_frame(frame))?; self.handle_events()?; @@ -65,7 +80,9 @@ impl App<'_> { KeyCode::Char('l') if key_event.modifiers == KeyModifiers::CONTROL => { match self.logview { Some(_) => self.logview = None, - None => self.logview = Some(LogView::new().block(Block::bordered().title(" Log "))), + None => { + self.logview = Some(LogView::new().block(Block::bordered().title(" Log "))) + } } } _ => self.view.handle_key_event(key_event), @@ -84,10 +101,11 @@ impl Widget for &mut App<'_> { { match &mut self.logview { Some(logview) => { - let layout = Layout::vertical([Constraint::Min(2), Constraint::Length(15)]).split(area); + let layout = + Layout::vertical([Constraint::Min(2), Constraint::Length(15)]).split(area); self.view.render(layout[0], buf); logview.render(layout[1], buf); - }, + } None => self.view.render(area, buf), } } diff --git a/src/config/constants.rs b/src/config/constants.rs new file mode 100644 index 0000000..5a8283a --- /dev/null +++ b/src/config/constants.rs @@ -0,0 +1,23 @@ +use lazy_static::lazy_static; + +lazy_static! { + pub static ref USER_CONFIG_DIR: String = { + match dirs::config_local_dir() { + Some(mut d) => { + d.push("neosheet"); + d.as_path().to_str().unwrap_or("").to_string() + } + None => String::new(), + } + }; + pub static ref USER_RC_PATH: String = { + match dirs::config_local_dir() { + Some(mut d) => { + d.push("neosheet"); + d.push("init.lua"); + d.as_path().to_str().unwrap_or("").to_string() + } + None => String::new(), + } + }; +} diff --git a/src/config/mod.rs b/src/config/mod.rs new file mode 100644 index 0000000..e6c4952 --- /dev/null +++ b/src/config/mod.rs @@ -0,0 +1,37 @@ +use std::sync::{RwLock, RwLockReadGuard, RwLockWriteGuard}; + +use mlua::{UserData, UserDataFields}; + +use self::theme::Theme; + +pub mod theme; +pub mod constants; + +pub struct GlobalConfig { + pub theme: Theme, +} + +static GLOBAL_CONFIG: RwLock<GlobalConfig> = RwLock::new(GlobalConfig::new()); +const DUMMY_CONFIG: GlobalConfig = GlobalConfig::new(); + +impl GlobalConfig { + pub const fn new() -> Self { + Self { + theme: Theme::new(), + } + } + + pub fn instance() -> RwLockReadGuard<'static, Self> { + GLOBAL_CONFIG.read().unwrap() + } + + pub fn instance_mut() -> RwLockWriteGuard<'static, Self> { + GLOBAL_CONFIG.write().unwrap() + } +} + +impl UserData for GlobalConfig { + fn add_fields<'lua, F: UserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_function_get("theme", |_, _| Ok(DUMMY_CONFIG.theme)) + } +} diff --git a/src/config/theme/colorpair.rs b/src/config/theme/colorpair.rs new file mode 100644 index 0000000..77e6af4 --- /dev/null +++ b/src/config/theme/colorpair.rs @@ -0,0 +1,106 @@ +use std::str::FromStr; + +use mlua::{FromLua, IntoLua}; +use ratatui::style::{Color, Styled, Stylize}; + +#[derive(Clone)] +pub struct ColorPair { + pub fg: Color, + pub bg: Color, +} + +impl ColorPair { + pub const fn new(fg: Color, bg: Color) -> Self { + ColorPair { bg, fg } + } + + pub fn apply<S, I>(&self, s: S) -> <I as Styled>::Item + where + S: Styled<Item = I>, + I: Styled, + { + s.bg(self.bg).fg(self.fg) + } +} + +impl<'lua> FromLua<'lua> for ColorPair { + fn from_lua( + value: mlua::prelude::LuaValue<'lua>, + _lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<Self> { + if value.is_table() { + let table = value.as_table().unwrap(); + if let Ok(fg) = table.get::<_, LuaColor>(1) { + if let Ok(bg) = table.get::<_, LuaColor>(2) { + return Ok(ColorPair::new(fg.into(), bg.into())); + } + } else if let Ok(fg) = table.get::<_, LuaColor>("fg") { + if let Ok(bg) = table.get::<_, LuaColor>("bg") { + return Ok(ColorPair::new(fg.into(), bg.into())); + } + } + } + + return Err(mlua::Error::runtime("could not parse colorpair")); + } +} + +impl<'lua> IntoLua<'lua> for ColorPair { + fn into_lua( + self, + lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> { + let table = lua.create_table()?; + table.set("fg", LuaColor::from(self.fg))?; + table.set("bg", LuaColor::from(self.bg))?; + Ok(table.into_lua(lua)?) + } +} + +struct LuaColor { + color: Color, +} + +impl LuaColor { + fn new(color: Color) -> Self { + Self { color } + } +} + +impl From<Color> for LuaColor { + fn from(value: Color) -> Self { + LuaColor::new(value) + } +} + +impl Into<Color> for LuaColor { + fn into(self) -> Color { + self.color + } +} + +impl<'lua> FromLua<'lua> for LuaColor { + fn from_lua( + value: mlua::prelude::LuaValue<'lua>, + _lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<Self> { + if value.is_string() { + let str = value.as_str().unwrap(); + match Color::from_str(str) { + Ok(color) => Ok(color.into()), + Err(_) => Err(mlua::Error::runtime("color has wrong format")), + } + } else { + Err(mlua::Error::runtime("color has wrong format")) + } + } +} + +impl<'lua> IntoLua<'lua> for LuaColor { + fn into_lua( + self, + lua: &'lua mlua::prelude::Lua, + ) -> mlua::prelude::LuaResult<mlua::prelude::LuaValue<'lua>> { + Ok(self.color.to_string().to_lowercase().into_lua(lua)?) + } +} diff --git a/src/config/theme/mod.rs b/src/config/theme/mod.rs new file mode 100644 index 0000000..b7baf3a --- /dev/null +++ b/src/config/theme/mod.rs @@ -0,0 +1,30 @@ + +use mlua::UserData; + +use self::sheetview::SheetViewTheme; + +use super::DUMMY_CONFIG; + +pub mod colorpair; +pub mod sheetview; + +#[derive(Clone)] +pub struct Theme { + pub sheetview: SheetViewTheme, +} + +impl Theme { + pub const fn new() -> Self { + Self { + sheetview: SheetViewTheme::new(), + } + } +} + +impl UserData for Theme { + fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_function_get("sheetview", |_, _| { + Ok(DUMMY_CONFIG.theme.sheetview) + }) + } +} diff --git a/src/config/theme/sheetview.rs b/src/config/theme/sheetview.rs new file mode 100644 index 0000000..de5751a --- /dev/null +++ b/src/config/theme/sheetview.rs @@ -0,0 +1,69 @@ +use mlua::{IntoLua, Table, UserData}; +use ratatui::style::Color; + +use crate::config::GlobalConfig; + +use super::colorpair::ColorPair; + +#[derive(Clone)] +pub struct SheetViewTheme { + pub cursor: ColorPair, + pub selection: ColorPair, + pub cell: (ColorPair, ColorPair), +} + +impl SheetViewTheme { + pub const fn new() -> Self { + Self { + cursor: ColorPair::new(Color::Black, Color::White), + selection: ColorPair::new(Color::White, Color::DarkGray), + cell: ( + ColorPair::new(Color::White, Color::Black), + ColorPair::new(Color::White, Color::DarkGray), + ), + } + } +} + +macro_rules! cfg { + () => { + GlobalConfig::instance().theme.sheetview + }; +} + +macro_rules! cfg_mut { + () => { + GlobalConfig::instance_mut().theme.sheetview + }; +} + +impl UserData for SheetViewTheme { + fn add_fields<'lua, F: mlua::prelude::LuaUserDataFields<'lua, Self>>(fields: &mut F) { + fields.add_field_function_get("cursor", |_, _| Ok(cfg!().cursor.clone())); + + fields.add_field_function_set("cursor", |_, _, pair: ColorPair| { + cfg_mut!().cursor = pair; + Ok(()) + }); + + fields.add_field_function_get("selection", |_, _| Ok(cfg!().selection.clone())); + + fields.add_field_function_set("selection", |_, _, pair: ColorPair| { + cfg_mut!().selection = pair; + Ok(()) + }); + + fields.add_field_function_get("cell", |lua, _| { + let table = lua.create_table()?; + let sel = cfg!().cell.clone(); + table.set(1, sel.0)?; + table.set(2, sel.1)?; + Ok(table.into_lua(lua)?) + }); + + fields.add_field_function_set("cell", |_, _, pair: Table| { + cfg_mut!().cell = (pair.get(1)?, pair.get(2)?); + Ok(()) + }); + } +} diff --git a/src/lua/mod.rs b/src/lua/mod.rs index 5d45ee9..0ddcd7f 100644 --- a/src/lua/mod.rs +++ b/src/lua/mod.rs @@ -1,10 +1,34 @@ -use std::time::{SystemTime, UNIX_EPOCH}; +use std::{ + path::Path, + sync::{Mutex, MutexGuard}, +}; +use lazy_static::lazy_static; use mlua::prelude::*; -use crate::sheet::{cell::CellRef, register::Register}; +use crate::sheet::cell::CellRef; pub mod iobuffer; +pub mod runtime; + +lazy_static! { + static ref LUA: Mutex<Lua> = { + let lock = Mutex::new(Lua::new()); + + { + let lua = lock.lock().unwrap(); + + let print_binding = lua.create_function(print).unwrap(); + lua.globals().set("print", print_binding).unwrap(); + + runtime::math(&lua).unwrap(); + runtime::neosheet(&lua).unwrap(); + runtime::package(&lua).unwrap(); + } + + lock + }; +} fn print(_: &Lua, args: LuaMultiValue) -> LuaResult<()> { let mut writer = iobuffer::iobuffer().write().unwrap(); @@ -28,53 +52,20 @@ fn print(_: &Lua, args: LuaMultiValue) -> LuaResult<()> { Ok(()) } -pub fn new_instance() -> LuaResult<Lua> { - let lua = Lua::new(); - - let print_binding = lua.create_function(print)?; - lua.globals().set("print", print_binding)?; - - { - let math = lua.globals().get::<_, LuaTable>("math"); - let randomseed = math.unwrap().get::<_, LuaFunction>("randomseed").unwrap(); - - let seed = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_millis(); - randomseed.call::<_, ()>(seed).unwrap(); - } - - let neosheet = lua.create_table()?; - - let register = Register; - neosheet.set("sheets", register)?; - - lua.globals().set("neosheet", neosheet)?; - - Ok(lua) +pub fn get() -> MutexGuard<'static, Lua> { + LUA.lock().unwrap() } -#[cfg(test)] -mod test { - use mlua::Function; - - use super::new_instance; +pub fn source(path: &str) -> LuaResult<()> { + let path = Path::new(path); - #[test] - fn function_eval() { - let lua = new_instance().unwrap(); - let func: Function = lua - .load( - r#" - function(a, b, c) - return a + b * c - end - "#, - ) - .eval() - .unwrap(); - eprintln!("{}", func.call::<_, String>((1, 2, 3, 4)).unwrap()); - assert!(false); + if path.exists() && path.is_file() { + get().load(path).exec()?; + Ok(()) + } else { + Err(LuaError::runtime(format!( + "could not source {:?} (file not found)", + path + ))) } } diff --git a/src/lua/runtime.rs b/src/lua/runtime.rs new file mode 100644 index 0000000..dfb37e6 --- /dev/null +++ b/src/lua/runtime.rs @@ -0,0 +1,52 @@ +use mlua::{Function, Lua, Result, Table}; +use std::time::{SystemTime, UNIX_EPOCH}; + +use crate::{ + config::{self, GlobalConfig}, + sheet::register::Register, +}; + +pub fn neosheet(lua: &Lua) -> Result<()> { + let exports = lua.create_table()?; + + exports + .set("sheets", lua.create_proxy::<Register>()?) + .unwrap(); + exports + .set("config", lua.create_userdata(GlobalConfig::new())?) + .unwrap(); + + lua.globals() + .get::<_, Table>("package")? + .get::<_, Table>("loaded")? + .set("neosheet", exports)?; + + Ok(()) +} + +pub fn math(lua: &Lua) -> Result<()> { + let math = lua.globals().get::<_, Table>("math"); + let randomseed = math.unwrap().get::<_, Function>("randomseed").unwrap(); + + let seed = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_millis(); + randomseed.call::<_, ()>(seed).unwrap(); + Ok(()) +} + +pub fn package(lua: &Lua) -> Result<()> { + let package = lua.globals().get::<_, Table>("package")?; + let path = package.get::<_, String>("path")?; + package.set( + "path", + format!( + "{};{}/?.lua;{}/?/init.lua", + path, + config::constants::USER_CONFIG_DIR.as_str(), + config::constants::USER_CONFIG_DIR.as_str() + ), + )?; + Ok(()) +} diff --git a/src/main.rs b/src/main.rs index bbfafb1..776489f 100644 --- a/src/main.rs +++ b/src/main.rs @@ -6,6 +6,7 @@ pub mod sheet; pub mod tui; pub mod widgets; pub mod lua; +pub mod config; fn run() -> io::Result<()> { let mut terminal = tui::init()?; diff --git a/src/sheet/eval.rs b/src/sheet/eval.rs index 4633949..2108e48 100644 --- a/src/sheet/eval.rs +++ b/src/sheet/eval.rs @@ -1,10 +1,12 @@ use std::{ - sync::{mpsc::channel, Arc, RwLock, RwLockReadGuard}, + sync::{Arc, RwLock}, thread::JoinHandle, }; use mlua::prelude::*; +use crate::lua; + use super::{cell::Cell, Sheet}; pub trait EvalFunction { @@ -24,147 +26,30 @@ impl EvalFunction for Arc<RwLock<Sheet>> { *self.read().unwrap().progress.lock().unwrap() = 0; let this = Arc::clone(self); std::thread::spawn(move || { - let chunks = range.chunks( - (range.len() / std::thread::available_parallelism().unwrap()) - .min(range.len()) - .max(1000), - ); - - if chunks.len() > 4 { - let (tx, rx) = channel(); - - std::thread::scope(|s| { - for chunk in chunks { - let tx = tx.clone(); - let this = Arc::clone(&this); - let func_text = func_text.clone(); - - s.spawn(move || { - let tx = tx.clone(); - let read_sheet = this.read().unwrap(); - let result = EvalRange::new(chunk.iter(), func_text, read_sheet); - match result { - Ok(evaluator) => { - for result in evaluator { - if result.is_err() { - tx.send(result).unwrap(); - break; - } - - tx.send(result).unwrap(); - } - } - Err(error) => tx.send(Err(error)).unwrap(), - } - }); - } - - drop(tx); + let lua = lua::get(); + let read_sheet = this.read().unwrap(); + let mut sheet = read_sheet.clone(); + let result = lua.load(func_text).eval::<LuaFunction>(); + match result { + Ok(func) => { let read_sheet = this.read().unwrap(); - let mut sheet = read_sheet.clone(); - let mut count = 0; + for (i, (row, column)) in range.iter().enumerate() { + *read_sheet.progress.lock().unwrap() = (i * 100 / range.len()) as u8; - for result in rx { - count += 1; - *read_sheet.progress.lock().unwrap() = (count * 100 / range.len()) as u8; + let cellref = read_sheet.get_ref(*row, *column); - match result { - Ok((row, column, cell)) => sheet.set_cell(row, column, cell), + match func.call::<_, Cell>(cellref) { + Ok(cell) => sheet.set_cell(*row, *column, cell), Err(error) => return Err(error.to_string()), } } - - crate::lua::new_instance().unwrap().expire_registry_values(); - Ok(sheet) - }) - } else { - let read_sheet = this.read().unwrap(); - let mut sheet = read_sheet.clone(); - let result = EvalRange::new((&range).iter(), func_text, read_sheet); - - match result { - Ok(evaluator) => { - for result in evaluator { - match result { - Ok((row, column, cell)) => sheet.set_cell(row, column, cell), - Err(error) => return Err(error.to_string()), - } - } - } - Err(error) => return Err(error), } - - crate::lua::new_instance().unwrap().expire_registry_values(); - - Ok(sheet) + Err(error) => return Err(error.to_string()), } - }) - } -} - -struct EvalRange<'a, I> -where - I: Iterator<Item = &'a (usize, usize)>, -{ - lua: Lua, - reg_func_key: LuaRegistryKey, - range: I, - read_sheet: RwLockReadGuard<'a, Sheet>, -} - -impl<'a, I> EvalRange<'a, I> -where - I: Iterator<Item = &'a (usize, usize)>, -{ - fn new( - range: I, - script: String, - read_sheet: RwLockReadGuard<'a, Sheet>, - ) -> Result<Self, String> { - let lua = crate::lua::new_instance().unwrap(); - let result = lua - .load(script.clone()) - .set_name("Temp Script") - .eval::<LuaFunction>(); - - if result.is_err() { - return Err(result.err().unwrap().to_string()); - } - let lua = crate::lua::new_instance().unwrap(); - let func = lua - .load(script) - .set_name("Temp Script") - .eval::<LuaFunction>() - .unwrap(); - - Ok(Self { - reg_func_key: lua.create_registry_value(func).unwrap(), - lua, - range, - read_sheet, + Ok(sheet) }) } } - -impl<'a, I> Iterator for EvalRange<'a, I> -where - I: Iterator<Item = &'a (usize, usize)>, -{ - type Item = Result<(usize, usize, Cell), String>; - - fn next(&mut self) -> Option<Self::Item> { - let func: LuaFunction = self.lua.registry_value(&self.reg_func_key).unwrap(); - if let Some((row, column)) = self.range.next() { - let cellref = self.read_sheet.get_ref(*row, *column); - match func.call::<_, Cell>(cellref) { - Ok(cell) => Some(Ok((*row, *column, cell))), - Err(error) => Some(Err(error.to_string())), - } - } else { - None - } - } -} diff --git a/src/widgets/sheetview.rs b/src/widgets/sheetview/mod.rs index c4f7a73..37e3d16 100644 --- a/src/widgets/sheetview.rs +++ b/src/widgets/sheetview/mod.rs @@ -1,25 +1,25 @@ use std::thread::JoinHandle; -use layout::Offset; use ratatui::{ crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, prelude::*, style::Stylize, text::ToLine, - widgets::{block::BlockExt, Block, Clear, Gauge, Widget}, + widgets::{Block, Borders, Clear, Gauge, Paragraph, Widget, Wrap}, }; -use crate::sheet::{ +use crate::{config::GlobalConfig, sheet::{ + eval::EvalFunction, register::{Register, SheetId}, - eval::EvalFunction, Sheet, -}; + Sheet, +}}; use super::luaeditor::LuaEditor; const DEFAULT_COLUMN_WIDTH: u16 = 10; pub struct SheetView<'a> { - block: Option<Block<'a>>, + block: Block<'a>, luaeditor: Option<LuaEditor<'a>>, sheet: SheetId, selection: Option<(u16, u16)>, @@ -32,7 +32,15 @@ pub struct SheetView<'a> { impl<'a> SheetView<'a> { pub fn new(sheet: SheetId) -> Self { Self { - block: None, + block: Block::default() + .title_bottom(" Sheet ") + .title_style(Style::default().on_magenta().black()) + .borders(Borders::BOTTOM) + .border_style( + Style::default() + .bg(Color::Rgb(29, 32, 33)) + .fg(Color::Rgb(29, 32, 33)), + ), luaeditor: None, sheet, selection: None, @@ -43,11 +51,6 @@ impl<'a> SheetView<'a> { } } - pub fn block(mut self, block: Block<'a>) -> Self { - self.block = Some(block); - self - } - pub fn sheet(mut self, sheet: SheetId) -> Self { self.sheet = sheet; self @@ -197,14 +200,43 @@ impl<'a> SheetView<'a> { KeyCode::Char('l') => self.move_cursor_by((0, 1)), KeyCode::Char('v') => self.toggle_select_mode(), KeyCode::Char('s') => { - let editor = LuaEditor::new("function(cell)\n\treturn \"\"\nend") - .block(Some(Block::bordered().title(" Temp Script "))); + let editor = LuaEditor::new("function(cell)\n\treturn \"\"\nend"); self.luaeditor = Some(editor) } _ => {} } } } + + fn areas(&self, area: Rect) -> (Rect, Rect, Rect, Rect) { + let mut sheet = area; + let mut editor = Rect::default(); + let mut error = Rect::default(); + let mut progress = Rect::default(); + + if self.luaeditor.is_some() { + let layout = + Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(sheet); + sheet = layout[0]; + editor = layout[1]; + + if self.error_window.is_some() { + let layout = + Layout::vertical([Constraint::Min(1), Constraint::Length(10)]).split(editor); + editor = layout[0]; + error = layout[1]; + } + + if self.process_handle.is_some() { + let layout = + Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(editor); + editor = layout[0]; + progress = layout[1]; + } + } + + (sheet, editor, error, progress) + } } impl Widget for &mut SheetView<'_> { @@ -212,25 +244,29 @@ impl Widget for &mut SheetView<'_> { where Self: Sized, { + let theme = GlobalConfig::instance().theme.sheetview.clone(); let progress; + let (sheet_area, editor_area, error_area, progress_area) = self.areas(area); + let separator = Block::default().borders(Borders::RIGHT).border_style( + Style::default() + .bg(Color::Rgb(29, 32, 33)) + .fg(Color::Rgb(29, 32, 33)), + ); { let lock = Register::get(self.sheet).unwrap(); let sheet = lock.read().unwrap(); - let splits = Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(area); - let block_area = if self.luaeditor.is_some() { - splits[0] - } else { - area - }; + let mut sheet_area_inner = self.block.inner(sheet_area); - let sheet_area = self.block.inner_if_some(block_area); + if self.luaeditor.is_some() { + sheet_area_inner = separator.inner(sheet_area_inner); + } let viewport_rows = sheet.height().min(sheet_area.height as usize); let viewport_columns = sheet .width() - .min((sheet_area.width / DEFAULT_COLUMN_WIDTH) as usize); + .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize); if self.cursor.0 >= viewport_rows as u16 + self.scroll.0 { self.scroll.0 = self.cursor.0 - viewport_rows as u16 + 1; @@ -252,19 +288,19 @@ impl Widget for &mut SheetView<'_> { let cell = cell.to_string() + " "; let line = if (cell_pos_y, cell_pos_x) == self.cursor { - cell.to_line().bg(Color::Rgb(120, 90, 90)).white() + theme.cursor.apply(cell.to_line()) } else if self.is_in_selection(cell_pos_y, cell_pos_x) { - cell.to_line().bg(Color::Rgb(120, 120, 90)).white() + theme.selection.apply(cell.to_line()) } else if (row + column) % 2 == 0 { - cell.to_line().bg(Color::Rgb(30, 30, 30)).white() + theme.cell.0.apply(cell.to_line()) } else { - cell.to_line().bg(Color::Rgb(50, 50, 50)).white() + theme.cell.1.apply(cell.to_line()) }; let rect = Rect::new( - sheet_area.x + column * DEFAULT_COLUMN_WIDTH, - sheet_area.y + row, - (sheet_area.width - column * DEFAULT_COLUMN_WIDTH) + sheet_area_inner.x + column * DEFAULT_COLUMN_WIDTH, + sheet_area_inner.y + row, + (sheet_area_inner.width - column * DEFAULT_COLUMN_WIDTH) .min(DEFAULT_COLUMN_WIDTH), 1, ); @@ -274,33 +310,30 @@ impl Widget for &mut SheetView<'_> { } } - self.block.render(block_area, buf); + self.block.clone().render(sheet_area, buf); if let Some(textarea) = &mut self.luaeditor { - textarea.render(splits[1], buf) + separator.render(self.block.inner(sheet_area), buf); + textarea.render(editor_area, buf) } if let Some(error_msg) = &self.error_window { let lines = error_msg.lines().collect::<Vec<_>>(); - let height = lines.len() as u16 + 2; - let width = lines.iter().map(|s| s.len()).max().unwrap_or(0) as u16 + 2; - let centered = Rect::new( - (area.width - width) / 2, - (area.height - height) / 2, - width, - height, - ); + let block = Block::default() + .title_bottom(" Error ") + .title_style(Style::default().on_red()) + .border_style(Style::default().black().on_black()) + .borders(Borders::BOTTOM); + let error_inner = block.inner(error_area); + block.render(error_area, buf); - let block = Block::bordered().red().on_black().bold().title(" Error "); - let inner_centered = block.inner(centered); - Clear::default().render(centered, buf); - block.render(centered, buf); + let text = Text::from_iter(lines.iter().map(|s| s.to_line().white())); - for (i, line) in lines.iter().enumerate() { - let line = line.replace('\t', " ").red().on_black().bold(); - line.render(inner_centered.offset(Offset { x: 0, y: i as i32 }), buf); - } + Paragraph::new(text) + .wrap(Wrap { trim: true }) + .bg(Color::Rgb(70, 25, 25)) + .render(error_inner, buf); } progress = sheet.progress(); @@ -309,28 +342,17 @@ impl Widget for &mut SheetView<'_> { self.join_process_handle_on_finished(); if self.process_handle.is_some() { - let height = 3; - let width = area.width / 2; - - let centered = Rect::new( - (area.width - width) / 2, - (area.height - height) / 2, - width, - height, - ); - let gauge = Gauge::default() - .block(Block::bordered().title("Progress").on_black()) .gauge_style( Style::default() .fg(Color::White) - .bg(Color::Black) + .bg(Color::DarkGray) .add_modifier(Modifier::ITALIC), ) .percent(progress as u16); - Clear::default().render(centered, buf); - gauge.render(centered, buf); + Clear::default().render(progress_area, buf); + gauge.render(progress_area, buf); } } } |