Skip to content

fix(datagrid): overlay the inline cell editor exactly on the drawn cell - #2579

Open
shreeve wants to merge 1 commit into
TableProApp:mainfrom
shreeve:fix/datagrid-inline-editor-alignment
Open

fix(datagrid): overlay the inline cell editor exactly on the drawn cell#2579
shreeve wants to merge 1 commit into
TableProApp:mainfrom
shreeve:fix/datagrid-inline-editor-alignment

Conversation

@shreeve

@shreeve shreeve commented Aug 29, 2026

Copy link
Copy Markdown

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 at x = minX + DataGridMetrics.cellHorizontalInset with the baseline centered in the row. The overlay (CellOverlayBase) sized itself from boundingRectForFont.height + 12, anchored at the cell top, with an NSTextView left on its default 5pt lineFragmentPadding and zero textContainerInset. 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 while CTLineDraw honors 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 detached NSLayoutManager. Reading the text view's own layoutManager would 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 through textLayoutManager.

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 (textContainerInset is 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-only CellOverlayViewer shares all of it.

Glyph-origin parity was probe-verified at delta 0.000 across three fonts before implementation, and frameOfCell(atColumn:row:) vs rect(ofColumn:) was measured to agree on x for every editable column.

Tests

DataGridCellTextGeometryTests (new):

  • Baseline parity measured through the overlay's real TextKit 2 layout (NSTextLineFragment.glyphOrigin), all four row heights x three fonts, including a font taller than the 20pt row.
  • Single-line overlay frame == cell frame at every row height.
  • Multiline height pinned against a raw NSLayoutManager and the written-out formula, so a drift in the shared geometry fails the test instead of being copied into the expectation.
  • Line-break counting parity with TextKit, and the rect-source agreement pin.

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 status cell 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.

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Aug 29, 2026

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review Completed 2026-08-29T21:29:29.967014Z ac94299 PR opened
ℹ️ 关于 Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

注册 for free to join this conversation on GitHub. Already have an account? 登录 to comment

标签

None yet

项目

None yet

Development

Successfully merging this pull request may close these issues.

1 participant