wip: locations should include the sheet

This commit is contained in:
Jeremy Wall 2025-02-26 21:21:17 -05:00
parent b5e0362a4e
commit 5012649210
7 changed files with 102 additions and 105 deletions

View File

@ -37,7 +37,7 @@ impl<'book> AddressRange<'book> {
for ri in row_range.iter() { for ri in row_range.iter() {
let mut row = Vec::with_capacity(col_range.len()); let mut row = Vec::with_capacity(col_range.len());
for ci in col_range.iter() { for ci in col_range.iter() {
row.push(Address { row: *ri, col: *ci }); row.push(Address { sheet: self.start.sheet, row: *ri, col: *ci });
} }
rows.push(row); rows.push(row);
} }
@ -49,7 +49,7 @@ impl<'book> AddressRange<'book> {
let mut rows = Vec::with_capacity(row_range.len() * col_range.len()); let mut rows = Vec::with_capacity(row_range.len() * col_range.len());
for ri in row_range.iter() { for ri in row_range.iter() {
for ci in col_range.iter() { for ci in col_range.iter() {
rows.push(Address { row: *ri, col: *ci }); rows.push(Address { sheet: self.start.sheet, row: *ri, col: *ci });
} }
} }
rows rows
@ -85,7 +85,6 @@ impl<'book> AddressRange<'book> {
/// A spreadsheet book with some internal state tracking. /// A spreadsheet book with some internal state tracking.
pub struct Book { pub struct Book {
pub(crate) model: UserModel, pub(crate) model: UserModel,
pub current_sheet: u32,
pub location: crate::ui::Address, pub location: crate::ui::Address,
} }
@ -94,7 +93,6 @@ impl Book {
pub fn new(model: UserModel) -> Self { pub fn new(model: UserModel) -> Self {
Self { Self {
model, model,
current_sheet: 0,
location: Address::default(), location: Address::default(),
} }
} }
@ -162,7 +160,7 @@ impl Book {
self.set_sheet_name(idx, name)?; self.set_sheet_name(idx, name)?;
} }
self.model self.model
.set_selected_sheet(self.current_sheet) .set_selected_sheet(self.location.sheet)
.map_err(|e| anyhow!(e))?; .map_err(|e| anyhow!(e))?;
Ok(()) Ok(())
} }
@ -173,7 +171,7 @@ impl Book {
} }
/// Move to a specific sheet location in the current sheet /// Move to a specific sheet location in the current sheet
pub fn move_to(&mut self, Address { row, col }: &Address) -> Result<()> { pub fn move_to(&mut self, Address { sheet: _, row, col }: &Address) -> Result<()> {
// FIXME(zaphar): Check that this is safe first. // FIXME(zaphar): Check that this is safe first.
self.location.row = *row; self.location.row = *row;
self.location.col = *col; self.location.col = *col;
@ -194,7 +192,7 @@ impl Book {
.model .model
.get_model() .get_model()
.extend_to( .extend_to(
self.current_sheet, self.location.sheet,
from.row as i32, from.row as i32,
from.col as i32, from.col as i32,
cell.row as i32, cell.row as i32,
@ -203,7 +201,7 @@ impl Book {
.map_err(|e| anyhow!(e))?; .map_err(|e| anyhow!(e))?;
self.model self.model
.set_user_input( .set_user_input(
self.current_sheet, self.location.sheet,
cell.row as i32, cell.row as i32,
cell.col as i32, cell.col as i32,
&contents, &contents,
@ -215,14 +213,14 @@ impl Book {
} }
pub fn clear_current_cell(&mut self) -> Result<()> { pub fn clear_current_cell(&mut self) -> Result<()> {
self.clear_cell_contents(self.current_sheet as u32, self.location.clone()) self.clear_cell_contents(self.location.clone())
} }
pub fn clear_current_cell_all(&mut self) -> Result<()> { pub fn clear_current_cell_all(&mut self) -> Result<()> {
self.clear_cell_all(self.current_sheet as u32, self.location.clone()) self.clear_cell_all(self.location.clone())
} }
pub fn clear_cell_contents(&mut self, sheet: u32, Address { row, col }: Address) -> Result<()> { pub fn clear_cell_contents(&mut self, Address { sheet, row, col }: Address) -> Result<()> {
Ok(self Ok(self
.model .model
.range_clear_contents(&Area { .range_clear_contents(&Area {
@ -235,15 +233,15 @@ impl Book {
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?) .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?)
} }
pub fn clear_cell_range(&mut self, sheet: u32, start: Address, end: Address) -> Result<()> { pub fn clear_cell_range(&mut self, start: Address, end: Address) -> Result<()> {
let area = calculate_area(sheet, &start, &end); let area = calculate_area(start.sheet, &start, &end);
self.model self.model
.range_clear_contents(&area) .range_clear_contents(&area)
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?; .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?;
Ok(()) Ok(())
} }
pub fn clear_cell_all(&mut self, sheet: u32, Address { row, col }: Address) -> Result<()> { pub fn clear_cell_all(&mut self, Address { sheet, row, col }: Address) -> Result<()> {
Ok(self Ok(self
.model .model
.range_clear_all(&Area { .range_clear_all(&Area {
@ -256,8 +254,8 @@ impl Book {
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?) .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?)
} }
pub fn clear_cell_range_all(&mut self, sheet: u32, start: Address, end: Address) -> Result<()> { pub fn clear_cell_range_all(&mut self, start: Address, end: Address) -> Result<()> {
let area = calculate_area(sheet, &start, &end); let area = calculate_area(start.sheet, &start, &end);
self.model self.model
.range_clear_all(&area) .range_clear_all(&area)
.map_err(|s| anyhow!("Unable to clear cell contents {}", s))?; .map_err(|s| anyhow!("Unable to clear cell contents {}", s))?;
@ -269,13 +267,13 @@ impl Book {
Ok(self.get_cell_addr_rendered(&self.location)?) Ok(self.get_cell_addr_rendered(&self.location)?)
} }
pub fn get_cell_style(&self, sheet: u32, cell: &Address) -> Option<Style> { pub fn get_cell_style(&self, cell: &Address) -> Option<Style> {
// TODO(jwall): This is modeled a little weird. We should probably record // TODO(jwall): This is modeled a little weird. We should probably record
// the error *somewhere* but for the user there is nothing to be done except // the error *somewhere* but for the user there is nothing to be done except
// not use a style. // not use a style.
match self match self
.model .model
.get_cell_style(sheet, cell.row as i32, cell.col as i32) .get_cell_style(cell.sheet, cell.row as i32, cell.col as i32)
{ {
Err(_) => None, Err(_) => None,
Ok(s) => Some(s), Ok(s) => Some(s),
@ -383,18 +381,18 @@ impl Book {
} }
/// Get a cells rendered content for display. /// Get a cells rendered content for display.
pub fn get_cell_addr_rendered(&self, Address { row, col }: &Address) -> Result<String> { pub fn get_cell_addr_rendered(&self, Address { sheet, row, col }: &Address) -> Result<String> {
Ok(self Ok(self
.model .model
.get_formatted_cell_value(self.current_sheet, *row as i32, *col as i32) .get_formatted_cell_value(*sheet, *row as i32, *col as i32)
.map_err(|s| anyhow!("Unable to format cell {}", s))?) .map_err(|s| anyhow!("Unable to format cell {}", s))?)
} }
/// Get a cells actual content unformatted as a string. /// Get a cells actual content unformatted as a string.
pub fn get_cell_addr_contents(&self, Address { row, col }: &Address) -> Result<String> { pub fn get_cell_addr_contents(&self, Address { sheet, row, col }: &Address) -> Result<String> {
Ok(self Ok(self
.model .model
.get_cell_content(self.current_sheet, *row as i32, *col as i32) .get_cell_content(*sheet, *row as i32, *col as i32)
.map_err(|s| anyhow!("Unable to format cell {}", s))?) .map_err(|s| anyhow!("Unable to format cell {}", s))?)
} }
@ -403,7 +401,7 @@ impl Book {
Ok(self Ok(self
.model .model
.get_cell_content( .get_cell_content(
self.current_sheet, self.location.sheet,
self.location.row as i32, self.location.row as i32,
self.location.col as i32, self.location.col as i32,
) )
@ -422,7 +420,7 @@ impl Book {
pub fn update_cell<S: AsRef<str>>(&mut self, location: &Address, value: S) -> Result<()> { pub fn update_cell<S: AsRef<str>>(&mut self, location: &Address, value: S) -> Result<()> {
self.model self.model
.set_user_input( .set_user_input(
self.current_sheet, location.sheet,
location.row as i32, location.row as i32,
location.col as i32, location.col as i32,
// TODO(jwall): This could probably be made more efficient // TODO(jwall): This could probably be made more efficient
@ -436,11 +434,12 @@ impl Book {
pub fn insert_rows(&mut self, row_idx: usize, count: usize) -> Result<()> { pub fn insert_rows(&mut self, row_idx: usize, count: usize) -> Result<()> {
for i in 0..count { for i in 0..count {
self.model self.model
.insert_row(self.current_sheet, (row_idx + i) as i32) .insert_row(self.location.sheet, (row_idx + i) as i32)
.map_err(|e| anyhow!("Unable to insert row(s): {}", e))?; .map_err(|e| anyhow!("Unable to insert row(s): {}", e))?;
} }
if self.location.row >= row_idx { if self.location.row >= row_idx {
self.move_to(&Address { self.move_to(&Address {
sheet: self.location.sheet,
row: self.location.row + count, row: self.location.row + count,
col: self.location.col, col: self.location.col,
})?; })?;
@ -452,11 +451,12 @@ impl Book {
pub fn insert_columns(&mut self, col_idx: usize, count: usize) -> Result<()> { pub fn insert_columns(&mut self, col_idx: usize, count: usize) -> Result<()> {
for i in 0..count { for i in 0..count {
self.model self.model
.insert_column(self.current_sheet, (col_idx + i) as i32) .insert_column(self.location.sheet, (col_idx + i) as i32)
.map_err(|e| anyhow!("Unable to insert column(s): {}", e))?; .map_err(|e| anyhow!("Unable to insert column(s): {}", e))?;
} }
if self.location.col >= col_idx { if self.location.col >= col_idx {
self.move_to(&Address { self.move_to(&Address {
sheet: self.location.sheet,
row: self.location.row, row: self.location.row,
col: self.location.col + count, col: self.location.col + count,
})?; })?;
@ -471,7 +471,7 @@ impl Book {
/// Get column size /// Get column size
pub fn get_col_size(&self, idx: usize) -> Result<usize> { pub fn get_col_size(&self, idx: usize) -> Result<usize> {
self.get_column_size_for_sheet(self.current_sheet, idx) self.get_column_size_for_sheet(self.location.sheet, idx)
} }
pub fn get_column_size_for_sheet( pub fn get_column_size_for_sheet(
@ -487,7 +487,7 @@ impl Book {
} }
pub fn set_col_size(&mut self, col: usize, width: usize) -> Result<()> { pub fn set_col_size(&mut self, col: usize, width: usize) -> Result<()> {
self.set_column_size_for_sheet(self.current_sheet, col, width) self.set_column_size_for_sheet(self.location.sheet, col, width)
} }
pub fn set_column_size_for_sheet( pub fn set_column_size_for_sheet(
@ -527,7 +527,7 @@ impl Book {
.enumerate() .enumerate()
.find(|(_idx, sheet)| sheet.name == name) .find(|(_idx, sheet)| sheet.name == name)
{ {
self.current_sheet = idx as u32; self.location.sheet = idx as u32;
return true; return true;
} }
false false
@ -540,27 +540,27 @@ impl Book {
pub fn select_next_sheet(&mut self) { pub fn select_next_sheet(&mut self) {
let len = self.model.get_model().workbook.worksheets.len() as u32; let len = self.model.get_model().workbook.worksheets.len() as u32;
let mut next = self.current_sheet + 1; let mut next = self.location.sheet + 1;
if next == len { if next == len {
next = 0; next = 0;
} }
self.model self.model
.set_selected_sheet(next) .set_selected_sheet(next)
.expect("Unexpected error selecting sheet"); .expect("Unexpected error selecting sheet");
self.current_sheet = next; self.location.sheet = next;
} }
pub fn select_prev_sheet(&mut self) { pub fn select_prev_sheet(&mut self) {
let len = self.model.get_model().workbook.worksheets.len() as u32; let len = self.model.get_model().workbook.worksheets.len() as u32;
let next = if self.current_sheet == 0 { let next = if self.location.sheet == 0 {
len - 1 len - 1
} else { } else {
self.current_sheet - 1 self.location.sheet - 1
}; };
self.model self.model
.set_selected_sheet(next) .set_selected_sheet(next)
.expect("Unexpected error selecting sheet"); .expect("Unexpected error selecting sheet");
self.current_sheet = next; self.location.sheet = next;
} }
/// Select a sheet by id. /// Select a sheet by id.
@ -577,7 +577,7 @@ impl Book {
self.model self.model
.set_selected_sheet(idx as u32) .set_selected_sheet(idx as u32)
.expect("Unexpected error selecting sheet"); .expect("Unexpected error selecting sheet");
self.current_sheet = idx as u32; self.location.sheet = idx as u32;
return true; return true;
} }
false false
@ -592,8 +592,8 @@ impl Book {
.model .model
.get_model() .get_model()
.workbook .workbook
.worksheet(self.current_sheet) .worksheet(self.location.sheet)
.map_err(|s| anyhow!("Invalid Worksheet id: {}: error: {}", self.current_sheet, s))?) .map_err(|s| anyhow!("Invalid Worksheet id: {}: error: {}", self.location.sheet, s))?)
} }
pub(crate) fn get_sheet_name_by_idx(&self, idx: usize) -> Result<&str> { pub(crate) fn get_sheet_name_by_idx(&self, idx: usize) -> Result<&str> {
@ -625,7 +625,7 @@ impl Default for Book {
fn default() -> Self { fn default() -> Self {
let mut book = let mut book =
Book::new(UserModel::new_empty("default_name", "en", "America/New_York").unwrap()); Book::new(UserModel::new_empty("default_name", "en", "America/New_York").unwrap());
book.update_cell(&Address { row: 1, col: 1 }, "").unwrap(); book.update_cell(&Address { sheet: 0, row: 1, col: 1 }, "").unwrap();
book book
} }
} }

View File

@ -36,7 +36,7 @@ fn test_book_default() {
#[test] #[test]
fn test_book_insert_cell_new_row() { fn test_book_insert_cell_new_row() {
let mut book = Book::default(); let mut book = Book::default();
book.update_cell(&Address { row: 2, col: 1 }, "1") book.update_cell(&Address { sheet: 0, row: 2, col: 1 }, "1")
.expect("failed to edit cell"); .expect("failed to edit cell");
book.evaluate(); book.evaluate();
let WorksheetDimension { let WorksheetDimension {
@ -52,7 +52,7 @@ fn test_book_insert_cell_new_row() {
#[test] #[test]
fn test_book_insert_cell_new_column() { fn test_book_insert_cell_new_column() {
let mut book = Book::default(); let mut book = Book::default();
book.update_cell(&Address { row: 1, col: 2 }, "1") book.update_cell(&Address { sheet: 0, row: 1, col: 2 }, "1")
.expect("failed to edit cell"); .expect("failed to edit cell");
let WorksheetDimension { let WorksheetDimension {
min_row, min_row,
@ -67,14 +67,14 @@ fn test_book_insert_cell_new_column() {
#[test] #[test]
fn test_book_insert_rows() { fn test_book_insert_rows() {
let mut book = Book::default(); let mut book = Book::default();
book.update_cell(&Address { row: 2, col: 2 }, "1") book.update_cell(&Address { sheet: 0, row: 2, col: 2 }, "1")
.expect("failed to edit cell"); .expect("failed to edit cell");
book.move_to(&Address { row: 2, col: 2 }) book.move_to(&Address { sheet: 0, row: 2, col: 2 })
.expect("Failed to move to location"); .expect("Failed to move to location");
assert_eq!((2, 2), book.get_size().expect("Failed to get size")); assert_eq!((2, 2), book.get_size().expect("Failed to get size"));
book.insert_rows(1, 5).expect("Failed to insert rows"); book.insert_rows(1, 5).expect("Failed to insert rows");
assert_eq!((7, 2), book.get_size().expect("Failed to get size")); assert_eq!((7, 2), book.get_size().expect("Failed to get size"));
assert_eq!(Address { row: 7, col: 2 }, book.location); assert_eq!(Address { sheet: 0, row: 7, col: 2 }, book.location);
assert_eq!( assert_eq!(
"1", "1",
book.get_current_cell_rendered() book.get_current_cell_rendered()
@ -85,14 +85,14 @@ fn test_book_insert_rows() {
#[test] #[test]
fn test_book_insert_columns() { fn test_book_insert_columns() {
let mut book = Book::default(); let mut book = Book::default();
book.update_cell(&Address { row: 2, col: 2 }, "1") book.update_cell(&Address { sheet: 0, row: 2, col: 2 }, "1")
.expect("failed to edit cell"); .expect("failed to edit cell");
book.move_to(&Address { row: 2, col: 2 }) book.move_to(&Address { sheet: 0, row: 2, col: 2 })
.expect("Failed to move to location"); .expect("Failed to move to location");
assert_eq!((2, 2), book.get_size().expect("Failed to get size")); assert_eq!((2, 2), book.get_size().expect("Failed to get size"));
book.insert_columns(1, 5).expect("Failed to insert rows"); book.insert_columns(1, 5).expect("Failed to insert rows");
assert_eq!((2, 7), book.get_size().expect("Failed to get size")); assert_eq!((2, 7), book.get_size().expect("Failed to get size"));
assert_eq!(Address { row: 2, col: 7 }, book.location); assert_eq!(Address { sheet: 0, row: 2, col: 7 }, book.location);
assert_eq!( assert_eq!(
"1", "1",
book.get_current_cell_rendered() book.get_current_cell_rendered()
@ -103,7 +103,7 @@ fn test_book_insert_columns() {
#[test] #[test]
fn test_book_col_size() { fn test_book_col_size() {
let mut book = Book::default(); let mut book = Book::default();
book.update_cell(&Address { row: 2, col: 2 }, "1") book.update_cell(&Address { sheet: 0, row: 2, col: 2 }, "1")
.expect("failed to edit cell"); .expect("failed to edit cell");
book.set_col_size(1, 20).expect("Failed to set column size"); book.set_col_size(1, 20).expect("Failed to set column size");
assert_eq!(20, book.get_col_size(1).expect("Failed to get column size")); assert_eq!(20, book.get_col_size(1).expect("Failed to get column size"));

View File

@ -45,10 +45,12 @@ impl RangeSelection {
if let (Some(start), Some(end)) = (&self.start, &self.end) { if let (Some(start), Some(end)) = (&self.start, &self.end) {
return Some(( return Some((
Address { Address {
sheet: start.sheet,
row: std::cmp::min(start.row, end.row), row: std::cmp::min(start.row, end.row),
col: std::cmp::min(start.col, end.col), col: std::cmp::min(start.col, end.col),
}, },
Address { Address {
sheet: end.sheet,
row: std::cmp::max(start.row, end.row), row: std::cmp::max(start.row, end.row),
col: std::cmp::max(start.col, end.col), col: std::cmp::max(start.col, end.col),
}, },
@ -133,13 +135,14 @@ impl<'ws> AppState<'ws> {
/// The Address in a Table. /// The Address in a Table.
#[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)] #[derive(Debug, PartialEq, PartialOrd, Ord, Eq, Clone)]
pub struct Address { pub struct Address {
pub sheet: u32,
pub row: usize, pub row: usize,
pub col: usize, pub col: usize,
} }
impl Address { impl Address {
pub fn new(row: usize, col: usize) -> Self { pub fn new(row: usize, col: usize) -> Self {
Self { row, col } Self { sheet: 0, row, col }
} }
pub fn to_range_part(&self) -> String { pub fn to_range_part(&self) -> String {
@ -215,7 +218,7 @@ impl<'ws> Workspace<'ws> {
format!(":{}", end.to_range_part()) format!(":{}", end.to_range_part())
); );
if let Some(range_sheet) = state.range_select.sheet { if let Some(range_sheet) = state.range_select.sheet {
if range_sheet != self.book.current_sheet { if range_sheet != self.book.location.sheet {
return format!( return format!(
"{}!{}", "{}!{}",
self.book self.book
@ -243,6 +246,7 @@ impl<'ws> Workspace<'ws> {
/// Move to the top row without changing columns /// Move to the top row without changing columns
pub fn move_to_top(&mut self) -> Result<()> { pub fn move_to_top(&mut self) -> Result<()> {
self.book.move_to(&Address { self.book.move_to(&Address {
sheet: self.book.location.sheet,
row: 1, row: 1,
col: self.book.location.col, col: self.book.location.col,
})?; })?;
@ -411,7 +415,7 @@ impl<'ws> Workspace<'ws> {
self.book.set_sheet_name(idx as u32, name)?; self.book.set_sheet_name(idx as u32, name)?;
} }
_ => { _ => {
self.book.set_sheet_name(self.book.current_sheet, name)?; self.book.set_sheet_name(self.book.location.sheet, name)?;
} }
} }
Ok(None) Ok(None)
@ -431,7 +435,7 @@ impl<'ws> Workspace<'ws> {
for r in row..(row + row_count) { for r in row..(row + row_count) {
self.book.set_row_style( self.book.set_row_style(
&[("fill.bg_color", &color)], &[("fill.bg_color", &color)],
self.book.current_sheet, self.book.location.sheet,
r, r,
)?; )?;
} }
@ -443,14 +447,14 @@ impl<'ws> Workspace<'ws> {
for c in col..(col + col_count) { for c in col..(col + col_count) {
self.book.set_col_style( self.book.set_col_style(
&[("fill.bg_color", &color)], &[("fill.bg_color", &color)],
self.book.current_sheet, self.book.location.sheet,
c, c,
)?; )?;
} }
Ok(None) Ok(None)
} }
Ok(Some(Cmd::ColorCell(color))) => { Ok(Some(Cmd::ColorCell(color))) => {
let sheet = self.book.current_sheet; let sheet = self.book.location.sheet;
let area = if let Some((start, end)) = self.state.range_select.get_range() { let area = if let Some((start, end)) = self.state.range_select.get_range() {
Area { Area {
sheet, sheet,
@ -510,10 +514,6 @@ impl<'ws> Workspace<'ws> {
KeyCode::Char('D') => { KeyCode::Char('D') => {
if let Some((start, end)) = dbg!(self.state.range_select.get_range()) { if let Some((start, end)) = dbg!(self.state.range_select.get_range()) {
self.book.clear_cell_range_all( self.book.clear_cell_range_all(
self.state
.range_select
.sheet
.unwrap_or_else(|| self.book.current_sheet),
start, start,
end, end,
)?; )?;
@ -522,10 +522,6 @@ impl<'ws> Workspace<'ws> {
KeyCode::Char('d') => { KeyCode::Char('d') => {
if let Some((start, end)) = self.state.range_select.get_range() { if let Some((start, end)) = self.state.range_select.get_range() {
self.book.clear_cell_range( self.book.clear_cell_range(
self.state
.range_select
.sheet
.unwrap_or_else(|| self.book.current_sheet),
start, start,
end, end,
)?; )?;
@ -570,7 +566,7 @@ impl<'ws> Workspace<'ws> {
ws.book.select_next_sheet(); ws.book.select_next_sheet();
Ok(()) Ok(())
})?; })?;
self.state.range_select.sheet = Some(self.book.current_sheet); self.state.range_select.sheet = Some(self.book.location.sheet);
} }
KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => { KeyCode::Char('p') if key.modifiers == KeyModifiers::CONTROL => {
self.state.range_select.reset_range_selection(); self.state.range_select.reset_range_selection();
@ -578,7 +574,7 @@ impl<'ws> Workspace<'ws> {
ws.book.select_prev_sheet(); ws.book.select_prev_sheet();
Ok(()) Ok(())
})?; })?;
self.state.range_select.sheet = Some(self.book.current_sheet); self.state.range_select.sheet = Some(self.book.location.sheet);
} }
KeyCode::Char('C') if key.modifiers.contains(KeyModifiers::CONTROL) => { KeyCode::Char('C') if key.modifiers.contains(KeyModifiers::CONTROL) => {
self.copy_range(true)?; self.copy_range(true)?;
@ -741,7 +737,7 @@ impl<'ws> Workspace<'ws> {
} }
KeyCode::Char('l') if key.modifiers == KeyModifiers::CONTROL => { KeyCode::Char('l') if key.modifiers == KeyModifiers::CONTROL => {
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> { self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
let Address { row: _, col } = &ws.book.location; let Address { sheet: _, row: _, col } = &ws.book.location;
ws.book ws.book
.set_col_size(*col, ws.book.get_col_size(*col)? + 1)?; .set_col_size(*col, ws.book.get_col_size(*col)? + 1)?;
Ok(()) Ok(())
@ -749,7 +745,7 @@ impl<'ws> Workspace<'ws> {
} }
KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => { KeyCode::Char('h') if key.modifiers == KeyModifiers::CONTROL => {
self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> { self.run_with_prefix(|ws: &mut Workspace<'_>| -> Result<()> {
let Address { row: _, col } = &ws.book.location; let Address { sheet: _, row: _, col } = &ws.book.location;
let curr_size = ws.book.get_col_size(*col)?; let curr_size = ws.book.get_col_size(*col)?;
if curr_size > 1 { if curr_size > 1 {
ws.book.set_col_size(*col, curr_size - 1)?; ws.book.set_col_size(*col, curr_size - 1)?;
@ -847,7 +843,7 @@ impl<'ws> Workspace<'ws> {
self.book.evaluate(); self.book.evaluate();
} }
Some(ClipboardContents::Range(ref rows)) => { Some(ClipboardContents::Range(ref rows)) => {
let Address { row, col } = self.book.location.clone(); let Address { sheet, row, col } = self.book.location.clone();
let row_len = rows.len(); let row_len = rows.len();
for ri in 0..row_len { for ri in 0..row_len {
let columns = &rows[ri]; let columns = &rows[ri];
@ -855,6 +851,7 @@ impl<'ws> Workspace<'ws> {
for ci in 0..col_len { for ci in 0..col_len {
self.book.update_cell( self.book.update_cell(
&Address { &Address {
sheet,
row: ri + row, row: ri + row,
col: ci + col, col: ci + col,
}, },
@ -896,8 +893,8 @@ impl<'ws> Workspace<'ws> {
} }
fn enter_range_select_mode(&mut self, init_start: bool) { fn enter_range_select_mode(&mut self, init_start: bool) {
self.state.range_select.sheet = Some(self.book.current_sheet); self.state.range_select.sheet = Some(self.book.location.sheet);
self.state.range_select.original_sheet = Some(self.book.current_sheet); self.state.range_select.original_sheet = Some(self.book.location.sheet);
self.state.range_select.original_location = Some(self.book.location.clone()); self.state.range_select.original_location = Some(self.book.location.clone());
if init_start { if init_start {
self.state.range_select.start = Some(self.book.location.clone()); self.state.range_select.start = Some(self.book.location.clone());
@ -932,7 +929,7 @@ impl<'ws> Workspace<'ws> {
} }
fn exit_range_select_mode(&mut self) -> Result<()> { fn exit_range_select_mode(&mut self) -> Result<()> {
self.book.current_sheet = self self.book.location.sheet = self
.state .state
.range_select .range_select
.original_sheet .original_sheet

View File

@ -36,7 +36,7 @@ impl<'ws> Workspace<'ws> {
.map(|(idx, name)| format!("{} {}", name, idx)) .map(|(idx, name)| format!("{} {}", name, idx))
.collect::<Vec<String>>(), .collect::<Vec<String>>(),
) )
.select(Some(ws.book.current_sheet as usize)); .select(Some(ws.book.location.sheet as usize));
tabs.render(rect, buf); tabs.render(rect, buf);
}), }),
Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| { Box::new(|rect: Rect, buf: &mut Buffer, ws: &mut Self| {

View File

@ -15,7 +15,7 @@ fn test_viewport_get_visible_columns() {
let width = dbg!(dbg!(default_size) * 12 / 2); let width = dbg!(dbg!(default_size) * 12 / 2);
let app_state = AppState::default(); let app_state = AppState::default();
let viewport = Viewport::new(&book, Some(&app_state.range_select)) let viewport = Viewport::new(&book, Some(&app_state.range_select))
.with_selected(Address { row: 1, col: 17 }); .with_selected(Address { sheet: 0, row: 1, col: 17 });
let cols = viewport let cols = viewport
.get_visible_columns((width + 5) as u16, &mut state) .get_visible_columns((width + 5) as u16, &mut state)
.expect("Failed to get visible columns"); .expect("Failed to get visible columns");
@ -32,7 +32,7 @@ fn test_viewport_get_visible_rows() {
let height = 6; let height = 6;
let app_state = AppState::default(); let app_state = AppState::default();
let viewport = Viewport::new(&book, Some(&app_state.range_select)) let viewport = Viewport::new(&book, Some(&app_state.range_select))
.with_selected(Address { row: 17, col: 1 }); .with_selected(Address { sheet: 0, row: 17, col: 1 });
let rows = dbg!(viewport.get_visible_rows(height as u16, &mut state)); let rows = dbg!(viewport.get_visible_rows(height as u16, &mut state));
assert_eq!(height - 1, rows.len()); assert_eq!(height - 1, rows.len());
assert_eq!( assert_eq!(
@ -53,7 +53,7 @@ fn test_viewport_visible_columns_after_length_change() {
{ {
let app_state = AppState::default(); let app_state = AppState::default();
let viewport = Viewport::new(&book, Some(&app_state.range_select)) let viewport = Viewport::new(&book, Some(&app_state.range_select))
.with_selected(Address { row: 1, col: 17 }); .with_selected(Address { sheet: 0, row: 1, col: 17 });
let cols = viewport let cols = viewport
.get_visible_columns((width + 5) as u16, &mut state) .get_visible_columns((width + 5) as u16, &mut state)
.expect("Failed to get visible columns"); .expect("Failed to get visible columns");
@ -66,7 +66,7 @@ fn test_viewport_visible_columns_after_length_change() {
{ {
let app_state = AppState::default(); let app_state = AppState::default();
let viewport = Viewport::new(&book, Some(&app_state.range_select)) let viewport = Viewport::new(&book, Some(&app_state.range_select))
.with_selected(Address { row: 1, col: 1 }); .with_selected(Address { sheet: 0, row: 1, col: 1 });
let cols = viewport let cols = viewport
.get_visible_columns((width + 5) as u16, &mut state) .get_visible_columns((width + 5) as u16, &mut state)
.expect("Failed to get visible columns"); .expect("Failed to get visible columns");

View File

@ -152,7 +152,7 @@ impl<'ws> Viewport<'ws> {
|VisibleColumn { idx: ci, length: _ }| { |VisibleColumn { idx: ci, length: _ }| {
let content = self let content = self
.book .book
.get_cell_addr_rendered(&Address { row: ri, col: *ci }) .get_cell_addr_rendered(&Address { row: ri, col: *ci, sheet: self.book.location.sheet})
.unwrap(); .unwrap();
self.compute_cell_style(ri, *ci, Cell::new(Text::raw(content))) self.compute_cell_style(ri, *ci, Cell::new(Text::raw(content)))
}, },
@ -195,7 +195,7 @@ impl<'ws> Viewport<'ws> {
) -> Cell<'widget> { ) -> Cell<'widget> {
let style = self let style = self
.book .book
.get_cell_style(self.book.current_sheet, &Address { row: ri, col: ci }); .get_cell_style(&Address { sheet: self.book.location.sheet, row: ri, col: ci });
let bg_color = map_color( let bg_color = map_color(
style.as_ref().map(|s| s.fill.bg_color.as_ref()).flatten(), style.as_ref().map(|s| s.fill.bg_color.as_ref()).flatten(),
Color::Rgb(35, 33, 54), Color::Rgb(35, 33, 54),

View File

@ -542,8 +542,9 @@ fn test_range_copy() {
let mut ws = new_workspace(); let mut ws = new_workspace();
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
let address = Address::default();
ws.book ws.book
.move_to(&Address { row: 1, col: 1 }) .move_to(&address)
.expect("Failed to move to row"); .expect("Failed to move to row");
let original_loc = ws.book.location.clone(); let original_loc = ws.book.location.clone();
script() script()
@ -564,7 +565,7 @@ fn test_range_copy() {
.run(&mut ws) .run(&mut ws)
.expect("Failed to handle key sequence"); .expect("Failed to handle key sequence");
assert_eq!( assert_eq!(
Some(Address { row: 1, col: 2 }), Some(Address { sheet: 0, row: 1, col: 2 }),
ws.state.range_select.start ws.state.range_select.start
); );
@ -576,18 +577,18 @@ fn test_range_copy() {
assert!(ws.state.range_select.original_location.is_none()); assert!(ws.state.range_select.original_location.is_none());
assert_eq!( assert_eq!(
Some(Address { row: 1, col: 2 }), Some(Address { sheet: 0, row: 1, col: 2 }),
ws.state.range_select.start ws.state.range_select.start
); );
assert_eq!(Some(Address { row: 2, col: 2 }), ws.state.range_select.end); assert_eq!(Some(Address { sheet: 0, row: 2, col: 2 }), ws.state.range_select.end);
assert_eq!(original_loc, ws.book.location); assert_eq!(original_loc, ws.book.location);
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.book ws.book
.move_to(&Address { row: 5, col: 5 }) .move_to(&Address { sheet: 0, row: 5, col: 5 })
.expect("Failed to move to row"); .expect("Failed to move to row");
let original_loc_2 = ws.book.location.clone(); let original_loc_2 = ws.book.location.clone();
assert_eq!(Address { row: 5, col: 5 }, original_loc_2); assert_eq!(Address { sheet: 0, row: 5, col: 5 }, original_loc_2);
script() script()
.char('v') .char('v')
@ -607,7 +608,7 @@ fn test_range_copy() {
.run(&mut ws) .run(&mut ws)
.expect("Failed to handle key sequence"); .expect("Failed to handle key sequence");
assert_eq!( assert_eq!(
Some(Address { row: 5, col: 5 }), Some(Address { sheet: 0, row: 5, col: 5 }),
ws.state.range_select.start ws.state.range_select.start
); );
@ -619,11 +620,11 @@ fn test_range_copy() {
assert!(ws.state.range_select.original_location.is_none()); assert!(ws.state.range_select.original_location.is_none());
assert_eq!( assert_eq!(
Some(Address { row: 5, col: 5 }), Some(Address { sheet: 0, row: 5, col: 5 }),
ws.state.range_select.start ws.state.range_select.start
); );
assert_eq!(Some(Address { row: 5, col: 4 }), ws.state.range_select.end); assert_eq!(Some(Address { sheet: 0, row: 5, col: 4 }), ws.state.range_select.end);
assert_eq!(Address { row: 4, col: 5 }, ws.book.location); assert_eq!(Address { sheet: 0, row: 4, col: 5 }, ws.book.location);
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
} }
@ -652,14 +653,14 @@ fn test_gg_movement() {
.char('j') .char('j')
.run(&mut ws) .run(&mut ws)
.expect("failed to handle event sequence"); .expect("failed to handle event sequence");
assert_eq!(ws.book.location, Address { row: 3, col: 1 }); assert_eq!(ws.book.location, Address { sheet: 0, row: 3, col: 1 });
script() script()
.char('l') .char('l')
.char('g') .char('g')
.char('g') .char('g')
.run(&mut ws) .run(&mut ws)
.expect("failed to handle event sequence"); .expect("failed to handle event sequence");
assert_eq!(ws.book.location, Address { row: 1, col: 2 }); assert_eq!(ws.book.location, Address { sheet: 0, row: 1, col: 2 });
} }
#[test] #[test]
@ -672,14 +673,14 @@ fn test_h_j_k_l_movement() {
.char('l') .char('l')
.run(&mut ws) .run(&mut ws)
.expect("failed to handle event sequence"); .expect("failed to handle event sequence");
assert_eq!(ws.book.location, Address { row: 3, col: 2 }); assert_eq!(ws.book.location, Address { sheet: 0, row: 3, col: 2 });
script() script()
.char('h') .char('h')
.char('2') .char('2')
.char('k') .char('k')
.run(&mut ws) .run(&mut ws)
.expect("failed to handle event sequence"); .expect("failed to handle event sequence");
assert_eq!(ws.book.location, Address { row: 1, col: 1 }); assert_eq!(ws.book.location, Address { sheet: 0, row: 1, col: 1 });
} }
macro_rules! assert_copy_paste { macro_rules! assert_copy_paste {
@ -929,8 +930,8 @@ fn test_command_mode_enter() {
fn test_edit_mode_paste() { fn test_edit_mode_paste() {
let mut ws = new_workspace(); let mut ws = new_workspace();
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
ws.state.range_select.start = Some(Address { row: 1, col: 1 }); ws.state.range_select.start = Some(Address { sheet: 0, row: 1, col: 1 });
ws.state.range_select.end = Some(Address { row: 2, col: 2 }); ws.state.range_select.end = Some(Address { sheet: 0, row: 2, col: 2 });
dbg!(ws.selected_range_to_string()); dbg!(ws.selected_range_to_string());
script() script()
.char('e') .char('e')
@ -979,8 +980,8 @@ macro_rules! assert_range_clear {
($script : expr) => {{ ($script : expr) => {{
let mut ws = new_workspace(); let mut ws = new_workspace();
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last()); assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
let first_corner = Address { row: 1, col: 1 }; let first_corner = Address { sheet: 0, row: 1, col: 1 };
let second_corner = Address { row: 2, col: 2 }; let second_corner = Address { sheet: 0, row: 2, col: 2 };
ws.book ws.book
.update_cell(&first_corner, "foo") .update_cell(&first_corner, "foo")
.expect("Failed to update cell"); .expect("Failed to update cell");
@ -1050,18 +1051,17 @@ fn test_range_select_movement() {
.char('k') .char('k')
.run(&mut ws) .run(&mut ws)
.expect("failed to run script"); .expect("failed to run script");
assert_eq!(&Address { row: 3, col: 3 }, &ws.book.location); assert_eq!(&Address { sheet: 0, row: 3, col: 3 }, &ws.book.location);
assert_eq!(0, ws.book.current_sheet);
script() script()
.ctrl('n') .ctrl('n')
.run(&mut ws) .run(&mut ws)
.expect("Unable to run script"); .expect("Unable to run script");
assert_eq!(1, ws.book.current_sheet); assert_eq!(1, ws.book.location.sheet);
script() script()
.ctrl('p') .ctrl('p')
.run(&mut ws) .run(&mut ws)
.expect("Unable to run script"); .expect("Unable to run script");
assert_eq!(0, ws.book.current_sheet); assert_eq!(0, ws.book.location.sheet);
} }
#[test] #[test]
@ -1072,8 +1072,8 @@ fn test_range_select_clear_lower_d() {
macro_rules! assert_range_copy { macro_rules! assert_range_copy {
($script: expr) => {{ ($script: expr) => {{
let mut ws = new_workspace(); let mut ws = new_workspace();
let top_left_addr = Address { row: 2, col: 2 }; let top_left_addr = Address { sheet: 0, row: 2, col: 2 };
let bot_right_addr = Address { row: 4, col: 4 }; let bot_right_addr = Address { sheet: 0, row: 4, col: 4 };
ws.book ws.book
.update_cell(&top_left_addr, "top_left") .update_cell(&top_left_addr, "top_left")
.expect("Failed to update top left"); .expect("Failed to update top left");
@ -1112,7 +1112,7 @@ macro_rules! assert_range_copy {
.expect("Didn't find a start of range") .expect("Didn't find a start of range")
); );
assert_eq!( assert_eq!(
&Address { row: 1, col: 1 }, &Address { sheet: 0, row: 1, col: 1 },
ws.state ws.state
.range_select .range_select
.original_location .original_location
@ -1188,7 +1188,7 @@ fn test_extend_to_range() {
.expect("Unable to run script"); .expect("Unable to run script");
let extended_cell = ws let extended_cell = ws
.book .book
.get_cell_addr_contents(&Address { row: 2, col: 1 }) .get_cell_addr_contents(&Address { sheet: 0, row: 2, col: 1 })
.expect("Failed to get cell contents"); .expect("Failed to get cell contents");
assert_eq!("=B2+1".to_string(), extended_cell); assert_eq!("=B2+1".to_string(), extended_cell);
} }
@ -1208,7 +1208,7 @@ fn test_color_cells() {
for ci in 1..=3 { for ci in 1..=3 {
let style = ws let style = ws
.book .book
.get_cell_style(ws.book.current_sheet, &Address { row: ri, col: ci }) .get_cell_style(&Address { sheet: ws.book.location.sheet, row: ri, col: ci })
.expect("failed to get style"); .expect("failed to get style");
assert_eq!( assert_eq!(
"#800000", "#800000",
@ -1234,7 +1234,7 @@ fn test_color_row() {
for ci in [1, book::LAST_COLUMN] { for ci in [1, book::LAST_COLUMN] {
let style = ws let style = ws
.book .book
.get_cell_style(ws.book.current_sheet, &Address { row: 1, col: ci as usize }) .get_cell_style(&Address { sheet: ws.book.location.sheet, row: 1, col: ci as usize })
.expect("failed to get style"); .expect("failed to get style");
assert_eq!( assert_eq!(
"#800000", "#800000",
@ -1259,7 +1259,7 @@ fn test_color_col() {
for ri in [1, book::LAST_ROW] { for ri in [1, book::LAST_ROW] {
let style = ws let style = ws
.book .book
.get_cell_style(ws.book.current_sheet, &Address { row: ri as usize, col: 1 }) .get_cell_style(&Address { sheet: ws.book.location.sheet, row: ri as usize, col: 1 })
.expect("failed to get style"); .expect("failed to get style");
assert_eq!( assert_eq!(
"#800000", "#800000",