ComboBox¶
Header: winui/ui/input/ComboBox.hpp. Spec: winui/ui/input/ComboBox.hpp.
A combo box: a closed box showing one item of a list of strings, and a chevron that opens the list as a popup floating above the rest of the tree, so opening it never reflows the layout.
By default the box is read-only and the value is always one of the items (or nothing, showing the placeholder). A click, Space, F4 or Alt+Down opens the list. While open, hovering or Up/Down moves the highlight, and a click or Enter picks it and closes the list; Escape or a click outside closes it unchanged. While closed, Up/Down change the selection directly. Typing jumps to the next item starting with the typed letters; letters typed within a second of each other add to the search.
set_editable(true) turns the box into a text field the user can type any value into. Typing at the end of the text completes it to the first item it starts, with the completed part selected so the next letter replaces it. Enter or losing focus commits the text: text equal to an item picks it, other non-blank changed text raises on_text_submitted(). Escape reverts to the last committed text. Only F4 or Alt+Down open the list (Space is an ordinary character), and the list always shows every item (AutoSuggestBox filters as you type). The control stays one tab stop; typing and caret movement go to an internal TextBox, which draws its own caret and selection.
Example:
// Declarative: ui::ComboBox builds a ui::ComboBoxControl.
ui::Property<std::optional<std::size_t>> size{1};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::ComboBox{
.header = "Size",
.items = {"Small", "Medium", "Large"},
.selected_index = size,
},
ui::Text{.text = ui::bind(size, [](std::optional<std::size_t> i) { return std::format("Index {}", i.value_or(0)); })},
},
});
// Imperative and editable: any text goes, the items are suggestions.
auto combo = std::make_unique<ui::ComboBoxControl>(std::vector<std::string>{"Red", "Green", "Blue"});
combo->set_editable(true);
combo->set_header("Colors");
combo->set_placeholder_text("Pick a color");
combo->on_item_chosen([](std::size_t index) { /* index into items() */ });
combo->on_text_submitted([](const ui::ComboBoxTextSubmittedArgs& args) {
return args.text.size() > 20 ? ui::Cancel : ui::Proceed; // too long: not committed
});
window.set_content(std::move(combo));
ui::ComboBox¶
A combo box: pick one of a list, or with editable type any value. Builds a ui::ComboBoxControl.
items is applied before text and selected_index, so a bound index always lands inside the list. When both are bound, text follows the selection.
Example:
ui::Property<std::optional<std::size_t>> size{1};
ui::Property<std::string> color{"Blue"};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::ComboBox{
.header = "Size",
.items = {"Small", "Medium", "Large"},
.selected_index = size,
.width = 200.0f,
},
ui::ComboBox{
.header = "Colors",
.placeholder_text = "Pick a color",
.items = {"Red", "Green", "Blue"},
.text = color,
.editable = true,
.width = 200.0f,
.on_text_submitted = [](const ui::ComboBoxTextSubmittedArgs&) { return ui::Proceed; }, // free text
},
},
});
Every field, in the order a designated initializer names them:
ui::ComboBox{
.header, .description, .error, .placeholder_text, .items, .selected_index, .text,
.editable, .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_item_chosen, .on_selection_changed,
.on_text_submitted, .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 nothing is selected or the text is empty ("Pick a color"). | |
items |
ui::Binding<std::vector<std::string>> |
The choices, in display order. Bound, a new list replaces them and closes the list, as ui::ComboBoxControl::set_items() does. | |
selected_index |
ui::TwoWayField<std::optional<std::size_t>> |
The selected item; unset (the default) selects nothing and shows placeholder_text. Two-way with ui::bind(): picking, stepping or (editable) typing an item writes its index, other text writes unset. |
|
text |
ui::TwoWayField<std::string> |
The text in the box: the selected item's, or when editable whatever was typed. Two-way with ui::bind(). |
|
editable |
ui::Binding<bool> |
{false} |
Whether the user can type a value of their own; false (the default) only picks items. |
on_text_changed |
ui::Callback<void(const std::string&)> |
Editable: fires with the new text after each user edit (typing, Backspace, Delete). | |
on_item_chosen |
ui::Callback<void(std::size_t)> |
Fires with the index of the item the user picked from the list, even if the box already showed it. | |
on_selection_changed |
ui::Callback<void(std::optional<std::size_t>)> |
Fires with the new index when the selection changes, unset when it was cleared; not for a write that lands on the current item. | |
on_text_submitted |
ui::Callback<ui::Decision(const ui::ComboBoxTextSubmittedArgs&)> |
Editable: typed text matching no item, committed by Enter or focus loss. Return ui::Cancel to reject it (after putting the last good value back with set_text() through .control). |
|
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::ComboBox> |
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::ComboBoxControl; the caller owns it.
ui::ComboBoxControl¶
Derives from ui::Element, ui::ExpandCollapseProvider, ui::ValueProvider.
A combo box: a closed box showing one item of a list of strings, and a chevron that opens the list as a popup floating above the rest of the tree, so opening it never reflows the layout.
By default the box is read-only and the value is always one of the items (or nothing, showing the placeholder). A click, Space, F4 or Alt+Down opens the list. While open, hovering or Up/Down moves the highlight, and a click or Enter picks it and closes the list; Escape or a click outside closes it unchanged. While closed, Up/Down change the selection directly. Typing jumps to the next item starting with the typed letters; letters typed within a second of each other add to the search.
set_editable(true) turns the box into a text field the user can type any value into. Typing at the end of the text completes it to the first item it starts, with the completed part selected so the next letter replaces it. Enter or losing focus commits the text: text equal to an item picks it, other non-blank changed text raises on_text_submitted(). Escape reverts to the last committed text. Only F4 or Alt+Down open the list (Space is an ordinary character), and the list always shows every item (AutoSuggestBox filters as you type). The control stays one tab stop; typing and caret movement go to an internal TextBox, which draws its own caret and selection.
Example:
// Declarative: ui::ComboBox builds a ui::ComboBoxControl.
ui::Property<std::optional<std::size_t>> size{1};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::ComboBox{
.header = "Size",
.items = {"Small", "Medium", "Large"},
.selected_index = size,
},
ui::Text{.text = ui::bind(size, [](std::optional<std::size_t> i) { return std::format("Index {}", i.value_or(0)); })},
},
});
// Imperative and editable: any text goes, the items are suggestions.
auto combo = std::make_unique<ui::ComboBoxControl>(std::vector<std::string>{"Red", "Green", "Blue"});
combo->set_editable(true);
combo->set_header("Colors");
combo->set_placeholder_text("Pick a color");
combo->on_item_chosen([](std::size_t index) { /* index into items() */ });
combo->on_text_submitted([](const ui::ComboBoxTextSubmittedArgs& args) {
return args.text.size() > 20 ? ui::Cancel : ui::Proceed; // too long: not committed
});
window.set_content(std::move(combo));
ComboBoxControl¶
An empty, read-only combo box (not focusable until it has items).
ComboBoxControl¶
ComboBoxControl(std::vector<std::string> items, std::optional<std::size_t> selected_index = std::nullopt)
A read-only combo box offering items. selected_index is clamped to the list; unset (the default) starts with nothing selected, showing the placeholder. Sets a 64 DIP minimum width.
editable¶
Whether the user can type a value of their own. False (the default) only picks items.
set_editable¶
Switches between the read-only box and the text field. The text carries over: turning editing off keeps the selection, and drops typed text that matches no item. Updates the layout.
items¶
The list's choices (UTF-8), in display order.
set_items¶
Replaces the item list and closes the popup. A read-only box clears its selection, firing on_selection_changed() when something was selected; an editable one keeps its text, and the selection follows it to the new list.
selected_index¶
Index into items() of the selected item: the item the text matches exactly, when editable. Unset when nothing is selected.
set_selected_index¶
Shows item index, as picking it would, without raising on_item_chosen(). Read-only, the index is clamped to the list; editable, an index past the list is ignored. Unset clears the selection (and, editable, the text when it shows an item).
text¶
The text in the box (UTF-8): the selected item's, or empty with nothing selected; when editable, whatever was typed or picked.
set_text¶
Read-only, selects the item equal to text, or nothing when none is. Editable, replaces the text and moves the caret to the end without firing on_text_changed(), which is for user edits only. The new text is also the committed text Escape reverts to.
placeholder_text¶
The hint shown while nothing is selected or the text is empty; empty by default.
set_placeholder_text¶
Sets the hint ("Pick a color"). Updates the layout, since a read-only box fits its width to it.
header¶
The label above the control. Empty by default.
set_header¶
Sets the header. Empty draws none and takes no room. A change updates the layout.
description¶
The secondary text under the box; empty (the default) draws none.
set_description¶
Sets the description. Empty draws none and takes no room. A change updates the layout.
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. A change updates the layout and redraws.
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.
on_selection_changed¶
Fires when the selection changes: an item picked, Up/Down or typing while closed, text typed or set that matches another item or none, set_items() or set_selected_index(). Unset when it was cleared; not for a write that lands on the current item.
on_item_chosen¶
Fires when the user picks an item from the open list (a click, or Enter on the highlighted item), even if the box already showed it, and when an editable box commits typed text equal to an item. Receives the index into items(); the box already shows the item when it fires.
on_text_changed¶
Editable only: fires with the new text after any user-driven text edit (typing, Backspace, Delete, UI Automation's SetValue), once completion has filled it in. Not fired after set_text() or when an item is picked (that is on_item_chosen()). Replaces any previous handler.
on_text_submitted¶
Editable only: fires when typed text matching no item is committed (see the class comment). Return ui::Cancel to reject the text, typically after putting the last good value back with set_text(); the text is then not committed.
expanded¶
Whether the item list is open. Opened by the user or through UI Automation's ExpandCollapse; never opens while items() is empty, and there is no public setter.
focused¶
Whether the control (and so, editable, its inner TextBox) holds keyboard focus.
focusable¶
True while enabled and holding at least one item; an editable box needs no items, since it is still a free-text field.
for_each_child¶
Lists the internal TextBox while editable, so it is updated, animated and drawn along with the combo box.
collect_focusables¶
One tab stop: the internal TextBox is part of this control, not a stop of its own.
automation_children¶
The internal TextBox is part of this node and not listed. While the list is open, a List node of the items, each a ListItem a client can select.
automation_focus¶
The highlighted item while the list is open, so a screen reader reads it as the arrow keys move.
handle_context_menu¶
Editable: the inner TextBox's context menu, Cut/Copy/Paste/Select All only, with no spelling section. Read-only: none.
cursor_at¶
Editable: the text field's cursor over the text field (an I-beam, an arrow over its buttons).
automation_role¶
A ComboBox, with its text as the Value pattern (read-only unless editable) and ExpandCollapse for open and close.
caret_bounds¶
Editable: the inner text box's caret.
visual_state¶
The Normal/Hover/Pressed/Disabled state the control draws in: the whole closed box when read-only, the chevron button when editable.
measure_override¶
Desired width fits the widest item, and the placeholder (read-only) or the inner TextBox's own desired width (editable), so the box doesn't reflow as the selection changes. Desired height is the header plus one row, open or closed, since the list floats as a popup.
arrange_override¶
Places the header, and when editable the inner TextBox beside the chevron column.
pointer_override¶
A click on the box (read-only) or on the chevron (editable) toggles the list; anything else in an editable box goes to the TextBox. Clicks on the open list reach the popup.
render_override¶
Draws the header, the box with its text (or placeholder) and the chevron. The open list is a popup the window draws.
tick_override¶
Ticks the inner TextBox, runs the list's slide-open, where the theme has one (Theme::popup_motion.list), and times out the typed search.
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 Window; mirrored into the inner TextBox. Losing focus closes the list, and an editable box commits its typed text.
key_override¶
F4 or Alt+Down/Up toggles the list; Up/Down/Enter/Escape drive it while open. Read-only, Space toggles it and Up/Down step the selection while closed. Editable, Enter commits and Escape reverts while closed, and everything else edits the text.
text_override¶
Typed text searches the items (read-only) or goes to the inner TextBox (editable).
focus_ring_rect¶
Read-only: around the box; inside the classic field, round the selected text, while the list is closed.
focus_ring_corner_radius¶
The ring's corner radius: the theme's control radius by default.
draws_focus_ring¶
Read-only only: an editable box shows focus with its accent border.
struct ComboBoxTextSubmittedArgs¶
What a ComboBox's on_text_submitted gets: the typed text.
| Field | Type | Default | |
|---|---|---|---|
text |
std::string |
The committed text, which matches no item. |
Tests¶
tests/test_combo_box.cpptests/test_combo_box_read_only.cpp