mirror of
https://github.com/zaphar/sheetsui.git
synced 2025-07-24 22:09:50 -04:00
Compare commits
12 Commits
b5e0362a4e
...
a6baa2564f
Author | SHA1 | Date | |
---|---|---|---|
a6baa2564f | |||
15f12ef8e7 | |||
2aa9fd69b1 | |||
621c35d7c4 | |||
e1c64db0a0 | |||
5012649210 | |||
4623a911f4 | |||
ba5ea3c627 | |||
dae3d71c54 | |||
8da0ebda4e | |||
db0397b8f4 | |||
c3f84b10ad |
@ -29,6 +29,7 @@ will clear the numeric prefix if you want to cancel it.
|
|||||||
**Modifying the Sheet or Cells**
|
**Modifying the Sheet or Cells**
|
||||||
|
|
||||||
* `e` or `i` will enter CellEdit mode for the current cell.
|
* `e` or `i` will enter CellEdit mode for the current cell.
|
||||||
|
* 'I' will toggle italic on the cell. 'B' will toggle bold.
|
||||||
* `Ctrl-h` will shorten the width of the column you are on.
|
* `Ctrl-h` will shorten the width of the column you are on.
|
||||||
* `Ctrl-l` will lengthen the width of the column you are on.
|
* `Ctrl-l` will lengthen the width of the column you are on.
|
||||||
|
|
||||||
|
Binary file not shown.
@ -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),
|
||||||
@ -343,12 +341,6 @@ impl Book {
|
|||||||
sheet: u32,
|
sheet: u32,
|
||||||
col_idx: usize,
|
col_idx: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// TODO(jeremy): This is a little hacky and the underlying model
|
|
||||||
// supports a better mechanism but UserModel doesn't support it yet.
|
|
||||||
// https://github.com/ironcalc/IronCalc/issues/273
|
|
||||||
// https://github.com/ironcalc/IronCalc/pull/276 is the coming fix.
|
|
||||||
// NOTE(jwall): Because of the number of cells required to modify
|
|
||||||
// this is crazy slow
|
|
||||||
let area = self.get_col_range(sheet, col_idx);
|
let area = self.get_col_range(sheet, col_idx);
|
||||||
self.set_cell_style(style, &area)?;
|
self.set_cell_style(style, &area)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -373,28 +365,24 @@ impl Book {
|
|||||||
sheet: u32,
|
sheet: u32,
|
||||||
row_idx: usize,
|
row_idx: usize,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// TODO(jeremy): This is a little hacky and the underlying model
|
|
||||||
// supports a better mechanism but UserModel doesn't support it yet.
|
|
||||||
// https://github.com/ironcalc/IronCalc/issues/273
|
|
||||||
// https://github.com/ironcalc/IronCalc/pull/276 is the coming fix.
|
|
||||||
let area = self.get_row_range(sheet, row_idx);
|
let area = self.get_row_range(sheet, row_idx);
|
||||||
self.set_cell_style(style, &area)?;
|
self.set_cell_style(style, &area)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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 +391,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 +410,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 +424,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 +441,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 +461,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 +477,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 +517,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 +530,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 +567,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 +582,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 +615,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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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"));
|
||||||
|
@ -34,8 +34,6 @@ pub enum Modality {
|
|||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct RangeSelection {
|
pub struct RangeSelection {
|
||||||
pub original_location: Option<Address>,
|
pub original_location: Option<Address>,
|
||||||
pub original_sheet: Option<u32>,
|
|
||||||
pub sheet: Option<u32>,
|
|
||||||
pub start: Option<Address>,
|
pub start: Option<Address>,
|
||||||
pub end: Option<Address>,
|
pub end: Option<Address>,
|
||||||
}
|
}
|
||||||
@ -45,10 +43,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),
|
||||||
},
|
},
|
||||||
@ -60,7 +60,6 @@ impl RangeSelection {
|
|||||||
pub fn reset_range_selection(&mut self) {
|
pub fn reset_range_selection(&mut self) {
|
||||||
self.start = None;
|
self.start = None;
|
||||||
self.end = None;
|
self.end = None;
|
||||||
self.sheet = None;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -133,13 +132,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 {
|
||||||
@ -214,12 +214,12 @@ impl<'ws> Workspace<'ws> {
|
|||||||
start.to_range_part(),
|
start.to_range_part(),
|
||||||
format!(":{}", end.to_range_part())
|
format!(":{}", end.to_range_part())
|
||||||
);
|
);
|
||||||
if let Some(range_sheet) = state.range_select.sheet {
|
if let Some(ref start_addr) = state.range_select.start {
|
||||||
if range_sheet != self.book.current_sheet {
|
if start_addr.sheet != self.book.location.sheet {
|
||||||
return format!(
|
return format!(
|
||||||
"{}!{}",
|
"{}!{}",
|
||||||
self.book
|
self.book
|
||||||
.get_sheet_name_by_idx(range_sheet as usize)
|
.get_sheet_name_by_idx(start_addr.sheet as usize)
|
||||||
.expect("No such sheet index"),
|
.expect("No such sheet index"),
|
||||||
a1
|
a1
|
||||||
);
|
);
|
||||||
@ -243,6 +243,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 +412,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 +432,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 +444,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,
|
||||||
@ -508,12 +509,8 @@ impl<'ws> Workspace<'ws> {
|
|||||||
self.handle_numeric_prefix(d);
|
self.handle_numeric_prefix(d);
|
||||||
}
|
}
|
||||||
KeyCode::Char('D') => {
|
KeyCode::Char('D') => {
|
||||||
if let Some((start, end)) = dbg!(self.state.range_select.get_range()) {
|
if let Some((start, end)) = 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 +519,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 +563,6 @@ 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);
|
|
||||||
}
|
}
|
||||||
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 +570,6 @@ 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);
|
|
||||||
}
|
}
|
||||||
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)?;
|
||||||
@ -669,6 +660,16 @@ impl<'ws> Workspace<'ws> {
|
|||||||
self.state.reset_n_prefix();
|
self.state.reset_n_prefix();
|
||||||
self.state.char_queue.clear();
|
self.state.char_queue.clear();
|
||||||
}
|
}
|
||||||
|
KeyCode::Char('B') => {
|
||||||
|
let address = self.book.location.clone();
|
||||||
|
let style = self.book.get_cell_style(&address).map(|s| s.font.b);
|
||||||
|
self.toggle_bool_style(style, "font.b", &address)?;
|
||||||
|
}
|
||||||
|
KeyCode::Char('I') => {
|
||||||
|
let address = self.book.location.clone();
|
||||||
|
let style = self.book.get_cell_style(&address).map(|s| s.font.i);
|
||||||
|
self.toggle_bool_style(style, "font.i", &address)?;
|
||||||
|
}
|
||||||
KeyCode::Char(d) if d.is_ascii_digit() => {
|
KeyCode::Char(d) if d.is_ascii_digit() => {
|
||||||
self.handle_numeric_prefix(d);
|
self.handle_numeric_prefix(d);
|
||||||
}
|
}
|
||||||
@ -741,7 +742,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 +750,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)?;
|
||||||
@ -840,6 +841,24 @@ impl<'ws> Workspace<'ws> {
|
|||||||
return Ok(None);
|
return Ok(None);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn toggle_bool_style(&mut self, current_val: Option<bool>, path: &str, address: &Address) -> Result<(), anyhow::Error> {
|
||||||
|
let value = if let Some(b_val) = current_val {
|
||||||
|
if b_val { "false" } else { "true" }
|
||||||
|
} else {
|
||||||
|
"true"
|
||||||
|
};
|
||||||
|
self.book.set_cell_style(
|
||||||
|
&[(path, value)],
|
||||||
|
&Area {
|
||||||
|
sheet: address.sheet,
|
||||||
|
row: address.row as i32,
|
||||||
|
column: address.col as i32,
|
||||||
|
width: 1,
|
||||||
|
height: 1,
|
||||||
|
})?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
fn paste_range(&mut self) -> Result<(), anyhow::Error> {
|
fn paste_range(&mut self) -> Result<(), anyhow::Error> {
|
||||||
match &self.state.clipboard {
|
match &self.state.clipboard {
|
||||||
Some(ClipboardContents::Cell(contents)) => {
|
Some(ClipboardContents::Cell(contents)) => {
|
||||||
@ -847,7 +866,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 +874,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 +916,6 @@ 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.original_sheet = Some(self.book.current_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,12 +950,6 @@ 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
|
|
||||||
.state
|
|
||||||
.range_select
|
|
||||||
.original_sheet
|
|
||||||
.clone()
|
|
||||||
.expect("Missing original sheet");
|
|
||||||
self.book.location = self
|
self.book.location = self
|
||||||
.state
|
.state
|
||||||
.range_select
|
.range_select
|
||||||
|
@ -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| {
|
||||||
|
@ -12,10 +12,10 @@ fn test_viewport_get_visible_columns() {
|
|||||||
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
||||||
);
|
);
|
||||||
let default_size = book.get_col_size(1).expect("Failed to get column size");
|
let default_size = book.get_col_size(1).expect("Failed to get column size");
|
||||||
let width = dbg!(dbg!(default_size) * 12 / 2);
|
let width = 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");
|
||||||
@ -25,15 +25,15 @@ fn test_viewport_get_visible_columns() {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_viewport_get_visible_rows() {
|
fn test_viewport_get_visible_rows() {
|
||||||
let mut state = dbg!(ViewportState::default());
|
let mut state = ViewportState::default();
|
||||||
let book = Book::from_model(
|
let book = Book::from_model(
|
||||||
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
||||||
);
|
);
|
||||||
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 = 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!(
|
||||||
17 - (height - 2),
|
17 - (height - 2),
|
||||||
@ -49,11 +49,11 @@ fn test_viewport_visible_columns_after_length_change() {
|
|||||||
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
Model::new_empty("test", "en", "America/New_York").expect("Failed to make model"),
|
||||||
);
|
);
|
||||||
let default_size = book.get_col_size(1).expect("Failed to get column size");
|
let default_size = book.get_col_size(1).expect("Failed to get column size");
|
||||||
let width = dbg!(dbg!(default_size) * 12 / 2);
|
let width = 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");
|
||||||
@ -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");
|
||||||
|
@ -72,8 +72,6 @@ impl<'ws> Viewport<'ws> {
|
|||||||
start = start + 1;
|
start = start + 1;
|
||||||
end = row_idx;
|
end = row_idx;
|
||||||
} else {
|
} else {
|
||||||
//dbg!(&start);
|
|
||||||
//dbg!(&end);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -152,7 +150,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)))
|
||||||
},
|
},
|
||||||
@ -193,15 +191,28 @@ impl<'ws> Viewport<'ws> {
|
|||||||
ci: usize,
|
ci: usize,
|
||||||
mut cell: Cell<'widget>,
|
mut cell: Cell<'widget>,
|
||||||
) -> Cell<'widget> {
|
) -> Cell<'widget> {
|
||||||
let style = self
|
// TODO(zaphar): Should probably create somekind of formatter abstraction.
|
||||||
|
if let Some(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 }) {
|
||||||
|
cell = self.compute_cell_colors(&style, ri, ci, cell);
|
||||||
|
cell = if style.font.b {
|
||||||
|
cell.bold()
|
||||||
|
} else { cell };
|
||||||
|
cell = if style.font.i {
|
||||||
|
cell.italic()
|
||||||
|
} else { cell };
|
||||||
|
}
|
||||||
|
cell
|
||||||
|
}
|
||||||
|
|
||||||
|
fn compute_cell_colors<'widget>(&self, style: &ironcalc::base::types::Style, ri: usize, ci: usize, mut cell: Cell<'widget>) -> Cell<'widget> {
|
||||||
let bg_color = map_color(
|
let bg_color = map_color(
|
||||||
style.as_ref().map(|s| s.fill.bg_color.as_ref()).flatten(),
|
style.fill.bg_color.as_ref(),
|
||||||
Color::Rgb(35, 33, 54),
|
Color::Rgb(35, 33, 54),
|
||||||
);
|
);
|
||||||
let fg_color = map_color(
|
let fg_color = map_color(
|
||||||
style.as_ref().map(|s| s.fill.fg_color.as_ref()).flatten(),
|
style.fill.fg_color.as_ref(),
|
||||||
Color::White,
|
Color::White,
|
||||||
);
|
);
|
||||||
if let Some((start, end)) = &self.range_selection.map_or(None, |r| r.get_range()) {
|
if let Some((start, end)) = &self.range_selection.map_or(None, |r| r.get_range()) {
|
||||||
@ -212,12 +223,12 @@ impl<'ws> Viewport<'ws> {
|
|||||||
} else {
|
} else {
|
||||||
cell = cell.bg(bg_color).fg(fg_color);
|
cell = cell.bg(bg_color).fg(fg_color);
|
||||||
}
|
}
|
||||||
match (self.book.location.row == ri, self.book.location.col == ci) {
|
cell = match (self.book.location.row == ri, self.book.location.col == ci) {
|
||||||
(true, true) => cell.fg(Color::White).bg(Color::Rgb(57, 61, 71)),
|
(true, true) => cell.fg(Color::White).bg(Color::Rgb(57, 61, 71)),
|
||||||
// TODO(zaphar): Support ironcalc style options
|
// TODO(zaphar): Support ironcalc style options
|
||||||
_ => cell,
|
_ => cell,
|
||||||
}
|
};
|
||||||
.bold()
|
cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
118
src/ui/test.rs
118
src/ui/test.rs
@ -320,7 +320,7 @@ fn test_input_navitation_enter_key() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_input_navitation_tab_key() {
|
fn test_input_navitation_tab_key() {
|
||||||
let mut ws = new_workspace();
|
let mut ws = new_workspace();
|
||||||
let col = dbg!(ws.book.location.col);
|
let col = ws.book.location.col;
|
||||||
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
|
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
|
||||||
script()
|
script()
|
||||||
.tab()
|
.tab()
|
||||||
@ -352,7 +352,7 @@ fn test_input_navitation_shift_enter_key() {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_input_navitation_shift_tab_key() {
|
fn test_input_navitation_shift_tab_key() {
|
||||||
let mut ws = new_workspace();
|
let mut ws = new_workspace();
|
||||||
let col = dbg!(ws.book.location.col);
|
let col = ws.book.location.col;
|
||||||
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
|
assert_eq!(Some(&Modality::Navigate), ws.state.modality_stack.last());
|
||||||
script()
|
script()
|
||||||
.tab()
|
.tab()
|
||||||
@ -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,9 +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());
|
|
||||||
script()
|
script()
|
||||||
.char('e')
|
.char('e')
|
||||||
.ctrl('p')
|
.ctrl('p')
|
||||||
@ -979,8 +979,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 +1050,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 +1071,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,25 +1111,17 @@ 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
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("Expected an original location")
|
.expect("Expected an original location")
|
||||||
);
|
);
|
||||||
assert_eq!(
|
|
||||||
0,
|
|
||||||
ws.state
|
|
||||||
.range_select
|
|
||||||
.original_sheet
|
|
||||||
.expect("Expected an original sheet")
|
|
||||||
);
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
Some(&Modality::RangeSelect),
|
Some(&Modality::RangeSelect),
|
||||||
ws.state.modality_stack.iter().last()
|
ws.state.modality_stack.iter().last()
|
||||||
);
|
);
|
||||||
dbg!(ws.state.range_select.get_range());
|
|
||||||
$script.run(&mut ws).expect("failed to run script");
|
$script.run(&mut ws).expect("failed to run script");
|
||||||
assert!(ws.state.clipboard.is_some());
|
assert!(ws.state.clipboard.is_some());
|
||||||
match ws.state.clipboard.unwrap() {
|
match ws.state.clipboard.unwrap() {
|
||||||
@ -1188,7 +1179,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 +1199,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 +1225,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 +1250,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",
|
||||||
@ -1272,6 +1263,53 @@ fn test_color_col() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_bold_text() {
|
||||||
|
let mut ws = new_workspace();
|
||||||
|
let before_style = ws
|
||||||
|
.book
|
||||||
|
.get_cell_style(&Address { sheet: 0, row: 1, col: 1 })
|
||||||
|
.expect("Failed to get style");
|
||||||
|
assert!(!before_style.font.b);
|
||||||
|
script()
|
||||||
|
.char('B')
|
||||||
|
.run(&mut ws)
|
||||||
|
.expect("Unable to run script");
|
||||||
|
let style = ws
|
||||||
|
.book
|
||||||
|
.get_cell_style(&Address { sheet: 0, row: 1, col: 1 })
|
||||||
|
.expect("Failed to get style");
|
||||||
|
assert!(style.font.b);
|
||||||
|
script()
|
||||||
|
.char('B')
|
||||||
|
.run(&mut ws)
|
||||||
|
.expect("Unable to run script");
|
||||||
|
assert!(!before_style.font.b);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_italic_text() {
|
||||||
|
let mut ws = new_workspace();
|
||||||
|
let before_style = ws
|
||||||
|
.book
|
||||||
|
.get_cell_style(&Address { sheet: 0, row: 1, col: 1 })
|
||||||
|
.expect("Failed to get style");
|
||||||
|
assert!(!before_style.font.i);
|
||||||
|
script()
|
||||||
|
.char('I')
|
||||||
|
.run(&mut ws)
|
||||||
|
.expect("Unable to run script");
|
||||||
|
let style = ws
|
||||||
|
.book
|
||||||
|
.get_cell_style(&Address { sheet: 0, row: 1, col: 1 })
|
||||||
|
.expect("Failed to get style");
|
||||||
|
assert!(style.font.i);
|
||||||
|
script()
|
||||||
|
.char('I')
|
||||||
|
.run(&mut ws)
|
||||||
|
.expect("Unable to run script");
|
||||||
|
assert!(!before_style.font.i);
|
||||||
|
}
|
||||||
|
|
||||||
fn new_workspace<'a>() -> Workspace<'a> {
|
fn new_workspace<'a>() -> Workspace<'a> {
|
||||||
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook")
|
Workspace::new_empty("en", "America/New_York").expect("Failed to get empty workbook")
|
||||||
|
Loading…
x
Reference in New Issue
Block a user