mirror of
https://github.com/zaphar/sheetsui.git
synced 2025-07-22 21:09:48 -04:00
parent
54d026773a
commit
bfc918e0d2
130
Cargo.lock
generated
130
Cargo.lock
generated
@ -625,6 +625,12 @@ version = "0.3.31"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "futures-timer"
|
||||||
|
version = "3.0.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f288b0a4f20f9a56b5d1da57e2227c661b7b16168e2f72365f57b63326e29b24"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "futures-util"
|
name = "futures-util"
|
||||||
version = "0.3.31"
|
version = "0.3.31"
|
||||||
@ -676,6 +682,12 @@ version = "0.29.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
checksum = "dc46dd3ec48fdd8e693a98d2b8bafae273a2d54c1de02a2a7e3d57d501f39677"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "glob"
|
||||||
|
version = "0.3.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@ -731,6 +743,16 @@ dependencies = [
|
|||||||
"cc",
|
"cc",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "707907fe3c25f5424cce2cb7e1cbcafee6bdbe735ca90ef77c29e84591e5b9da"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "indoc"
|
name = "indoc"
|
||||||
version = "2.0.5"
|
version = "2.0.5"
|
||||||
@ -1071,6 +1093,15 @@ dependencies = [
|
|||||||
"zerocopy",
|
"zerocopy",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro-crate"
|
||||||
|
version = "3.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b"
|
||||||
|
dependencies = [
|
||||||
|
"toml_edit",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "proc-macro2"
|
name = "proc-macro2"
|
||||||
version = "1.0.89"
|
version = "1.0.89"
|
||||||
@ -1140,6 +1171,15 @@ dependencies = [
|
|||||||
"unicode-width 0.2.0",
|
"unicode-width 0.2.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ratatui-macros"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "6fef540f80dbe8a0773266fa6077788ceb65ef624cdbf36e131aaf90b4a52df4"
|
||||||
|
dependencies = [
|
||||||
|
"ratatui",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "redox_syscall"
|
name = "redox_syscall"
|
||||||
version = "0.5.7"
|
version = "0.5.7"
|
||||||
@ -1200,18 +1240,63 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "relative-path"
|
||||||
|
version = "1.9.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "roxmltree"
|
name = "roxmltree"
|
||||||
version = "0.19.0"
|
version = "0.19.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0a2c585be59b6b5dd66a9d2084aa1d8bd52fbdb806eafdeffb52791147862035"
|
||||||
|
dependencies = [
|
||||||
|
"futures",
|
||||||
|
"futures-timer",
|
||||||
|
"rstest_macros",
|
||||||
|
"rustc_version",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rstest_macros"
|
||||||
|
version = "0.23.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "825ea780781b15345a146be27eaefb05085e337e869bff01b4306a4fd4a9ad5a"
|
||||||
|
dependencies = [
|
||||||
|
"cfg-if",
|
||||||
|
"glob",
|
||||||
|
"proc-macro-crate",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex 1.11.1",
|
||||||
|
"relative-path",
|
||||||
|
"rustc_version",
|
||||||
|
"syn",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustc-demangle"
|
name = "rustc-demangle"
|
||||||
version = "0.1.24"
|
version = "0.1.24"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustix"
|
name = "rustix"
|
||||||
version = "0.38.37"
|
version = "0.38.37"
|
||||||
@ -1243,6 +1328,12 @@ version = "1.2.0"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.23"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde"
|
name = "serde"
|
||||||
version = "1.0.214"
|
version = "1.0.214"
|
||||||
@ -1309,6 +1400,7 @@ dependencies = [
|
|||||||
"ironcalc",
|
"ironcalc",
|
||||||
"ratatui",
|
"ratatui",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
|
"tui-prompts",
|
||||||
"tui-textarea",
|
"tui-textarea",
|
||||||
]
|
]
|
||||||
|
|
||||||
@ -1468,6 +1560,35 @@ version = "0.1.2"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_datetime"
|
||||||
|
version = "0.6.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "toml_edit"
|
||||||
|
version = "0.22.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5"
|
||||||
|
dependencies = [
|
||||||
|
"indexmap",
|
||||||
|
"toml_datetime",
|
||||||
|
"winnow",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tui-prompts"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "eb6e0d8a972545cc209b933a1c06dab8932674b54ae19947834ec854fec2364f"
|
||||||
|
dependencies = [
|
||||||
|
"itertools 0.13.0",
|
||||||
|
"ratatui",
|
||||||
|
"ratatui-macros",
|
||||||
|
"rstest",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tui-textarea"
|
name = "tui-textarea"
|
||||||
version = "0.7.0"
|
version = "0.7.0"
|
||||||
@ -1718,6 +1839,15 @@ version = "0.52.6"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "winnow"
|
||||||
|
version = "0.6.20"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "36c1fec1a2bb5866f07c25f68c26e565c4c200aebb96d7e55710c19d3e8ac49b"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "zerocopy"
|
name = "zerocopy"
|
||||||
version = "0.7.35"
|
version = "0.7.35"
|
||||||
|
@ -15,3 +15,4 @@ futures = "0.3.31"
|
|||||||
ratatui = "0.29.0"
|
ratatui = "0.29.0"
|
||||||
thiserror = "1.0.65"
|
thiserror = "1.0.65"
|
||||||
tui-textarea = "0.7.0"
|
tui-textarea = "0.7.0"
|
||||||
|
tui-prompts = "0.5.0"
|
||||||
|
200
src/ui/mod.rs
200
src/ui/mod.rs
@ -14,6 +14,7 @@ use ratatui::{
|
|||||||
widgets::{Block, Cell, Paragraph, Row, Table, TableState, Widget},
|
widgets::{Block, Cell, Paragraph, Row, Table, TableState, Widget},
|
||||||
Frame,
|
Frame,
|
||||||
};
|
};
|
||||||
|
use tui_prompts::{State, Status, TextPrompt, TextState};
|
||||||
use tui_textarea::{CursorMove, TextArea};
|
use tui_textarea::{CursorMove, TextArea};
|
||||||
|
|
||||||
#[derive(Default, Debug, PartialEq)]
|
#[derive(Default, Debug, PartialEq)]
|
||||||
@ -21,13 +22,15 @@ pub enum Modality {
|
|||||||
#[default]
|
#[default]
|
||||||
Navigate,
|
Navigate,
|
||||||
CellEdit,
|
CellEdit,
|
||||||
|
Command,
|
||||||
// TODO(zaphar): Command Mode?
|
// TODO(zaphar): Command Mode?
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct AppState {
|
pub struct AppState<'ws> {
|
||||||
pub modality: Modality,
|
pub modality: Modality,
|
||||||
pub table_state: TableState,
|
pub table_state: TableState,
|
||||||
|
pub command_state: TextState<'ws>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(jwall): This should probably move to a different module.
|
// TODO(jwall): This should probably move to a different module.
|
||||||
@ -56,7 +59,7 @@ impl Default for Address {
|
|||||||
pub struct Workspace<'ws> {
|
pub struct Workspace<'ws> {
|
||||||
name: PathBuf,
|
name: PathBuf,
|
||||||
book: Book,
|
book: Book,
|
||||||
state: AppState,
|
state: AppState<'ws>,
|
||||||
text_area: TextArea<'ws>,
|
text_area: TextArea<'ws>,
|
||||||
dirty: bool,
|
dirty: bool,
|
||||||
show_help: bool,
|
show_help: bool,
|
||||||
@ -128,6 +131,7 @@ impl<'ws> Workspace<'ws> {
|
|||||||
let result = match self.state.modality {
|
let result = match self.state.modality {
|
||||||
Modality::Navigate => self.handle_navigation_input(key)?,
|
Modality::Navigate => self.handle_navigation_input(key)?,
|
||||||
Modality::CellEdit => self.handle_edit_input(key)?,
|
Modality::CellEdit => self.handle_edit_input(key)?,
|
||||||
|
Modality::Command => self.handle_command_input(key)?,
|
||||||
};
|
};
|
||||||
return Ok(result);
|
return Ok(result);
|
||||||
}
|
}
|
||||||
@ -151,18 +155,34 @@ impl<'ws> Workspace<'ws> {
|
|||||||
"* ESC: Exit edit mode".into(),
|
"* ESC: Exit edit mode".into(),
|
||||||
"Otherwise edit as normal".into(),
|
"Otherwise edit as normal".into(),
|
||||||
]),
|
]),
|
||||||
|
Modality::Command => Text::from(vec![
|
||||||
|
"Command Mode:".into(),
|
||||||
|
"* ESC: Exit command mode".into(),
|
||||||
|
]),
|
||||||
})
|
})
|
||||||
.block(info_block)
|
.block(info_block)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_command_input(&mut self, key: event::KeyEvent) -> Result<Option<ExitCode>> {
|
||||||
|
if key.kind == KeyEventKind::Press {
|
||||||
|
match key.code {
|
||||||
|
KeyCode::Esc | KeyCode::Enter => self.exit_command_mode()?,
|
||||||
|
_ => {
|
||||||
|
// NOOP
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.state.command_state.handle_key_event(key);
|
||||||
|
Ok(None)
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_edit_input(&mut self, key: event::KeyEvent) -> Result<Option<ExitCode>> {
|
fn handle_edit_input(&mut self, key: event::KeyEvent) -> Result<Option<ExitCode>> {
|
||||||
if key.kind == KeyEventKind::Press {
|
if key.kind == KeyEventKind::Press {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.show_help = !self.show_help;
|
self.show_help = !self.show_help;
|
||||||
}
|
}
|
||||||
KeyCode::Esc => self.exit_edit_mode()?,
|
KeyCode::Esc | KeyCode::Enter => self.exit_edit_mode()?,
|
||||||
KeyCode::Enter => self.exit_edit_mode()?,
|
|
||||||
_ => {
|
_ => {
|
||||||
// NOOP
|
// NOOP
|
||||||
}
|
}
|
||||||
@ -178,29 +198,21 @@ impl<'ws> Workspace<'ws> {
|
|||||||
Ok(None)
|
Ok(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn exit_edit_mode(&mut self) -> Result<(), anyhow::Error> {
|
fn handle_command(&mut self, cmd: String) -> Result<bool> {
|
||||||
self.state.modality = Modality::Navigate;
|
if cmd.is_empty() {
|
||||||
self.text_area.set_cursor_line_style(Style::default());
|
return Ok(true);
|
||||||
self.text_area.set_cursor_style(Style::default());
|
|
||||||
let contents = self.text_area.lines().join("\n");
|
|
||||||
if self.dirty {
|
|
||||||
self.book.edit_current_cell(contents)?;
|
|
||||||
self.book.evaluate();
|
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn handle_navigation_input(&mut self, key: event::KeyEvent) -> Result<Option<ExitCode>> {
|
fn handle_navigation_input(&mut self, key: event::KeyEvent) -> Result<Option<ExitCode>> {
|
||||||
if key.kind == KeyEventKind::Press {
|
if key.kind == KeyEventKind::Press {
|
||||||
match key.code {
|
match key.code {
|
||||||
KeyCode::Char('e') => {
|
KeyCode::Char('e') => {
|
||||||
self.state.modality = Modality::CellEdit;
|
self.enter_edit_mode();
|
||||||
self.text_area
|
}
|
||||||
.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
KeyCode::Char(':') => {
|
||||||
self.text_area
|
self.enter_command_mode();
|
||||||
.set_cursor_style(Style::default().add_modifier(Modifier::SLOW_BLINK));
|
|
||||||
self.text_area.move_cursor(CursorMove::Bottom);
|
|
||||||
self.text_area.move_cursor(CursorMove::End);
|
|
||||||
}
|
}
|
||||||
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
self.show_help = !self.show_help;
|
self.show_help = !self.show_help;
|
||||||
@ -210,7 +222,13 @@ impl<'ws> Workspace<'ws> {
|
|||||||
}
|
}
|
||||||
KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('r') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
let (row_count, _) = self.book.get_size()?;
|
let (row_count, _) = self.book.get_size()?;
|
||||||
self.book.update_entry(&Address {row: row_count+1, col: 1 }, "")?;
|
self.book.update_entry(
|
||||||
|
&Address {
|
||||||
|
row: row_count + 1,
|
||||||
|
col: 1,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
)?;
|
||||||
let (row, _) = self.book.get_size()?;
|
let (row, _) = self.book.get_size()?;
|
||||||
let mut loc = self.book.location.clone();
|
let mut loc = self.book.location.clone();
|
||||||
if loc.row < row as usize {
|
if loc.row < row as usize {
|
||||||
@ -221,7 +239,13 @@ impl<'ws> Workspace<'ws> {
|
|||||||
}
|
}
|
||||||
KeyCode::Char('t') if key.modifiers == KeyModifiers::CONTROL => {
|
KeyCode::Char('t') if key.modifiers == KeyModifiers::CONTROL => {
|
||||||
let (_, col_count) = self.book.get_size()?;
|
let (_, col_count) = self.book.get_size()?;
|
||||||
self.book.update_entry(&Address {row: 1, col: col_count+1 }, "")?;
|
self.book.update_entry(
|
||||||
|
&Address {
|
||||||
|
row: 1,
|
||||||
|
col: col_count + 1,
|
||||||
|
},
|
||||||
|
"",
|
||||||
|
)?;
|
||||||
}
|
}
|
||||||
KeyCode::Char('q') => {
|
KeyCode::Char('q') => {
|
||||||
return Ok(Some(ExitCode::SUCCESS));
|
return Ok(Some(ExitCode::SUCCESS));
|
||||||
@ -255,6 +279,49 @@ impl<'ws> Workspace<'ws> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn enter_navigation_mode(&mut self) {
|
||||||
|
self.state.modality = Modality::Navigate;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_command_mode(&mut self) {
|
||||||
|
self.state.modality = Modality::Command;
|
||||||
|
self.state.command_state.truncate();
|
||||||
|
*self.state.command_state.status_mut() = Status::Pending;
|
||||||
|
self.state.command_state.focus();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_edit_mode(&mut self) {
|
||||||
|
self.state.modality = Modality::CellEdit;
|
||||||
|
self.text_area
|
||||||
|
.set_cursor_line_style(Style::default().add_modifier(Modifier::UNDERLINED));
|
||||||
|
self.text_area
|
||||||
|
.set_cursor_style(Style::default().add_modifier(Modifier::SLOW_BLINK));
|
||||||
|
self.text_area.move_cursor(CursorMove::Bottom);
|
||||||
|
self.text_area.move_cursor(CursorMove::End);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_command_mode(&mut self) -> Result<()> {
|
||||||
|
let cmd = self.state.command_state.value().to_owned();
|
||||||
|
self.state.command_state.blur();
|
||||||
|
*self.state.command_state.status_mut() = Status::Done;
|
||||||
|
self.handle_command(cmd)?;
|
||||||
|
self.enter_navigation_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn exit_edit_mode(&mut self) -> Result<()> {
|
||||||
|
self.text_area.set_cursor_line_style(Style::default());
|
||||||
|
self.text_area.set_cursor_style(Style::default());
|
||||||
|
let contents = self.text_area.lines().join("\n");
|
||||||
|
if self.dirty {
|
||||||
|
self.book.edit_current_cell(contents)?;
|
||||||
|
self.book.evaluate();
|
||||||
|
self.dirty = false;
|
||||||
|
}
|
||||||
|
self.enter_navigation_mode();
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_movement_change(&mut self) {
|
fn handle_movement_change(&mut self) {
|
||||||
let contents = self
|
let contents = self
|
||||||
.book
|
.book
|
||||||
@ -264,7 +331,8 @@ impl<'ws> Workspace<'ws> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn save_file(&self) -> Result<()> {
|
fn save_file(&self) -> Result<()> {
|
||||||
self.book.save_to_xlsx(&self.name.to_string_lossy().to_string())?;
|
self.book
|
||||||
|
.save_to_xlsx(&self.name.to_string_lossy().to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -282,6 +350,7 @@ impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
|
|||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
|
use ratatui::widgets::StatefulWidget;
|
||||||
let outer_block = Block::bordered()
|
let outer_block = Block::bordered()
|
||||||
.title(Line::from(
|
.title(Line::from(
|
||||||
self.name
|
self.name
|
||||||
@ -292,6 +361,7 @@ impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
|
|||||||
.title_bottom(match &self.state.modality {
|
.title_bottom(match &self.state.modality {
|
||||||
Modality::Navigate => "navigate",
|
Modality::Navigate => "navigate",
|
||||||
Modality::CellEdit => "edit",
|
Modality::CellEdit => "edit",
|
||||||
|
Modality::Command => "command",
|
||||||
})
|
})
|
||||||
.title_bottom(
|
.title_bottom(
|
||||||
Line::from(format!(
|
Line::from(format!(
|
||||||
@ -300,33 +370,45 @@ impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
|
|||||||
))
|
))
|
||||||
.right_aligned(),
|
.right_aligned(),
|
||||||
);
|
);
|
||||||
let [edit_rect, table_rect] = if self.show_help {
|
let [edit_rect, table_rect] = if self.show_help || self.state.modality == Modality::Command
|
||||||
|
{
|
||||||
let [edit_rect, table_rect, info_rect] = Layout::vertical(&[
|
let [edit_rect, table_rect, info_rect] = Layout::vertical(&[
|
||||||
Constraint::Fill(4),
|
Constraint::Fill(4),
|
||||||
Constraint::Fill(30),
|
Constraint::Fill(30),
|
||||||
Constraint::Fill(9),
|
if self.state.modality == Modality::Command {
|
||||||
|
Constraint::Max(1)
|
||||||
|
} else {
|
||||||
|
Constraint::Fill(9)
|
||||||
|
},
|
||||||
])
|
])
|
||||||
.vertical_margin(2)
|
.vertical_margin(2)
|
||||||
.horizontal_margin(2)
|
.horizontal_margin(2)
|
||||||
.flex(Flex::Legacy)
|
.flex(Flex::Legacy)
|
||||||
.areas(area.clone());
|
.areas(area.clone());
|
||||||
|
|
||||||
// Help panel widget display
|
// Help panel widget display
|
||||||
let info_para = self.render_help_text();
|
if self.state.modality == Modality::Command {
|
||||||
info_para.render(info_rect, buf);
|
StatefulWidget::render(
|
||||||
|
TextPrompt::from("Command"),
|
||||||
|
info_rect,
|
||||||
|
buf,
|
||||||
|
&mut self.state.command_state,
|
||||||
|
);
|
||||||
|
} else if self.show_help {
|
||||||
|
let info_para = self.render_help_text();
|
||||||
|
info_para.render(info_rect, buf);
|
||||||
|
}
|
||||||
[edit_rect, table_rect]
|
[edit_rect, table_rect]
|
||||||
} else {
|
} else {
|
||||||
let [edit_rect, table_rect] = Layout::vertical(&[
|
let [edit_rect, table_rect] =
|
||||||
Constraint::Fill(4),
|
Layout::vertical(&[Constraint::Fill(4), Constraint::Fill(30)])
|
||||||
Constraint::Fill(30),
|
.vertical_margin(2)
|
||||||
])
|
.horizontal_margin(2)
|
||||||
.vertical_margin(2)
|
.flex(Flex::Legacy)
|
||||||
.horizontal_margin(2)
|
.areas(area.clone());
|
||||||
.flex(Flex::Legacy)
|
|
||||||
.areas(area.clone());
|
|
||||||
[edit_rect, table_rect]
|
[edit_rect, table_rect]
|
||||||
};
|
};
|
||||||
|
|
||||||
outer_block.render(area, buf);
|
outer_block.render(area, buf);
|
||||||
|
|
||||||
// Input widget display
|
// Input widget display
|
||||||
@ -341,9 +423,7 @@ impl<'widget, 'ws: 'widget> Widget for &'widget mut Workspace<'ws> {
|
|||||||
// TODO(zaphar): Apparently scrolling by columns doesn't work?
|
// TODO(zaphar): Apparently scrolling by columns doesn't work?
|
||||||
self.state.table_state.select_cell(Some((row, col)));
|
self.state.table_state.select_cell(Some((row, col)));
|
||||||
self.state.table_state.select_column(Some(col));
|
self.state.table_state.select_column(Some(col));
|
||||||
use ratatui::widgets::StatefulWidget;
|
|
||||||
StatefulWidget::render(table, table_rect, buf, &mut self.state.table_state);
|
StatefulWidget::render(table, table_rect, buf, &mut self.state.table_state);
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,28 +441,26 @@ impl<'t, 'book: 't> TryFrom<&'book Book> for Table<'t> {
|
|||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|ri| {
|
.map(|ri| {
|
||||||
let mut cells = vec![Cell::new(Text::from(ri.to_string()))];
|
let mut cells = vec![Cell::new(Text::from(ri.to_string()))];
|
||||||
cells.extend((1..=col_count)
|
cells.extend((1..=col_count).into_iter().map(|ci| {
|
||||||
.into_iter()
|
// TODO(zaphar): Is this safe?
|
||||||
.map(|ci| {
|
let content = value.get_cell_addr_rendered(ri, ci).unwrap();
|
||||||
// TODO(zaphar): Is this safe?
|
let cell = Cell::new(Text::raw(content));
|
||||||
let content = value.get_cell_addr_rendered(ri, ci).unwrap();
|
match (value.location.row == ri, value.location.col == ci) {
|
||||||
let cell = Cell::new(Text::raw(content));
|
(true, true) => cell.fg(Color::White).underlined(),
|
||||||
match (value.location.row == ri, value.location.col == ci) {
|
_ => cell
|
||||||
(true, true) => cell.fg(Color::White).underlined(),
|
.bg(if ri % 2 == 0 {
|
||||||
_ => cell
|
Color::Rgb(57, 61, 71)
|
||||||
.bg(if ri % 2 == 0 {
|
} else {
|
||||||
Color::Rgb(57, 61, 71)
|
Color::Rgb(165, 169, 160)
|
||||||
} else {
|
})
|
||||||
Color::Rgb(165, 169, 160)
|
.fg(if ri % 2 == 0 {
|
||||||
})
|
Color::White
|
||||||
.fg(if ri % 2 == 0 {
|
} else {
|
||||||
Color::White
|
Color::Rgb(31, 32, 34)
|
||||||
} else {
|
}),
|
||||||
Color::Rgb(31, 32, 34)
|
}
|
||||||
}),
|
.bold()
|
||||||
}
|
}));
|
||||||
.bold()
|
|
||||||
}));
|
|
||||||
Row::new(cells)
|
Row::new(cells)
|
||||||
})
|
})
|
||||||
.collect();
|
.collect();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user