Skip to content

AutoSuggestBox

Header: winui/ui/text/AutoSuggestBox.hpp. Spec: winui/ui/text/AutoSuggestBox.hpp.

A text box with a floating list of suggestions the app supplies as the user types, for search boxes and type-ahead pickers.

The control never filters. Handle on_text_changed(), and when the reason is AutoSuggestBoxTextChangeReason::UserInput, compute the matches and pass them to set_suggestions(). The popup opens whenever the box is focused and the list is non-empty.

Up and Down preview suggestions in the text. Stepping past either end restores what the user typed. A row click, or Enter on a highlighted row, fires on_suggestion_chosen(). Enter or a row click fires on_query_submitted(). Escape restores the typed text and closes the list. An optional query button (set_query_icon()) submits the typed text as a query.

Example:

static const std::vector<std::string> Cities{"Amsterdam", "Berlin", "Bern", "Zurich"};

auto box = std::make_unique<ui::AutoSuggestBoxControl>();
box->set_header("City");
box->set_placeholder_text("Search cities");
box->set_query_icon(ui::symbol_glyph(ui::Symbol::Find));
box->on_text_changed([b = box.get()](const ui::AutoSuggestBoxTextChangedArgs& args) {
    if (args.reason != ui::AutoSuggestBoxTextChangeReason::UserInput)
        return; // our own preview/revert writes; nothing to re-filter
    std::vector<std::string> matches;
    for (const std::string& city : Cities)
        if (!args.text.empty() && city.starts_with(args.text))
            matches.push_back(city);
    b->set_suggestions(std::move(matches));
});
box->on_query_submitted([](const ui::AutoSuggestBoxQuerySubmittedArgs& args) {
    // args.chosen_suggestion is unset for a free-typed query
});
window.set_content(std::move(box));

ui::AutoSuggestBox

A search field with a live suggestion list. Builds a ui::AutoSuggestBoxControl.

Usually .suggestions is bound to a property, .on_text_changed recomputes it as the user types, and .on_query_submitted runs the search.

Example:

const std::vector<std::string>         titles{"Inbox", "Invoices", "Settings"};
ui::Property<std::vector<std::string>> matches;

window.set_content(ui::AutoSuggestBox{
    .placeholder_text = "Search",
    .suggestions      = matches,
    .query_icon       = ui::symbol_glyph(ui::Symbol::Find),
    .width            = 300.0f,
    .on_text_changed  =
        [&](const ui::AutoSuggestBoxTextChangedArgs& args)
    {
        if (args.reason == ui::AutoSuggestBoxTextChangeReason::UserInput)
        {
            std::vector<std::string> found;
            for (const std::string& title : titles)
            {
                if (title.starts_with(args.text))
                {
                    found.push_back(title);
                }
            }
            matches = std::move(found);
        }
    },
    .on_query_submitted = [](const ui::AutoSuggestBoxQuerySubmittedArgs& args)
    { std::println("Search for {}", args.query_text); },
});

Every field, in the order a designated initializer names them:

ui::AutoSuggestBox{
    .header, .description, .error, .placeholder_text, .text, .suggestions, .query_icon, .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_suggestion_chosen, .on_query_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 the text is empty.
text ui::TwoWayField<std::string> The query text in the box.
suggestions ui::Binding<std::vector<std::string>> The suggestion list. Bind it with ui::bind() and assign the property from .on_text_changed to update it as the user types.
query_icon ui::Binding<ui::IconSource> The query button's icon: a Symbol, a glyph, a path or a picture; empty has no button.
on_text_changed ui::Callback<void(const ui::AutoSuggestBoxTextChangedArgs&)> The new text and why it changed. Recompute suggestions only for UserInput.
on_suggestion_chosen ui::Callback<void(std::size_t)> The user picked a suggestion: a row click, or Enter on the highlighted row.
on_query_submitted ui::Callback<void(const ui::AutoSuggestBoxQuerySubmittedArgs&)> Enter, a suggestion click or the query button. The chosen index is unset for typed text.
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::AutoSuggestBox> 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

std::unique_ptr<Element> build()&&

Consumes the spec and returns the built ui::AutoSuggestBoxControl; the caller owns it.

ui::AutoSuggestBoxControl

Derives from ui::Element, ui::ValueProvider.

A text box with a floating list of suggestions the app supplies as the user types, for search boxes and type-ahead pickers.

The control never filters. Handle on_text_changed(), and when the reason is AutoSuggestBoxTextChangeReason::UserInput, compute the matches and pass them to set_suggestions(). The popup opens whenever the box is focused and the list is non-empty.

Up and Down preview suggestions in the text. Stepping past either end restores what the user typed. A row click, or Enter on a highlighted row, fires on_suggestion_chosen(). Enter or a row click fires on_query_submitted(). Escape restores the typed text and closes the list. An optional query button (set_query_icon()) submits the typed text as a query.

Example:

static const std::vector<std::string> Cities{"Amsterdam", "Berlin", "Bern", "Zurich"};

auto box = std::make_unique<ui::AutoSuggestBoxControl>();
box->set_header("City");
box->set_placeholder_text("Search cities");
box->set_query_icon(ui::symbol_glyph(ui::Symbol::Find));
box->on_text_changed([b = box.get()](const ui::AutoSuggestBoxTextChangedArgs& args) {
    if (args.reason != ui::AutoSuggestBoxTextChangeReason::UserInput)
        return; // our own preview/revert writes; nothing to re-filter
    std::vector<std::string> matches;
    for (const std::string& city : Cities)
        if (!args.text.empty() && city.starts_with(args.text))
            matches.push_back(city);
    b->set_suggestions(std::move(matches));
});
box->on_query_submitted([](const ui::AutoSuggestBoxQuerySubmittedArgs& args) {
    // args.chosen_suggestion is unset for a free-typed query
});
window.set_content(std::move(box));

AutoSuggestBoxControl

AutoSuggestBoxControl()

An empty box with no suggestions, header or query button.

AutoSuggestBoxControl

AutoSuggestBoxControl(std::string text)

A box starting with text, which is also what Escape reverts to until the user types.

text

const std::string& text() const

The text box's current text (UTF-8), including a previewed suggestion.

set_text

void set_text(std::string text)

Replaces the text and moves the caret to the end. Doesn't fire on_text_changed() or change the suggestion list. It also doesn't change the text Escape restores, which only the user's typing sets.

suggestions

const std::vector<std::string>& suggestions() const

The list last passed to set_suggestions(), shown as-is in the popup.

set_suggestions

void set_suggestions(std::vector<std::string> suggestions)

Replaces the shown suggestion list and clears the hover and keyboard highlight. Opens the popup when the control is focused and the new list is non-empty. Otherwise is_suggestion_list_open() stays as it was.

is_suggestion_list_open

bool is_suggestion_list_open() const

Whether the suggestion popup is showing.

query_icon

const IconSource& query_icon() const

The query button's icon; empty for no button.

set_query_icon

void set_query_icon(IconSource icon)

Shows a query button with this icon (such as Symbol::Find); a click on it submits the typed text with no suggestion chosen. An empty source, the default, has no button.

query_button_rect

Rect query_button_rect() const

The query button's rect in window DIPs, valid after arrange. Empty without a query icon.

placeholder_text

const std::string& placeholder_text() const

The hint shown while the text is empty.

set_placeholder_text

void set_placeholder_text(std::string text)

Sets the hint shown while the text is empty.

const std::string& header() const

The header text above the box; empty for none.

set_header

void set_header(std::string header)

The label above the control. Empty (the default) draws none and takes no room.

description

const std::string& description() const

The secondary text under the box; empty (the default) draws none.

set_description

void set_description(std::string description)

Sets the description. Empty draws none and takes no room.

error

const std::string& error() const

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

void set_error(std::string error)

Sets the error. Empty shows the description again.

automation_help_text_fallback

std::string automation_help_text_fallback() const override

The description, or the error while one is set, is the help text a screen reader offers when accessibility().description is empty.

automation_invalid

bool automation_invalid() const override

Invalid while an error is set.

automation_name_fallback

std::string automation_name_fallback() const override

The header names the control when accessibility().name does not.

on_text_changed

void on_text_changed(Callback<void(const AutoSuggestBoxTextChangedArgs&)> handler)

Fired after every change to the text, with the new text and the reason (AutoSuggestBoxTextChangeReason). set_text() doesn't fire it. When the reason is UserInput, filter the suggestions here and call set_suggestions().

on_suggestion_chosen

void on_suggestion_chosen(Callback<void(std::size_t)> handler)

Fires with the suggestion's index when the user picks it with a row click, or with Enter while an arrow key has it highlighted. A highlight alone doesn't fire it. That shows in on_text_changed() with the reason AutoSuggestBoxTextChangeReason::Suggestion. Fires right before on_query_submitted(), with the suggestion already in the box.

on_query_submitted

void on_query_submitted(Callback<void(const AutoSuggestBoxQuerySubmittedArgs&)> handler)

Fired on Enter, a query button click, or after on_suggestion_chosen() for a row click. It fires even with nothing highlighted, for a typed query with no chosen index. The popup closes afterward.

focused

bool focused() const

Whether the box has keyboard focus.

focusable

bool focusable() const override

Focusable while enabled.

for_each_child

void for_each_child(ChildVisitor visit) const override

Visits the internal TextBox.

collect_focusables

void collect_focusables(std::vector<Element*>& out) override

One tab stop: the internal TextBox is part of this control, not a stop of its own.

automation_children

void automation_children(std::vector<Element*>& out) const override

The internal TextBox is part of this control and not listed. While the suggestions show, lists them as a list of items. Selecting an item fills the box, and invoking it chooses it.

automation_focus

Element* automation_focus() const override

The highlighted suggestion while the list shows, so a screen reader reads it as the arrow keys move.

handle_context_menu

bool handle_context_menu(std::optional<Point> position, ContextMenuHost& host) override

Shows Cut, Copy, Paste and Select All, with no spelling suggestions.

cursor_at

std::optional<CursorShape> cursor_at(Point p) const override

The text field's cursor over the text field: an I-beam, or an arrow over its buttons.

automation_role

AutomationRole automation_role() const override

Always AutomationRole::Edit.

caret_bounds

std::optional<Rect> caret_bounds() const override

The inner text box's caret.

measure_override

Size measure_override(Size available, const MeasureContext& context) const override

Sizes exactly as the internal TextBox does, plus the header. The popup floats and does not add to the measured size.

arrange_override

void arrange_override(const Rect& final_rect, const ArrangeContext& context) override

Arranges the internal TextBox below the header.

pointer_override

bool pointer_override(const PointerEvent& e) override

Routes clicks to the text box, its delete button or the query button.

covers_children_at

bool covers_children_at(Point p) const override

Whether a part of this element drawn over its children is at p (a scroll bar over the content), so the element is hit there instead of a child. Default: false.

render_override

void render_override(Renderer& renderer, const RenderContext& context) const override

Draws the header, the text box and the query button.

enabled_changed

void enabled_changed() override

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

void focus_changed(bool focused) override

Focus is forwarded to the internal TextBox; losing it closes the popup.

key_override

bool key_override(const KeyEvent& e) override

Up/Down walk the suggestions, Enter submits, Escape reverts; other keys edit the text.

text_override

bool text_override(const TextInputEvent& text_event) override

Typed text goes to the internal TextBox and fires on_text_changed() with UserInput.

draws_focus_ring

bool draws_focus_ring() const override

None: the field shows focus with its accent border.

struct AutoSuggestBoxTextChangedArgs

What an AutoSuggestBox's on_text_changed gets: the text and what changed it.

Field Type Default
text const std::string& The text now in the box.
reason AutoSuggestBoxTextChangeReason UserInput is the one to update the suggestions on.

struct AutoSuggestBoxQuerySubmittedArgs

What on_query_submitted gets: the text, and the suggestion it came from, if any.

Field Type Default
query_text const std::string& The submitted text.
chosen_suggestion std::optional<std::size_t> The suggestion's index; unset for typed text.

Enumerations

AutoSuggestBoxTextChangeReason

enum class AutoSuggestBoxTextChangeReason

Why the text box's text changed. Lets an on_text_changed handler tell the user's typing, which is worth filtering suggestions for, apart from the text this control writes itself.

Value
UserInput The user typed, deleted or pasted text.
ProgrammaticChange Escape, or an arrow key past either end of the list, restored the typed text.
Suggestion A suggestion was put in the box, by an arrow key or a click.

Tests

  • tests/test_auto_suggest_box.cpp