ui::Property¶
Header: winui/core/Property.hpp.
Derives from ui::PropertyBase.
An observable value: application state written once, from which every display of it is derived through bind() or observe().
T must be equality comparable, because the equality gate is not optional: set() compares before it assigns, so a no-op write notifies nobody and a dependency cycle terminates instead of recursing. Non-copyable and non-movable, since bindings hold a reference to it. A Property may outlive the controls bound to it or be destroyed first, which ends the bindings. UI thread only: set() is not synchronized.
Example:
ui::Property<int> clicks{0};
window.set_content(ui::VerticalStack{
.spacing = 12,
.content = {
ui::Text{.text = ui::bind(clicks, [](int n) { return std::format("Clicks: {}", n); })},
ui::Button{.text = "Click me", .on_click = [&] { ++clicks; }},
ui::Button{.text = "Reset", .on_click = [&] { clicks = 0; }},
},
});
value_type¶
The stored type.
Property¶
Holds a value-initialized T.
Property¶
Holds value; no observer exists yet, so nothing is notified.
get¶
The current value. The reference is invalidated by the next set().
operator*¶
Same as get().
operator->¶
Member access to the current value, e.g. title->size().
set¶
Assign and, if the value changed (!= by T's operator==), notify every observer synchronously before returning. UI thread only: a build with thread checks reports a write from another thread than the property's first notifying one, naming where (the caller's file and line; leave it defaulted).
operator=¶
Same as set(): clicks = 0;, and a thread check report names the caller's line as set()'s does. A braced value of several parts goes through set(): size.set({4, 3});.
operator+=¶
Property& operator+=(detail::AssignedValue<T> delta)
requires (!std::same_as<T, bool>) && requires(const T& a, const T& b) { { a + b } -> std::convertible_to<T>; }
Adds delta through set(), so the equality gate applies: count += 5;, log += "Saved\n";. Exists when T + T gives a T, except for bool.
operator-=¶
Property& operator-=(detail::AssignedValue<T> delta)
requires (!std::same_as<T, bool>) && requires(const T& a, const T& b) { { a - b } -> std::convertible_to<T>; }
Subtracts delta through set(): remaining -= 1;. Exists when T - T gives a T, except for bool.
operator++¶
Adds one through set(): ++clicks;. Exists when T has a ++.
operator--¶
Subtracts one through set(): --remaining;. Exists when T has a --.
operator++¶
As ++clicks, returning the value from before, as ++ on the T itself would.
operator--¶
As --remaining, returning the value from before.
toggle¶
void toggle(std::source_location where = std::source_location::current())
requires std::same_as<T, bool>
Flips a Property<bool> through set(): dark_mode.toggle();.
update¶
template <typename Fn>
void update(Fn&& fn, std::source_location where = std::source_location::current())
requires std::invocable<Fn&, T&>
Read-modify-write for values that are awkward to rebuild by hand (a container, a struct with several fields). fn mutates a copy; the equality gate then decides whether anything is notified, so a mutation that changes nothing still costs nothing. where is for the thread check, as in set().
observe¶
Observe changes. The returned Subscription must be kept alive for fn to keep firing; store it on the Element that depends on it (Element::add_subscription) so subscription lifetime is node lifetime. Observers are called newest subscription first. fn is not called for the current value. A caller that wants the initial value reads get(), as Binding