1use crate::element::{Attach, ControlElement, Element, ElementClass, IntoChild, IntoChildren, IntoElement, PropEntry, PropValue};
26use crate::reactive::{Memo, Signal, TwoWay};
27use ferroui_base::data::BindingBase;
28use ferroui_base::interactivity::{IRoutedEventArgs, Interactive, RoutedEvent};
29use ferroui_base::{AttachedProperty, BoxedValue, DirectProperty, FerroObject, FerroProperty, ObjectType, PropertyValue, Ref, StyledProperty, Upcast};
30use ferroui_controls::Control;
31use std::any::Any;
32use std::cell::RefCell;
33use std::marker::PhantomData;
34use std::rc::Rc;
35
36pub trait ViewClass: ObjectType + Upcast<Control> {
38 fn create() -> Ref<Self>;
40}
41
42pub trait Builder {
46 type Output;
48 fn builder() -> Self::Output;
50}
51
52impl<T: ViewClass> Builder for T {
53 type Output = El<T>;
54 fn builder() -> El<T> {
55 El::new()
56 }
57}
58
59pub trait TypedProperty: 'static {
62 type Value: PropertyValue;
64 fn untyped(&'static self) -> &'static FerroProperty;
66}
67
68impl<T: PropertyValue> TypedProperty for StyledProperty<T> {
69 type Value = T;
70 fn untyped(&'static self) -> &'static FerroProperty {
71 self.as_property()
72 }
73}
74
75impl<T: PropertyValue> TypedProperty for AttachedProperty<T> {
76 type Value = T;
77 fn untyped(&'static self) -> &'static FerroProperty {
78 let styled: &'static StyledProperty<T> = self;
79 styled.as_property()
80 }
81}
82
83impl<O: ObjectType, T: PropertyValue> TypedProperty for DirectProperty<O, T> {
84 type Value = T;
85 fn untyped(&'static self) -> &'static FerroProperty {
86 let base: &'static ferroui_base::DirectPropertyBase<T> = self;
87 base.as_property()
88 }
89}
90
91pub trait IntoValue<T> {
95 fn into_value(self) -> T;
97}
98
99impl<T> IntoValue<T> for T {
100 fn into_value(self) -> T {
101 self
102 }
103}
104
105pub trait FromValue<T>: Sized {
108 fn from_value(value: &T) -> Option<Self>;
111}
112
113impl<T: Clone> FromValue<T> for T {
114 fn from_value(value: &T) -> Option<Self> {
115 Some(value.clone())
116 }
117}
118
119pub mod markers {
121 pub struct Value;
123 pub struct FromSignal<V>(std::marker::PhantomData<V>);
125 pub struct FromMemo<V>(std::marker::PhantomData<V>);
127 pub struct FromClosure<V>(std::marker::PhantomData<V>);
129 pub struct FromTwoWay<V>(std::marker::PhantomData<V>);
131 pub struct FromElement;
133 pub struct FromBinding;
135}
136
137pub trait IntoProp<T, M> {
140 fn into_prop(self) -> PropValue;
142}
143
144fn boxed<T: PropertyValue>(value: T) -> BoxedValue {
145 Rc::new(value)
146}
147
148impl<T: PropertyValue, V: IntoValue<T>> IntoProp<T, markers::Value> for V {
149 fn into_prop(self) -> PropValue {
150 PropValue::Value(boxed(self.into_value()))
151 }
152}
153
154impl<T: PropertyValue, V: IntoValue<T> + Clone + 'static> IntoProp<T, markers::FromSignal<V>> for Signal<V> {
155 fn into_prop(self) -> PropValue {
156 PropValue::Dynamic(Rc::new(move || boxed(self.get().into_value())))
157 }
158}
159
160impl<T: PropertyValue, V: IntoValue<T> + Clone + 'static> IntoProp<T, markers::FromMemo<V>> for Memo<V> {
161 fn into_prop(self) -> PropValue {
162 PropValue::Dynamic(Rc::new(move || boxed(self.get().into_value())))
163 }
164}
165
166impl<T: PropertyValue, V: IntoValue<T>, F: Fn() -> V + 'static> IntoProp<T, markers::FromClosure<V>> for F {
167 fn into_prop(self) -> PropValue {
168 PropValue::Dynamic(Rc::new(move || boxed(self().into_value())))
169 }
170}
171
172impl<T: PropertyValue, V: IntoValue<T> + FromValue<T> + Clone + PartialEq + 'static> IntoProp<T, markers::FromTwoWay<V>>
173 for TwoWay<V>
174{
175 fn into_prop(self) -> PropValue {
176 let signal = self.0;
177 PropValue::TwoWay {
178 get: Rc::new(move || boxed(signal.get().into_value())),
179 set: Rc::new(move |value: &dyn Any| {
180 if let Some(value) = value.downcast_ref::<T>().and_then(V::from_value) {
181 if signal.is_alive() {
182 signal.set_if_changed(value);
183 }
184 }
185 }),
186 }
187 }
188}
189
190impl<E: IntoElement> IntoProp<Option<BoxedValue>, markers::FromElement> for E {
191 fn into_prop(self) -> PropValue {
192 PropValue::Element(Rc::new(RefCell::new(Some(self.into_element()))))
193 }
194}
195
196pub struct Bind<B: BindingBase + 'static>(pub B);
199
200impl<T: PropertyValue, B: BindingBase + 'static> IntoProp<T, markers::FromBinding> for Bind<B> {
201 fn into_prop(self) -> PropValue {
202 PropValue::Binding(Rc::new(self.0))
203 }
204}
205
206pub struct El<T> {
209 element: ControlElement,
210 _marker: PhantomData<fn() -> T>,
211}
212
213impl<T: ViewClass> El<T> {
214 pub fn new() -> Self {
216 fn create<T: ViewClass>() -> Ref<Control> {
217 T::create().upcast()
218 }
219 let class = ElementClass {
220 name: std::any::type_name::<T>(),
221 type_id: std::any::TypeId::of::<T>,
222 create: create::<T>,
223 };
224 Self { element: ControlElement::new(class), _marker: PhantomData }
225 }
226}
227
228impl<T: ViewClass> Default for El<T> {
229 fn default() -> Self {
230 Self::new()
231 }
232}
233
234pub trait ElementBuilder: Sized {
236 fn element_mut(&mut self) -> &mut ControlElement;
238
239 fn prop<P: TypedProperty + ?Sized, M>(mut self, property: &'static P, value: impl IntoProp<P::Value, M>) -> Self {
242 let property = property.untyped();
243 let value = value.into_prop();
244 let props = &mut self.element_mut().props;
245 props.retain(|entry| !std::ptr::eq(entry.property, property));
246 props.push(PropEntry { property, value });
247 self
248 }
249
250 fn routed<A: IRoutedEventArgs + 'static>(
253 mut self,
254 event: &'static RoutedEvent<A>,
255 handler: impl Fn(&Interactive, &A) + 'static,
256 ) -> Self {
257 let handler = Rc::new(handler);
258 let attach: Attach = Rc::new(move |control: &Ref<Control>| {
259 let handler = handler.clone();
260 control.add_handler(event, move |sender, e| handler(sender, e));
261 });
262 self.element_mut().handlers.push(attach);
263 self
264 }
265}
266
267impl<T> ElementBuilder for El<T> {
268 fn element_mut(&mut self) -> &mut ControlElement {
269 &mut self.element
270 }
271}
272
273impl<T> El<T> {
274 pub fn key(mut self, key: impl Into<crate::Key>) -> Self {
276 self.element.key = Some(key.into());
277 self
278 }
279
280 pub fn name(mut self, name: impl Into<String>) -> Self {
282 self.element.name = Some(name.into());
283 self
284 }
285
286 pub fn class(mut self, classes: &str) -> Self {
288 self.element.classes.extend(classes.split_whitespace().map(str::to_string));
289 self
290 }
291
292 pub fn classes(self, classes: &str) -> Self {
295 self.class(classes)
296 }
297
298 pub fn set<P: TypedProperty + ?Sized, M>(self, property: &'static P, value: impl IntoProp<P::Value, M>) -> Self {
302 self.prop(property, value)
303 }
304
305 pub fn bind<P: TypedProperty + ?Sized>(mut self, property: &'static P, binding: impl BindingBase + 'static) -> Self {
308 let property = property.untyped();
309 self.element.props.push(PropEntry { property, value: PropValue::Binding(Rc::new(binding)) });
310 self
311 }
312
313 pub fn on<A: IRoutedEventArgs + 'static>(
315 self,
316 event: &'static RoutedEvent<A>,
317 handler: impl Fn(&Interactive, &A) + 'static,
318 ) -> Self {
319 self.routed(event, handler)
320 }
321
322 pub fn on_changed<P: TypedProperty + ?Sized>(mut self, property: &'static P, handler: impl Fn(&P::Value) + 'static) -> Self {
325 let untyped = property.untyped();
326 let handler = Rc::new(handler);
327 let attach: Attach = Rc::new(move |control: &Ref<Control>| {
328 let handler = handler.clone();
329 let subscription = control.property_changed(move |e| {
330 if std::ptr::eq(e.property(), untyped) {
331 if let Some(value) = e.new_value().downcast_ref::<P::Value>() {
332 handler(value);
333 }
334 }
335 });
336 crate::reactive::on_cleanup(move || subscription.dispose());
337 });
338 self.element.handlers.push(attach);
339 self
340 }
341
342 pub fn with(mut self, f: impl Fn(&Ref<T>) + 'static) -> Self
346 where
347 T: ObjectType,
348 {
349 let attach: Attach = Rc::new(move |control: &Ref<Control>| {
350 if let Some(control) = control.cast::<T>() {
351 f(&control);
352 }
353 });
354 self.element.on_mount.push(attach);
355 self
356 }
357
358 pub fn node_ref(self, node_ref: crate::NodeRef<T>) -> Self
360 where
361 T: ObjectType,
362 {
363 self.with(move |control| node_ref.set(control.clone()))
364 }
365
366 pub fn child(mut self, child: impl IntoChild) -> Self {
371 self.element.children.push(child.into_child());
372 self
373 }
374
375 pub fn children(mut self, children: impl IntoChildren) -> Self {
377 self.element.children.extend(children.into_children());
378 self
379 }
380
381 pub fn build(self) -> Element {
383 Element::Control(Box::new(self.element))
384 }
385}
386
387impl<T> IntoElement for El<T> {
388 fn into_element(self) -> Element {
389 self.build()
390 }
391}
392
393impl<T> From<El<T>> for Element {
394 fn from(builder: El<T>) -> Self {
395 builder.build()
396 }
397}
398
399#[doc(hidden)]
401pub fn object_of(control: &Ref<Control>) -> &FerroObject {
402 control
403}