use ratatui::{ crossterm::event::{KeyCode, KeyEvent}, prelude::*, text::ToLine, widgets::{Paragraph, Widget}, }; use crate::{ config::{keymap::ViewKeyMap, theme::view::bar::SheetViewBarTheme, GlobalConfig}, lua, state::{ view::{bar::SheetViewBarState, mode::Mode}, GlobalState, }, }; use super::statusbar::StatusBar; const DEFAULT_COLUMN_WIDTH: u16 = 10; #[derive(Default)] pub struct SheetView { bar: StatusBar, scroll: (usize, usize), } impl SheetView { pub fn new() -> Self { Self { bar: StatusBar::new(), scroll: (0, 0), } } pub fn handle_key_event(&mut self, event: KeyEvent) { let mode = { GlobalState::get().view.mode }; match mode { Mode::Command => match event.code { KeyCode::Enter => { if let Err(_error) = lua::get().load(self.bar.input().unwrap_or("")).exec() { // TODO: push errors to buffer } GlobalState::get().view.cancel_mode(); self.bar.set_input_mode(false); } KeyCode::Esc => { GlobalState::get().view.cancel_mode(); self.bar.set_input_mode(false); } _ => self.bar.handle_keyevent(event), }, _ => { ViewKeyMap::handle(event); let mode = { GlobalState::get().view.mode }; if let Mode::Command = mode { self.bar.set_input_mode(true) } } } } } impl Widget for &mut SheetView { fn render(self, area: ratatui::prelude::Rect, buf: &mut ratatui::prelude::Buffer) where Self: Sized, { let mode = { let theme = GlobalConfig::get().theme.view.clone(); let state = GlobalState::get(); let sheet = state.view.active_sheet().unwrap(); let cursor = &state.view.cursor; let sheet_area_inner = self.bar.area(area); 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 { self.scroll.0 = (cursor.y() - viewport_rows) + 1; } else if cursor.y() < self.scroll.0 { self.scroll.0 = cursor.y(); } if cursor.x() >= viewport_columns + self.scroll.1 { self.scroll.1 = (cursor.x() - viewport_columns) + 1; } else if cursor.x() < self.scroll.1 { self.scroll.1 = cursor.x(); } theme .background .get((), lua::get()) .unwrap_or_default() .apply(Paragraph::default()) .render(sheet_area_inner, buf); 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); if let Some(cell_ref) = sheet.get_ref(cell_pos_y, cell_pos_x) { let cell = cell_ref.value().to_string() + " "; 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.view.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()) }; 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, ); line.render(rect, buf); } } } state.view.mode }; SheetViewBarState::apply(&mut self.bar, mode, lua::get()); SheetViewBarTheme::apply(&mut self.bar, mode, lua::get()); self.bar.render(area, buf); } }