text_view: Add `max_lines` to clamp rendered content to whole lines (#2829)
https://github.com/user-attachments/assets/6e83fabd-563b-4293-956f-9487104dbb21
A collapsed preview of rendered Markdown has nowhere clean to end.
Clipping a `TextView` at a fixed height cuts a line of glyphs in half as
soon as a paragraph gap or a heading shifts the layout, so the caller
ends up hiding the cut under a gradient. Truncating the source instead
throws away the formatting the view exists to render.
`max_lines(n)` caps the view at `n` line heights and keeps the cut off
the text:
```rust
TextView::markdown("preview", source).max_lines(5)
```
A line that would straddle the bottom of the box is left out whole, and
so is anything with less than a line of itself to show — the border and
padding a table row leads with strands otherwise, and a few pixels of it
reads as a rendering fault rather than as a row. Whatever has more than
that is cut on the box edge, so an image crossing it shows the part that
fits rather than disappearing and leaving blank space behind.
`TextViewState::is_clamped()` answers for the frame that was last
painted, so an expand button can appear and disappear on its own.
Two things to know: `n` counts lines of body text, so paragraph spacing
and taller lines mean fewer of them fit inside the cap (and a line
taller than the whole budget keeps the part that fits rather than
emptying the box), and there is no ellipsis on the last line — that
needs the line re-shaped, and is its own change. Selection is untouched,
so copy still returns the whole document.
**Note for review:** `TextViewState`'s root now stretches to its box
only when it is *not* clamping. A clamped view has to keep its natural
height, or it cannot measure the overflow it is clipping — with the
content stretched, nothing looked clipped and lines were cut in half
inside a real application layout. The regression test nests the view the
way the example does, because a root without a height of its own does
not reproduce it.
Found while building a collapsed reasoning preview in a downstream app,
where the gradient was covering for the cut.
## Test Plan
- `cargo test -p gpui-component --lib` (464 passed)
- `cargo clippy -p gpui-component -p text_max_lines`
- `cargo run -p text_max_lines` — a slider over a document with nested
lists, a table, inline and block images, and a code fence: the cut stays
off the text at every setting.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com> F
Floyd Wang committed
a9edc3e294a7209e37561ac92e418e910f86f11e
Parent: 26849e0
Committed by GitHub <noreply@github.com>
on 8/26/2026, 5:41:25 AM