Skip to content

Select

select · Elements · interactive

A closed box that opens a list, and — with filter on — a combobox.

Import

cpp
#include "gbui/widgets/select.hpp"

Example

Nothing is downloaded until you press it.

cpp
[[nodiscard]] SelectResult select(Ui& ui, const Interaction& input, std::string_view id, const std::vector<std::string>& items, std::optional<std::size_t> selected, SelectState& state, const SelectOptions& options = {});
cpp
[[nodiscard]] SelectResult select(Ui& ui, const Interaction& input, std::string_view id, const std::vector<std::string>& items, const std::vector<std::size_t>& selected, SelectState& state, const SelectOptions& options = {});

A closed box that opens a list. Closed and focused, Up and Down step the value and Return or Space opens the list. Open, they walk the highlight, Home and End jump to the ends, Return or Space commits, and Escape closes without changing anything. The list keeps the highlighted row in view as it moves, and the box — not the rows — keeps the keyboard, so Tab leaves the control rather than walking into the popup.

The same control, holding more than one value. selected is the caller's own set, as indices into items — a vector rather than a std::set because the order a reader chose things in is worth keeping and because most callers already have one. SelectResult::chosen is the row that was toggled this frame, not the new value: the component says what happened and the caller decides what its set becomes, exactly as checkbox reports a press rather than writing a bool. if (const auto hit = result.chosen) { const auto at = std::find(picked.begin(), picked.end(), *hit); if (at != picked.end()) picked.erase(at); else picked.push_back(*hit); } Set SelectOptions::multiple with it. Without that the list closes on the first choice and the rows draw as values rather than as things to tick, which is the single-value control with a set awkwardly attached.

API

SelectOptions

OptionTypeDefaultWhat it does
namestd::string_viewWhat this is called, for a reader who cannot see the caption beside it. Unnecessary when a label or a field names it — those attach the relation, and a name given twice is a name read out twice. Necessary the rest of the time, and the placeholder is not a substitute: a box named by its placeholder loses its name the moment somebody types in it.
placeholderstd::string_view"Select…"
disabledboolfalse
widthfloatkAuto
growfloat0.0f
heightfloat0.0fZero takes the active design's control height, so a select lines up with every other control on its row without anyone matching numbers.
maxVisiblestd::size_t12How many rows fit before the list scrolls. The rest are reachable by scrolling or by walking to them with the arrow keys.
maxListHeightfloatkAutoA hard ceiling in pixels, which wins over maxVisible when both would apply — a row count cannot know how tall the window is. kAuto leaves the row count in charge.
listScrollScrollAxis
None · Vertical · Horizontal
ScrollAxis::VerticalWhether the open list scrolls at all. None clips it instead, for a caller that would rather constrain the list than let it move.
filterboolfalseA box at the top of the open list that narrows it as the reader types. Hand SelectResult::focus to Interaction::focus when this is on. Without it the filter box still works — a click focuses it — but it will not have the keyboard the moment the list opens, which is the whole gesture.
filterPlaceholderstd::string_view"Type to filter…"The filter box's own hint.
emptyMessagestd::string_view"No matches"Drawn where the rows would be when nothing matches. An empty list with no explanation reads as a list that failed to load.
multipleboolfalseThe reader may take several rows rather than one. An option and not a multiSelect component beside this, for the third time the same argument has been settled here: everything that makes a select a select is unchanged by how many rows it keeps. The value is still the caller's, the highlight is still separate from it, the list is still a popover. What changes is that a row toggles instead of replacing, that the list stays open so a second row can be taken, and that the closed box has more than one thing to say. multiple is the interaction; the caller's container is the value. Use the overload that takes a vector of indices to pass more than one — the single-value form still works and simply holds at most one.
summariseFromstd::size_t3How the closed box reads once several rows are taken. Below the threshold the labels are listed; at or above it the box says "N selected", because a box listing nine branch names is a box whose own label has gone. Zero always lists them.

Released under the LGPL-3.0-or-later licence.