RichText¶
Header: winui/ui/text/RichText.hpp. Spec: winui/ui/text/RichText.hpp.
Read-only paragraphs of formatted inlines, wrapping by default, selectable, with links and highlighters. Content that doesn't fit can flow into RichTextOverflow containers.
The content is set with set_blocks(), as Paragraphs of Inlines. Offsets, such as the selection and TextHighlighter ranges, count in plain_text(). Selection is on by default. The user can drag, double-click a word, triple-click a paragraph, press Ctrl+A and Ctrl+C, and use a context menu with Copy and Select All. In a spec tree it is built by ui::RichText.
Example:
auto row = std::make_unique<ui::StackControl>(ui::Orientation::Horizontal);
auto* rich = row->add(std::make_unique<ui::RichTextControl>());
auto* overflow = row->add(std::make_unique<ui::RichTextOverflowControl>()); // measured after it
rich->set_blocks({
{.inlines = {{.text = "RichText shows "}, {.text = "formatted", .weight = ui::FontWeight::Bold},
{.text = " paragraphs. "}, {.text = "Links", .navigate_uri = "https://example.com/"},
{.text = " work too."}}},
{.inlines = {{.text = "What does not fit the block's height goes on in the overflow."}},
.margin = ui::Edges{.top = 12.0f}},
});
rich->set_text_alignment(ui::TextAlignment::Justify);
rich->set_width(200.0f);
rich->set_height(120.0f);
overflow->set_width(200.0f);
rich->set_overflow_target(overflow);
window.set_content(std::move(row));
ui::RichText¶
Paragraphs of formatted inlines. Builds a ui::RichTextControl.
Read-only, selectable by default and wrapping by default. Content that does not fit can flow on into a ui::RichTextOverflow.
Example:
window.set_content(ui::RichText{
.blocks = {
{ .inlines = { { .text = "I am a " },
{ .text = "RichText", .weight = ui::FontWeight::Bold },
{ .text = " with an ", .italic = true },
{ .text = "italic run." } } },
{ .inlines = { { .text = "A second paragraph." } }, .margin = ui::Edges{.top = 8} },
},
.max_width = 400,
});
Every field, in the order a designated initializer names them:
ui::RichText{
.blocks, .selection_enabled, .text_highlighters, .overflow, .text_style, .wrapping,
.text_alignment, .character_spacing, .selection_highlight_color, .foreground, .style,
.margin, .width, .height, .min_width, .min_height, .max_width, .max_height,
.horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .accessibility,
.on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
.on_wheel, .control
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
blocks |
ui::Binding<std::vector<ui::Paragraph>> |
The content: paragraphs of inlines. Space paragraphs apart with each Paragraph's margin. |
|
selection_enabled |
ui::Binding<bool> |
{true} |
The user can select the text and copy it; on by default. |
text_highlighters |
ui::Binding<std::vector<ui::TextHighlighter>> |
{std::vector<ui::TextHighlighter>{}} |
Ranges painted with their own background (and foreground). |
overflow |
ui::ControlLink<ui::RichTextOverflow> |
Where the content that does not fit continues, by the RichTextOverflow's handle (RichTextControl::set_overflow_target()). | |
text_style |
ui::Binding<std::optional<ui::TextStyle>> |
Base style for every run; unset follows the theme's body text. | |
wrapping |
ui::Binding<ui::TextWrapping> |
{ui::TextWrapping::Wrap} |
Whether lines break at the available width; Wrap by default. |
text_alignment |
ui::Binding<ui::TextAlignment> |
{ui::TextAlignment::Left} |
How each line sits between the left and right edges. |
character_spacing |
ui::Binding<int> |
{0} |
Extra space between characters, in thousandths of an em. |
selection_highlight_color |
ui::Binding<ui::Paint> |
The color behind selected text: a fixed color or a theme resource, which follows theme switches. Unset is the accent. | |
foreground |
ui::Binding<ui::Paint> |
The color of runs without a color of their own: a fixed color or a theme resource. Unset uses the theme's text color. | |
on_pointer_entered |
ui::Action |
The pointer moved onto the control. | |
on_pointer_exited |
ui::Action |
The pointer left the control. | |
on_got_focus |
ui::Action |
Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this. | |
on_lost_focus |
ui::Action |
Keyboard focus left the control and everything inside it. | |
on_key_down |
ui::Callback<ui::Handling(const ui::KeyEvent&)> |
A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()). | |
on_wheel |
ui::Callback<ui::Handling(const ui::PointerEvent&)> |
A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()). | |
control |
ui::ControlOut<ui::RichText> |
Out: the handle that receives the built control. |
Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, accessibility every spec carries (conventions).
build¶
Consumes the spec and returns the built ui::RichTextControl; the caller owns it.
ui::RichTextOverflow¶
Where a RichText's content continues when it does not fit. Builds a ui::RichTextOverflowControl, named by the RichText's .overflow, or by the previous RichTextOverflow's for a chain. It has no content of its own. It shows whatever of its source's paragraphs the previous container in the chain could not fit.
Example:
const std::string long_text = "A text longer than one column holds...";
ui::Control<ui::RichTextOverflow> more;
window.set_content(ui::HorizontalStack{
.spacing = 12,
.height = 300,
.content = {
ui::RichText{ .blocks = {{ .inlines = {{ .text = long_text }} }}, .overflow = more, .width = 240 },
ui::RichTextOverflow{ .width = 240, .control = more }, // continues where the text stops
},
});
Every field, in the order a designated initializer names them:
ui::RichTextOverflow{
.overflow, .style, .margin, .width, .height, .min_width, .min_height, .max_width,
.max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip,
.accessibility, .on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus,
.on_key_down, .on_wheel, .control
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
overflow |
ui::ControlLink<ui::RichTextOverflow> |
The next container of the chain, by its handle (RichTextOverflowControl::set_overflow_target()). | |
on_pointer_entered |
ui::Action |
The pointer moved onto the control. | |
on_pointer_exited |
ui::Action |
The pointer left the control. | |
on_got_focus |
ui::Action |
Keyboard focus arrived on the control, or on anything inside it, from outside it. A control with nothing focusable in it never gets focus, and never raises this. | |
on_lost_focus |
ui::Action |
Keyboard focus left the control and everything inside it. | |
on_key_down |
ui::Callback<ui::Handling(const ui::KeyEvent&)> |
A key pressed while focus is on the control or inside it, before the control's own handling; ui::Handled stops it there (Element::on_key_down()). | |
on_wheel |
ui::Callback<ui::Handling(const ui::PointerEvent&)> |
A wheel turn over the control, before the control scrolls; ui::Handled stops it there (Element::on_wheel()). | |
control |
ui::ControlOut<ui::RichTextOverflow> |
Out: a non-owning observer of the built control; needed to link it into a chain. |
Plus the common fields style, margin, width, height, min_width, min_height, max_width, max_height, horizontal_alignment, vertical_alignment, visible, tool_tip, accessibility every spec carries (conventions).
build¶
Consumes the spec and returns the built ui::RichTextOverflowControl; the caller owns it.
ui::RichTextControl¶
Derives from ui::Element.
Read-only paragraphs of formatted inlines, wrapping by default, selectable, with links and highlighters. Content that doesn't fit can flow into RichTextOverflow containers.
The content is set with set_blocks(), as Paragraphs of Inlines. Offsets, such as the selection and TextHighlighter ranges, count in plain_text(). Selection is on by default. The user can drag, double-click a word, triple-click a paragraph, press Ctrl+A and Ctrl+C, and use a context menu with Copy and Select All. In a spec tree it is built by ui::RichText.
Example:
auto row = std::make_unique<ui::StackControl>(ui::Orientation::Horizontal);
auto* rich = row->add(std::make_unique<ui::RichTextControl>());
auto* overflow = row->add(std::make_unique<ui::RichTextOverflowControl>()); // measured after it
rich->set_blocks({
{.inlines = {{.text = "RichText shows "}, {.text = "formatted", .weight = ui::FontWeight::Bold},
{.text = " paragraphs. "}, {.text = "Links", .navigate_uri = "https://example.com/"},
{.text = " work too."}}},
{.inlines = {{.text = "What does not fit the block's height goes on in the overflow."}},
.margin = ui::Edges{.top = 12.0f}},
});
rich->set_text_alignment(ui::TextAlignment::Justify);
rich->set_width(200.0f);
rich->set_height(120.0f);
overflow->set_width(200.0f);
rich->set_overflow_target(overflow);
window.set_content(std::move(row));
RichTextControl¶
An empty block: Wrap, Left alignment, selection enabled.
~RichTextControl¶
Unlinks any overflow containers, which then show nothing.
RichTextControl¶
operator=¶
blocks¶
The paragraphs, as set by set_blocks().
set_blocks¶
Replaces the content and clears the selection.
plain_text¶
The content as plain text: the runs in order, paragraphs separated by \n. Every offset (selection, highlighters) counts in this text.
text_style¶
The base style every run derives from (font family, size, weight).
set_text_style¶
The base style every run derives from. Unset (the default) is the theme's body text. Once set, the theme's body style no longer applies.
foreground¶
The color of runs without a color of their own: a fixed color or a theme resource. Unset follows the theme's text color.
set_foreground¶
Sets the color of runs without their own; an empty source follows the theme.
wrapping¶
Whether lines break at the width; Wrap by default.
set_wrapping¶
Sets the wrapping mode, for the block and its overflow containers.
text_alignment¶
The alignment of every paragraph, Justify included; Left by default.
set_text_alignment¶
Sets the alignment, for the block and its overflow containers.
character_spacing¶
Extra space added after every character, in thousandths of an em; 0 by default.
set_character_spacing¶
Sets the character spacing, for the block and its overflow containers.
selection_enabled¶
Whether the user can select and copy the text; true by default. While on, the block is a tab stop.
set_selection_enabled¶
Turns selection on or off. Either change clears the selection.
selection_highlight_color¶
The fixed color behind selected text; nullopt (the default) when unset or a theme resource is set. Unset is the accent.
selection_highlight_resource¶
The color behind selected text as a theme resource, or nullptr.
set_selection_highlight_color¶
Sets the fixed color behind selected text and clears a theme resource; nullopt goes back to the accent.
set_selection_highlight_color¶
Sets the color behind selected text to a theme resource, re-read under every theme, and clears a fixed one.
selection¶
The selection as [start, end) offsets into plain_text(); empty when start == end.
select¶
Selects [start, end) of plain_text(). The selection shows only while the block, or one of its overflow containers, has focus.
select_all¶
Selects the whole plain_text() (Ctrl+A).
selected_text¶
The selected text; what Ctrl+C copies.
text_highlighters¶
The highlighters painted over the text.
set_text_highlighters¶
Replaces every highlighter.
overflow_target¶
The first overflow container (non-owning), or nullptr.
set_overflow_target¶
Sets where content that does not fit continues. The overflow must be in the same tree and measured after this block, such as a later sibling or a later cell of the same Grid. nullptr unlinks it.
has_overflow_content¶
Whether some of the content did not fit and went on to the overflow target.
focusable¶
A tab stop while text selection is enabled.
focused¶
Whether this block itself holds keyboard focus.
handle_context_menu¶
Copy and Select All, while selection is enabled.
cursor_at¶
A hand over a link, an I-beam over selectable text.
automation_role¶
AutomationRole::Document.
automation_name_fallback¶
automation_children¶
One node per link in the lines this block shows. Invoking a node follows its link.
measure_override¶
Lays the content out at the offered width (per wrapping()); with an overflow target, keeps only the whole lines that fit the offered height.
arrange_override¶
Re-wraps at the final width when it changed, and re-places the lines for the final height, which decides where the overflow picks up.
render_override¶
The lines this block keeps, with its highlighters and the selection (while the chain has focus).
pointer_override¶
Drag to select, double-click a word or triple-click a paragraph. A click on a link follows it.
focus_changed¶
Called by Window as focus moves. The selection shows while the chain has focus.
key_override¶
Ctrl+A selects all, Ctrl+C copies the selection.
draws_focus_ring¶
None: selectable text shows focus through its selection.
class RichTextOverflowControl¶
Derives from ui::Element.
Shows the part of a RichText's content that did not fit the block or the previous overflow container, laid out at its own width. Link one with RichTextControl::set_overflow_target(), and chain another one with this one's set_overflow_target(). It has no content of its own, so an unlinked overflow is empty.
The links don't own anything. Each container is owned by the tree like any element and must be measured after the one before it. Destroying one in the chain unlinks everything after it. Selection, copying and links work here as in the RichText, on the RichText's own selection.
Example:
const std::string long_article_text = "An article longer than one column holds...";
auto columns = std::make_unique<ui::StackControl>(ui::Orientation::Horizontal);
auto* rich = columns->add(std::make_unique<ui::RichTextControl>());
auto* second = columns->add(std::make_unique<ui::RichTextOverflowControl>());
auto* third = columns->add(std::make_unique<ui::RichTextOverflowControl>());
rich->set_blocks({{.inlines = {{.text = long_article_text}}}});
const auto column = [](ui::Element* e) { e->set_width(180.0f); e->set_height(240.0f); };
column(rich);
column(second);
column(third);
rich->set_overflow_target(second);
second->set_overflow_target(third);
window.set_content(std::move(columns));
RichTextOverflowControl¶
An unlinked, empty container.
~RichTextOverflowControl¶
Unlinks itself; the containers after it lose their source.
RichTextOverflowControl¶
operator=¶
content_source¶
The RichText whose content this shows, nullptr while unlinked.
overflow_target¶
The next container in the chain (non-owning), or nullptr.
set_overflow_target¶
Sets where this container's leftover goes. Same rules as RichTextControl::set_overflow_target(); nullptr unlinks it.
has_overflow_content¶
Whether some content still did not fit here and went on to the next container.
content_start¶
The offset into the RichText's plain text this container starts at (npos while it shows nothing).
focusable¶
A tab stop while linked, showing something, and the RichText allows selection.
handle_context_menu¶
The RichText's Copy/Select All menu.
cursor_at¶
As the RichText: a hand over a link, an I-beam over selectable text.
automation_role¶
AutomationRole::Document.
measure_override¶
Lays the rest of the RichText's content out at the offered width, keeping the whole lines that fit the offered height (all of it when nothing is linked after it).
arrange_override¶
Re-places the lines for the final height.
render_override¶
The lines this container shows, in the RichText's formatting, highlighters and selection.
pointer_override¶
Selection and links, as in the RichText, acting on the RichText's selection.
focus_changed¶
Called by Window as focus moves. The selection shows while the chain has focus.
key_override¶
Ctrl+A and Ctrl+C, as the RichText's.
draws_focus_ring¶
None: selectable text shows focus through its selection.
Tests¶
tests/test_rich_text.cpp