feat: implement move column move row with tests
This commit is contained in:
committed by
Nicolás Hatcher Andrés
parent
7dc49d5dd7
commit
c2777c73ac
@@ -457,7 +457,7 @@ impl Model {
|
||||
/// * Column is one of the extremes of the range. The new extreme would be target_column.
|
||||
/// Range is then normalized
|
||||
/// * Any other case, range is left unchanged.
|
||||
/// NOTE: This does NOT move the data in the columns or move the colum styles
|
||||
/// NOTE: This moves the data and column styles along with the formulas
|
||||
pub fn move_column_action(
|
||||
&mut self,
|
||||
sheet: u32,
|
||||
@@ -473,7 +473,70 @@ impl Model {
|
||||
return Err("Initial column out of boundaries".to_string());
|
||||
}
|
||||
|
||||
// TODO: Add the actual displacement of data and styles
|
||||
if delta == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Preserve cell contents, width and style of the column being moved
|
||||
let original_refs = self
|
||||
.workbook
|
||||
.worksheet(sheet)?
|
||||
.column_cell_references(column)?;
|
||||
let mut original_cells = Vec::new();
|
||||
for r in &original_refs {
|
||||
let cell = self
|
||||
.workbook
|
||||
.worksheet(sheet)?
|
||||
.cell(r.row, column)
|
||||
.ok_or("Expected Cell to exist")?;
|
||||
let style_idx = cell.get_style();
|
||||
let formula_or_value = self
|
||||
.get_cell_formula(sheet, r.row, column)?
|
||||
.unwrap_or_else(|| cell.get_text(&self.workbook.shared_strings, &self.language));
|
||||
original_cells.push((r.row, formula_or_value, style_idx));
|
||||
self.cell_clear_all(sheet, r.row, column)?;
|
||||
}
|
||||
|
||||
let width = self.workbook.worksheet(sheet)?.get_column_width(column)?;
|
||||
let style = self.workbook.worksheet(sheet)?.get_column_style(column)?;
|
||||
|
||||
if delta > 0 {
|
||||
for c in column + 1..=target_column {
|
||||
let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
|
||||
for r in refs {
|
||||
self.move_cell(sheet, r.row, c, r.row, c - 1)?;
|
||||
}
|
||||
|
||||
let w = self.workbook.worksheet(sheet)?.get_column_width(c)?;
|
||||
let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
|
||||
self.workbook
|
||||
.worksheet_mut(sheet)?
|
||||
.set_column_width_and_style(c - 1, w, s)?;
|
||||
}
|
||||
} else {
|
||||
for c in (target_column..=column - 1).rev() {
|
||||
let refs = self.workbook.worksheet(sheet)?.column_cell_references(c)?;
|
||||
for r in refs {
|
||||
self.move_cell(sheet, r.row, c, r.row, c + 1)?;
|
||||
}
|
||||
|
||||
let w = self.workbook.worksheet(sheet)?.get_column_width(c)?;
|
||||
let s = self.workbook.worksheet(sheet)?.get_column_style(c)?;
|
||||
self.workbook
|
||||
.worksheet_mut(sheet)?
|
||||
.set_column_width_and_style(c + 1, w, s)?;
|
||||
}
|
||||
}
|
||||
|
||||
for (r, value, style_idx) in original_cells {
|
||||
self.set_user_input(sheet, r, target_column, value)?;
|
||||
self.workbook
|
||||
.worksheet_mut(sheet)?
|
||||
.set_cell_style(r, target_column, style_idx)?;
|
||||
}
|
||||
self.workbook
|
||||
.worksheet_mut(sheet)?
|
||||
.set_column_width_and_style(target_column, width, style)?;
|
||||
|
||||
// Update all formulas in the workbook
|
||||
self.displace_cells(
|
||||
@@ -486,4 +549,88 @@ impl Model {
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Displaces cells due to a move row action
|
||||
/// from initial_row to target_row = initial_row + row_delta
|
||||
/// References will be updated following the same rules as move_column_action
|
||||
/// NOTE: This moves the data and row styles along with the formulas
|
||||
pub fn move_row_action(&mut self, sheet: u32, row: i32, delta: i32) -> Result<(), String> {
|
||||
// Check boundaries
|
||||
let target_row = row + delta;
|
||||
if !(1..=LAST_ROW).contains(&target_row) {
|
||||
return Err("Target row out of boundaries".to_string());
|
||||
}
|
||||
if !(1..=LAST_ROW).contains(&row) {
|
||||
return Err("Initial row out of boundaries".to_string());
|
||||
}
|
||||
|
||||
if delta == 0 {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let original_cols = self.get_columns_for_row(sheet, row, false)?;
|
||||
let mut original_cells = Vec::new();
|
||||
for c in &original_cols {
|
||||
let cell = self
|
||||
.workbook
|
||||
.worksheet(sheet)?
|
||||
.cell(row, *c)
|
||||
.ok_or("Expected Cell to exist")?;
|
||||
let style_idx = cell.get_style();
|
||||
let formula_or_value = self
|
||||
.get_cell_formula(sheet, row, *c)?
|
||||
.unwrap_or_else(|| cell.get_text(&self.workbook.shared_strings, &self.language));
|
||||
original_cells.push((*c, formula_or_value, style_idx));
|
||||
self.cell_clear_all(sheet, row, *c)?;
|
||||
}
|
||||
|
||||
if delta > 0 {
|
||||
for r in row + 1..=target_row {
|
||||
let cols = self.get_columns_for_row(sheet, r, false)?;
|
||||
for c in cols {
|
||||
self.move_cell(sheet, r, c, r - 1, c)?;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for r in (target_row..=row - 1).rev() {
|
||||
let cols = self.get_columns_for_row(sheet, r, false)?;
|
||||
for c in cols {
|
||||
self.move_cell(sheet, r, c, r + 1, c)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (c, value, style_idx) in original_cells {
|
||||
self.set_user_input(sheet, target_row, c, value)?;
|
||||
self.workbook
|
||||
.worksheet_mut(sheet)?
|
||||
.set_cell_style(target_row, c, style_idx)?;
|
||||
}
|
||||
|
||||
let worksheet = &mut self.workbook.worksheets[sheet as usize];
|
||||
let mut new_rows = Vec::new();
|
||||
for r in worksheet.rows.iter() {
|
||||
if r.r == row {
|
||||
let mut nr = r.clone();
|
||||
nr.r = target_row;
|
||||
new_rows.push(nr);
|
||||
} else if delta > 0 && r.r > row && r.r <= target_row {
|
||||
let mut nr = r.clone();
|
||||
nr.r -= 1;
|
||||
new_rows.push(nr);
|
||||
} else if delta < 0 && r.r < row && r.r >= target_row {
|
||||
let mut nr = r.clone();
|
||||
nr.r += 1;
|
||||
new_rows.push(nr);
|
||||
} else {
|
||||
new_rows.push(r.clone());
|
||||
}
|
||||
}
|
||||
worksheet.rows = new_rows;
|
||||
|
||||
// Update all formulas in the workbook
|
||||
self.displace_cells(&(DisplaceData::RowMove { sheet, row, delta }))?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,11 @@ pub enum DisplaceData {
|
||||
column: i32,
|
||||
delta: i32,
|
||||
},
|
||||
RowMove {
|
||||
sheet: u32,
|
||||
row: i32,
|
||||
delta: i32,
|
||||
},
|
||||
ColumnMove {
|
||||
sheet: u32,
|
||||
column: i32,
|
||||
@@ -159,6 +164,21 @@ pub(crate) fn stringify_reference(
|
||||
}
|
||||
}
|
||||
}
|
||||
DisplaceData::RowMove {
|
||||
sheet,
|
||||
row: move_row,
|
||||
delta,
|
||||
} => {
|
||||
if sheet_index == *sheet {
|
||||
if row == *move_row {
|
||||
row += *delta;
|
||||
} else if (*delta > 0 && row > *move_row && row <= *move_row + *delta)
|
||||
|| (*delta < 0 && row < *move_row && row >= *move_row + *delta)
|
||||
{
|
||||
row -= *delta;
|
||||
}
|
||||
}
|
||||
}
|
||||
DisplaceData::ColumnMove {
|
||||
sheet,
|
||||
column: move_column,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#![allow(clippy::unwrap_used)]
|
||||
|
||||
use crate::constants::{DEFAULT_ROW_HEIGHT, LAST_COLUMN};
|
||||
use crate::constants::{DEFAULT_ROW_HEIGHT, LAST_COLUMN, LAST_ROW};
|
||||
use crate::model::Model;
|
||||
use crate::test::util::new_empty_model;
|
||||
use crate::types::Col;
|
||||
@@ -508,6 +508,10 @@ fn test_move_column_right() {
|
||||
assert_eq!(model._get_formula("E5"), "=SUM(H3:J7)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM(H3:H7)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM(G3:G7)");
|
||||
|
||||
// Data moved as well
|
||||
assert_eq!(model._get_text("G1"), "1");
|
||||
assert_eq!(model._get_text("H1"), "3");
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -532,5 +536,177 @@ fn tets_move_column_error() {
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_row_down() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
// Formulas referencing rows 3 and 4
|
||||
model._set("E3", "=G3");
|
||||
model._set("E4", "=G4");
|
||||
model._set("E5", "=SUM(G3:J3)");
|
||||
model._set("E6", "=SUM(G3:G3)");
|
||||
model._set("E7", "=SUM(G4:G4)");
|
||||
model.evaluate();
|
||||
|
||||
// Move row 3 down by one position
|
||||
let result = model.move_row_action(0, 3, 1);
|
||||
assert!(result.is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E3"), "=G3");
|
||||
assert_eq!(model._get_formula("E4"), "=G4");
|
||||
assert_eq!(model._get_formula("E5"), "=SUM(G4:J4)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM(G4:G4)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM(G3:G3)");
|
||||
|
||||
// Data moved as well
|
||||
assert_eq!(model._get_text("G4"), "-2");
|
||||
assert_eq!(model._get_text("G3"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_row_up() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
// Formulas referencing rows 4 and 5
|
||||
model._set("E4", "=G4");
|
||||
model._set("E5", "=G5");
|
||||
model._set("E6", "=SUM(G4:J4)");
|
||||
model._set("E7", "=SUM(G4:G4)");
|
||||
model._set("E8", "=SUM(G5:G5)");
|
||||
model.evaluate();
|
||||
|
||||
// Move row 5 up by one position
|
||||
let result = model.move_row_action(0, 5, -1);
|
||||
assert!(result.is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E4"), "=G4");
|
||||
assert_eq!(model._get_formula("E5"), "=G5");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM(G5:J5)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM(G5:G5)");
|
||||
assert_eq!(model._get_formula("E8"), "=SUM(G4:G4)");
|
||||
|
||||
// Data moved as well
|
||||
assert_eq!(model._get_text("G4"), "");
|
||||
assert_eq!(model._get_text("G5"), "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_row_error() {
|
||||
let mut model = new_empty_model();
|
||||
model.evaluate();
|
||||
|
||||
let result = model.move_row_action(0, 7, -10);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = model.move_row_action(0, -7, 20);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = model.move_row_action(0, LAST_ROW, 1);
|
||||
assert!(result.is_err());
|
||||
|
||||
let result = model.move_row_action(0, LAST_ROW + 1, -10);
|
||||
assert!(result.is_err());
|
||||
|
||||
// This works
|
||||
let result = model.move_row_action(0, LAST_ROW, -1);
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_row_down_absolute_refs() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
// Absolute references
|
||||
model._set("E3", "=$G$3");
|
||||
model._set("E4", "=$G$4");
|
||||
model._set("E5", "=SUM($G$3:$J$3)");
|
||||
model._set("E6", "=SUM($G$3:$G$3)");
|
||||
model._set("E7", "=SUM($G$4:$G$4)");
|
||||
model.evaluate();
|
||||
|
||||
assert!(model.move_row_action(0, 3, 1).is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E3"), "=$G$3");
|
||||
assert_eq!(model._get_formula("E4"), "=$G$4");
|
||||
assert_eq!(model._get_formula("E5"), "=SUM($G$4:$J$4)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM($G$4:$G$4)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM($G$3:$G$3)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_column_right_absolute_refs() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
// Absolute references
|
||||
model._set("E3", "=$G$3");
|
||||
model._set("E4", "=$H$3");
|
||||
model._set("E5", "=SUM($G$3:$J$7)");
|
||||
model._set("E6", "=SUM($G$3:$G$7)");
|
||||
model._set("E7", "=SUM($H$3:$H$7)");
|
||||
model.evaluate();
|
||||
|
||||
assert!(model.move_column_action(0, 7, 1).is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E3"), "=$H$3");
|
||||
assert_eq!(model._get_formula("E4"), "=$G$3");
|
||||
assert_eq!(model._get_formula("E5"), "=SUM($H$3:$J$7)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM($H$3:$H$7)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM($G$3:$G$7)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_row_down_mixed_refs() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
model._set("E3", "=$G3"); // absolute col, relative row
|
||||
model._set("E4", "=$G4");
|
||||
model._set("E5", "=SUM($G3:$J3)");
|
||||
model._set("E6", "=SUM($G3:$G3)");
|
||||
model._set("E7", "=SUM($G4:$G4)");
|
||||
model._set("F3", "=H$3"); // relative col, absolute row
|
||||
model._set("F4", "=G$3");
|
||||
model.evaluate();
|
||||
|
||||
assert!(model.move_row_action(0, 3, 1).is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E3"), "=$G3");
|
||||
assert_eq!(model._get_formula("E4"), "=$G4");
|
||||
assert_eq!(model._get_formula("E5"), "=SUM($G4:$J4)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM($G4:$G4)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM($G3:$G3)");
|
||||
assert_eq!(model._get_formula("F3"), "=G$4");
|
||||
assert_eq!(model._get_formula("F4"), "=H$4");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_move_column_right_mixed_refs() {
|
||||
let mut model = new_empty_model();
|
||||
populate_table(&mut model);
|
||||
model._set("E3", "=$G3");
|
||||
model._set("E4", "=$H3");
|
||||
model._set("E5", "=SUM($G3:$J7)");
|
||||
model._set("E6", "=SUM($G3:$G7)");
|
||||
model._set("E7", "=SUM($H3:$H7)");
|
||||
model._set("F3", "=H$3");
|
||||
model._set("F4", "=H$3");
|
||||
model.evaluate();
|
||||
|
||||
assert!(model.move_column_action(0, 7, 1).is_ok());
|
||||
model.evaluate();
|
||||
|
||||
assert_eq!(model._get_formula("E3"), "=$H3");
|
||||
assert_eq!(model._get_formula("E4"), "=$G3");
|
||||
assert_eq!(model._get_formula("E5"), "=SUM($H3:$J7)");
|
||||
assert_eq!(model._get_formula("E6"), "=SUM($H3:$H7)");
|
||||
assert_eq!(model._get_formula("E7"), "=SUM($G3:$G7)");
|
||||
assert_eq!(model._get_formula("F3"), "=G$3");
|
||||
assert_eq!(model._get_formula("F4"), "=G$3");
|
||||
}
|
||||
|
||||
// A B C D E F G H I J K L M N O P Q R
|
||||
// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
|
||||
|
||||
@@ -1013,6 +1013,21 @@ impl UserModel {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Moves a column horizontally and adjusts formulas
|
||||
pub fn move_column_action(
|
||||
&mut self,
|
||||
sheet: u32,
|
||||
column: i32,
|
||||
delta: i32,
|
||||
) -> Result<(), String> {
|
||||
self.model.move_column_action(sheet, column, delta)
|
||||
}
|
||||
|
||||
/// Moves a row vertically and adjusts formulas
|
||||
pub fn move_row_action(&mut self, sheet: u32, row: i32, delta: i32) -> Result<(), String> {
|
||||
self.model.move_row_action(sheet, row, delta)
|
||||
}
|
||||
|
||||
/// Sets the width of a group of columns in a single diff list
|
||||
///
|
||||
/// See also:
|
||||
|
||||
Reference in New Issue
Block a user