wip: Sheet tab rendering

addresses: #2
This commit is contained in:
Jeremy Wall 2024-11-29 09:44:53 -05:00
parent b2034e7f2b
commit 8f902d63f2
4 changed files with 118 additions and 49 deletions

View File

@ -215,14 +215,15 @@ impl Book {
/// Select a sheet by name.
pub fn select_sheet_by_name(&mut self, name: &str) -> bool {
if let Some(sheet) = self
if let Some((idx, _sheet)) = self
.model
.workbook
.worksheets
.iter()
.find(|sheet| sheet.name == name)
.enumerate()
.find(|(_idx, sheet)| sheet.name == name)
{
self.current_sheet = sheet.sheet_id;
self.current_sheet =idx as u32;
return true;
}
false
@ -233,16 +234,37 @@ impl Book {
self.model.workbook.get_worksheet_names()
}
pub fn select_next_sheet(&mut self) {
let len = self.model.workbook.worksheets.len() as u32;
let mut next = self.current_sheet + 1;
if next == len {
next = 0;
}
self.current_sheet = next;
}
pub fn select_prev_sheet(&mut self) {
let len = self.model.workbook.worksheets.len() as u32;
let next = if self.current_sheet == 0 {
len - 1
} else {
self.current_sheet - 1
};
self.current_sheet = next;
}
/// Select a sheet by id.
pub fn select_sheet_by_id(&mut self, id: u32) -> bool {
if let Some(sheet) = self
if let Some((idx, _sheet)) = self
.model
.workbook
.worksheets
.iter()
.find(|sheet| sheet.sheet_id == id)
.enumerate()
.find(|(_idx, sheet)| sheet.sheet_id == id)
{
self.current_sheet = sheet.sheet_id;
self.current_sheet = idx as u32;
return true;
}
false
@ -254,7 +276,7 @@ impl Book {
.model
.workbook
.worksheet(self.current_sheet)
.map_err(|s| anyhow!("Invalid Worksheet: {}", s))?)
.map_err(|s| anyhow!("Invalid Worksheet id: {}: error: {}", self.current_sheet, s))?)
}
pub(crate) fn get_sheet_mut(&mut self) -> Result<&mut Worksheet> {

View File

@ -9,6 +9,7 @@ pub enum Cmd<'a> {
InsertColumns(usize),
RenameSheet(Option<usize>, &'a str),
NewSheet(Option<&'a str>),
SelectSheet(&'a str),
Edit(&'a str),
Help(Option<&'a str>),
Quit,
@ -24,6 +25,9 @@ pub fn parse<'cmd, 'i: 'cmd>(input: &'i str) -> Result<Option<Cmd<'cmd>>, &'stat
if let Some(cmd) = try_consume_new_sheet(cursor.clone())? {
return Ok(Some(cmd));
}
if let Some(cmd) = try_consume_select_sheet(cursor.clone())? {
return Ok(Some(cmd));
}
// try consume insert-row command.
if let Some(cmd) = try_consume_insert_row(cursor.clone())? {
return Ok(Some(cmd));
@ -114,6 +118,25 @@ fn try_consume_new_sheet<'cmd, 'i: 'cmd>(
})));
}
fn try_consume_select_sheet<'cmd, 'i: 'cmd>(
mut input: StrCursor<'i>,
) -> Result<Option<Cmd<'cmd>>, &'static str> {
const LONG: &'static str = "select-sheet";
if compare(input.clone(), LONG) {
input.seek(LONG.len());
} else {
return Ok(None);
}
if input.remaining() > 0 && !is_ws(&mut input) {
return Err("Invalid command: Did you mean to type `write <sheet-name>`?");
}
let arg = input.span(0..).trim();
if arg.is_empty() {
return Err("Invalid command: Did you forget the sheet name? `write <sheet-name>`?");
}
return Ok(Some(Cmd::SelectSheet(arg)));
}
fn try_consume_insert_row<'cmd, 'i: 'cmd>(
mut input: StrCursor<'i>,

View File

@ -293,6 +293,10 @@ impl<'ws> Workspace<'ws> {
self.book.new_sheet(name)?;
Ok(true)
}
Ok(Some(Cmd::SelectSheet(name))) => {
self.book.select_sheet_by_name(name);
Ok(true)
}
Ok(Some(Cmd::Quit)) => {
// TODO(zaphar): We probably need to do better than this
std::process::exit(0);
@ -323,6 +327,12 @@ impl<'ws> Workspace<'ws> {
KeyCode::Char('?') => {
self.enter_dialog_mode(self.render_help_text());
}
KeyCode::Char('n') if key.modifiers == KeyModifiers::CONTROL => {
self.book.select_next_sheet();
}
KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => {
self.book.select_prev_sheet();
}
KeyCode::Char('s')
if key.modifiers == KeyModifiers::HYPER
|| key.modifiers == KeyModifiers::SUPER =>
@ -467,47 +477,6 @@ impl<'ws> Workspace<'ws> {
Ok(())
}
fn get_render_parts(
&mut self,
area: Rect,
) -> Vec<(Rect, Box<dyn Fn(Rect, &mut Buffer, &mut Self)>)> {
use ratatui::widgets::StatefulWidget;
let mut cs = vec![Constraint::Fill(4), Constraint::Fill(30)];
let mut rs: Vec<Box<dyn Fn(Rect, &mut Buffer, &mut Self)>> = vec![
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| ws.text_area.render(rect, buf)),
Box::new(move |rect: Rect, buf: &mut Buffer, ws: &mut Self| {
let sheet_name = ws.book.get_sheet_name().unwrap_or("Unknown");
let table_block = Block::bordered().title_top(sheet_name);
let viewport = Viewport::new(&ws.book).with_selected(ws.book.location.clone()).block(table_block);
StatefulWidget::render(viewport, rect, buf, &mut ws.state.viewport_state);
}),
];
if self.state.modality() == &Modality::Command {
cs.push(Constraint::Max(1));
rs.push(Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {
StatefulWidget::render(
TextPrompt::from("Command"),
rect,
buf,
&mut ws.state.command_state,
)
}));
}
let rects: Vec<Rect> = Vec::from(
Layout::vertical(cs)
.vertical_margin(2)
.horizontal_margin(2)
.flex(Flex::Legacy)
.split(area.clone())
.as_ref(),
);
rects
.into_iter()
.zip(rs.into_iter())
.map(|(rect, f)| (rect, f))
.collect()
}
}
fn load_book(path: &PathBuf, locale: &str, tz: &str) -> Result<Book, anyhow::Error> {

View File

@ -2,7 +2,7 @@ use ratatui::{
self,
layout::Rect,
text::{Line, Text},
widgets::{Block, Widget},
widgets::{Block, Tabs, Widget},
Frame,
};
use tui_popup::Popup;
@ -15,6 +15,61 @@ pub use viewport::Viewport;
#[cfg(test)]
mod test;
impl<'ws> Workspace<'ws> {
fn get_render_parts(
&mut self,
area: Rect,
) -> Vec<(Rect, Box<dyn Fn(Rect, &mut Buffer, &mut Self)>)> {
use ratatui::widgets::StatefulWidget;
let mut cs = vec![
Constraint::Length(2),
Constraint::Length(3),
Constraint::Fill(1),
];
let mut rs: Vec<Box<dyn Fn(Rect, &mut Buffer, &mut Self)>> = vec![
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {
let tabs = Tabs::new(ws.book.get_sheet_names())
.select(Some(ws.book.current_sheet as usize));
tabs.render(rect, buf);
}),
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| ws.text_area.render(rect, buf)),
Box::new(move |rect: Rect, buf: &mut Buffer, ws: &mut Self| {
let sheet_name = ws.book.get_sheet_name().unwrap_or("Unknown");
let table_block = Block::bordered().title_top(sheet_name);
let viewport = Viewport::new(&ws.book)
.with_selected(ws.book.location.clone())
.block(table_block);
StatefulWidget::render(viewport, rect, buf, &mut ws.state.viewport_state);
}),
];
if self.state.modality() == &Modality::Command {
cs.push(Constraint::Max(1));
rs.push(Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {
StatefulWidget::render(
TextPrompt::from("Command"),
rect,
buf,
&mut ws.state.command_state,
)
}));
}
let rects: Vec<Rect> = Vec::from(
Layout::vertical(cs)
.vertical_margin(2)
.horizontal_margin(2)
.flex(Flex::Legacy)
.split(area.clone())
.as_ref(),
);
rects
.into_iter()
.zip(rs.into_iter())
.map(|(rect, f)| (rect, f))
.collect()
}
}
impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
where