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}