|
| | BindingEngine (std::shared_ptr< IViewAdapter > adapter) |
| | Convenience constructor: no dispatcher, Direct policy.
|
| | BindingEngine (std::shared_ptr< IViewAdapter > adapter, std::shared_ptr< runtime::IDispatcher > ui_dispatcher, DispatchPolicy policy=DispatchPolicy::SmartMarshal) |
| | Constructor that opts into a dispatch policy.
|
| | ~BindingEngine () |
| | Retires all lifetime gates before releasing the bindings.
|
| | BindingEngine (const BindingEngine &)=delete |
| BindingEngine & | operator= (const BindingEngine &)=delete |
| | BindingEngine (BindingEngine &&)=delete |
| BindingEngine & | operator= (BindingEngine &&)=delete |
| IViewAdapter & | adapter () noexcept |
| DispatchPolicy | dispatch_policy () const noexcept |
| bool | has_dispatcher () const noexcept |
| void | bind_text_oneway (Property< std::string > &prop, IView &view) |
| template<ReadOnlyReactiveOf< std::string > Src> |
| void | bind_text_oneway (Src &src, IView &view) |
| void | bind_text (Property< std::string > &prop, IView &view) |
| void | bind_bool_oneway (Property< bool > &prop, IView &view) |
| template<ReadOnlyReactiveOf< bool > Src> |
| void | bind_bool_oneway (Src &src, IView &view) |
| void | bind_bool (Property< bool > &prop, IView &view) |
| void | bind_int_oneway (Property< int > &prop, IView &view) |
| template<ReadOnlyReactiveOf< int > Src> |
| void | bind_int_oneway (Src &src, IView &view) |
| void | bind_int (Property< int > &prop, IView &view) |
| void | bind_int64_oneway (Property< std::int64_t > &prop, IView &view) |
| template<ReadOnlyReactiveOf< std::int64_t > Src> |
| void | bind_int64_oneway (Src &src, IView &view) |
| void | bind_int64 (Property< std::int64_t > &prop, IView &view) |
| void | bind_uint64_oneway (Property< std::uint64_t > &prop, IView &view) |
| template<ReadOnlyReactiveOf< std::uint64_t > Src> |
| void | bind_uint64_oneway (Src &src, IView &view) |
| void | bind_uint64 (Property< std::uint64_t > &prop, IView &view) |
| void | bind_float_oneway (Property< float > &prop, IView &view) |
| template<ReadOnlyReactiveOf< float > Src> |
| void | bind_float_oneway (Src &src, IView &view) |
| void | bind_float (Property< float > &prop, IView &view) |
| void | bind_double_oneway (Property< double > &prop, IView &view) |
| template<ReadOnlyReactiveOf< double > Src> |
| void | bind_double_oneway (Src &src, IView &view) |
| void | bind_double (Property< double > &prop, IView &view) |
| void | bind_visible (Property< bool > &prop, IView &view) |
| template<ReadOnlyReactiveOf< bool > Src> |
| void | bind_visible (Src &src, IView &view) |
| void | bind_enabled (Property< bool > &prop, IView &view) |
| template<ReadOnlyReactiveOf< bool > Src> |
| void | bind_enabled (Src &src, IView &view) |
| template<ReadOnlyReactive Src> |
| void | bind_text_converted_oneway (Src &src, IView &view, Converter< typename Src::value_type, std::string > conv) |
| template<typename T> |
| void | bind_text_converted (Property< T > &prop, IView &view, Converter< T, std::string > conv) |
| template<typename T> |
| void | bind_int_converted (Property< T > &prop, IView &view, Converter< T, int > conv) |
| | Bind a model value to an integer-valued control.
|
| template<ReadOnlyReactive Src, typename Project> |
| void | bind_text_projected (Src &src, IView &view, Project project) |
| | Bind a read-only text view to src, rendered through project (T -> std::string).
|
| template<ReadOnlyReactiveOptional Src, typename Project> |
| void | bind_optional_text (Src &src, IView &view, Project project, std::string empty_text=std::string{}) |
| | Bind a read-only text view to a reactive std::optional<T> source.
|
| template<typename... Args> |
| void | bind_command (Command< Args... > &cmd, IView &view, const Args &... args) |
| void | bind_view_lifetime (IView &view, std::function< void()> on_view_destroyed) |
| void | adopt (IView &view, Subscription s) |
| | Adopt an arbitrary Subscription into view's per-view bucket.
|
| void | clear () noexcept |
| | Drop every active binding.
|
BindingEngine: connects ViewModel properties to platform views via an adapter.
── Lifetime contract ─────────────────────────────────────────────────
- The typical ownership shape is that the platform widget tree owns every IView (QWidget parent-owned, NSView superview-owned, ...) and the BindingEngine is a member of the corresponding ViewModel / scope. Either side may outlive the other:
- If the engine is destroyed first (normal scope exit), all bindings are released and the views are untouched.
- If a view is destroyed first, every binding wired to that view is released automatically via IView::on_destroy, so subsequent property changes do not dereference the dead view. Other views bound to the same engine keep working.
- Destroying the BindingEngine (or calling clear()) releases every active binding in one shot.
── One-way vs two-way naming ───────────────────────────────────────── Controls split into two families:
── Threading / binding dispatch policy ────────────────────────────── Native UI toolkits are main-thread-affine: AppKit/UIKit explicitly forbid touching NS/UIView from a background thread, and Qt requires widget access on the GUI thread. Aria's reactive graph is itself single-threaded. BindingEngine accepts an optional dispatcher for that owning thread and a policy applied in both binding directions. In particular, adapters such as HTTP may deliver input from workers; marshalling keeps those callbacks from accessing the graph there. Application Property writes must still respect graph affinity.
- DispatchPolicy::Direct (default) — every callback is invoked synchronously on its originating thread. Use when every Property write and adapter callback originates on the graph/UI thread (the common single-threaded MVVM case).
- DispatchPolicy::SmartMarshal (recommended for production) — VM→View setters, View→VM edits and commands are invoked directly when dispatcher.is_main_thread() is true, otherwise posted to the dispatcher. Zero overhead on the UI thread, and a guaranteed thread-correct path on background threads.
- DispatchPolicy::AlwaysPost — every binding update is posted, even from the UI thread. Useful for tests that want a deterministic "property-emit happens before, view-update
happens later" ordering, or to coalesce a synchronous burst of writes into the next event-loop iteration.
Initial synchronization during bind runs inline on the owning thread. Synchronous setter echoes are suppressed before posting. Binding setup, clear and view/engine destruction also belong on the owning thread; the dispatcher does not make the graph or the binding registry safe for concurrent access.
Posted callbacks in both directions use a per-view lifetime token: if the view is destroyed between dispatcher.post(fn) and fn() running, the bucket's weak handle no-ops the call so the posted lambda never dereferences a dead IView.
MSVC C4251: BindingEngine contains template methods that inline-access private STL members (shared_ptr, unordered_map). Full Pimpl would require explicit template instantiation for every bind_* variant, adding maintenance burden with no real ABI benefit — the class is always consumed through its non-template public API, and the shared_ptr members point to DLL-exported interfaces. Suppression is safe.