ui::TextLayout¶
Header: winui/render/TextLayout.hpp.
Paragraphs of differently styled runs broken into lines, with the geometry selection needs: the engine behind TextControl inlines, RichText and a multi-line TextBox.
Built only on TextMeasurer::measure(), so it lays out the same with every measurer. Lines break greedily after runs of spaces (a trailing space hangs), a word spanning runs stays whole, and under Wrap an overlong word is split between characters. Each piece between break opportunities is measured once and cached, so laying out again at another width measures nothing. All offsets address text(): every run's text in order, paragraphs joined by one \n. Mixed font sizes on one line share a bottom edge rather than a baseline.
Call order: set_content(), then layout(), then the queries and render().
Example:
ui::TextStyle body{.font_size = 14.0f, .horizontal = ui::TextAlign::Leading};
ui::TextStyle bold = body;
bold.weight = ui::FontWeight::Bold;
ui::TextLayout layout; // a member of the element that shows the text
layout.set_content({
{{{"Hello ", bold}, {"world, this wraps.", body}}},
{{{"Second paragraph", body}}, ui::Edges{.top = 8}},
});
// In measure_override(), with the measurer it is handed:
ui::TextLayoutOptions options;
options.max_width = 120.0f;
options.wrapping = ui::TextWrapping::Wrap;
options.base_style = body;
layout.layout(options, measurer);
// In pointer_override():
const std::size_t caret = layout.hit_test({30.0f, 5.0f}); // click -> offset
const ui::Rect bar = layout.caret_rect(caret); // where to draw the caret
// In render_override(), with the renderer it is handed:
ui::TextLayout::Paint paint;
paint.foreground = ui::colors::Black;
paint.selection = ui::TextHighlight{0, 5, ui::colors::White, std::nullopt};
layout.render(renderer, {10.0f, 20.0f}, paint);
TextLayout¶
Empty: no content, no lines.
TextLayout¶
A copy of the content and the last layout, indexing its own copy of the runs.
operator=¶
As the copy constructor.
TextLayout¶
Moves; the runs stay where they are, so the index moves along.
operator=¶
As the move constructor.
~TextLayout¶
set_content¶
Replaces the content. Measurements are kept when only colors or tags changed, and everything is kept when nothing did. Returns whether the text or its formatting changed.
content¶
The paragraphs from the last set_content().
layout¶
Lays the content out from start (an offset into text(); an overflow container's continuation), dropping whatever came before it. A start inside a paragraph drops that paragraph's top margin.
size¶
The widest line (with its paragraph's margins) and every line's height with the margins.
lines¶
The lines of the last layout(), top to bottom.
fragments¶
Every drawn piece of the last layout(), grouped per line (see Line::fragment_begin).
text¶
The whole content as one plain UTF-8 string: runs in order, paragraphs joined by \n. Every offset this class takes or returns indexes into it.
length¶
Byte length of text().
run¶
Run index of the paragraphs' runs, flattened in order (Fragment::run indexes this).
run_count¶
Number of runs across all paragraphs.
start¶
The start offset the last layout() was given; 0 unless this is an overflow continuation.
fitting_lines¶
How many lines from the first fit whole in height: the lines a height-limited block shows before the rest overflows. May be 0.
height_of¶
The height the first count lines take, their paragraphs' margins included.
hit_test¶
The caret offset nearest p (layout coordinates): the line under it (the first above the text, the last below), then the nearer edge of the character under it.
caret_rect¶
The caret at offset: a zero-width rect as tall as its line. An offset where a line wraps is drawn at the start of the next line.
range_rects¶
The rects [begin, end) covers, one per line it touches.
tag_at¶
The tag of the run under p; unset when there is none or p is between runs.
line_of¶
The index of the line holding offset (as caret_rect() places it).
offset_at_x¶
The offset on line line nearest the horizontal position x.
render¶
Draws the laid-out lines with their top-left at origin: highlight and selection backgrounds first, then the text (one draw_text() per run per line, one per word under Justify), then the recolored highlighted/selected text clipped to its range.
struct Fragment¶
One drawn piece of a run on one line. Rect is relative to the layout's origin.
| Field | Type | Default | |
|---|---|---|---|
rect |
Rect |
Bounds of the piece in layout coordinates (DIPs). | |
run |
std::size_t |
0 |
index into runs() (the paragraphs' runs, flattened) |
begin |
std::size_t |
0 |
offsets into text() |
end |
std::size_t |
0 |
one past the piece's last byte in text() |
text |
std::string |
the piece's own text, as drawn |
struct Line¶
One laid-out line: its box, its range of text() and its range of fragments().
| Field | Type | Default | |
|---|---|---|---|
top |
float |
0.0f |
top edge in layout coordinates, margins included |
height |
float |
0.0f |
line box height in DIPs |
left |
float |
0.0f |
where the first fragment starts |
width |
float |
0.0f |
content width, a trailing space excluded |
begin |
std::size_t |
0 |
offsets into text(): the line's characters |
end |
std::size_t |
0 |
one past its last, a hard break's \n excluded |
next |
std::size_t |
0 |
where the next line starts |
fragment_begin |
std::size_t |
0 |
first index into fragments() |
fragment_end |
std::size_t |
0 |
one past the last index into fragments() |
paragraph |
std::size_t |
0 |
index into content() |
hard_break |
bool |
false |
ended by \n or the paragraph's end |
struct Paint¶
Per-draw state for render(): colors, the visible line window, highlights and selection.
| Field | Type | Default | |
|---|---|---|---|
foreground |
Color |
Color of runs that don't set their own. | |
first_line |
std::size_t |
0 |
Only lines [first_line, last_line) are drawn, shifted up so first_line's top sits at the origin; an unset last_line draws to the end. This is how a block shows its part before an overflow takes over. |
last_line |
std::optional<std::size_t> |
||
highlights |
std::span<const TextHighlight> |
Drawn under the selection, each background first, then its foreground over its text. A non-owning view; the highlights must outlive the render() call. | |
selection |
std::optional<TextHighlight> |
The selection, drawn last over the highlights; unset draws none. | |
colors |
std::span<const TextColorSpan> |
Colors for ranges of the text, sorted by begin and not overlapping: the runs' text is drawn in pieces at their edges. Cheaper than foreground highlights for many ranges. A non-owning view; the spans must outlive the render() call. |
|
tag_color |
Callback<std::optional<Color>(std::size_t tag) const> |
Overrides a run's color by its tag (hyperlink hover and press); called for runs with a tag, and nullopt keeps the run's color. |