mirror of
https://github.com/zaphar/sheetsui.git
synced 2025-07-23 05:19:48 -04:00
parent
b2034e7f2b
commit
8f902d63f2
@ -215,14 +215,15 @@ impl Book {
|
|||||||
|
|
||||||
/// Select a sheet by name.
|
/// Select a sheet by name.
|
||||||
pub fn select_sheet_by_name(&mut self, name: &str) -> bool {
|
pub fn select_sheet_by_name(&mut self, name: &str) -> bool {
|
||||||
if let Some(sheet) = self
|
if let Some((idx, _sheet)) = self
|
||||||
.model
|
.model
|
||||||
.workbook
|
.workbook
|
||||||
.worksheets
|
.worksheets
|
||||||
.iter()
|
.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;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -233,16 +234,37 @@ impl Book {
|
|||||||
self.model.workbook.get_worksheet_names()
|
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.
|
/// Select a sheet by id.
|
||||||
pub fn select_sheet_by_id(&mut self, id: u32) -> bool {
|
pub fn select_sheet_by_id(&mut self, id: u32) -> bool {
|
||||||
if let Some(sheet) = self
|
if let Some((idx, _sheet)) = self
|
||||||
.model
|
.model
|
||||||
.workbook
|
.workbook
|
||||||
.worksheets
|
.worksheets
|
||||||
.iter()
|
.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;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
@ -254,7 +276,7 @@ impl Book {
|
|||||||
.model
|
.model
|
||||||
.workbook
|
.workbook
|
||||||
.worksheet(self.current_sheet)
|
.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> {
|
pub(crate) fn get_sheet_mut(&mut self) -> Result<&mut Worksheet> {
|
||||||
|
@ -9,6 +9,7 @@ pub enum Cmd<'a> {
|
|||||||
InsertColumns(usize),
|
InsertColumns(usize),
|
||||||
RenameSheet(Option<usize>, &'a str),
|
RenameSheet(Option<usize>, &'a str),
|
||||||
NewSheet(Option<&'a str>),
|
NewSheet(Option<&'a str>),
|
||||||
|
SelectSheet(&'a str),
|
||||||
Edit(&'a str),
|
Edit(&'a str),
|
||||||
Help(Option<&'a str>),
|
Help(Option<&'a str>),
|
||||||
Quit,
|
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())? {
|
if let Some(cmd) = try_consume_new_sheet(cursor.clone())? {
|
||||||
return Ok(Some(cmd));
|
return Ok(Some(cmd));
|
||||||
}
|
}
|
||||||
|
if let Some(cmd) = try_consume_select_sheet(cursor.clone())? {
|
||||||
|
return Ok(Some(cmd));
|
||||||
|
}
|
||||||
// try consume insert-row command.
|
// try consume insert-row command.
|
||||||
if let Some(cmd) = try_consume_insert_row(cursor.clone())? {
|
if let Some(cmd) = try_consume_insert_row(cursor.clone())? {
|
||||||
return Ok(Some(cmd));
|
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>(
|
fn try_consume_insert_row<'cmd, 'i: 'cmd>(
|
||||||
mut input: StrCursor<'i>,
|
mut input: StrCursor<'i>,
|
||||||
|
@ -293,6 +293,10 @@ impl<'ws> Workspace<'ws> {
|
|||||||
self.book.new_sheet(name)?;
|
self.book.new_sheet(name)?;
|
||||||
Ok(true)
|
Ok(true)
|
||||||
}
|
}
|
||||||
|
Ok(Some(Cmd::SelectSheet(name))) => {
|
||||||
|
self.book.select_sheet_by_name(name);
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
Ok(Some(Cmd::Quit)) => {
|
Ok(Some(Cmd::Quit)) => {
|
||||||
// TODO(zaphar): We probably need to do better than this
|
// TODO(zaphar): We probably need to do better than this
|
||||||
std::process::exit(0);
|
std::process::exit(0);
|
||||||
@ -323,6 +327,12 @@ impl<'ws> Workspace<'ws> {
|
|||||||
KeyCode::Char('?') => {
|
KeyCode::Char('?') => {
|
||||||
self.enter_dialog_mode(self.render_help_text());
|
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')
|
KeyCode::Char('s')
|
||||||
if key.modifiers == KeyModifiers::HYPER
|
if key.modifiers == KeyModifiers::HYPER
|
||||||
|| key.modifiers == KeyModifiers::SUPER =>
|
|| key.modifiers == KeyModifiers::SUPER =>
|
||||||
@ -467,47 +477,6 @@ impl<'ws> Workspace<'ws> {
|
|||||||
Ok(())
|
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> {
|
fn load_book(path: &PathBuf, locale: &str, tz: &str) -> Result<Book, anyhow::Error> {
|
||||||
|
@ -2,7 +2,7 @@ use ratatui::{
|
|||||||
self,
|
self,
|
||||||
layout::Rect,
|
layout::Rect,
|
||||||
text::{Line, Text},
|
text::{Line, Text},
|
||||||
widgets::{Block, Widget},
|
widgets::{Block, Tabs, Widget},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
use tui_popup::Popup;
|
use tui_popup::Popup;
|
||||||
@ -15,6 +15,61 @@ pub use viewport::Viewport;
|
|||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod 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> {
|
impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
|
||||||
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
|
fn render(self, area: Rect, buf: &mut ratatui::prelude::Buffer)
|
||||||
where
|
where
|
||||||
|
Loading…
x
Reference in New Issue
Block a user