Table: split and cell fill stay inside the table (#400)

This commit is contained in:
MarekSpodBiedry
2026-10-13 17:38:20 +02:00
parent 457406a7a5
commit 33ade63687
3 changed files with 33 additions and 3 deletions
+5 -1
View File
@@ -205,7 +205,11 @@ fn cell_fill(s: &mut Session, p: &Value) -> Result<Value> {
with_table(s, p, "table.cellFill", move |t| {
let list: Vec<(usize, usize)> = match (&cells, sel) {
(Some(c), _) => c.clone(),
(None, Some((_, a, b))) => (a.0.min(b.0)..=a.0.max(b.0)).flat_map(|r| (a.1.min(b.1)..=a.1.max(b.1)).map(move |c| (r, c))).collect(),
(None, Some((_, a, b))) => {
// The selection can name cells outside the table; only walk the ones that exist.
let (r1, c1) = (a.0.max(b.0).min(t.n_rows().saturating_sub(1)), a.1.max(b.1).min(t.n_cols().saturating_sub(1)));
(a.0.min(b.0)..=r1).flat_map(|r| (a.1.min(b.1)..=c1).map(move |c| (r, c))).collect()
}
_ => (0..t.n_rows()).flat_map(|r| (0..t.n_cols()).map(move |c| (r, c))).collect(),
};
for (r, c) in list {
+14
View File
@@ -835,3 +835,17 @@ fn slide_ranges_parse_like_powerpoints_print_box() {
let opts = cmd::file::pdf_options(&json!({"slides": [0, 7, 1]}), 2).unwrap();
assert_eq!(opts.slides, Some(vec![0, 1]), "indices past the end are dropped");
}
#[test]
fn cell_fill_walks_only_the_cells_a_huge_selection_covers() {
// A selection may reach far past the table; filling it touches the real cells and nothing more.
let mut s = session();
let id = s.execute("insert.table", &json!({"rows": 2, "cols": 2, "rect": [100.0, 100.0, 200.0, 40.0]})).unwrap()["id"].clone();
s.execute("table.selectCells", &json!({"id": id, "from": [1, 1], "to": [1_000_000_000u64, 1_000_000_000u64]})).unwrap();
s.execute("table.cellFill", &json!({"none": true})).unwrap();
let st = s.doc().unwrap();
let sh = st.shapes().iter().find(|sh| json!(sh.id) == id).unwrap();
let deckcraft_model::ShapeKind::Table(t) = &sh.kind else { panic!("not a table") };
let fills: Vec<bool> = [(0, 0), (0, 1), (1, 0), (1, 1)].iter().map(|&(r, c)| t.cell(r, c).is_some_and(|x| x.fill.is_some())).collect();
assert_eq!(fills, [false, false, false, true]);
}
+14 -2
View File
@@ -178,8 +178,9 @@ impl Table {
if gs == 1 && rs == 1 {
return false;
}
for rr in r..r + rs {
for cc in c..c + gs {
// Spans come from the file and may reach past the table; only walk the cells that exist.
for rr in r..r.saturating_add(rs).min(self.rows.len()) {
for cc in c..c.saturating_add(gs).min(self.cols.len()) {
if let Some(x) = self.cell_mut(rr, cc) {
x.h_merge = false;
x.v_merge = false;
@@ -349,4 +350,15 @@ mod tests {
assert!(!one.delete_row(0) && !one.delete_col(0));
assert!(table_styles().len() > 30);
}
#[test]
fn split_ignores_spans_past_the_table() {
let mut t = Table::new(2, 2, 100.0, 10.0);
if let Some(c) = t.cell_mut(0, 0) {
c.grid_span = u32::MAX;
c.row_span = u32::MAX;
}
assert!(t.split(0, 0));
assert_eq!(t.cell(0, 0).map(|c| (c.grid_span, c.row_span)), Some((1, 1)));
}
}