Skip to content

Centered horizontal reveal (follow-up to #33, stacked on #34/#35/#37)#39

Draft
andrewtheart wants to merge 4 commits into
FrozenAssassine:masterfrom
andrewtheart:feat/centered-horizontal-match-reveal
Draft

Centered horizontal reveal (follow-up to #33, stacked on #34/#35/#37)#39
andrewtheart wants to merge 4 commits into
FrozenAssassine:masterfrom
andrewtheart:feat/centered-horizontal-match-reveal

Conversation

@andrewtheart

Copy link
Copy Markdown
Contributor

Stacked on #34, #35, #37. Builds on the scroll seam (#34), horizontal virtualization (#35) and word wrap
(#37), so until they merge this PR's diff also includes their files. Please review that chain first; I'll
rebase onto master as it lands. Draft for that reason.

Follow-up to #33: centered horizontal reveal

Adds a public ScrollIntoViewHorizontallyCentered() that horizontally centers the current cursor column
in the viewport, and fixes the existing edge-reveal to account for horizontal virtualization.

Why

ScrollIntoViewHorizontal reveals a column at the nearest edge — so jumping to a search match far to the
right lands it pinned against the right edge, which is awkward to read. Centering puts the match in the middle
of the visible width, which is what "jump to match" wants.

While adding it I also had the two share a single slice-aware GetCurrentCursorPixelPositionInLine() helper:
the old edge-reveal computed the column straight from CurrentLineTextLayout and so mis-placed the reveal on
a horizontally-virtualized long line (#35), where the layout is a moving slice. Now both agree.

Changes

  • Core/ScrollManager.cs — new ScrollCursorIntoViewHorizontallyCentered(...); ScrollIntoViewHorizontal
    refactored onto the shared GetCurrentCursorPixelPositionInLine() (slice-aware). Word-wrap pins horizontal
    at 0 (no horizontal scroll), matching the rest of the wrap path.
  • Core/CoreTextControlBox.xaml.cs / TextControlBox.cs — public ScrollIntoViewHorizontallyCentered().
  • Core/Renderer/TextRenderer.csCachedCharWidth made internal so the shared helper can estimate the
    column when the cursor is outside the virtualized slice.

The resolved column is recorded in OldHorizontalScrollValue so the per-draw
EnsureHorizontalScrollBoundsScrollIntoViewHorizontal pass keeps the centered offset instead of
re-revealing at the edge.

Verification

This is scroll/interaction logic over the tested offset seam, so there's no new headless-testable math.
Library + test project build clean (x64).

Introduce a small IScrollOffsetSource abstraction so the editor scroll position is expressed in pixels rather than legacy scrollbar units (SingleLineHeight / DefaultVerticalScrollSensitivity). ScrollManager routes every offset read/write through it; the ScrollBarOffsetSource adapter backs it with the existing two ScrollBar primitives and does the pixel<->scrollbar-unit conversion internally (ScrollOffsetMath). Behavior is unchanged. Foundation for word-wrap scrolling, long-line horizontal virtualization and diagonal 2-axis touchpad scrolling (FrozenAssassine#33). Adds ScrollOffsetMathTests.
On files with pathologically long lines (minified JS/CSS/JSON, JSONL logs), the renderer joined every visible line into one multi-megabyte string and laid it out on every frame, causing horizontal-scroll stutter. TextRenderer.Draw now lays out only a sliced window of each visible line when a visible line exceeds 50k chars; ordinary files keep the untouched render path. The uniform slice window is re-aligned to document coordinates via HorizontalOffset + a per-line prefix sum, and the caret, click hit-testing and selection map through GetRenderedCharacterIndexForDocumentCharacter / GetDocumentCharacterIndexFromRenderedIndex / GetRenderedLayoutIndexForDocument (all no-ops when inactive). The window/index arithmetic is extracted into a WinUI-free HorizontalSliceMath with unit tests. Step 2 of FrozenAssassine#33.
Introduce word wrap on the previously no-wrap-only control. A document line can span multiple visual rows, so scrolling, caret, selection, click hit-testing and arrow navigation work in visual-row space via a WrapRowMetrics prefix-sum model (document line <-> visual row, with cheap incremental updates) and WrapGeometry pointer math, both pure and unit-tested. Excludes very-long-line wrapped-line virtualization (row count falls back to an estimate above 100k chars). Behavior is unchanged when WordWrap is false. Step 3 of FrozenAssassine#33.
New public ScrollIntoViewHorizontallyCentered() centers the current cursor column in the viewport instead of pinning it to an edge (what jump-to-match wants), and the existing edge-reveal is refactored onto a shared slice-aware GetCurrentCursorPixelPositionInLine() so it is correct on horizontally-virtualized long lines. Follow-up to FrozenAssassine#33.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant