diff options
| author | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-02 11:38:19 +0200 |
|---|---|---|
| committer | Nathan Reiner <nathan@nathanreiner.xyz> | 2024-08-02 11:38:19 +0200 |
| commit | 04a5a938994ddb95cfaa9a74b180e457d3a2b5d0 (patch) | |
| tree | 31ce9525ed3f3423678397323b65c910d63eadb1 /src/widgets/sheetview | |
| parent | fe0938b1de0c46fc2afcaa3dcd6a0f4ec870d21a (diff) | |
implement new lua interface
Diffstat (limited to 'src/widgets/sheetview')
| -rw-r--r-- | src/widgets/sheetview/mod.rs | 571 |
1 files changed, 142 insertions, 429 deletions
diff --git a/src/widgets/sheetview/mod.rs b/src/widgets/sheetview/mod.rs index 4ae37f0..aac2080 100644 --- a/src/widgets/sheetview/mod.rs +++ b/src/widgets/sheetview/mod.rs @@ -1,49 +1,27 @@ -use std::thread::JoinHandle; - use ratatui::{ - crossterm::event::{KeyCode, KeyEvent, KeyModifiers}, + crossterm::event::{KeyCode, KeyEvent}, prelude::*, style::Stylize, text::ToLine, - widgets::{Block, Borders, Clear, Gauge, Paragraph, Widget, Wrap}, + widgets::Widget, }; use crate::{ config::GlobalConfig, cursor::CursorMove, lua, - sheet::{ - cell::Cell, - eval::EvalFunction, - Sheet, - }, - state::GlobalState, + sheet::cell::Cell, + state::{view::mode::Mode, window::Window, GlobalState}, }; -use super::{luaeditor::LuaEditor, statusbar::StatusBar}; +use super::statusbar::StatusBar; const DEFAULT_COLUMN_WIDTH: u16 = 10; #[derive(Default)] -enum SheetViewMode { - #[default] - Normal, - Insert, - Visual, - Command, - CommandError(String), - Script, - Processing(Option<JoinHandle<Result<Sheet, String>>>), - EditorError(String), -} - -#[derive(Default)] pub struct SheetView { bar: StatusBar, - scroll: (u16, u16), - selection_anchor: Option<(u16, u16)>, - mode: SheetViewMode, - editor: LuaEditor, + scroll: (usize, usize), } impl SheetView { @@ -56,314 +34,126 @@ impl SheetView { .right("") .right_style(Style::default().on_red()), scroll: (0, 0), - mode: SheetViewMode::Normal, - selection_anchor: None, - editor: LuaEditor::new(), } } - pub fn move_cursor_by(&mut self, delta: (isize, isize)) { - let mut state = GlobalState::instance_mut(); - state - .sheetview - .cursor - .move_checked(CursorMove::Relative(delta)); + fn set_mode(&self, mode: Mode) { + GlobalState::instance_mut().sheetview.mode = mode; } - pub fn selection(&self) -> Vec<(u16, u16)> { - let mut selection = Vec::new(); - - if let Some((row, column)) = self.selection_range() { - for i in row.0..=row.1 { - for j in column.0..=column.1 { - selection.push((i, j)) - } - } - } - - selection - } - - fn is_in_selection(&mut self, row: u16, column: u16) -> bool { - if let Some((row_range, column_range)) = self.selection_range() { - row >= row_range.0 - && row <= row_range.1 - && column >= column_range.0 - && column <= column_range.1 - } else { - false - } + fn move_cursor(&self, cm: CursorMove) { + GlobalState::instance_mut() + .sheetview + .cursor + .move_checked(cm) } - fn selection_range(&self) -> Option<((u16, u16), (u16, u16))> { + fn cursor(&self) -> (usize, usize) { let state = GlobalState::instance(); - let cursor = &state.sheetview.cursor; - if let Some(selection) = self.selection_anchor { - let row = if selection.0 as usize > cursor.y() { - (cursor.y() as u16, selection.0) - } else { - (selection.0, cursor.y() as u16) - }; - - let column = if selection.1 as usize > cursor.x() { - (cursor.x() as u16, selection.1) - } else { - (selection.1, cursor.x() as u16) - }; + (state.sheetview.cursor.y(), state.sheetview.cursor.x()) + } - Some((row, column)) - } else { - None - } + fn start_selection(&self) { + let mut state = GlobalState::instance_mut(); + state.sheetview.selection_anchor = Some(self.cursor()); + state.sheetview.mode = Mode::Visual; } - fn join_process_handle_on_finished(&mut self) { - if let SheetViewMode::Processing(opt) = &mut self.mode { - let handle = opt.take().unwrap(); - if handle.is_finished() { - match handle.join().unwrap() { - Ok(sheet) => { - GlobalState::instance() - .sheetview - .active_sheet() - .unwrap() - .write() - .unwrap() - .apply(sheet); - self.mode = SheetViewMode::Script; - } - Err(message) => self.mode = SheetViewMode::EditorError(message), - }; - } else { - self.mode = SheetViewMode::Processing(Some(handle)); - } - } + fn open_editor(&self) { + let mut state = GlobalState::instance_mut(); + state.editor.buffer.set_lines_from_string( +r#"require('neosheet') + .state + .view + .active:foreach(function(cell) + return "" +end)"#, + ); + state.set_focus(Window::Editor) } pub fn handle_key_event(&mut self, event: KeyEvent) { - match &self.mode { - SheetViewMode::Normal => match event.code { - KeyCode::Char('j') => self.move_cursor_by((0, 1)), - KeyCode::Char('k') => self.move_cursor_by((0, -1)), - KeyCode::Char('h') => self.move_cursor_by((-1, 0)), - KeyCode::Char('l') => self.move_cursor_by((1, 0)), - KeyCode::Char('v') => { - let state = GlobalState::instance(); - let cursor = &state.sheetview.cursor; - self.selection_anchor = Some((cursor.y() as u16, cursor.x() as u16)); - self.mode = SheetViewMode::Visual - } - KeyCode::Char('s') => { - self.editor.set_text("function(cell)\n\treturn \"\"\nend"); - self.mode = SheetViewMode::Script; - } + let mode = { GlobalState::instance().sheetview.mode }; + match mode { + Mode::Normal => match event.code { + KeyCode::Char('j') => self.move_cursor(CursorMove::Down(1)), + KeyCode::Char('k') => self.move_cursor(CursorMove::Up(1)), + KeyCode::Char('h') => self.move_cursor(CursorMove::Left(1)), + KeyCode::Char('l') => self.move_cursor(CursorMove::Right(1)), + KeyCode::Char('v') => self.start_selection(), + KeyCode::Char('s') => self.open_editor(), KeyCode::Char(':') => { - self.mode = SheetViewMode::Command; - self.bar.set_input_mode(true) + self.set_mode(Mode::Command); + self.bar.set_input_mode(true); } KeyCode::Enter => { - self.mode = SheetViewMode::Insert; + self.set_mode(Mode::Insert); self.bar.set_input_mode(true); } _ => {} }, - SheetViewMode::Insert => match event.code { + Mode::Insert => match event.code { KeyCode::Enter => { - let lock = GlobalState::instance().sheetview.active_sheet().unwrap(); + let mut state = GlobalState::instance_mut(); + let lock = state.sheetview.active_sheet().unwrap(); let mut sheet = lock.write().unwrap(); - let state = GlobalState::instance(); - let cursor = &state.sheetview.cursor; - - if self.selection_anchor.is_some() { - self.selection().iter().map(|(r, c)| (*r, *c)).collect() - } else { - vec![(cursor.y() as u16, cursor.x() as u16)] - } - .into_iter() - .for_each(|(r, c)| { - sheet.set_cell(r as usize, c as usize, { - let s = self.bar.input().unwrap(); - match s.parse() { - Ok(n) => Cell::Number(n), - Err(_) => Cell::String(s.to_string()), - } - }) - }); + state + .sheetview + .selection_or_cursor() + .into_iter() + .for_each(|(r, c)| { + sheet.set_cell(r as usize, c as usize, { + let s = self.bar.input().unwrap(); + match s.parse() { + Ok(n) => Cell::Number(n), + Err(_) => Cell::String(s.to_string()), + } + }) + }); + state.sheetview.cancel_mode(); self.bar.set_input_mode(false); - if self.selection_anchor.is_some() { - self.mode = SheetViewMode::Visual; - } else { - self.mode = SheetViewMode::Normal; - } } KeyCode::Esc => { - if self.selection_anchor.is_some() { - self.mode = SheetViewMode::Visual; - } else { - self.mode = SheetViewMode::Normal; - } + GlobalState::instance_mut().sheetview.cancel_mode(); self.bar.set_input_mode(false); } _ => self.bar.handle_keyevent(event), }, - SheetViewMode::Visual => match event.code { - KeyCode::Char('j') => self.move_cursor_by((0, 1)), - KeyCode::Char('k') => self.move_cursor_by((0, -1)), - KeyCode::Char('h') => self.move_cursor_by((-1, 0)), - KeyCode::Char('l') => self.move_cursor_by((1, 0)), - KeyCode::Esc | KeyCode::Char('v') => { - self.selection_anchor = None; - self.mode = SheetViewMode::Normal; - } - KeyCode::Char('s') => { - self.editor.set_text("function(cell)\n\treturn \"\"\nend"); - self.mode = SheetViewMode::Script; - } + Mode::Visual => match event.code { + KeyCode::Char('j') => self.move_cursor(CursorMove::Down(1)), + KeyCode::Char('k') => self.move_cursor(CursorMove::Up(1)), + KeyCode::Char('h') => self.move_cursor(CursorMove::Left(1)), + KeyCode::Char('l') => self.move_cursor(CursorMove::Right(1)), + KeyCode::Char('v') => self.start_selection(), + KeyCode::Char('s') => self.open_editor(), KeyCode::Char(':') => { - self.mode = SheetViewMode::Command; - self.bar.set_input_mode(true) + self.set_mode(Mode::Command); + self.bar.set_input_mode(true); } KeyCode::Enter => { - self.mode = SheetViewMode::Insert; + self.set_mode(Mode::Insert); self.bar.set_input_mode(true); } _ => {} }, - SheetViewMode::Command => match event.code { + Mode::Command => match event.code { KeyCode::Enter => { - if let Err(error) = lua::get().load(self.bar.input().unwrap_or("")).exec() { - self.mode = SheetViewMode::CommandError(error.to_string()) - } else if self.selection_anchor.is_some() { - self.mode = SheetViewMode::Visual; - } else { - self.mode = SheetViewMode::Normal; + if let Err(_error) = lua::get().load(self.bar.input().unwrap_or("")).exec() { + // TODO: push errors to buffer } + GlobalState::instance_mut().sheetview.cancel_mode(); self.bar.set_input_mode(false); } KeyCode::Esc => { - if self.selection_anchor.is_some() { - self.mode = SheetViewMode::Visual; - } else { - self.mode = SheetViewMode::Normal; - } + GlobalState::instance_mut().sheetview.cancel_mode(); self.bar.set_input_mode(false); } _ => self.bar.handle_keyevent(event), }, - SheetViewMode::CommandError(_) => match event.code { - KeyCode::Esc | KeyCode::Enter => self.mode = SheetViewMode::Normal, - _ => {} - }, - SheetViewMode::Script => match event.code { - KeyCode::Char('r') if event.modifiers == KeyModifiers::CONTROL => { - let script = self.editor.text(); - - let (width, height) = { - let lock = GlobalState::instance().sheetview.active_sheet().unwrap(); - let sheet = lock.read().unwrap(); - (sheet.width(), sheet.height()) - }; - - let mut cells = Vec::new(); - - if self.selection_anchor.is_some() { - cells = self - .selection() - .iter() - .map(|(r, c)| (*r as usize, *c as usize)) - .collect() - } else { - for row in 0..height { - for column in 0..width { - cells.push((row, column)); - } - } - } - - self.mode = SheetViewMode::Processing(Some( - GlobalState::instance() - .sheetview - .active_sheet() - .unwrap() - .eval_function(script, cells), - )); - } - KeyCode::Esc => { - if self.selection_anchor.is_some() { - self.mode = SheetViewMode::Visual; - } else { - self.mode = SheetViewMode::Normal; - } - } - _ => self.editor.handle_key_event(event), - }, - SheetViewMode::Processing(_) => { - self.join_process_handle_on_finished(); - } - SheetViewMode::EditorError(_) => match event.code { - KeyCode::Esc | KeyCode::Enter => self.mode = SheetViewMode::Script, - _ => {} - }, - } - } - - fn is_editor_visible(&self) -> bool { - matches!( - self.mode, - SheetViewMode::Script | SheetViewMode::Processing(_) | SheetViewMode::EditorError(_), - ) - } - - fn is_error_window_visible(&self) -> bool { - matches!(self.mode, SheetViewMode::EditorError(_)) - } - - fn is_progress_visible(&self) -> bool { - matches!(self.mode, SheetViewMode::Processing(_)) - } - - fn areas(&self, area: Rect) -> (Rect, Rect, Rect, Rect, Rect) { - let mut sheet = area; - let mut editor = Rect::default(); - let mut error = Rect::default(); - let mut progress = Rect::default(); - let mut command_error = Rect::default(); - - if let SheetViewMode::CommandError(message) = &self.mode { - let layout = Layout::vertical([ - Constraint::Min(1), - Constraint::Length(message.lines().count().min(15) as u16 + 1), - ]) - .split(sheet); - sheet = layout[0]; - command_error = layout[1]; } - - if self.is_editor_visible() { - let layout = - Layout::horizontal([Constraint::Min(1), Constraint::Length(50)]).split(sheet); - sheet = layout[0]; - editor = layout[1]; - - if self.is_error_window_visible() { - let layout = - Layout::vertical([Constraint::Min(1), Constraint::Length(10)]).split(editor); - editor = layout[0]; - error = layout[1]; - } - - if self.is_progress_visible() { - let layout = - Layout::vertical([Constraint::Min(1), Constraint::Length(1)]).split(editor); - editor = layout[0]; - progress = layout[1]; - } - } - - (sheet, editor, error, progress, command_error) } } @@ -373,168 +163,91 @@ impl Widget for &mut SheetView { Self: Sized, { let theme = GlobalConfig::instance().theme.sheetview.clone(); - let progress; - let (sheet_area, editor_area, error_area, progress_area, cmd_error_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 = GlobalState::instance().sheetview.active_sheet().unwrap(); - let sheet = lock.read().unwrap(); - - let state = GlobalState::instance(); - let cursor = &state.sheetview.cursor; - - let mut sheet_area_inner = self.bar.area(sheet_area); - - if self.is_editor_visible() { - sheet_area_inner = separator.inner(sheet_area_inner); - } - - let viewport_rows = sheet.height().min(sheet_area_inner.height as usize); - let viewport_columns = sheet - .width() - .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize); - if cursor.y() >= viewport_rows + self.scroll.0 as usize { - self.scroll.0 = (cursor.y() - viewport_rows) as u16 + 1; - } else if cursor.y() < self.scroll.0 as usize { - self.scroll.0 = cursor.y() as u16; - } + let state = GlobalState::instance(); + let lock = state.sheetview.active_sheet().unwrap(); + let sheet = lock.read().unwrap(); + let cursor = &state.sheetview.cursor; - if cursor.x() >= viewport_columns + self.scroll.1 as usize { - self.scroll.1 = (cursor.x() - viewport_columns) as u16 + 1; - } else if cursor.x() < self.scroll.1 as usize { - self.scroll.1 = cursor.x() as u16; - } + let sheet_area_inner = self.bar.area(area); - for row in 0..viewport_rows as u16 { - for column in 0..(viewport_columns + 1) as u16 { - let (cell_pos_y, cell_pos_x) = (row + self.scroll.0, column + self.scroll.1); + let viewport_rows = sheet.height().min(sheet_area_inner.height as usize); + let viewport_columns = sheet + .width() + .min((sheet_area_inner.width / DEFAULT_COLUMN_WIDTH) as usize); - if let Some(cell_ref) = sheet.get_ref(cell_pos_y as usize, cell_pos_x as usize) - { - let cell = cell_ref.value().to_string() + " "; + if cursor.y() >= viewport_rows + self.scroll.0 { + self.scroll.0 = (cursor.y() - viewport_rows) + 1; + } else if cursor.y() < self.scroll.0 { + self.scroll.0 = cursor.y(); + } - let line = - if (cell_pos_y, cell_pos_x) == (cursor.y() as u16, cursor.x() as u16) { - theme - .cursor - .get(cell_ref, &lua::get()) - .unwrap_or_default() - .apply(cell.to_line()) - } else if self.is_in_selection(cell_pos_y, cell_pos_x) { - theme - .selection - .get(cell_ref, &lua::get()) - .unwrap_or_default() - .apply(cell.to_line()) - } else { - theme - .cell - .get(cell_ref, &lua::get()) - .unwrap_or_default() - .apply(cell.to_line()) - }; + if cursor.x() >= viewport_columns + self.scroll.1 as usize { + self.scroll.1 = (cursor.x() - viewport_columns) + 1; + } else if cursor.x() < self.scroll.1 { + self.scroll.1 = cursor.x(); + } - let rect = Rect::new( - 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, - ); + for row in 0..viewport_rows { + for column in 0..(viewport_columns + 1) { + let (cell_pos_y, cell_pos_x) = (row + self.scroll.0, column + self.scroll.1); - line.render(rect, buf); - } - } - } + if let Some(cell_ref) = sheet.get_ref(cell_pos_y as usize, cell_pos_x as usize) { + let cell = cell_ref.value().to_string() + " "; - match &self.mode { - SheetViewMode::Command => { - self.bar.set_left(" COMMAND "); - } + let line = if (cell_pos_y, cell_pos_x) == (cursor.y(), cursor.x()) { + theme + .cursor + .get(cell_ref, &lua::get()) + .unwrap_or_default() + .apply(cell.to_line()) + } else if state.sheetview.selection_contains(cell_pos_y, cell_pos_x) { + theme + .selection + .get(cell_ref, &lua::get()) + .unwrap_or_default() + .apply(cell.to_line()) + } else { + theme + .cell + .get(cell_ref, &lua::get()) + .unwrap_or_default() + .apply(cell.to_line()) + }; - SheetViewMode::Insert => { - self.bar.set_left(" INSERT "); - } + let rect = Rect::new( + sheet_area_inner.x + (column as u16) * DEFAULT_COLUMN_WIDTH, + sheet_area_inner.y + (row as u16), + (sheet_area_inner.width - (column as u16) * DEFAULT_COLUMN_WIDTH) + .min(DEFAULT_COLUMN_WIDTH), + 1, + ); - _ => { - if self.selection_anchor.is_some() { - self.bar.set_left(" VISUAL "); - } else { - self.bar.set_left(" NORMAL "); - self.bar.set_middle_alignment(Alignment::Center); - self.bar.set_middle("Sheet"); - } + line.render(rect, buf); } } + } - self.bar.render(sheet_area, buf); - - if self.is_editor_visible() { - separator.render(sheet_area, buf); - self.editor.render(editor_area, buf) + match state.sheetview.mode { + Mode::Command => { + self.bar.set_left(" COMMAND "); } - if let SheetViewMode::EditorError(error_msg) = &self.mode { - let lines = error_msg.lines().collect::<Vec<_>>(); - - 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 text = Text::from_iter(lines.iter().map(|s| s.to_line().white())); - - Paragraph::new(text) - .wrap(Wrap { trim: true }) - .bg(Color::Rgb(70, 25, 25)) - .render(error_inner, buf); + Mode::Insert => { + self.bar.set_left(" INSERT "); } - if let SheetViewMode::CommandError(message) = &self.mode { - let lines = message.lines().collect::<Vec<_>>(); - - 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(cmd_error_area); - block.render(cmd_error_area, buf); - - let text = Text::from_iter(lines.iter().map(|s| s.to_line().white())); - - Paragraph::new(text) - .wrap(Wrap { trim: true }) - .bg(Color::Rgb(70, 25, 25)) - .render(error_inner, buf); + _ => { + if state.sheetview.selection_anchor.is_some() { + self.bar.set_left(" VISUAL "); + } else { + self.bar.set_left(" NORMAL "); + self.bar.set_middle_alignment(Alignment::Center); + self.bar.set_middle("Sheet"); + } } - - progress = sheet.progress(); } - self.join_process_handle_on_finished(); - - if self.is_progress_visible() { - let gauge = Gauge::default() - .gauge_style( - Style::default() - .fg(Color::White) - .bg(Color::DarkGray) - .add_modifier(Modifier::ITALIC), - ) - .percent(progress as u16); - - Clear.render(progress_area, buf); - gauge.render(progress_area, buf); - } + self.bar.render(area, buf); } } |