Paragraph scroll, Clear and Fill - #78
Merged
Merged
Conversation
Three of the smaller gaps in #64, taken together because they are all about the surface a widget sits on rather than the widget. `drawText` wrapped and then drew from the first line. Long wrapped text could be displayed but not scrolled: you had to pre-slice the string, which is wrong the moment the width changes and the line count changes with it. `scroll` and `scrollX` are applied after wrapping, which is the only place either can be correct -- the caller does not know how many lines their text became. `clear` resets a region so an overlay can own it. `modal` already did this privately; without it in the open, anything else that floats is drawn *into* whatever it lands on and shows the widget underneath through the gaps between its words. `fill` floods a region with one repeated symbol. Two details that needed deciding rather than assuming. A horizontal scroll can land in the middle of a double-width character. It cannot draw half of one, so what is left of that character is a space -- which is what a terminal shows when a wide cell is clipped. That is `dropColumns`, new in every port's unicode layer, and it walks graphemes because a byte slice would cut inside one and corrupt it. Styled text needed the same thing without losing the styles of the runs that survive, so `dropSpanColumns` cuts inside whichever run straddles the offset and keeps its style for the remainder. A fill with a wide symbol steps over both cells rather than writing one glyph per column: each glyph owns a continuation cell, and writing the next on top of it leaves a row of half-characters. Where the region does not divide evenly the last glyph is dropped rather than clipped, because half a wide character is not a fill, it is damage. Six ports and seven new fixtures, all additive -- nothing existing moved. Verified: 310 TS tests under bun and node; 76 widget scenes matching the reference in Rust, Go, Python, Zig and C++; 11/11 ctest; every gallery; the site builds. Part of #64 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three of the smaller gaps in #64, taken together because they are all about the surface a widget sits on rather than the widget.
Paragraph scroll
drawTextwrapped and then drew from the first line. Long wrapped text could be displayed but not scrolled: you had to pre-slice the string, which is wrong the moment the width changes and the line count changes with it.scrollandscrollXare applied after wrapping, which is the only place either can be correct — the caller does not know how many lines their text became.Clear and Fill
clearresets a region so an overlay can own it.modalalready did this privately; without it in the open, anything else that floats is drawn into whatever it lands on and shows the widget underneath through the gaps between its words.fillfloods a region with one repeated symbol.Two details that needed deciding
A horizontal scroll can land in the middle of a double-width character. It cannot draw half of one, so what is left of that character is a space — which is what a terminal shows when a wide cell is clipped. That is
dropColumns, new in every port's unicode layer, and it walks graphemes because a byte slice would cut inside one and corrupt it. Styled text needed the same thing without losing the styles of the runs that survive, sodropSpanColumnscuts inside whichever run straddles the offset and keeps its style for the remainder.A fill with a wide symbol steps over both cells rather than writing one glyph per column: each glyph owns a continuation cell, and writing the next on top of it leaves a row of half-characters. Where the region does not divide evenly the last glyph is dropped rather than clipped, because half a wide character is not a fill, it is damage.
Scope
Six ports and seven new fixtures, all additive — nothing existing moved. This is one slice of #64; the calendar and the bounds-aware canvas are still open there, which is why this closes nothing.
Verified
bun testandnode --test, typecheck cleannext buildfor the site🤖 Generated with Claude Code
https://claude.ai/code/session_017Df2FNu5DhinMV2soRz3cy