Skip to main content

ferroui_reactiveui/binding/interfaces/
i_view_for.rs

1//! `IViewFor` (ReactiveUI.Binding, `Interfaces/IViewFor.cs`).
2
3use super::{IActivatableView, IViewForOf};
4use std::any::Any;
5use std::rc::Rc;
6
7/// A view that displays a view model (`IViewFor`). A view is shared as an `Rc`.
8///
9/// This is the form of a view that does not name the type of its view model: the view model is an
10/// `Rc<dyn Any>`. A view of a known view model also implements [`IViewForOf<VM>`], and the macro
11/// [`view_for!`](crate::view_for) writes both traits (with [`IActivatableView`]) from the typed
12/// accessors.
13///
14/// The trait is object safe, so that a view is held as `Rc<dyn IViewFor>`; it is `Any` (through
15/// [`IActivatableView`]), so that `Rc<dyn IViewFor>` upcasts to `Rc<dyn Any>` and downcasts to the
16/// type of the view.
17pub trait IViewFor: IActivatableView {
18    /// The view model displayed by the view (the getter of `ViewModel`).
19    fn view_model(&self) -> Option<Rc<dyn Any>>;
20
21    /// Sets the view model displayed by the view (the setter of `ViewModel`). A typed view panics
22    /// when `view_model` is of another type than its own, as the cast of upstream's views throws.
23    fn set_view_model(&self, view_model: Option<Rc<dyn Any>>);
24
25    /// The view as an `Rc<dyn IViewForOf<VM>>` in a `Box<dyn Any>`, for the type `VM` of view
26    /// model it is typed for, or `None` for a view that names no type.
27    ///
28    /// Not an upstream member: upstream asks `view is IViewFor<TViewModel>` at run time, which
29    /// Rust cannot ask of a trait object. [`view_for_of`] reads it; [`view_for!`](crate::view_for)
30    /// writes it, and a view implementing the traits by hand returns
31    /// [`typed_view_for`]`::<VM, Self>(self)`.
32    // Deviation (DEVIATIONS.md, ReactiveUI): the run-time test `is IViewFor<TViewModel>` is this
33    // method, which a typed view implements.
34    fn as_view_for_of(self: Rc<Self>) -> Option<Box<dyn Any>> {
35        None
36    }
37}
38
39/// The value of [`IViewFor::as_view_for_of`] for a view typed for `VM`.
40pub fn typed_view_for<VM: 'static, V: IViewForOf<VM> + 'static>(view: Rc<V>) -> Option<Box<dyn Any>> {
41    Some(Box::new(view as Rc<dyn IViewForOf<VM>>))
42}
43
44/// The view as an `IViewForOf<VM>` when it is typed for `VM` (`view as IViewFor<TViewModel>`),
45/// through [`IViewFor::as_view_for_of`].
46pub fn view_for_of<VM: 'static>(view: Rc<dyn IViewFor>) -> Option<Rc<dyn IViewForOf<VM>>> {
47    let typed = view.as_view_for_of()?;
48    typed.downcast::<Rc<dyn IViewForOf<VM>>>().ok().map(|view| *view)
49}
50
51/// A view as an `Rc<dyn IViewFor>`, whether its type is a view (`Rc<MyView>`) or a trait object of
52/// one (`Rc<dyn IViewForOf<VM>>`, `Rc<dyn IViewFor>`): the bound of the services a view mapping
53/// takes from the service locator
54/// ([`ViewMappingBuilder::map_from_service_locator`](crate::ViewMappingBuilder::map_from_service_locator)).
55pub trait IntoViewFor {
56    /// The view as an `Rc<dyn IViewFor>`.
57    fn into_view_for(self: Rc<Self>) -> Rc<dyn IViewFor>;
58}
59
60impl<V: IViewFor> IntoViewFor for V {
61    fn into_view_for(self: Rc<Self>) -> Rc<dyn IViewFor> {
62        self
63    }
64}
65
66impl IntoViewFor for dyn IViewFor {
67    fn into_view_for(self: Rc<Self>) -> Rc<dyn IViewFor> {
68        self
69    }
70}
71
72impl<VM: 'static> IntoViewFor for dyn IViewForOf<VM> {
73    fn into_view_for(self: Rc<Self>) -> Rc<dyn IViewFor> {
74        self
75    }
76}