|
morph 0.1.0
Asynchronous bridge between GUI thread and business models
|
A view tree mounted on a backend: its widgets, built once, and the bindings that keep them current. More...
#include <mount.hpp>
Public Member Functions | |
| Mounted (reactive::Runtime &runtime, IViewBackend &backend, Node root, ContainerWidget *parent=nullptr, std::size_t depth=0) | |
| Mounted (Mounted const &)=delete | |
| Mounted & | operator= (Mounted const &)=delete |
| Mounted (Mounted &&)=delete | |
| Mounted & | operator= (Mounted &&)=delete |
| Widget & | root () const noexcept |
| The root node's widget. | |
| bool | focus (std::string_view focusId) |
Moves keyboard focus to the mounted widget whose Common::focusId is focusId, at any depth, including the content of a Switch case, a Tabs page, a Dialog and every row mounted now. | |
A view tree mounted on a backend: its widgets, built once, and the bindings that keep them current.
Destroying it destroys every binding before the widget it drives and every child before its parent. The runtime, the backend and every signal a binding reads must outlive it; the owner of the runtime constructs and destroys it. Non-copyable and non-movable: bindings point into it.
Every widget callback, a drop's accepts predicate included, runs as a reactive::Runtime::widgetEvent and untracked, so a binding whose setter makes the backend call a handler does not subscribe to what the handler reads. An exception a callback throws is reported (detail::site::kCallbackThrew) and goes no further: the backend's call returns normally, a predicate that threw refuses the drop, and what the callback wrote before throwing still flushes.
An input whose value is a slot is controlled: a text input's text, a checkbox's state, a select's key, a slider's, date-time input's or file picker's value, a collapsible panel's state, a Tabs index, a Dialog's open and a Table's selection. After a user's request, and after the flush it caused, a posted turn shows the slot's value if the widget shows anything else: a refused request snaps back, an accepted one costs no setter call, and a transformed one shows the request for that one turn. That holds with no handler too, which makes a slot without one read-only. A constant is not a slot: the widget keeps what the user did to it.
A mount that throws goes no further than the content it was building, and leaves none of it behind. A node mounted directly, a constant Switch selector, Tabs index or Dialog open included, throws out of the constructor. Content a binding mounts, which a bound selector, index or open does in an Effect even during the constructor, is reported (reactive::detail::site::kEffectThrew) instead: a Switch shows nothing for that key, having torn the old case down first; a Tabs keeps the previous page shown and selected; a Dialog stays closed. A Tabs then calls its onSelect with the page still shown, and a Dialog its onDismiss, each as a widget callback and as the last thing the mount does, so the application's state follows the widget and its next pick of the failed tab, or opening of the dialog, mounts again. A Switch selector is equality-gated, so its next change of key mounts again. A Tabs index and a Dialog open are not: while one still names content that failed — no onSelect or onDismiss moved it — every run of its binding, a change to any signal the binding reads included, tries that mount again, once per run, reporting each failure and calling onSelect or onDismiss again.
A ForEach or a Table mounts its rows in an Effect too, one row at a time: a row that fails is reported, leaves nothing behind and is left out, the other rows are mounted and ordered all the same, and the next change of the rows mounts it again. A key repeated in one snapshot is reported (detail::site::kDuplicateKey), and only its first row is shown.
Bindings run in owner order. The tree's own bindings are at the depth the mount was given; the content of a Switch case, a Tabs page or a Dialog, and every ForEach or Table row, is in a scope one level deeper than the binding that mounted it. So an Effect that removes content runs before the content's own bindings in the same flush, and a binding never evaluates against state its owner is about to take away. A mount placed under an outer scope (a screen's, a row's) takes that scope's depth plus one, so the outer scope's Effects run first too.
A mount must run to completion: nothing it calls, a backend factory or setter or a binding's evaluation, may destroy this object, its runtime or its backend. Widget callbacks are events, not part of a mount, and may.
|
inline |
| runtime | The runtime the bindings are made in. Borrowed. |
| backend | Makes the widgets. Borrowed. |
| root | The tree; kept alive as long as the mount. |
| parent | The container the root widget is appended to, or null for a backend root. |
| depth | The scope depth of the tree's own bindings (reactive::Scope::depth): zero for a mount no other scope owns, one more than the owner's for a mount placed under a scope. |
| std::invalid_argument | when root is null. |
|
inlinenodiscardnoexcept |
|
inline |
Moves keyboard focus to the mounted widget whose Common::focusId is focusId, at any depth, including the content of a Switch case, a Tabs page, a Dialog and every row mounted now.
Of several such widgets, the one mounted first is taken; one whose content has since been unmounted is not found.
| focusId | The name. |