Tables: one selected cell's border drags in its row only (#970)

Selecting one cell (Table Layout > Select > Select Cell, or a click along
its left edge) and dragging its left or right border moved the border in
the whole column: one selected cell wasn't told apart from text selected
inside it. `ResizeTarget::limit_to_selection` now takes the selected cells
from `Session::cell_range` (#637) when they're in the dragged table, so one
selected cell's border moves in its row only, as in Word, like the border
of cells selected across (#720). Text selected within a cell still moves
the whole column.

Tests: engine `a_selected_cells_border_moves_only_in_its_row`; kittest
`a_selected_cells_border_drags_in_its_row_only` (a click along the cell's
edge, then a real drag). Progress docs: ui-parity, ROADMAP.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
This commit is contained in:
rabis7575
2026-10-15 00:35:32 -04:00
committed by GitHub
co-authored by Claude Opus 5.5 Brandon Thomas
parent 5c8dba229f
commit cb6418dc6c
4 changed files with 79 additions and 19 deletions
+2
View File
@@ -278,6 +278,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
| 2026-10-11 | Hide White Space (#645): `view.hideWhiteSpace` (View › Show checkbox, or double-click the gap between pages) collapses margins, headers and footers between pages to a thin line in Print Layout; screen only, page count unchanged |
| 2026-10-11 | Fonts: Palatino Linotype and Book Antiqua fall back to Palatino (macOS) or TeX Gyre Pagella / P052 / URW Palladio (Linux) before generic serif (#670) |
|---|---|
| 2026-10-12 | Tables: dragging the border of one selected cell (Select Cell, or a click along its left edge) moves it in that cell's row only, as in Word, like the border of cells selected across (#962) |
| 2026-10-13 | Korean interface catalog completed: all 2,463 entries translated; the ko-KR system locale and saved language choice select the interface. The UI prefers Korean embedded/system font faces so Hangul renders instead of boxes, and print settings and printer feedback are translated. Korean proofing remains unavailable; macOS Korean IME fixes are still pending (#163, #164) |
| 2026-10-12 | Word default shortcuts expanded (gap #24): 140 commands bound on macOS (169 combinations), 158 on Linux/Windows (188 combinations). Bound Style Inspector (Shift+F1), Macros (Alt+F8), Strikethrough (Mod+Shift+X), Find Next (Shift+F4), Copy/Paste (Mod+Insert, Shift+Insert), Go Back (Mod+Alt+Z), File Open/Save/Save As/Print (Mod+Alt+F2, Alt+Shift+F2, Alt+F2, Mod+Alt+I), Field navigation (Alt+F1, Alt+Shift+F1), Outline show first line (Alt+Shift+L), Next Footnote (Alt+Shift+>), Link to Previous (Alt+Shift+R), AutoText (Alt+F3), Spelling on Mac (Mod+Alt+L) |
| 2026-10-13 | Web and Draft Layout now render visible full-resolution raster tiles for exceptionally tall pages rather than downsampling the entire page into one texture; verified with a 440-paragraph headless UI reproduction |
@@ -395,6 +396,7 @@ Ranked, to beta (detail and remaining milestones in [docs/roadmap.md](docs/roadm
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | trivial | Progress log: one selected cell's border drags in its row only (#962) |
| 2026-10-12 | minor | Progress log: Word default shortcuts expanded to 140 commands on macOS (169 combinations) and 158 on Linux/Windows (188 combinations), addressing gap #24 |
| 2026-10-13 | trivial | Web/Draft long-page blur fixed with visible full-resolution raster tiles |
| 2026-10-14 | trivial | Progress log: VML freeforms read and kept on save |
+54 -19
View File
@@ -210,30 +210,20 @@ impl ResizeTarget {
}
}
/// When the selection runs across cells of this table (Word selects them whole) and the
/// column border grabbed at page y `y` is one of theirs, in one of their rows, the drag
/// moves it only in those rows (`only`), as Word does with selected cells' borders. Not the
/// table's left edge, which moves the whole table, nor a border of a cell merged down into
/// rows outside the selection. Text selected within one cell isn't a cell selection.
/// When cells of this table are selected whole (one cell too) or the selection runs across
/// cells of it (Word selects them whole), and the column border grabbed at page y `y` is one
/// of theirs, in one of their rows, the drag moves it only in those rows (`only`), as Word
/// does with selected cells' borders. Not the table's left edge, which moves the whole table,
/// nor a border of a cell merged down into rows outside the selection. Text selected within
/// one cell isn't a cell selection.
pub fn limit_to_selection(&mut self, s: &Session, y: f32) {
self.only.clear();
let Edge::Column(line) = self.edge else { return };
let (a, b) = s.sel.ordered();
if line == 0 || s.sel.is_collapsed() || a.story != self.story || b.story != self.story {
if line == 0 {
return;
}
let (Some(t), Some((ra, ca)), Some((rb, cb))) =
(s.doc.table(self.story, &self.table), cell_in(&self.table, &a.path), cell_in(&self.table, &b.path))
else {
return;
};
if (ra, ca) == (rb, cb) {
return;
}
let span = |r: usize, c: usize| t.rows.get(r).and_then(|row| row.cells.get(c)).map_or(1, Cell::span);
let (ga, gb) = (t.grid_col(ra, ca), t.grid_col(rb, cb));
let (lo, hi) = (ga.min(gb), (ga + span(ra, ca)).max(gb + span(rb, cb)));
let (r0, r1) = (ra.min(rb), ra.max(rb));
let Some(t) = s.doc.table(self.story, &self.table) else { return };
let Some(((r0, r1), (lo, hi))) = self.selected_block(s, t) else { return };
let here = self.rows.iter().find(|r| y >= r.top && y <= r.bottom).map(|r| r.row);
if line < lo || line > hi || !here.is_some_and(|r| (r0..=r1).contains(&r)) {
return;
@@ -258,6 +248,30 @@ impl ResizeTarget {
}
self.only = only;
}
/// The selected cells of this table, as their rows (first, last) and the grid lines of
/// their outer borders (left, right): whole cells (`Session::cell_range`), or a selection
/// running from one of its cells into another. `None` for text within one cell.
fn selected_block(&self, s: &Session, t: &Table) -> Option<((usize, usize), (usize, usize))> {
let lines = |cells: &[(usize, usize)]| -> Option<(usize, usize)> {
let span = |r: usize, c: usize| t.rows.get(r).and_then(|row| row.cells.get(c)).map_or(1, Cell::span);
let lo = cells.iter().map(|&(r, c)| t.grid_col(r, c)).min()?;
let hi = cells.iter().map(|&(r, c)| t.grid_col(r, c).saturating_add(span(r, c))).max()?;
Some((lo, hi))
};
if let Some(range) = s.cell_range().filter(|c| c.story == self.story && c.table == self.table) {
return Some((range.rows, lines(&range.cells(t))?));
}
let (a, b) = s.sel.ordered();
if s.sel.is_collapsed() || a.story != self.story || b.story != self.story {
return None;
}
let ((ra, ca), (rb, cb)) = (cell_in(&self.table, &a.path)?, cell_in(&self.table, &b.path)?);
if (ra, ca) == (rb, cb) {
return None;
}
Some(((ra.min(rb), ra.max(rb)), lines(&[(ra, ca), (rb, cb)])?))
}
}
/// Moves the border right of column `i` of `w` (column widths, left to right) by up to `dx`
@@ -964,6 +978,27 @@ mod tests {
assert_eq!(first_table(&s), start, "one undo step each");
}
/// One selected cell (Select Cell, unlike its text) is a cell selection too: its border
/// moves in its row only (#962).
#[test]
fn a_selected_cells_border_moves_only_in_its_row() {
let mut s = table_session(3, 3);
let (b, c) = (cell_rect(&mut s, 1, 1), cell_rect(&mut s, 1, 2));
let y = b.y + b.h / 2.0;
s.run("caret.set", &json!({"page": 0, "x": b.x + b.w / 2.0, "y": y})).unwrap();
s.run("table.selectCell", &json!({})).unwrap();
assert!(s.cell_range().is_some());
let start = first_table(&s);
s.run("table.resizeDrag", &json!({"page": 0, "x": b.right(), "y": y, "toX": b.right() + 30.0, "toY": y})).unwrap();
let (b1, c1) = (cell_rect(&mut s, 1, 1), cell_rect(&mut s, 1, 2));
assert!((b1.w - (b.w + 30.0)).abs() < 1.0 && (c1.w - (c.w - 30.0)).abs() < 1.0, "{b1:?} {c1:?}");
for r in [0, 2] {
assert!((cell_rect(&mut s, r, 1).right() - b.right()).abs() < 0.5, "row {r} keeps its border");
}
s.run("edit.undo", &json!({})).unwrap();
assert_eq!(first_table(&s), start, "one undo step");
}
/// A cell merged down past the selection keeps one width: a border of it moves in the whole
/// column.
#[test]
+22
View File
@@ -565,6 +565,28 @@ pub(crate) mod tests {
assert!(widths(&mut h).is_empty(), "gone with the drag");
}
/// A cell selected with a click along its left edge: dragging its right border with the
/// mouse moves it in that cell's row only (#962).
#[test]
fn a_selected_cells_border_drags_in_its_row_only() {
let mut h = table_window();
let (above, b, c) = (cell(&mut h, 0, 1), cell(&mut h, 1, 1), cell(&mut h, 1, 2));
let y = b.y + b.h / 2.0;
let at = screen(&mut h, b.x + 4.5, y);
h.event(egui::Event::PointerMoved(at));
h.step();
button(&h, at, true);
h.step();
button(&h, at, false);
steps(&mut h, 2);
assert_eq!(h.state().session.cell_range().map(|r| (r.rows, r.cols)), Some(((1, 1), (1, 1))), "the cell is selected");
let (start, end) = (screen(&mut h, b.right(), y), screen(&mut h, b.right() + 30.0, y));
drag(&mut h, start, end);
let (b1, c1) = (cell(&mut h, 1, 1), cell(&mut h, 1, 2));
assert!((b1.w - (b.w + 30.0)).abs() < 1.0 && (c1.w - (c.w - 30.0)).abs() < 1.0, "{b1:?} {c1:?}");
assert!((cell(&mut h, 0, 1).right() - above.right()).abs() < 0.5, "the first row keeps its border");
}
/// Clicks inside a cell still place the caret (the border doesn't steal them), and Esc drops a
/// drag without changing anything.
#[test]
+1
View File
@@ -52,6 +52,7 @@ Document title opens Rename Document; the account badge opens Options › Genera
| Date | Change | Summary |
|---|---|---|
| 2026-10-12 | trivial | Tables on the page: one selected cell (Select Cell, or a click along its left edge) is a cell selection when its border is dragged: the border moves in its row only (#962) |
| 2026-10-12 | minor | Default keyboard shortcuts expanded to 140 commands on macOS (169 combinations) and 158 on Linux/Windows (188 combinations): Style Inspector (Shift+F1), Macros (Alt+F8), Strikethrough (Mod+Shift+X), Find Next (Shift+F4), Copy/Paste Insert keys (Mod+Insert, Shift+Insert), Go Back (Mod+Alt+Z), File Open/Save/Save As/Print (Mod+Alt+F2, Alt+Shift+F2, Alt+F2, Mod+Alt+I), Field navigation (Alt+F1, Alt+Shift+F1), Outline show first line (Alt+Shift+L), Next Footnote (Alt+Shift+>), Link to Previous (Alt+Shift+R), AutoText (Alt+F3), Spelling on Mac (Mod+Alt+L) |
| 2026-10-13 | trivial | Web/Draft Layout long-page blur fixed using visible full-resolution raster tiles, tested against a 440-paragraph document |
| 2026-10-14 | trivial | A click on the ribbon's chrome that isn't a command button (a Styles gallery tile, a tab name, empty ribbon space, a View check box, a status-bar item) hands the keyboard to the page in the same frame, as a ribbon command button does: typing right after the click reaches the document instead of being swallowed (#1093 follow-up) |