Skip to main content

ferroui_reactiveui_controls/
view_control.rs

1//! The control behind a view of ReactiveUI.
2//!
3//! Not an upstream file. Upstream's views are controls that implement `IViewFor`; a host puts the
4//! view it resolves into its `Content`, and the activation fetcher watches the view as a visual.
5//!
6//! Deviation (DEVIATIONS.md, ReactiveUI controls): a control is an object of the class model
7//! (`Ref<T>`), and ReactiveUI's views are shared trait objects (`Rc<dyn IViewFor>`). A control is
8//! seen as a view through a small adapter: [`ControlViewFor`] for any control class that states its
9//! view model ([`IControlViewFor`]), and the adapters of the reactive views
10//! ([`ReactiveViewFor`](crate::ReactiveViewFor), [`ReactiveView`](crate::ReactiveView)). An
11//! adapter type registers itself on this thread ([`register_view_control`]), so that untyped code
12//! finds the control behind any view ([`view_control`]), as upstream's `view as Control`.
13
14use ferroui_base::{ObjectType, Ref, Upcast, WeakRef};
15use ferroui_controls::Control;
16use ferroui_reactiveui::{typed_view_for, IActivatableView, IViewFor, IViewForOf};
17use std::any::{Any, TypeId};
18use std::cell::RefCell;
19use std::collections::HashMap;
20use std::rc::Rc;
21
22/// Finds the control behind a view of a registered adapter type.
23type ControlOfView = fn(&dyn Any) -> Option<Ref<Control>>;
24
25ferroui_base::ferro_thread_local! {
26    /// The registered adapter types, by type.
27    static VIEW_CONTROLS: RefCell<HashMap<TypeId, ControlOfView>> = RefCell::new(HashMap::new());
28}
29
30/// An adapter that shows a control as a view of ReactiveUI.
31pub trait IViewControl: Any {
32    /// The control behind the view, while it is alive.
33    fn view_control(&self) -> Option<Ref<Control>>;
34}
35
36fn control_of_view<T: IViewControl>(view: &dyn Any) -> Option<Ref<Control>> {
37    view.downcast_ref::<T>()?.view_control()
38}
39
40/// Registers the adapter type `T` on this thread, so that [`view_control`] finds the control of
41/// its views. Idempotent; the adapters of this crate register themselves when they are created.
42pub fn register_view_control<T: IViewControl>() {
43    VIEW_CONTROLS.with(|types| {
44        types.borrow_mut().entry(TypeId::of::<T>()).or_insert(control_of_view::<T>);
45    });
46}
47
48/// The control behind a view (`view as Control`): the control of an adapter of a registered type,
49/// `None` for any other view.
50pub fn view_control(view: &dyn IActivatableView) -> Option<Ref<Control>> {
51    let view: &dyn Any = view;
52    let find = VIEW_CONTROLS.with(|types| types.borrow().get(&view.type_id()).copied())?;
53    find(view)
54}
55
56/// How an adapter holds its control: strongly, for a view that is handed to others (a view a
57/// locator creates), or weakly, for the view a control uses for itself (its own activation), so
58/// that the control and its handlers do not keep each other alive.
59pub enum ViewLink<C: ObjectType> {
60    /// A strong reference.
61    Strong(Ref<C>),
62    /// A weak reference.
63    Weak(WeakRef<C>),
64}
65
66impl<C: ObjectType> ViewLink<C> {
67    /// The control, while it is alive.
68    pub fn get(&self) -> Option<Ref<C>> {
69        match self {
70            ViewLink::Strong(control) => Some(control.clone()),
71            ViewLink::Weak(control) => control.upgrade(),
72        }
73    }
74}
75
76impl<C: ObjectType> Clone for ViewLink<C> {
77    fn clone(&self) -> Self {
78        match self {
79            ViewLink::Strong(control) => ViewLink::Strong(control.clone()),
80            ViewLink::Weak(control) => ViewLink::Weak(control.clone()),
81        }
82    }
83}
84
85/// A control class that is a view of the view model type `ViewModel` (a control that implements
86/// `IViewFor<TViewModel>` upstream, with a plain `ViewModel` property).
87pub trait IControlViewFor: ObjectType + Upcast<Control> {
88    /// The type of the view model.
89    type ViewModel: PartialEq + 'static;
90
91    /// The view model (`ViewModel { get; }`).
92    fn view_model_of(&self) -> Option<Rc<Self::ViewModel>>;
93
94    /// Sets the view model (`ViewModel { set; }`).
95    fn set_view_model_of(&self, view_model: Option<Rc<Self::ViewModel>>);
96}
97
98/// A control of a class that implements [`IControlViewFor`], as a view of ReactiveUI
99/// (`IViewFor<TViewModel>` on the control).
100pub struct ControlViewFor<V: IControlViewFor> {
101    view: ViewLink<V>,
102}
103
104impl<V: IControlViewFor> ControlViewFor<V> {
105    /// The view of `control`, which it keeps alive.
106    pub fn new(control: Ref<V>) -> Rc<Self> {
107        Self::with_link(ViewLink::Strong(control))
108    }
109
110    /// The view of `control`, which it does not keep alive.
111    pub fn weak(control: &Ref<V>) -> Rc<Self> {
112        Self::with_link(ViewLink::Weak(control.downgrade()))
113    }
114
115    fn with_link(view: ViewLink<V>) -> Rc<Self> {
116        register_view_control::<Self>();
117        crate::register_view_model::<V::ViewModel>();
118        Rc::new(Self { view })
119    }
120
121    /// The control, while it is alive.
122    pub fn control(&self) -> Option<Ref<V>> {
123        self.view.get()
124    }
125
126    /// The view as `IViewFor<TViewModel>`.
127    pub fn into_view_for_of(self: Rc<Self>) -> Rc<dyn IViewForOf<V::ViewModel>> {
128        self
129    }
130}
131
132impl<V: IControlViewFor> IViewControl for ControlViewFor<V> {
133    fn view_control(&self) -> Option<Ref<Control>> {
134        self.view.get().map(|control| control.upcast())
135    }
136}
137
138impl<V: IControlViewFor> IActivatableView for ControlViewFor<V> {
139    fn as_view_for(&self) -> Option<&dyn IViewFor> {
140        Some(self)
141    }
142}
143
144impl<V: IControlViewFor> IViewFor for ControlViewFor<V> {
145    fn view_model(&self) -> Option<Rc<dyn Any>> {
146        let control = self.view.get()?;
147        control.view_model_of().map(|view_model| view_model as Rc<dyn Any>)
148    }
149
150    fn set_view_model(&self, view_model: Option<Rc<dyn Any>>) {
151        let typed = view_model.map(|view_model| match view_model.downcast::<V::ViewModel>() {
152            Ok(view_model) => view_model,
153            Err(_) => panic!(
154                "InvalidCastException: the view model of {} is a {}",
155                std::any::type_name::<V>(),
156                std::any::type_name::<V::ViewModel>()
157            ),
158        });
159        if let Some(control) = self.view.get() {
160            control.set_view_model_of(typed);
161        }
162    }
163
164    fn as_view_for_of(self: Rc<Self>) -> Option<Box<dyn Any>> {
165        typed_view_for::<V::ViewModel, Self>(self)
166    }
167}
168
169impl<V: IControlViewFor> IViewForOf<V::ViewModel> for ControlViewFor<V> {
170    fn view_model(&self) -> Option<Rc<V::ViewModel>> {
171        self.view.get().and_then(|control| control.view_model_of())
172    }
173
174    fn set_view_model(&self, view_model: Option<Rc<V::ViewModel>>) {
175        if let Some(control) = self.view.get() {
176            control.set_view_model_of(view_model);
177        }
178    }
179}