ferroui_reactiveui_controls/
view_control.rs1use 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
22type ControlOfView = fn(&dyn Any) -> Option<Ref<Control>>;
24
25ferroui_base::ferro_thread_local! {
26 static VIEW_CONTROLS: RefCell<HashMap<TypeId, ControlOfView>> = RefCell::new(HashMap::new());
28}
29
30pub trait IViewControl: Any {
32 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
40pub 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
48pub 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
56pub enum ViewLink<C: ObjectType> {
60 Strong(Ref<C>),
62 Weak(WeakRef<C>),
64}
65
66impl<C: ObjectType> ViewLink<C> {
67 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
85pub trait IControlViewFor: ObjectType + Upcast<Control> {
88 type ViewModel: PartialEq + 'static;
90
91 fn view_model_of(&self) -> Option<Rc<Self::ViewModel>>;
93
94 fn set_view_model_of(&self, view_model: Option<Rc<Self::ViewModel>>);
96}
97
98pub struct ControlViewFor<V: IControlViewFor> {
101 view: ViewLink<V>,
102}
103
104impl<V: IControlViewFor> ControlViewFor<V> {
105 pub fn new(control: Ref<V>) -> Rc<Self> {
107 Self::with_link(ViewLink::Strong(control))
108 }
109
110 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 pub fn control(&self) -> Option<Ref<V>> {
123 self.view.get()
124 }
125
126 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}