TableView¶
Header: winui/ui/collections/TableView.hpp. Spec: winui/ui/collections/TableView.hpp.
A table: a header strip over virtualized rows, with sorting, resizable columns, frozen leading and trailing columns, four selection modes, cell templates, cell editing and collapsible groups.
Only the rows in view are realized, so a grid of 100,000 rows costs what a screenful does. Columns are fixed, Auto or fraction-sized (TableViewColumnWidth). When they are wider than the viewport, the table scrolls sideways with Shift+wheel, a horizontal wheel, Left and Right, or the horizontal scroll bar. Leading and trailing frozen columns stay in place. A click on a header sorts by that column through its TableViewSortCycle and draws a chevron. Sorting reorders the table's own view of the rows, never rows(), and the selection stays on the same rows. Dragging a header's trailing divider resizes the column between its min and max width.
A cell shows its text in rows() unless set_cell_factory() builds an Element for it, such as a check box, a progress bar or an icon beside a caption. Those are built only for the rows in view and kept while their row stays in view or holds focus. They are children of the table, so they take pointer input and focus and screen readers see them. An Auto column measures them too, and the column still sorts by its text.
Editing is off until set_read_only(false). Then F2, or a double-click on a cell, opens an editor in the current cell. That is a TextBox over a text cell, or what set_cell_editor_factory() builds, which is the only way to edit a template cell. Enter commits and Escape cancels. A press elsewhere in the table, or focus leaving it, commits. on_cell_edited() sees every commit and can reject it, which keeps the editor open. While editing is possible, Left and Right move the current column instead of scrolling sideways, and scrolling follows it. The current column holds the cell F2 edits, ringed when focused.
set_group_keys() files the rows under collapsible group header rows, the groups in the order their keys first appear. A sort orders the rows within each group. A header row toggles on a click, Space or Enter. Left collapses it and Right expands it. Selection never lands on a header row, and it stays put while the columns scroll sideways.
The grid itself is the single tab stop. Up, Down, PageUp, PageDown, Home and End move through the rows and group headers in view order and scroll the row into view. Space selects in Single and Extended mode and toggles in Multiple mode. Shift extends a range in Multiple and Extended mode, and Ctrl moves without selecting there. Row indices everywhere in this API (selected_row(), selected_rows(), on_selection_changed(), on_cell_edited()) are indices into rows(), whatever the sort or grouping. Up from the first row moves onto the column headers. There Left and Right move between them, Enter or Space sorts, Ctrl+Left and Ctrl+Right narrow and widen the column, and Down goes back to the rows.
Example:
// Declarative: ui::TableView builds a ui::TableViewControl, one row per item.
using ui::operator""_fr;
struct Person { std::string name; std::string role; int years = 0; };
ui::Property<std::string> status{"Nothing selected"};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::TableView<Person>{
.items = {{"Ada Lovelace", "Engineer", 5},
{"Grace Hopper", "Architect", 12},
{"Alan Turing", "Researcher", 8}},
.columns = {{.header = "Name", .width = 1_fr, .text = [](const Person& p) { return p.name; }},
{.header = "Role", .width = 160.0f, .text = [](const Person& p) { return p.role; }},
{.header = "Years",
.width = 80.0f,
.text = [](const Person& p) { return std::to_string(p.years); },
.compare = [](const Person& a, const Person& b) { return a.years <=> b.years; }}},
.height = 240,
.on_selection_changed = [&status](std::optional<Person> p)
{ status = p ? p->name + " selected" : "Nothing selected"; },
},
ui::Text{.text = status},
},
});
// Imperative: the same control built by hand.
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto grid = std::make_unique<ui::TableViewControl>();
grid->set_columns({{.header = "Name", .width = 100.0f}, {.header = "Age", .width = 60.0f}});
grid->set_rows({{"Ada", "36"}, {"Grace", "85"}});
grid->on_selection_changed([&status](std::optional<std::size_t> row)
{ status = row ? std::format("Row {}", *row) : ""; });
grid->set_selected_row(1); // fires the handler, scrolls row 1 into view
grid->sort_by_column(1, ui::TableViewSortDirection::Descending);
grid->set_read_only(false); // F2 or a double-click edits a cell
grid->on_cell_edited([](const ui::TableViewCellEditArgs& args)
{ return args.text.empty() ? ui::Cancel : ui::Proceed; });
grid->set_group_keys({"Staff", "Guests"}); // one key per row
stack->add(std::move(grid));
ui::TableView¶
A virtualized table: one row per item, one cell per column. Builds a ui::TableViewControl.
.items is the collection and takes the same sources as ui::ListView. Each column says which text it shows for an item, or gives a cell template that builds the cell's content from the item. Only visible rows are realized, so a large table costs what the viewport costs. When a bound collection changes, the rows are read again.
A header click sorts the table's view of the rows by that column, never .items, and the selection stays on the same items. Dragging a header's divider resizes the column and reports it through .on_column_resized. Columns wider than the table scroll sideways, except frozen ones. Row indices (.selected_row, .selected_indices) are positions in .items, whatever the sort or grouping. Nothing is selected until the user or .selected_row picks a row.
.read_only = false lets the user edit cells with F2 or a double-click. Enter commits and Escape cancels. .on_cell_edited writes each value into its item or rejects it. .group_by files the rows under collapsible group headers.
Example:
struct Person
{
std::string name;
std::string role;
int years = 0;
friend bool operator==(const Person&, const Person&) = default;
};
using ui::operator""_fr;
ui::Property<std::vector<Person>> people{{{"Ada Lovelace", "Engineer", 5}, {"Grace Hopper", "Architect", 12}}};
ui::Property<std::string> status{};
window.set_content(ui::VerticalStack{.content = {
ui::TableView<Person>{
.items = people,
.columns = {
{.header = "Name", .width = 140.0f, .text = [](const Person& p) { return p.name; }, .frozen = true},
{.header = "Role", .width = 1_fr, .text = [](const Person& p) { return p.role; }},
{.header = "Years",
.width = 80.0f,
.text = [](const Person& p) { return std::to_string(p.years); },
.compare = [](const Person& a, const Person& b) { return a.years <=> b.years; },
.read_only = true},
},
.alternating_rows = true,
.read_only = false,
.group_by = [](const Person& p) { return p.role; },
.height = 300.0f,
.on_selection_changed = [&](std::optional<Person> p) { status = p ? p->name : ""; },
.on_cell_edited = [](const ui::TableViewCellEditedArgs<Person>& args)
{
if (args.text.empty())
{
return ui::Cancel; // keeps the editor open
}
(args.column == 0 ? args.item.name : args.item.role) = args.text;
return ui::Proceed;
},
.empty_content = ui::Text{.text = "No people yet"},
},
ui::Text{.text = status},
}});
Every field, in the order a designated initializer names them:
ui::TableView{
.items, .key, .columns, .selected_row, .selected_indices, .row_height,
.size_rows_to_content, .selection_mode, .can_user_sort_columns, .can_user_resize_columns,
.grid_lines_visibility, .alternating_rows, .read_only, .group_by, .grouping_enabled,
.sticky_group_headers, .alternating_row_background, .style, .margin, .width, .height,
.min_width, .min_height, .max_width, .max_height, .horizontal_alignment,
.vertical_alignment, .visible, .tool_tip, .enabled, .accessibility, .on_selection_changed,
.on_sorting, .on_sorted, .on_column_resized, .on_beginning_edit, .on_cell_edited,
.on_pointer_entered, .on_pointer_exited, .on_got_focus, .on_lost_focus, .on_key_down,
.on_wheel, .control, .empty_content, .group_header_template, .item_context_menu
}
Fields, in declaration order (designators must follow it):
| Field | Type | Default | |
|---|---|---|---|
items |
ui::Binding<std::vector<T>> |
The collection, one row per item: a ui::ObservableVectorui::Property<std::vector<T>>, a plain vector or a braced list. |
|
key |
ui::ItemKey<std::type_identity_t<T>> |
What identifies an item when .items is bound to a property: a member pointer or a callable returning the key (see ui::ItemKey). Keys must be unique. With a key, a new vector keeps the selection on the same items. Without one, the rows before and after an edit keep theirs. A change that arrives as a reset, such as an ObservableVector's assign() or clear() or a very large reorder, clears the selection. |
|
columns |
std::vector<ui::TableColumn<std::type_identity_t<T>>> |
The columns, left to right: header, width in DIPs and the cell text for an item. | |
selected_row |
ui::TwoWayField<std::optional<std::size_t>> |
The selected row. Unset, the default, selects nothing. For a table that opens on its first row, set 0. | |
selected_indices |
ui::Binding<std::vector<std::size_t>> |
Every selected row, as ascending positions in .items. Bind a ui::Property<std::vector<std::size_t>> to read and set the selection under Multiple or Extended. Two-way with ui::bind(). Left empty and unbound, .selected_row decides. |
|
row_height |
ui::Binding<float> |
{40.0f} |
Row height, DIPs. 30 and 48 are the compact and comfortable densities. |
size_rows_to_content |
ui::Binding<bool> |
{false} |
Whether rows grow past row_height to fit their tallest cell, wrapping the text cells. |
selection_mode |
ui::Binding<ui::SelectionMode> |
{ui::SelectionMode::Single} |
None, Single (the default), Multiple or Extended. |
can_user_sort_columns |
ui::Binding<bool> |
{true} |
Whether header clicks sort. Columns opt out with can_sort. |
can_user_resize_columns |
ui::Binding<bool> |
{true} |
Whether header dividers can be dragged. Columns opt out with can_resize. |
grid_lines_visibility |
ui::Binding<ui::TableViewGridLinesVisibility> |
{ui::TableViewGridLinesVisibility::All} |
Which grid lines show: All (the default), Horizontal, Vertical or None. |
alternating_rows |
ui::Binding<bool> |
{false} |
Whether rows are banded: every second row takes alternating_row_background. |
read_only |
ui::Binding<bool> |
{true} |
False turns editing on, by F2 or a double-click on a cell. Columns opt out with their own read_only. Each commit reaches .on_cell_edited. |
group_by |
ui::Callback<std::string(const T&)> |
The group an item is filed under, as the text its header shows. Groups follow the order their keys first appear in .items, and a sort orders the rows within each group. |
|
grouping_enabled |
ui::Binding<bool> |
{true} |
Whether .group_by groups the rows. False shows them ungrouped, and true groups them again, with the collapsed groups still collapsed. |
sticky_group_headers |
ui::Binding<bool> |
{true} |
Whether the current group's header stays pinned under the column headers while its rows scroll under it. |
alternating_row_background |
ui::Binding<ui::Paint> |
The banding color. Empty, the default, uses the theme's banding color (palette.tabular_row_alternating under Fluent). Drawn only with alternating_rows. |
|
on_selection_changed |
ui::Callback<void(ui::SelectedItem<std::type_identity_t<T>>)> |
Fires on every selection change, by click, keys or a bound write. Called with a copy of the selected row's item, the first in .items order under Multiple and Extended, or unset when nothing is selected (see ui::SelectedItem). The copy is safe to keep. |
|
on_sorting |
ui::Callback<ui::Decision(const ui::TableViewSortingArgs&)> |
Called with the column and the direction a header click or key is about to sort it in. Return ui::Cancel to keep the current order. | |
on_sorted |
ui::Callback<void(const ui::TableViewSortedArgs&)> |
Called with the column and its new direction after a sort change. The column is unset when the sort was cleared. | |
on_column_resized |
ui::Callback<void(const ui::TableViewColumnResizedArgs&)> |
Called with the column and its new width in DIPs on every step of a user resize. | |
on_beginning_edit |
ui::Callback<ui::Decision(const ui::TableViewBeginningEditArgs<std::type_identity_t<T>>&)> |
Called with the item and the column before an editor opens. Return ui::Cancel to keep it closed. | |
on_cell_edited |
ui::Callback<ui::Decision(const ui::TableViewCellEditedArgs<std::type_identity_t<T>>&)> |
Called on a commit with the item, the column and the editor's text. Write the value into the item and return ui::Proceed or nothing. Return ui::Cancel to reject it and keep the editor open. For a column with an editing_cell, item is the edited copy and the text is its cell text, and proceeding copies it into the collection. A ui::Property collection is written back after an accepted edit. An ObservableVector's item is changed in place, without a notification. Without a handler a text edit only changes the cell as shown, until the rows are read again. |
|
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<std::type_identity_t<ui::TableView>> |
Out: the handle that receives the built control. Its type uses std::type_identity_t, so passing a handle does not take part in deducing the type argument. | |
empty_content |
ui::Spec |
Shown, centered in the body, while there are no rows. | |
group_header_template |
ui::Callback<ui::Spec(const std::string& key)> |
A group header's content from its key, shown after the chevron in place of the key and the count. Needs .group_by. |
|
item_context_menu |
ui::Callback<std::vector<ui::MenuItem>(const std::type_identity_t<T>&)> |
The context menu of an item: a callable taking the item and returning the menu's entries. Called on a right-click on its row, or on Shift+F10 or the Menu key while it is current. The row is selected first. An empty list opens no menu. An item of a ui::ObservableVector stays where it is while it is in the collection, so an entry's on_click may keep a reference to it. Any other item is valid during the call only, so capture it by value. |
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::TableViewControl; the caller owns it.
ui::TableViewControl¶
Derives from ui::Element, ui::SelectionProvider, ui::GridProvider, ui::TableProvider.
A table: a header strip over virtualized rows, with sorting, resizable columns, frozen leading and trailing columns, four selection modes, cell templates, cell editing and collapsible groups.
Only the rows in view are realized, so a grid of 100,000 rows costs what a screenful does. Columns are fixed, Auto or fraction-sized (TableViewColumnWidth). When they are wider than the viewport, the table scrolls sideways with Shift+wheel, a horizontal wheel, Left and Right, or the horizontal scroll bar. Leading and trailing frozen columns stay in place. A click on a header sorts by that column through its TableViewSortCycle and draws a chevron. Sorting reorders the table's own view of the rows, never rows(), and the selection stays on the same rows. Dragging a header's trailing divider resizes the column between its min and max width.
A cell shows its text in rows() unless set_cell_factory() builds an Element for it, such as a check box, a progress bar or an icon beside a caption. Those are built only for the rows in view and kept while their row stays in view or holds focus. They are children of the table, so they take pointer input and focus and screen readers see them. An Auto column measures them too, and the column still sorts by its text.
Editing is off until set_read_only(false). Then F2, or a double-click on a cell, opens an editor in the current cell. That is a TextBox over a text cell, or what set_cell_editor_factory() builds, which is the only way to edit a template cell. Enter commits and Escape cancels. A press elsewhere in the table, or focus leaving it, commits. on_cell_edited() sees every commit and can reject it, which keeps the editor open. While editing is possible, Left and Right move the current column instead of scrolling sideways, and scrolling follows it. The current column holds the cell F2 edits, ringed when focused.
set_group_keys() files the rows under collapsible group header rows, the groups in the order their keys first appear. A sort orders the rows within each group. A header row toggles on a click, Space or Enter. Left collapses it and Right expands it. Selection never lands on a header row, and it stays put while the columns scroll sideways.
The grid itself is the single tab stop. Up, Down, PageUp, PageDown, Home and End move through the rows and group headers in view order and scroll the row into view. Space selects in Single and Extended mode and toggles in Multiple mode. Shift extends a range in Multiple and Extended mode, and Ctrl moves without selecting there. Row indices everywhere in this API (selected_row(), selected_rows(), on_selection_changed(), on_cell_edited()) are indices into rows(), whatever the sort or grouping. Up from the first row moves onto the column headers. There Left and Right move between them, Enter or Space sorts, Ctrl+Left and Ctrl+Right narrow and widen the column, and Down goes back to the rows.
Example:
// Declarative: ui::TableView builds a ui::TableViewControl, one row per item.
using ui::operator""_fr;
struct Person { std::string name; std::string role; int years = 0; };
ui::Property<std::string> status{"Nothing selected"};
window.set_content(ui::VerticalStack{
.spacing = 8,
.content = {
ui::TableView<Person>{
.items = {{"Ada Lovelace", "Engineer", 5},
{"Grace Hopper", "Architect", 12},
{"Alan Turing", "Researcher", 8}},
.columns = {{.header = "Name", .width = 1_fr, .text = [](const Person& p) { return p.name; }},
{.header = "Role", .width = 160.0f, .text = [](const Person& p) { return p.role; }},
{.header = "Years",
.width = 80.0f,
.text = [](const Person& p) { return std::to_string(p.years); },
.compare = [](const Person& a, const Person& b) { return a.years <=> b.years; }}},
.height = 240,
.on_selection_changed = [&status](std::optional<Person> p)
{ status = p ? p->name + " selected" : "Nothing selected"; },
},
ui::Text{.text = status},
},
});
// Imperative: the same control built by hand.
auto stack = std::make_unique<ui::StackControl>(ui::Orientation::Vertical);
auto grid = std::make_unique<ui::TableViewControl>();
grid->set_columns({{.header = "Name", .width = 100.0f}, {.header = "Age", .width = 60.0f}});
grid->set_rows({{"Ada", "36"}, {"Grace", "85"}});
grid->on_selection_changed([&status](std::optional<std::size_t> row)
{ status = row ? std::format("Row {}", *row) : ""; });
grid->set_selected_row(1); // fires the handler, scrolls row 1 into view
grid->sort_by_column(1, ui::TableViewSortDirection::Descending);
grid->set_read_only(false); // F2 or a double-click edits a cell
grid->on_cell_edited([](const ui::TableViewCellEditArgs& args)
{ return args.text.empty() ? ui::Cancel : ui::Proceed; });
grid->set_group_keys({"Staff", "Guests"}); // one key per row
stack->add(std::move(grid));
SortComparer¶
using ui::TableViewControl::SortComparer = Callback<std::weak_ordering(std::size_t column, std::size_t row_a, std::size_t row_b)>
Orders two rows, by their indices into rows(), for the column being sorted.
CellFactory¶
using ui::TableViewControl::CellFactory = Callback<std::unique_ptr<Element>(std::size_t row, std::size_t column)>
Builds the content of one cell, by its row (an index into rows()) and column. Returns nullptr to leave that cell as text.
CellToolTip¶
using ui::TableViewControl::CellToolTip = Callback<std::string(std::size_t row, std::size_t column) const>
The tip for a body cell, by its row (an index into rows()) and column. Empty means none, and the table's own tool_tip() shows instead.
GroupHeaderFactory¶
using ui::TableViewControl::GroupHeaderFactory = Callback<std::unique_ptr<Element>(std::size_t group)>
Builds the content of group group's header row.
TableViewControl¶
An empty grid: no columns, no rows, nothing selected, 40 DIP rows.
~TableViewControl¶
TableViewControl¶
Not movable or copyable.
operator=¶
TableViewControl¶
operator=¶
columns¶
The columns, left to right. A row's cell i is drawn under column i.
set_columns¶
Replaces every column, clears the sort, and lays the table out again.
rows¶
The rows in source order. Each inner vector is one row's cell text, indexed by column. A row with fewer cells than there are columns shows blank cells past its own size.
set_rows¶
Replaces every row, sorts them again by the current sort, clears the selection, and lays the table out again. Nothing is selected afterwards, in every selection mode. Raises on_selection_changed() when something was selected before.
row_height¶
Height of every body row in DIPs; 40 by default. The header strip has its own fixed height of 40.
set_row_height¶
Changes the row height, lays the table out again and rebuilds the visible rows.
size_rows_to_content¶
Whether each row grows past row_height() to fit its tallest cell. Text cells then wrap instead of being trimmed, and a template cell or an editor is measured at its own height. Off by default. A row keeps its height while it is out of view. Rows not yet seen count as row_height() until they scroll in.
set_size_rows_to_content¶
column_actual_width¶
The width the last arrange gave a column, whether it is fixed, Auto or a fraction. 0 before the first arrange or past the last column.
set_column_width¶
Sets one column's width. A user resize does this, then raises on_column_resized().
set_column_frozen¶
Pins one column, or lets it scroll again (TableViewColumn::frozen). Keeps the sort.
set_column_frozen_trailing¶
Pins one column against the right edge, or lets it scroll again (TableViewColumn::frozen_trailing). Keeps the sort.
can_user_resize_columns¶
The table-wide switch for dragging header dividers; on by default. A column opts out with TableViewColumn::can_resize.
set_can_user_resize_columns¶
See can_user_resize_columns().
on_column_resized¶
Called with the column and its new width in DIPs on every step of a user resize drag.
horizontal_offset¶
How far the columns are scrolled sideways, DIPs. 0 is the leading edge.
set_horizontal_offset¶
Scrolls sideways, clamped to how far the columns overflow the viewport, and redraws.
can_user_sort_columns¶
The table-wide switch for sorting by a header click; on by default. A column opts out with TableViewColumn::can_sort.
set_can_user_sort_columns¶
sort_column¶
The sorted column. Unset while the rows are in source order.
sort_direction¶
The sorted column's direction. None while unsorted.
sort_by_column¶
Sorts the view by column and clears any other column's sort. TableViewSortDirection::None restores the source order. The selected rows stay selected. Returns true when the sort changed, and then raises on_sorted().
toggle_sort_direction¶
Does what a header click does: steps column to the next direction in its sort_cycle, unless on_sorting() cancels. Returns true when the sort changed.
header_cursor¶
The column header the keyboard is on. Unset while the keys move through the rows.
clear_sort¶
Restores the source order. Returns true when there was a sort to clear.
set_sort_comparer¶
Sets how two rows compare for a sort. Without one, the default, the cell text is compared, as numbers when both cells read as numbers (compare_text()).
on_sorting¶
Called before a header click or key sorts a column. Return ui::Cancel to keep the current order. sort_by_column() does not raise it.
on_sorted¶
Called after every sort change, with the column and its new direction. When the sort was cleared, the direction is None and there is no column.
row_at_position¶
The index into rows() of the row shown at position in the sorted and grouped view. Unset for a group header and past the end.
position_of_row¶
Where row row, an index into rows(), is shown in the view. Unset while its group is collapsed.
position_count¶
How many positions the view has: the rows shown plus the group header rows.
selection_mode¶
The selection mode; Single by default. Switching to None clears the selection. Switching to Single keeps at most one row, the first selected.
set_selection_mode¶
See selection_mode().
selected_row¶
The selected row with the lowest index into rows(). Unset when nothing is selected.
set_selected_row¶
Selects index alone, clamped to the rows, and scrolls it into view. Does nothing, and fires no callback, if it is already the selection, if there are no rows, or under SelectionMode::None. Unset clears the selection.
selected_rows¶
Every selected row, as ascending indices into rows().
set_selected_rows¶
Replaces the selection. Indices past the rows are dropped, Single keeps the lowest, None keeps nothing, and an empty list clears it. Raises on_selection_changed() when the selection changed.
is_row_selected¶
Whether row row, an index into rows(), is selected.
on_selection_changed¶
Called with selected_row() whenever the selection changes: by click, by key, by set_selected_row() or set_selected_rows(), or by set_rows() clearing a selection. Replaces any previous handler.
set_item_context_menu¶
Sets the context menu of row row, an index into rows(). It is asked for on a right-click on the row, or on Shift+F10 or the Menu key while the row is the current one. The row is selected first, unless it already is or the selection mode is None. An empty list opens no menu. Replaces any previous one.
raise_context_requested¶
Opens the row's context menu (set_item_context_menu()) when no on_context_requested() handler inside the row took the request.
set_cell_factory¶
Sets the cell factory. A cell it returns an Element for shows that Element instead of its text, stretched over the whole cell with no cell padding. Called once per cell as its row comes into view. The Element is kept while the row stays in view or holds focus, and built again after set_rows() or set_columns(). Replacing the factory rebuilds every cell.
set_cell_tool_tip¶
Sets the per-cell tips; see CellToolTip.
tool_tip_at¶
The tip of the cell under p (set_cell_tool_tip()), else tool_tip().
cell_element¶
The Element a template cell shows while its row is in view. nullptr for a text cell or a row out of view.
read_only¶
Whether editing is off for the whole table, the default. A column opts out on its own with TableViewColumn::read_only.
set_read_only¶
Turns editing off or on. Turning it off commits an open edit, or cancels it when the commit is rejected.
set_cell_editor_factory¶
Sets the factory for a cell's editor, by its row and column. Returning nullptr gives the default, a TextBox over a text cell. A template cell without an editor is not editable. The editor is laid over the cell while the edit is open. The table stays the focused element and passes the keys and text on to the editor's first focusable control.
is_editing¶
Whether a cell editor is open.
editing_row¶
The row of the open edit, an index into rows(). Unset when none is open.
editing_column¶
The column of the open edit. Unset when none is open.
editor¶
The open editor, either the TextBox or what the editor factory built, or nullptr.
commit_edit¶
Closes the open edit and keeps its text. on_cell_edited() runs first and may reject it. Returns false when nothing was open or the commit was rejected.
cancel_edit¶
Closes the open edit and leaves the cell as it was. Returns false when nothing was open.
current_column¶
The current column, where a click on a cell or Left and Right put it. F2 edits its cell on the keyboard's row. Unset until then, and F2 then edits the first editable column.
set_current_column¶
Moves the current column, clamped to the columns, and scrolls it into view.
on_beginning_edit¶
Called before an editor opens on a cell, with its row and column. Return ui::Cancel to keep it closed.
on_cell_edited¶
Called on a commit with the cell and the editor's text. For an editor from the factory, the text is the cell's own text. Return ui::Cancel to reject the value, which keeps the editor open. The handler may rewrite text to what the cell should show. Without a handler a commit stores the text in rows().
set_cell_text¶
Replaces one cell's text in rows() and redraws. The row keeps its place until the next sort.
set_group_keys¶
Sets one key per row, by index into rows(), the text its group header shows. Rows with the same key form a group. Groups follow the order their keys first appear in rows(), and a sort orders the rows within each group. A collapsed group stays collapsed while its key is in use. An empty list, the default, shows the rows ungrouped. A row past the end of the list is filed under an empty key.
set_group_header_factory¶
Sets the group header content, shown after the chevron in place of the key and count. It is built for the headers in view only, and again when the groups change. A click on the row still toggles the group.
group_header_element¶
Group group's built header content while its header is in view, else nullptr.
group_keys¶
The keys set_group_keys() was given.
grouping_enabled¶
Whether the keys group the rows; on by default. Off shows the rows ungrouped and keeps the keys, so turning it back on groups them again.
set_grouping_enabled¶
See grouping_enabled().
sticky_group_headers¶
Whether group headers are sticky; on by default. The current group's header stays pinned under the column headers until the next group's header pushes it up. A click on it still toggles the group.
set_sticky_group_headers¶
group_count¶
How many groups there are. 0 while the rows are not grouped.
group_key¶
A group's key, the text on its header. Empty past the last group.
group_size¶
How many rows a group holds, in or out of view. 0 past the last group.
is_group_expanded¶
Whether a group's rows are shown.
set_group_expanded¶
Expands or collapses a group. A keyboard cursor on a row that this hides moves to the group's header. The selection stays.
expand_all_groups¶
Expands every group.
collapse_all_groups¶
Collapses every group.
group_of_row¶
The group a row is filed under. Unset while the rows are not grouped.
is_group_header¶
Whether view position position is a group header row.
group_at_position¶
The group of the row or header at view position position. Unset past the end or while the rows are not grouped.
grid_lines_visibility¶
Row separators, column separators, both (the default) or neither.
set_grid_lines_visibility¶
alternating_rows¶
Whether rows are banded: every second row in view order gets the alternating background. Off by default.
set_alternating_rows¶
See alternating_rows().
set_alternating_row_background¶
Sets the banding color. Empty, the default, uses the theme's banding color (palette.tabular_row_alternating under Fluent).
alternating_row_background¶
See set_alternating_row_background().
set_empty_content¶
Sets what the body shows, centered, while there are no rows. nullptr, the default, shows nothing. The table owns it.
empty_content¶
The empty-state content, or nullptr.
scroll_offset¶
The body's current vertical scroll offset, DIPs.
handle_shortcut¶
Handles the table's shortcuts. An Escape that closes a popup the editor opened, such as a ComboBox's list, leaves the edit open.
cursor_at¶
The resize cursor over a resizable header divider.
focused¶
Whether the grid holds keyboard focus and draws its focus ring.
focusable¶
Focusable while enabled and holding at least one row.
collect_focusables¶
The table, then the focusable controls in its template cells and empty content. Never the editor, because the table keeps focus while one is open.
for_each_child¶
The table body, the empty-state content while it shows, the realized template cells and the open editor.
automation_role¶
Reported to screen readers as a data grid. It holds a header with one item per column, and invoking a sortable one sorts by it. Then come the rows in view, each selectable, and the group headers, which expand and collapse. A row holds its cells, one per column, each named by its text. A template cell's Element or an open editor sits under its cell. As in every virtualizing list, only the rows in view are reported.
automation_children¶
The empty-state content while it shows, the header, then the rows in view.
automation_focus¶
The row the keyboard is on, so a screen reader reads it as the arrow keys move.
automation_parent_of¶
The cell around a template cell or an open editor, and the header row around a group header's content, while their row is in view.
compare_text¶
The default cell comparison. Compares as numbers when both texts parse entirely as numbers, else as text, ignoring ASCII case first.
clear_dirty_override¶
Clears the row context menu too, which is listed as a child only while it shows.
measure_override¶
Desired size: the sum of the column widths, with fraction columns at their content width, by the header height plus every row. Each is clamped to available where it is bounded.
arrange_override¶
Resolves the column widths against the final width, splits the bounds into the header strip and the body, and arranges the body and the empty-state content.
render_override¶
Draws the header strip, the realized rows, the grid lines, the scroll bars and the focus ring.
pointer_override¶
A press on a header sorts, or starts a resize on its trailing divider. A press on a body row selects it by the selection mode's rules.
handled_pointer_override¶
A press a template cell took still commits an open edit elsewhere in the table.
wheel_override¶
Vertical turns scroll the rows. Horizontal turns and Shift+wheel scroll the columns sideways when they overflow.
hit_test_children¶
The children p (in this element's layout coordinates) is tested against, topmost first. Default: what for_each_child() lists, last first. A container that draws in another order (Panel's z-index), a control with parts for_each_child() does not list, or one whose parts cover others in places (a pane drawn over the content) lists them here.
covers_children_at¶
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.
tick_override¶
Ticks the children and the horizontal scroll bar's fade, and commits an edit whose focus left the table.
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.
key_override¶
The row keys described in the class comment. Left and Right scroll sideways, move the current column while editing is possible, or collapse and expand the group header the cursor is on. F2 opens an editor. While one is open, Enter commits, Escape cancels and every other key goes to the editor. Ignored while disabled or unfocused.
text_override¶
Typed text goes to an open editor; without one it is ignored.
focus_changed¶
Called by Window as focus arrives or leaves. Redraws. An open editor follows the table's focus. Focus leaving commits the edit on the next tick unless it has come back by then.
focus_ring_rect¶
Inside the header cell, group header, cell or row the keyboard is on, cut to the part in view. Around the whole table otherwise, and nowhere while an editor is open.
focus_ring_corner_radius¶
The ring's corner radius: the theme's control radius by default.
focus_ring_inside¶
Whether the ring goes inside focus_ring_rect() in every era, its outer edge on the rect. False by default, which leaves it to the theme. An item inside a viewport (a list row, a tab, a menu item) returns true, as does a control in a tight spot, so a neighbor or the viewport edge never cuts its ring.
struct TableViewSortedArgs¶
What on_sorted gets after a sort change.
| Field | Type | Default | |
|---|---|---|---|
column |
std::optional<std::size_t> |
The sorted column; unset when cleared. | |
direction |
TableViewSortDirection |
TableViewSortDirection::None |
Its new direction. |
struct TableViewSortingArgs¶
What on_sorting gets before a user sort: the column and the direction it is about to take. Return ui::Cancel from the handler to keep the current order.
| Field | Type | Default | |
|---|---|---|---|
column |
std::size_t |
0 |
The column about to be sorted. |
direction |
TableViewSortDirection |
TableViewSortDirection::None |
The direction it is about to take. |
struct TableViewColumnResizedArgs¶
What on_column_resized gets on every step of a user resize.
| Field | Type | Default | |
|---|---|---|---|
column |
std::size_t |
0 |
The resized column. |
width |
float |
0.0f |
Its new width, DIPs. |
struct TableViewCellArgs¶
A TableViewControl's on_beginning_edit: the cell an editor is about to open on.
| Field | Type | Default | |
|---|---|---|---|
row |
std::size_t |
0 |
An index into rows(). |
column |
std::size_t |
0 |
The column. |
struct TableViewCellEditArgs¶
A TableViewControl's on_cell_edited: the cell and the editor's text, which the handler may rewrite to what the cell should show.
| Field | Type | Default | |
|---|---|---|---|
row |
std::size_t |
An index into rows(). | |
column |
std::size_t |
The column. | |
text |
std::string& |
The editor's text. |
struct TableViewBeginningEditArgs¶
A TableView spec's on_beginning_edit: the item and the column an editor is about to open on.
| Field | Type | Default | |
|---|---|---|---|
item |
T& |
The row's item. | |
column |
std::size_t |
The column. |
struct TableViewCellEditedArgs¶
A TableView spec's on_cell_edited: the item, the column and the editor's text.
| Field | Type | Default | |
|---|---|---|---|
item |
T& |
The row's item (the edited copy for an editing_cell column). |
|
column |
std::size_t |
The column. | |
text |
const std::string& |
The editor's text. |
struct TableViewColumnWidth¶
Derives from ui::GridLength.
A TableView column's width, in the same units as a ui::Grid track:
- Fixed DIPs: a plain number, or
120_dipin a spec. - Auto:
ui::GridLength::auto_(), orui::AutoSizein a spec. As wide as its header and the cells in view. - A fraction:
ui::GridLength::fraction(), or1_frin a spec. A weighted share of what the fixed and Auto columns leave of the viewport.
Converts implicitly from a number and from a ui::GridLength, so {"Name", 140.0f} and {"Name", 1_fr} both name a column.
TableViewColumnWidth¶
A fixed width of dips.
TableViewColumnWidth¶
Auto, a fraction or fixed DIPs, as length says.
struct TableViewColumn¶
One column of a TableView: its header, its width and how the user may size, sort and pin it.
| Field | Type | Default | |
|---|---|---|---|
header |
std::string |
Caption drawn in the header strip above the rows. A caption too wide for the column is trimmed with an ellipsis, like the cells. | |
width |
TableViewColumnWidth |
100.0f |
Fixed DIPs including the cell padding, Auto or a fraction; see TableViewColumnWidth. Cell text past the resolved width is trimmed with a trailing ellipsis. A user resize makes the column fixed at the width it was dragged to, and fixes the fraction columns before it at the widths they had, so only the columns after it give or take the room. |
min_width |
float |
20.0f |
The smallest and largest width, DIPs. The resolved width and a user resize stay inside them. |
max_width |
float |
Unbounded |
See min_width. |
can_resize |
bool |
true |
Whether the user can drag this column's header divider. The table's can_user_resize_columns() must be on too. |
can_sort |
bool |
true |
Whether a click on this header sorts by it. The table's can_user_sort_columns() must be on too. sort_by_column() works either way. |
sort_cycle |
TableViewSortCycle |
TableViewSortCycle::AscendingDescending |
The directions repeated header clicks step through. |
frozen |
bool |
false |
Whether the column stays put while the others scroll sideways. Only a leading run of frozen columns is pinned. A frozen column after an unfrozen one scrolls. |
frozen_trailing |
bool |
false |
Whether the column stays put against the right edge while the others scroll sideways. Only a trailing run of such columns is pinned, and frozen wins on a column that sets both. |
read_only |
bool |
false |
Whether the column's cells are never edited, even in a table that allows editing (TableViewControl::set_read_only(false)). A read-only column can still be the current one. |
struct TableColumn¶
One column of a ui::TableView: its header, its width, the text it shows for an item, and how the user may size, sort and pin it.
The callables are std::function because columns are written as a braced list, which copies its entries.
| Field | Type | Default | |
|---|---|---|---|
header |
std::string |
Caption drawn in the header strip above the rows. | |
width |
TableViewColumnWidth |
100.0f |
A number of DIPs including the cell padding, ui::AutoSize (as wide as the header and the cells in view) or a fraction (1_fr, 2_fr: a weighted share of what the other columns leave). Header and cell text past the width is trimmed with an ellipsis. |
text |
std::function<std::string(const T&)> |
The cell text for an item. | |
min_width |
float |
20.0f |
The column's minimum and maximum width, DIPs. The resolved width and a user resize stay inside them. |
max_width |
float |
Unbounded |
See min_width. |
frozen |
bool |
false |
Whether the column stays put while the other columns scroll sideways. Only a leading run of frozen columns is pinned. |
frozen_trailing |
bool |
false |
Whether the column stays put against the right edge while the other columns scroll sideways. Only a trailing run of such columns is pinned. |
can_resize |
bool |
true |
Whether the user can drag this header's divider. The table's .can_user_resize_columns must be on too. |
can_sort |
bool |
true |
Whether a header click sorts by this column. The table's .can_user_sort_columns must be on too. |
sort_cycle |
TableViewSortCycle |
TableViewSortCycle::AscendingDescending |
The directions repeated header clicks step through. |
compare |
std::function<std::weak_ordering(const T&, const T&)> |
How two items order for this column, for example [](const T& a, const T& b) { return a.size <=> b.size; }. Left out, the cell text is compared, as numbers when both cells are numbers. |
|
read_only |
bool |
false |
Whether this column is never edited, even in a table with .read_only = false. |
cell |
std::function<Spec(T&)> |
The cell's content for an item, for example [](Task& t) -> ui::Spec { return ui::CheckBox{.checked = t.done}; }, stretched over the whole cell with no padding. Built for the rows in view only, and again when the collection changes. text still sorts the column and names the cell. |
|
editing_cell |
std::function<Spec(T&)> |
The editor for an item, shown over the cell while it is edited. It works on a copy of the item. A commit copies it back after .on_cell_edited accepts it, and a cancel drops it, so the item must be copyable. Without an editor, a text column edits in a TextBox and a column with a cell template cannot be edited. |
|
tool_tip |
std::function<std::string(const T&)> |
The tip shown while the pointer rests on the item's cell in this column, for example the full text of a trimmed cell. Empty for none. |
Enumerations¶
TableViewGridLinesVisibility¶
Which grid lines a TableView draws. Row banding is separate (set_alternating_rows()).
| Value | |
|---|---|
All |
Row separators and column separators (the default). |
Horizontal |
Row separators only. |
None |
No grid lines. |
Vertical |
Column separators only. |
TableViewSortDirection¶
The direction a TableView column is sorted in.
| Value | |
|---|---|
None |
Not sorted; the rows keep their source order. |
Ascending |
Smallest first; the header shows an up chevron. |
Descending |
Largest first; the header shows a down chevron. |
TableViewSortCycle¶
The directions repeated clicks on a column header step through. An unsorted column starts at the first one named. The *None cycles end on unsorted, so a third click restores the source order.
| Value | |
|---|---|
AscendingDescending |
Ascending, Descending, Ascending, and so on (the default). |
AscendingDescendingNone |
Ascending, Descending, None. |
DescendingAscending |
Descending, Ascending, Descending, and so on. Suits metrics, largest first. |
DescendingAscendingNone |
Descending, Ascending, None. |
Tests¶
tests/test_table_view.cpp