fix(datagrid): overlay the inline cell editor exactly on the drawn cell - #2579
fix(datagrid): overlay the inline cell editor exactly on the drawn cell#2579shreeve wants to merge 1 commit into
Conversation
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
|
Thanks for the write-up and for measuring before you wrote code. The bug is real: the overlay was taller than the row, it top-aligned its text, and its glyphs started 1pt right of the drawn ones. Opening an edit should not move the value. We are not taking this change. Three reasons, heaviest first. 1. It moves the drawn text of every cell to accommodate the editor
Measured against the default grid font,
Nearly a full point, not half of one, and it lands on the default configuration rather than an edge case. It also varies by font (0.1543 for Menlo 12pt, 0.5352 for System Mono 12pt, 0.9131 for System Mono 13pt), so "uniformly" holds within a font and not across the setting. The editor does not need 2. It makes the "you are editing" affordance fainter than the cursor it replaces
This PR takes the overlay to 1pt and leaves the cursor at 2pt, so committing to an edit makes the indicator thinner than merely putting the cursor on the cell. That reads backwards, and it is the only signal left: The stated end result does not hold either. "The only change is a 1px focus-colored stroke" is true for glyph position and not for the cell. The overlay paints 3. The load-bearing constant is an undocumented equality between the two TextKit engines
Two smaller instances of the same habit: What we would takeA narrow PR for the two parts nothing else explains away:
For the vertical alignment we want the editor to meet the drawn baseline, not the renderer to move to meet the editor. Keep the centered fractional baseline in Closing rather than running a long review thread on a change we would end up rewriting. The measurement work here is good and the bug stays on our list. Thanks for taking the time. |
|
Thanks for your review. I stopped using TablePro and I built a new DuckDB viewer in Rust with GPUI called DuckTable at: |
Problem
Opening an inline edit on a data-grid cell visibly shifted the value it was editing. The editor overlay was taller than the row at every row-height setting (spilling over the row below), its text sat at the top of the overlay while the drawn cell centers its baseline in the row, and its glyphs started 1pt right of the drawn text. Entering edit mode should not move the glyphs at all.
Root cause
Two unrelated geometry owners for the same glyphs. The CoreText renderer (
DataGridCellRenderer) draws atx = minX + DataGridMetrics.cellHorizontalInsetwith the baseline centered in the row. The overlay (CellOverlayBase) sized itself fromboundingRectForFont.height + 12, anchored at the cell top, with anNSTextViewleft on its default 5ptlineFragmentPaddingand zerotextContainerInset. Nothing shared a constant, so height, baseline and x inset all disagreed.Measured on macOS 15 (mono 12pt, 28pt row): overlay 30.33pt tall in a 28pt row, first baseline at 12.0 (top-aligned) vs 18.54 drawn, glyph x 5 vs 4.
Fix
One geometry owner,
DataGridCellTextGeometry, consumed by both sides:baselineY(rowHeight:font:): the centered-baseline formula, floored to a whole point. Measured at 1x and 2x backing: TextKit floors rendered baselines to integral points at every scale whileCTLineDrawhonors fractions, so the drawn side adopts the floor (moves at most 0.5pt, once, uniformly) and the editor lands on it exactly.textContainerTopInset(rowHeight:font:):baselineY - defaultBaselineOffset(for: font), computed from a detachedNSLayoutManager. Reading the text view's ownlayoutManagerwould downgrade it to TextKit 1 and revert the no-wrap overlay layout (Data grid breaks with many columns: flickering, columns stop rendering, horizontal scroll lags behind viewport #2381), so neither the code nor the tests touch it; the parity test measures throughtextLayoutManager.A single-line value's overlay is now exactly the cell rect, with the vertical scroller and elasticity off (a grid font taller than the row would otherwise scroll its own descenders). A value that breaks into lines still grows, capped at 120pt, with its height derived from the same geometry (
textContainerInsetis symmetric, so the content pays the top inset twice). Line breaks are counted the way TextKit lays them out (LF, lone CR, NEL, U+2028, U+2029, CRLF as one), and an edit that becomes multiline after opening (Option+Return, paste) reframes the overlay and re-arms the scroller. The border slimmed from 2pt to a 1px stroke; a layer border paints over edge pixels and cannot displace glyphs. The read-onlyCellOverlayViewershares all of it.Glyph-origin parity was probe-verified at delta 0.000 across three fonts before implementation, and
frameOfCell(atColumn:row:)vsrect(ofColumn:)was measured to agree on x for every editable column.Tests
DataGridCellTextGeometryTests(new):NSTextLineFragment.glyphOrigin), all four row heights x three fonts, including a font taller than the 20pt row.NSLayoutManagerand the written-out formula, so a drift in the shared geometry fails the test instead of being copied into the expectation.All suites owning the touched types pass (128 tests):
DataGridCellTextGeometryTests,CellOverlayTextLayoutTests,CellOverlayAppearanceTests,DataGridCellAccessoryAppearanceTests,KeyHandlingTableViewOverlayTests,TableViewCoordinatorLayoutTests,ValueFontTests.The open-editor-and-type flow itself is not UI-automated: the overlay editor requires a live connection and grid focus, which does not run deterministically on the CI runner (the drawn grid publishes cells XCUITest refuses to click; see the accessibility invariant). The geometry it exercises is covered by the unit suites above.
Before / After
Before: opening an edit on the
statuscell of row 4 shows a bordered box 1.5 rows tall covering the top of row 5, with the value shifted up and left.After: the value does not move; the only change is a 1px focus-colored stroke around the cell.
Screenshots to be attached as a comment.