ferroui_reactiveui_controls/
reactive_view.rs1use crate::view_control::{register_view_control, IViewControl, ViewLink};
18use crate::view_model_value::{is_view_model_of, register_view_model, value_of_optional_view_model, view_model_of_value};
19use ferroui_base::{BoxedValue, IntoRef, ObjectType, Ref, StyledProperty, Upcast};
20use ferroui_controls::Control;
21use ferroui_reactiveui::{typed_view_for, IActivatableView, IViewFor, IViewForOf};
22use std::any::Any;
23use std::marker::PhantomData;
24use std::ops::Deref;
25use std::rc::Rc;
26
27pub type ViewModelFilter = fn(&BoxedValue) -> bool;
30
31pub trait IReactiveViewClass: ObjectType + Upcast<Control> {
33 fn view_model_property() -> &'static StyledProperty<Option<BoxedValue>>;
35
36 fn is_valid_view_model_value(&self, value: Option<&BoxedValue>) -> bool {
38 let _ = value;
39 true
40 }
41}
42
43pub trait ITypedReactiveViewClass: IReactiveViewClass {
45 fn create() -> Ref<Self>;
47
48 fn set_view_model_filter(&self, filter: Option<ViewModelFilter>);
50}
51
52pub struct ReactiveViewFor<C: IReactiveViewClass> {
54 view: ViewLink<C>,
55}
56
57impl<C: IReactiveViewClass> ReactiveViewFor<C> {
58 pub fn new(control: Ref<C>) -> Rc<Self> {
60 Self::with_link(ViewLink::Strong(control))
61 }
62
63 pub fn weak(control: &Ref<C>) -> Rc<Self> {
66 Self::with_link(ViewLink::Weak(control.downgrade()))
67 }
68
69 fn with_link(view: ViewLink<C>) -> Rc<Self> {
70 register_view_control::<Self>();
71 Rc::new(Self { view })
72 }
73
74 pub fn control(&self) -> Option<Ref<C>> {
76 self.view.get()
77 }
78
79 pub fn into_view_for(self: Rc<Self>) -> Rc<dyn IViewFor> {
81 self
82 }
83}
84
85impl<C: IReactiveViewClass> IViewControl for ReactiveViewFor<C> {
86 fn view_control(&self) -> Option<Ref<Control>> {
87 self.view.get().map(|control| control.upcast())
88 }
89}
90
91impl<C: IReactiveViewClass> IActivatableView for ReactiveViewFor<C> {
92 fn as_view_for(&self) -> Option<&dyn IViewFor> {
93 Some(self)
94 }
95}
96
97impl<C: IReactiveViewClass> IViewFor for ReactiveViewFor<C> {
98 fn view_model(&self) -> Option<Rc<dyn Any>> {
99 let control = self.view.get()?;
100 as_control(&*control).get_value(C::view_model_property()).as_ref().map(view_model_of_value)
101 }
102
103 fn set_view_model(&self, view_model: Option<Rc<dyn Any>>) {
104 if let Some(control) = self.view.get() {
105 as_control(&*control).set_value(C::view_model_property(), value_of_optional_view_model(view_model));
106 }
107 }
108}
109
110pub(crate) fn as_control<C: Upcast<Control>>(control: &C) -> &Control {
112 Upcast::<Control>::upcast(control)
113}
114
115fn accepts_view_model_of<VM: 'static>(value: &BoxedValue) -> bool {
118 is_view_model_of::<VM>(value)
119}
120
121pub struct ReactiveView<C: ITypedReactiveViewClass, VM: PartialEq + 'static> {
125 control: Ref<C>,
126 _view_model: PhantomData<fn() -> VM>,
127}
128
129impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> Clone for ReactiveView<C, VM> {
130 fn clone(&self) -> Self {
131 Self { control: self.control.clone(), _view_model: PhantomData }
132 }
133}
134
135impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> ReactiveView<C, VM> {
136 pub fn new() -> Self {
138 Self::from_ref(C::create())
139 }
140
141 pub fn from_control(control: impl IntoRef<C>) -> Self {
144 Self::from_ref(control.into_ref())
145 }
146
147 fn from_ref(control: Ref<C>) -> Self {
148 register_view_control::<Self>();
149 register_view_model::<VM>();
150 control.set_view_model_filter(Some(accepts_view_model_of::<VM>));
151 Self { control, _view_model: PhantomData }
152 }
153
154 pub fn control(&self) -> &Ref<C> {
156 &self.control
157 }
158
159 pub fn view_model(&self) -> Option<Rc<VM>> {
162 let value = as_control(&*self.control).get_value(C::view_model_property())?;
163 view_model_of_value(&value).downcast::<VM>().ok()
164 }
165
166 pub fn set_view_model(&self, view_model: Option<Rc<VM>>) {
168 as_control(&*self.control).set_value(C::view_model_property(), view_model.map(|view_model| view_model as BoxedValue));
169 }
170
171 pub fn into_view_for_of(self) -> Rc<dyn IViewForOf<VM>> {
173 Rc::new(self)
174 }
175
176 pub fn into_view_for(self) -> Rc<dyn IViewFor> {
178 Rc::new(self)
179 }
180}
181
182impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> Default for ReactiveView<C, VM> {
183 fn default() -> Self {
184 Self::new()
185 }
186}
187
188impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> Deref for ReactiveView<C, VM> {
189 type Target = C;
190
191 fn deref(&self) -> &C {
192 &self.control
193 }
194}
195
196impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> IViewControl for ReactiveView<C, VM> {
197 fn view_control(&self) -> Option<Ref<Control>> {
198 Some(self.control.clone().upcast())
199 }
200}
201
202impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> IActivatableView for ReactiveView<C, VM> {
203 fn as_view_for(&self) -> Option<&dyn IViewFor> {
204 Some(self)
205 }
206}
207
208impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> IViewFor for ReactiveView<C, VM> {
209 fn view_model(&self) -> Option<Rc<dyn Any>> {
210 ReactiveView::view_model(self).map(|view_model| view_model as Rc<dyn Any>)
211 }
212
213 fn set_view_model(&self, view_model: Option<Rc<dyn Any>>) {
214 let typed = view_model.map(|view_model| match view_model.downcast::<VM>() {
215 Ok(view_model) => view_model,
216 Err(_) => panic!(
217 "InvalidCastException: the view model of {} is a {}",
218 std::any::type_name::<C>(),
219 std::any::type_name::<VM>()
220 ),
221 });
222 ReactiveView::set_view_model(self, typed);
223 }
224
225 fn as_view_for_of(self: Rc<Self>) -> Option<Box<dyn Any>> {
226 typed_view_for::<VM, Self>(self)
227 }
228}
229
230impl<C: ITypedReactiveViewClass, VM: PartialEq + 'static> IViewForOf<VM> for ReactiveView<C, VM> {
231 fn view_model(&self) -> Option<Rc<VM>> {
232 ReactiveView::view_model(self)
233 }
234
235 fn set_view_model(&self, view_model: Option<Rc<VM>>) {
236 ReactiveView::set_view_model(self, view_model);
237 }
238}