TextBox¶
Header: winui/ui/text/TextBox.hpp. Spec: winui/ui/text/TextBox.hpp.
Single- or multi-line editable text.
A focusable field with a blinking caret, mouse and keyboard selection, and Ctrl+C/X/V through the host's Clipboard. When the host provides a SpellChecker, misspellings get squiggles and a context menu of suggestions. It can show a header above the field and a placeholder while empty. set_accepts_return() or set_wrapping() make it multi-line. A multi-line box grows a line at a time and scrolls when it is shorter than its text. set_password_char() makes it PasswordBox's edit part. on_text_changed() reports user edits only, never set_text(). In a spec tree it is built by ui::TextBox.
Example:
ui::Property<std::string> name{"Ada"};
ui::Control<ui::TextBox> notes;
window.set_content(ui::VerticalStack{
.spacing = 12,
.content = {
ui::TextBox{.header = "Name",
.placeholder_text = "Your name",
.text = name,
.on_submit = [&] { /* save name.get() */ }},
ui::TextBox{.header = "Notes",
.accepts_return = true,
.wrapping = ui::TextWrapping::Wrap,
.height = 120,
.control = notes},
ui::Text{.text = ui::bind(name, [](const std::string& n) { return "Hello, " + n; })},
},
});
// Or imperatively:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* code = stack->add(std::make_unique<ui::TextBoxControl>());
code->set_header("Activation code");
code->set_max_length(8);
code->set_character_casing(ui::CharacterCasing::Upper);
code->set_spell_check(false);
code->on_text_changed([](const std::string& text) { /* validate */ });
ui::TextBox¶
An editable text field, single-line by default. Builds a ui::TextBoxControl.
.text = prop is two-way: the box shows the property, and what the user types is written back into it before .on_text_changed runs. A bind with a conversion, a const property and ui::bind_one_way() only display the value. .accepts_return or a .wrapping other than NoWrap make it multi-line.
Example:
ui::Property<std::string> name{"Ada"};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::TextBox{ .header = "Name",
.placeholder_text = "Your name",
.text = name,
.max_length = 40,
.width = 240,
.on_submit = [] { save(); } },
ui::Text{ .text = ui::bind(name, [](const std::string& n) { return "Hello, " + n; }) },
},
});
Every field, in the order a designated initializer names them:
ui::TextBox{
.header, .description, .error, .placeholder_text, .text, .max_length, .spell_check,
.read_only, .accepts_return, .accepts_tab, .character_casing, .wrapping, .font_family,
.font_size, .font_weight, .italic, .character_spacing, .foreground,
.selection_highlight_color, .unfocused_selection_highlight_color, .color_spans,
.line_numbers, .style, .margin, .width, .height, .min_width, .min_height, .max_width,
.max_height, .horizontal_alignment, .vertical_alignment, .visible, .tool_tip, .enabled,
.accessibility, .on_text_changed, .on_edit, .on_submit, .on_selection_changed,
.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 | |
|---|---|---|---|
header |
ui::Binding<std::string> |
The label above the control. Empty draws none. | |
description |
ui::Binding<std::string> |
Secondary text under the box, such as what to enter or why; empty draws none. Screen readers offer it as help text. | |
error |
ui::Binding<std::string> |
What is wrong with the value: non-empty shows it in the description's place in the critical color and draws the border in that color, and screen readers report the field invalid. The field shows exactly what it is given, so bind it to a check of the value and decide there when it shows (after the first submit, say). | |
placeholder_text |
ui::Binding<std::string> |
The hint shown while the text is empty. | |
text |
ui::TwoWayField<std::string> |
The edited string (UTF-8). ui::bind(prop) is two-way: an edit is written back into the property. Each new bound value replaces the whole text; the text the box already holds changes nothing, so the write-back keeps the caret. |
|
max_length |
ui::Binding<std::size_t> |
{0} |
The most characters the user can type or paste; 0 is no limit. |
spell_check |
ui::Binding<bool> |
{true} |
Spell checking with squiggles, suggestions and autocorrect. Turn it off for fields that don't hold prose, such as codes, identifiers and user names. |
read_only |
ui::Binding<bool> |
{false} |
Selectable and copyable, never edited. |
accepts_return |
ui::Binding<bool> |
{false} |
Enter inserts a line break; the box becomes multi-line. |
accepts_tab |
ui::Binding<bool> |
{false} |
Tab inserts a tab character instead of moving focus (TextBoxControl::set_accepts_tab()). |
character_casing |
ui::Binding<ui::CharacterCasing> |
{ui::CharacterCasing::Normal} |
The casing applied to what the user types or pastes. |
wrapping |
ui::Binding<ui::TextWrapping> |
{ui::TextWrapping::NoWrap} |
Anything but NoWrap breaks lines at the box's width (multi-line). |
font_family |
ui::Binding<std::optional<std::string>> |
The font family; unset follows the theme. | |
font_size |
ui::Binding<std::optional<float>> |
The font size, DIPs; unset follows the theme. | |
font_weight |
ui::Binding<std::optional<ui::FontWeight>> |
The font weight; unset follows the theme. | |
italic |
ui::Binding<bool> |
{false} |
Italic text. |
character_spacing |
ui::Binding<int> |
{0} |
Extra space between characters, in thousandths of an em. |
foreground |
ui::Binding<ui::Paint> |
The text color: a fixed color or a theme resource; unset follows the theme. | |
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. | |
unfocused_selection_highlight_color |
ui::Binding<ui::Paint> |
The color behind selected text while the box doesn't have focus; empty (the default) hides the selection then (TextBoxControl::set_unfocused_selection_highlight_color()). | |
color_spans |
ui::Binding<std::vector<ui::TextColorSpan>> |
Colors for ranges of the text, as a syntax highlighter makes them (TextBoxControl::set_color_spans()). | |
line_numbers |
ui::Binding<bool> |
{false} |
A column of line numbers left of a multi-line box's text. |
on_text_changed |
ui::Callback<void(const std::string&)> |
Fired after every user edit, with the new text. Handlers in a spec returned from a function must capture by value or capture state that outlives the window (see spec/Spec.hpp). | |
on_edit |
ui::Callback<void(const ui::TextBoxEditArgs&)> |
Every change to the text with what changed, for an incremental parser (TextBoxControl::on_edit()). | |
on_submit |
ui::Action |
Fired on Enter (unless .accepts_return makes Enter insert a line break). |
|
on_selection_changed |
ui::Action |
The caret or the selection moved (TextBoxControl::on_selection_changed()). | |
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::TextBox> |
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, enabled, accessibility every spec carries (conventions).
build¶
Consumes the spec and returns the built ui::TextBoxControl; the caller owns it.
ui::TextBoxControl¶
Derives from ui::Element, ui::ValueProvider.
Single- or multi-line editable text.
A focusable field with a blinking caret, mouse and keyboard selection, and Ctrl+C/X/V through the host's Clipboard. When the host provides a SpellChecker, misspellings get squiggles and a context menu of suggestions. It can show a header above the field and a placeholder while empty. set_accepts_return() or set_wrapping() make it multi-line. A multi-line box grows a line at a time and scrolls when it is shorter than its text. set_password_char() makes it PasswordBox's edit part. on_text_changed() reports user edits only, never set_text(). In a spec tree it is built by ui::TextBox.
Example:
ui::Property<std::string> name{"Ada"};
ui::Control<ui::TextBox> notes;
window.set_content(ui::VerticalStack{
.spacing = 12,
.content = {
ui::TextBox{.header = "Name",
.placeholder_text = "Your name",
.text = name,
.on_submit = [&] { /* save name.get() */ }},
ui::TextBox{.header = "Notes",
.accepts_return = true,
.wrapping = ui::TextWrapping::Wrap,
.height = 120,
.control = notes},
ui::Text{.text = ui::bind(name, [](const std::string& n) { return "Hello, " + n; })},
},
});
// Or imperatively:
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto* code = stack->add(std::make_unique<ui::TextBoxControl>());
code->set_header("Activation code");
code->set_max_length(8);
code->set_character_casing(ui::CharacterCasing::Upper);
code->set_spell_check(false);
code->on_text_changed([](const std::string& text) { /* validate */ });
MaxUndoSteps¶
How many edits undo() keeps; older ones drop out.
SpellCheckDelayMs¶
Idle time in ms after the last edit before the whole text is checked again.
MaxSpellingSuggestions¶
The most spelling suggestions the context menu lists, best first.
CaretBlinkIntervalMs¶
How long the caret stays shown, and then hidden, in ms. The user's blink setting is not read.
TextBoxControl¶
An empty, enabled, single-line box.
TextBoxControl¶
A box holding text (UTF-8), the caret at its end.
text¶
The edited string, UTF-8.
set_text¶
Replaces the text and moves the caret to the end. Setting the same text again changes nothing, caret and selection included, so a two-way binding writing the value back is harmless. Doesn't fire on_text_changed(), which reports user edits only.
caret¶
The caret position, a byte offset into text().
selection_range¶
The selected range as [start, end) byte offsets; start == end means no selection.
has_selection¶
Whether selection_range() is non-empty.
header¶
The header text; empty when there is none.
set_header¶
The label above the control. Empty (the default) draws none and takes no room.
description¶
The secondary text under the box; empty (the default) draws none.
set_description¶
Sets the description. Empty draws none and takes no room.
error¶
What is wrong with the value, shown in the description's place in the critical color, with the border in the same color; empty (the default) shows the description.
set_error¶
Sets the error. Empty shows the description again.
automation_help_text_fallback¶
The description, or the error while one is set, is the help text a screen reader offers when accessibility().description is empty.
automation_invalid¶
Invalid while an error is set.
automation_name_fallback¶
The header names the control when accessibility().name does not.
hovered¶
Whether the pointer is over the box, which draws it in its hover colors.
placeholder_text¶
The placeholder hint; empty (the default) draws none.
set_uses_theme_min_height¶
Whether measure applies the theme's minimum text box height (32 by default). On for a TextBox of its own, off for one serving as another control's edit part (ComboBox, NumberBox), which sizes itself.
set_placeholder_text¶
A hint drawn in a secondary text color while text() is empty, focused or not. Never part of text(), never measured into the desired size.
set_chromeless¶
A chromeless box draws no background fill, border stroke or focus underline, only the text, selection and caret, so a host control (ComboBox) can paint one background and border under its whole surface.
font_family¶
The font family override; unset (the default) follows the theme.
set_font_family¶
Sets or clears the font family; unset follows the theme.
font_size¶
The font size override in DIPs; unset (the default) follows the theme.
set_font_size¶
Sets or clears the font size; unset follows the theme. The box grows with it.
font_weight¶
The font weight override; nullopt (the default) follows the theme.
set_font_weight¶
Sets the font weight; nullopt follows the theme.
italic¶
Whether the text is italic; false by default.
set_italic¶
Italic text.
character_spacing¶
The extra space between characters in thousandths of an em; 0 by default.
set_character_spacing¶
Sets the extra space between characters, in thousandths of an em.
foreground¶
The text color: a fixed color or a theme resource; empty (the default) follows the theme.
set_foreground¶
The text's color; unset follows the theme. The placeholder keeps its own.
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.
unfocused_selection_highlight_color¶
The color behind selected text while the box doesn't have focus, as a fixed color or a theme resource. Empty (the default) hides the selection then.
set_unfocused_selection_highlight_color¶
Shows the selection in color while the box doesn't have focus, with the text over it unchanged. A find bar uses this to keep the match it selected visible. nullopt hides it again.
set_unfocused_selection_highlight_color¶
The theme-resource form of set_unfocused_selection_highlight_color().
text_style¶
The style the text is measured and drawn in: the theme's, under the font properties above.
read_only¶
Whether the text is read-only; false by default.
set_read_only¶
The text can be selected and copied but not changed, and no caret is drawn.
accepts_return¶
Whether Enter inserts a line break; false by default.
set_accepts_return¶
Enter inserts a line break instead of submitting, and pasted line breaks are kept. Makes the box multi-line.
wrapping¶
Whether lines break at the box's width; NoWrap by default.
set_wrapping¶
Anything but NoWrap breaks the text at the box's width and makes it multi-line.
multiline¶
Whether the text is laid out as lines, which accepts_return() or wrapping turns on.
accepts_tab¶
Whether Tab inserts a tab character; false by default.
set_accepts_tab¶
Tab inserts a tab character instead of moving focus, while the box is editable. Shift+Tab then does nothing. An on_key_down handler can take both keys to indent and outdent.
takes_tab_key¶
While accepts_tab() and editable.
color_spans¶
Colors for ranges of the text; empty by default.
set_color_spans¶
Draws ranges of the text in their own colors, as a syntax highlighter does. The selection still draws over them. Ranges are byte offsets. The spans are sorted and overlaps are cut. An edit moves the spans after it and stretches or cuts the ones it touches, so they stay on their text until the next call. Multi-line boxes only.
line_numbers¶
Whether a multi-line box numbers its lines; false by default.
set_line_numbers¶
Shows a column of line numbers left of a multi-line box's text, one per line of the text (a wrapped line keeps one number).
max_length¶
The most characters the user can enter, in code points; 0 (the default) is no limit.
set_max_length¶
The most characters (code points) the user can type or paste; 0, the default, is no limit. A paste is cut to what fits. Text already in the box or set from code is left alone, even when it is longer.
character_casing¶
How typed text is cased; Normal (keep as typed) by default.
set_character_casing¶
What the user types or pastes from now on is lower- or upper-cased. Text already in the box keeps its case.
set_delete_button_enabled¶
Whether this box has a delete button at all. On for a TextBox of its own. A control that uses one as its edit part and shows no delete button (ComboBox) turns it off.
set_trailing_space¶
Room kept free at the right edge, after the delete button, for a button the owning control draws there itself, such as PasswordBox's reveal button or AutoSuggestBox's query button. The text never runs under it.
trailing_space¶
The width in DIPs kept free by set_trailing_space(); 0 by default.
cursor_at¶
An I-beam over the text (the padding included), an arrow over the delete button and any button a containing control keeps in the trailing space.
delete_button_visible¶
Whether the delete button is shown and clickable right now. It shows while the box is single-line, editable, non-empty, focused and wider than five ems, under a theme that has one (none of the desktop eras does), unless set_delete_button_enabled() turned it off.
delete_button_rect¶
The delete button's rect (empty while it is hidden).
set_password_char¶
Draws every character as mask, and turns off copying, cutting, spell checking and word selection, which a password must not offer. nullopt shows the text.
password_char¶
The mask character; nullopt (the default) when the text is shown.
on_text_changed¶
Fired after any user-driven edit (typing, Backspace, Delete, cut, paste, undo and redo) and after replace_selection(), undo() and redo(), with the new text. set_text() raises nothing.
on_edit¶
Fired for every change to the text, with what changed: typing, pasting, undo and redo, replace_selection() and set_text() too. Runs right after the change, before on_text_changed() and before the caret moves. Don't edit the box from it. Use it to feed an incremental parser.
on_submit¶
Fired when the user presses Enter, unless accepts_return() makes Enter insert a line break.
on_selection_changed¶
Fired when the caret or the selection moved, by a key, the pointer, an edit or a call.
select¶
Selects from anchor to caret (byte offsets; the caret goes to caret, so an anchor after it selects backwards). Both are clamped to the text and moved back to a character boundary. The view scrolls to the caret.
select_all¶
Selects the whole text.
selected_text¶
The selected text; empty with nothing selected.
replace_selection¶
Replaces the selection with text, or inserts it at the caret, and puts the caret after it. An edit like a typed one: it can be undone and raises on_text_changed(). Neither read_only nor max_length stops it.
can_undo¶
Whether there is an edit undo() can take back. Ctrl+Z does the same.
can_redo¶
Whether there is an undone edit redo() can make again. Ctrl+Y and Ctrl+Shift+Z do the same.
undo¶
Takes back the last edit and puts the caret and the selection back where they were before it; nothing when can_undo() is false. Typing is undone a word at a time, a run of Backspace or Delete presses at once.
redo¶
Makes the last undone edit again; nothing when can_redo() is false.
clear_undo_history¶
Forgets every edit, so undo() has nothing to take back. set_text() does this too, so a loaded document starts with an empty history.
parallel_measure_safe¶
True: measuring reads only the box's own state, the measurer and the theme.
focused¶
Whether the box has keyboard focus (caret shown, blink running).
focusable¶
Focusable while enabled (a read-only box too, for selecting and copying).
spell_check¶
Whether spell checking is on; true by default. Without a SpellChecker from the host it does nothing either way.
set_spell_check¶
Turns spell checking on or off; turning it off drops every squiggle at once.
spelling_errors¶
The current misspellings, sorted by start. Includes the word being typed, which gets no squiggle until it's finished.
sync_spell_checker¶
Keeps the host's SpellChecker and schedules a full check with it. The Window calls this as the box gains focus. The box doesn't own the checker.
handle_context_menu¶
Shows spelling suggestions, "Add to dictionary" and "Ignore" over a squiggle, then Cut, Copy, Paste and Select All, each enabled when it can act.
caret_visible¶
The current blink phase: whether a focused box draws its caret right now.
automation_role¶
Always AutomationRole::Edit.
caret_bounds¶
The caret, one DIP wide and a line tall, while focused and editable.
measure_override¶
As wide as the text or placeholder plus padding and buttons, at most available.width. As tall as its lines plus padding, at least the theme's minimum text box height while set_uses_theme_min_height() is on. The header and description add their own room.
arrange_override¶
Places the header and the field; a multi-line box re-flows its text to the new width.
pointer_override¶
Click to place the caret, drag / Shift+click to select, double-click a word, triple-click everything. The delete button clears the text.
wheel_override¶
A multi-line box taller than its view scrolls on the wheel, and shows a scroll bar.
tick_override¶
Toggles caret visibility every CaretBlinkIntervalMs while focused.
render_override¶
Background, border and focus underline (unless chromeless), header, placeholder or text, selection highlight, squiggles, caret and delete button.
enabled_changed¶
is_enabled() flipped, through set_enabled() here or on an ancestor, or a move to another parent. Drop any press or hover state. The element is redrawn either way. No-op by default.
focus_changed¶
Called by the Window as focus moves; restarts the caret blink on a gain.
key_override¶
Caret movement and selection keys, Backspace/Delete, Enter (submit or line break), Ctrl+A/C/X/V, and Ctrl+Z right after an autocorrection.
text_override¶
Inserts typed UTF-8 text at the caret (replacing the selection) and fires on_text_changed(); control characters are dropped, CharacterCasing and MaxLength apply.
draws_focus_ring¶
None: the field shows focus with its accent border.
struct TextBoxEditArgs¶
What a TextBox's on_edit gets: removed at position became inserted. Byte offsets into the text before the change; the views are valid during the call.
| Field | Type | Default | |
|---|---|---|---|
position |
std::size_t |
0 |
Where the change starts. |
removed |
std::string_view |
The text that was there. | |
inserted |
std::string_view |
The text that is there now. |
Tests¶
tests/test_text_box.cpp