wip: ui: enter and tab key navigate

This commit is contained in:
Jeremy Wall 2024-11-30 18:45:12 -05:00
parent f196879fb0
commit 4ce7872be0
2 changed files with 111 additions and 8 deletions

View File

@ -3,8 +3,9 @@ use std::{path::PathBuf, process::ExitCode};
use crate::book::Book;
use anyhow::Result;
use anyhow::{anyhow, Result};
use crossterm::event::{self, Event, KeyCode, KeyEventKind, KeyModifiers};
use ironcalc::base::Model;
use ratatui::{
buffer::Buffer,
layout::{Constraint, Flex, Layout},
@ -87,7 +88,7 @@ impl Default for Address {
pub struct Workspace<'ws> {
name: PathBuf,
book: Book,
state: AppState<'ws>,
pub(crate) state: AppState<'ws>,
text_area: TextArea<'ws>,
}
@ -104,6 +105,13 @@ impl<'ws> Workspace<'ws> {
ws
}
pub fn new_empty(locale: &str, tz: &str) -> Result<Self> {
Ok(Self::new(
Book::new(Model::new_empty("", locale, tz).map_err(|e| anyhow!("{}", e))?),
PathBuf::default(),
))
}
/// Loads a workspace from a path.
pub fn load(path: &PathBuf, locale: &str, tz: &str) -> Result<Self> {
let book = load_book(path, locale, tz)?;
@ -178,23 +186,28 @@ impl<'ws> Workspace<'ws> {
match self.state.modality() {
Modality::Navigate => vec![
"Navigate Mode:".to_string(),
"* e: Enter edit mode for current cell".to_string(),
"* e,i: Enter edit mode for current cell".to_string(),
"* ENTER/RETURN: Go down one cell".to_string(),
"* TAB: Go over one cell".to_string(),
"* h,j,k,l: vim style navigation".to_string(),
"* CTRl-r: Add a row".to_string(),
"* CTRl-c: Add a column".to_string(),
"* CTRl-l: Grow column width by 1".to_string(),
"* CTRl-h: Shrink column width by 1".to_string(),
"* CTRl-n: Next sheet. Starts over at beginning if at end.".to_string(),
"* CTRl-p: Previous sheet. Starts over at end if at beginning.".to_string(),
"* q exit".to_string(),
"* Ctrl-S Save sheet".to_string(),
],
Modality::CellEdit => vec![
"Edit Mode:".to_string(),
"* ESC: Exit edit mode".to_string(),
"* ESC, ENTER/RETURN: Exit edit mode".to_string(),
"Otherwise edit as normal".to_string(),
],
Modality::Command => vec![
"Command Mode:".to_string(),
"* ESC: Exit command mode".to_string(),
"* ENTER/RETURN: run command and exit command mode".to_string(),
],
_ => vec!["General help".to_string()],
}
@ -284,7 +297,8 @@ impl<'ws> Workspace<'ws> {
self.book.set_sheet_name(idx, name)?;
}
_ => {
self.book.set_sheet_name(self.book.current_sheet as usize, name)?;
self.book
.set_sheet_name(self.book.current_sheet as usize, name)?;
}
}
Ok(true)
@ -381,10 +395,24 @@ impl<'ws> Workspace<'ws> {
KeyCode::Char('q') => {
return Ok(Some(ExitCode::SUCCESS));
}
KeyCode::Char('j') | KeyCode::Down if key.modifiers != KeyModifiers::CONTROL => {
KeyCode::Char('j') | KeyCode::Down
if key.modifiers != KeyModifiers::CONTROL =>
{
self.move_down()?;
self.handle_movement_change();
}
KeyCode::Enter
if key.modifiers != KeyModifiers::SHIFT =>
{
self.move_down()?;
self.handle_movement_change();
}
KeyCode::Enter
if key.modifiers == KeyModifiers::SHIFT =>
{
self.move_up()?;
self.handle_movement_change();
}
KeyCode::Char('k') | KeyCode::Up if key.modifiers != KeyModifiers::CONTROL => {
self.move_up()?;
self.handle_movement_change();
@ -393,10 +421,24 @@ impl<'ws> Workspace<'ws> {
self.move_left()?;
self.handle_movement_change();
}
KeyCode::Char('l') | KeyCode::Right if key.modifiers != KeyModifiers::CONTROL => {
KeyCode::Char('l') | KeyCode::Right
if key.modifiers != KeyModifiers::CONTROL =>
{
self.move_right()?;
self.handle_movement_change();
}
KeyCode::Tab
if key.modifiers != KeyModifiers::SHIFT =>
{
self.move_right()?;
self.handle_movement_change();
}
KeyCode::Tab
if key.modifiers == KeyModifiers::SHIFT =>
{
self.move_left()?;
self.handle_movement_change();
}
_ => {
// noop
}
@ -476,7 +518,6 @@ impl<'ws> Workspace<'ws> {
self.book.save_to_xlsx(path.into().as_str())?;
Ok(())
}
}
fn load_book(path: &PathBuf, locale: &str, tz: &str) -> Result<Book, anyhow::Error> {

View File

@ -1,4 +1,9 @@
use crossterm::event::{Event, KeyCode, KeyEvent, KeyModifiers};
use crate::ui::Modality;
use super::cmd::{parse, Cmd};
use super::Workspace;
#[test]
fn test_write_cmd() {
@ -187,4 +192,61 @@ fn test_cmd_rename_sheet_with_idx_and_name() {
assert_eq!(cmd, Cmd::RenameSheet(Some(0), "test"));
}
fn construct_key_event(code: KeyCode) -> Event {
construct_modified_key_event(code, KeyModifiers::empty())
}
fn construct_modified_key_event(code: KeyCode, mods: KeyModifiers) -> Event {
Event::Key(KeyEvent::new(code, mods))
}
// TODO(zaphar): Interaction testing for input.
#[test]
fn test_input_navitation_enter_key() {
let mut ws =
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook");
let row = ws.book.location.row;
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.handle_input(construct_key_event(KeyCode::Enter))
.expect("Failed to handle enter key");
assert_eq!(row + 1, ws.book.location.row);
}
#[test]
fn test_input_navitation_tab_key() {
let mut ws =
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook");
let col = dbg!(ws.book.location.col);
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.handle_input(construct_key_event(KeyCode::Tab))
.expect("Failed to handle enter key");
assert_eq!(col + 1, ws.book.location.col);
}
#[test]
fn test_input_navitation_shift_enter_key() {
let mut ws =
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook");
let row = ws.book.location.row;
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.handle_input(construct_key_event(KeyCode::Enter))
.expect("Failed to handle enter key");
assert_eq!(row + 1, ws.book.location.row);
ws.handle_input(construct_modified_key_event(KeyCode::Enter, KeyModifiers::SHIFT))
.expect("Failed to handle enter key");
assert_eq!(row, ws.book.location.row);
}
#[test]
fn test_input_navitation_shift_tab_key() {
let mut ws =
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook");
let col = dbg!(ws.book.location.col);
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.handle_input(construct_key_event(KeyCode::Tab))
.expect("Failed to handle enter key");
assert_eq!(col + 1, ws.book.location.col);
ws.handle_input(construct_modified_key_event(KeyCode::Tab, KeyModifiers::SHIFT))
.expect("Failed to handle enter key");
assert_eq!(col, ws.book.location.col);
}