ui::ObservableVector¶
Header: winui/core/ObservableVector.hpp.
Derives from ui::PropertyBase.
A collection that reports each edit: what was inserted, removed, replaced or moved, and where.
It is the collection counterpart of ui::Property. A Property holding a std::vector can only say "changed"; an ObservableVector says what changed, so a ui::ForEach or a list view bound to it builds one row for an inserted item, destroys one for a removed item and leaves every other row, and its state, alone.
Each item lives at its own address, which stays the same for as long as the item is in the collection, whatever is inserted or removed around it. A row may therefore keep a reference to its item and bind to the item's own Property members. Positions do shift, so a row should not keep its index; index_of() finds an item's current position.
Observers are held the same way a Property holds them: the collection or the observer may be destroyed first. Destroying the collection reports a Reset to empty, so a tree still showing it shows nothing afterwards. Non-copyable and non-movable; UI-thread only.
Example:
struct Todo
{
explicit Todo(std::string text) : title{std::move(text)} {}
ui::Property<std::string> title;
ui::Property<bool> done{false};
};
ui::ObservableVector<Todo> todos;
window.set_content(ui::VerticalStack{.content = {
ui::Button{.text = "Add", .on_click = [&] { todos.emplace_back("New task"); }},
ui::ForEach<Todo>{
.items = todos,
.item = [&todos](Todo& todo) -> ui::Spec
{
return ui::HorizontalStack{.spacing = 8, .content = {
ui::ToggleSwitch{.checked = todo.done},
ui::Text{.text = todo.title},
ui::Button{.text = "Remove", .on_click = [&todos, &todo] { todos.remove(todo); }},
}};
},
},
}});
value_type¶
The item type.
iterator¶
Iterates the items as T&, in order.
const_iterator¶
Iterates the items as const T&, in order.
ObservableVector¶
An empty collection.
ObservableVector¶
Holds a copy of each of items; no observer exists yet, so nothing is notified.
~ObservableVector¶
Reports a Reset to empty, then leaves every remaining Subscription dead.
size¶
How many items the collection holds.
empty¶
True when the collection holds no items.
count¶
How many items the collection holds, as a Property, so a binding follows it: an empty-state text that shows while there is nothing, a count in a heading. It changes after the collection's own observers were told.
Example:
ui::ObservableVector<std::string> files;
window.set_content(ui::Text{
.text = "No files yet",
.visible = ui::bind(files.count(), [](std::size_t n) { return n == 0; }),
});
operator[]¶
The item at index, which must be less than size(). The reference stays valid until that item is removed or replaced.
operator[]¶
The item at index, which must be less than size().
front¶
The first item; the collection must not be empty.
front¶
The first item; the collection must not be empty.
back¶
The last item; the collection must not be empty.
back¶
The last item; the collection must not be empty.
index_of¶
The position of item, found by its address; unset when it is not an item of this collection.
begin¶
The first item, for iterating as T&.
end¶
One past the last item.
begin¶
The first item, for iterating as const T&.
end¶
One past the last item.
push_back¶
T& push_back(T item, std::source_location where = std::source_location::current())
requires std::move_constructible<T>
Appends item and returns it in its place.
emplace_back¶
template <typename... Args>
T& emplace_back(Args&& ... args)
requires std::constructible_from<T, Args...>
Appends an item constructed in place from args, for an item type that cannot be moved (one holding a Property), and returns it.
insert¶
T& insert(std::size_t index, T item, std::source_location where = std::source_location::current())
requires std::move_constructible<T>
Inserts item before position index (clamped to the end) and returns it in its place.
emplace¶
template <typename... Args>
T& emplace(std::size_t index, Args&& ... args)
requires std::constructible_from<T, Args...>
Inserts an item constructed in place from args before position index (clamped to the end) and returns it.
erase¶
void erase(std::size_t index, std::size_t count = 1, std::source_location where = std::source_location::current())
Removes count items starting at index; a range reaching past the end is cut short. The items are destroyed after the observers have been told.
remove¶
Removes item, found by its address. False when it is not an item of this collection.
erase_if¶
template <typename Predicate>
std::size_t erase_if(Predicate predicate, std::source_location where = std::source_location::current())
requires std::predicate<Predicate&, const T&>
Removes every item for which predicate answers true, and returns how many went. Each run of neighboring matches is reported as one Removed, from the back of the collection to the front. predicate is asked about every item before the first one goes.
replace¶
T& replace(std::size_t index, T item, std::source_location where = std::source_location::current())
requires std::move_constructible<T>
Replaces the item at index with item; whatever was built for the old one is built again. For an item that holds Properties, set those instead: nothing needs rebuilding.
move¶
void move(std::size_t from, std::size_t to, std::source_location where = std::source_location::current())
Moves the item at from to position to, both less than size(). The item keeps its address, and what was built for it stays as it is.
clear¶
Removes every item, reported as one Reset.
assign¶
template <typename Range>
void assign(const Range& items, std::source_location where = std::source_location::current())
requires requires(const Range& range) { { *std::begin(range) } -> std::convertible_to<T>; }
Replaces the whole content with a copy of each of items, reported as one Reset. Nothing is reported when the collection was empty and stays empty.
assign¶
void assign(std::initializer_list<T> items, std::source_location where = std::source_location::current())
requires std::copy_constructible<T>
Replaces the whole content with a copy of each of items, reported as one Reset.
batch¶
template <typename Edits>
void batch(Edits edits, std::source_location where = std::source_location::current())
requires std::invocable<Edits&>
Runs edits, which edit this collection, and reports everything they did as one Reset when they return, instead of one change per edit. For a bulk edit (an undo step, a sort, a reload) whose rows are cheaper to build again once than to follow edit by edit. A batch inside a batch reports once, when the outermost returns. Items removed or replaced during the batch are destroyed after the Reset was reported, so nothing built for them dangles. Nothing is reported when edits changed nothing.
Example:
ui::ObservableVector<std::string> pages{"Intro", "Setup", "Usage", "Cover"};
pages.batch([&] {
std::string moved = pages[3];
pages.erase(3);
pages.insert(0, std::move(moved));
});
observe¶
Observe changes. The returned Subscription must be kept alive for fn to keep firing; an Element keeps it with Element::add_subscription(). fn runs after the collection has changed, and is not called for the current content.