Tables: Tab and Shift+Tab skip cells hidden under a merge

Tab moved into the cells covered by a merged cell, editing text that is
never drawn. It now goes to the next visible cell; Tab in the last
visible cell still adds a row, and Shift+Tab in the first cell stays.
This commit is contained in:
pierrevbc
2026-10-13 11:30:13 +02:00
parent 456c5c5373
commit d63d250d58
2 changed files with 34 additions and 8 deletions
+15 -8
View File
@@ -285,15 +285,22 @@ impl Session {
let Some(t) = st.selection.text.clone() else { return Ok(Value::Null) };
let Some((r, c)) = t.cell else { return Ok(Value::Null) };
let Some(deckcraft_model::ShapeKind::Table(tb)) = st.shape(t.shape).map(|s| s.kind.clone()) else { return Ok(Value::Null) };
let (nr, nc) = (tb.n_rows(), tb.n_cols());
let (nr, nc) = (tb.n_rows(), tb.n_cols().max(1));
let idx = r * nc + c;
if !back && idx + 1 >= nr * nc {
// Tab in the last cell adds a row.
self.execute("table.insertRowBelow", &json!({"id": t.shape, "row": r}))?;
return self.execute("text.edit", &json!({"id": t.shape, "cell": [r + 1, 0]}));
}
let ni = if back { idx.saturating_sub(1) } else { idx + 1 };
self.execute("text.edit", &json!({"id": t.shape, "cell": [ni / nc.max(1), ni % nc.max(1)]}))?;
// Cells hidden under a merged cell are skipped.
let visible = |i: &usize| tb.cell(i / nc, i % nc).is_some_and(|x| !x.is_covered());
let next = if back { (0..idx).rev().find(visible) } else { (idx + 1..nr * nc).find(visible) };
let ni = match next {
Some(ni) => ni,
None if !back => {
// Tab in the last cell adds a row.
let last = nr.saturating_sub(1);
self.execute("table.insertRowBelow", &json!({"id": t.shape, "row": last}))?;
return self.execute("text.edit", &json!({"id": t.shape, "cell": [last + 1, 0]}));
}
None => idx,
};
self.execute("text.edit", &json!({"id": t.shape, "cell": [ni / nc, ni % nc]}))?;
self.execute("text.selectAll", &json!({}))
}
}
+19
View File
@@ -676,6 +676,25 @@ fn rotated_tables_hit_the_cell_under_the_pointer() {
assert_eq!(text_sel(&s).and_then(|t| t.cell), Some((1, 1)));
}
#[test]
fn tab_moves_between_cells_skipping_merged_ones() {
let mut s = session();
let id = table_at(&mut s, &[100.0, 100.0, 100.0], json!([["a", "b", "c"], ["d", "e", "f"]]));
s.execute("table.merge", &json!({"id": id, "from": [0, 0], "to": [0, 1]})).unwrap();
s.execute("text.edit", &json!({"id": id, "cell": [0, 0]})).unwrap();
let cell = |s: &Session| text_sel(s).and_then(|t| t.cell);
let shift = Mods { shift: true, ..Default::default() };
s.key("Tab", Mods::default()).unwrap();
assert_eq!(cell(&s), Some((0, 2)), "the cell under the merge is skipped");
s.key("Tab", shift).unwrap();
assert_eq!(cell(&s), Some((0, 0)));
s.key("Tab", shift).unwrap();
assert_eq!(cell(&s), Some((0, 0)), "Shift+Tab in the first cell stays");
s.execute("text.edit", &json!({"id": id, "cell": [1, 2]})).unwrap();
s.key("Tab", Mods::default()).unwrap();
assert_eq!((cell(&s), table_of(&s, id).n_rows()), (Some((2, 0)), 3), "Tab in the last cell adds a row");
}
#[test]
fn right_to_left_paragraphs() {
let mut s = session();