frui_core/api/
mod.rs

1use std::any::{Any, TypeId};
2
3use crate::prelude::{Constraints, Offset, PaintContext, Size};
4
5use self::{
6    contexts::{build_ctx::STATE_UPDATE_SUPRESSED, render_ctx::AnyRenderContext, Context},
7    events::Event,
8    implementors::{
9        inherited::InheritedWidgetOS, leaf::LeafWidgetOS, multi::MultiChildWidgetOS,
10        single::SingleChildWidgetOS, view::ViewWidgetOS,
11    },
12    local_key::LocalKeyAny,
13};
14
15pub mod contexts;
16pub mod events;
17pub mod implementors;
18pub mod impls;
19pub mod local_key;
20pub mod widget_eq;
21
22pub trait Widget: WidgetDebug {
23    /// Implementation should return the same unique TypeId for given structure definition,
24    /// even if that structure contains generic parameters. This is used to preserve state
25    /// between generic widgets.
26    fn unique_type(&self) -> TypeId;
27
28    fn kind(&self) -> WidgetKind;
29}
30
31#[derive(Clone, Copy)]
32pub enum WidgetKind<'a> {
33    View(&'a (dyn ViewWidgetOS + 'a)),
34    Inherited(&'a (dyn InheritedWidgetOS + 'a)),
35    Leaf(&'a (dyn LeafWidgetOS + 'a)),
36    SingleChild(&'a (dyn SingleChildWidgetOS + 'a)),
37    MultiChild(&'a (dyn MultiChildWidgetOS + 'a)),
38}
39
40#[derive(Clone)]
41pub struct WidgetPtr<'a> {
42    /// Reference to the exact type of this widget. Used to properly dispatch methods.
43    pub(crate) kind: WidgetKind<'a>,
44
45    /// Whether this pointer references or owns a widget. Used to properly drop it.
46    pub(crate) owned: Option<*mut (dyn Widget + 'a)>,
47}
48
49impl<'a> WidgetPtr<'a> {
50    pub fn from_ref(widget: &'a (dyn Widget + 'a)) -> Self {
51        Self {
52            kind: widget.kind(),
53            owned: None,
54        }
55    }
56
57    /// # Note
58    ///
59    /// `widget` will not be dropped until you manually call [`WidgetPtr::drop`].
60    pub fn from_owned(widget: Box<dyn Widget + 'a>) -> Self {
61        // Safety:
62        //
63        // `widget.kind()` returns `WidgetKind` which holds a reference to the `widget`.
64        // Since that reference points to a heap, we can safely extend lifetime of it to
65        // the lifetime of `WidgetPtr` until `drop` is called.
66        unsafe {
67            Self {
68                kind: std::mem::transmute::<WidgetKind, WidgetKind>(widget.kind()),
69                owned: Some(Box::into_raw(widget)),
70            }
71        }
72    }
73
74    /// ## Safety
75    ///
76    /// Data referenced by this [`WidgetPtr`] didn't move.
77    ///
78    /// Additionally, make sure there is no other [`WidgetPtr`] that may reference
79    /// this [`WidgetPtr`] after [`WidgetNode::drop`] has been called on it.
80    pub unsafe fn drop(self) {
81        if let Some(widget) = self.owned {
82            drop(Box::from_raw(widget));
83        }
84    }
85
86    /// # Note
87    ///
88    /// Returned [`WidgetPtr`] has erased lifetime, but its invariants must be upheld.
89    pub fn build(&self, ctx: &Context) -> Vec<WidgetPtr<'static>> {
90        let ptrs = match self.kind {
91            WidgetKind::View(w) => vec![w.build(ctx)],
92            WidgetKind::Leaf(_) => vec![],
93            WidgetKind::SingleChild(w) => vec![w.build(ctx)],
94            WidgetKind::MultiChild(w) => w.build(ctx),
95            WidgetKind::Inherited(w) => vec![w.child()],
96        };
97
98        // Safety: Consumers of `WidgetPtr` must upheld correct invariants.
99        unsafe { std::mem::transmute::<Vec<WidgetPtr>, Vec<WidgetPtr>>(ptrs) }
100    }
101
102    pub fn create_state(&self) -> Box<dyn Any> {
103        match self.kind {
104            WidgetKind::View(w) => w.create_state(),
105            WidgetKind::Leaf(w) => w.create_state(),
106            WidgetKind::SingleChild(w) => w.create_state(),
107            WidgetKind::MultiChild(w) => w.create_state(),
108            WidgetKind::Inherited(w) => w.create_state(),
109        }
110    }
111
112    pub fn create_render_state(&self) -> Box<dyn Any> {
113        match self.kind {
114            WidgetKind::View(_) => Box::new(()),
115            WidgetKind::Leaf(w) => w.create_render_state(),
116            WidgetKind::MultiChild(w) => w.create_render_state(),
117            WidgetKind::SingleChild(w) => w.create_render_state(),
118            WidgetKind::Inherited(w) => w.create_render_state(),
119        }
120    }
121
122    pub fn layout<'b>(
123        &self,
124        render_ctx: &'b mut AnyRenderContext,
125        constraints: Constraints,
126    ) -> Size {
127        match self.kind {
128            WidgetKind::View(w) => w.layout(render_ctx, constraints),
129            WidgetKind::Leaf(w) => w.layout(render_ctx, constraints),
130            WidgetKind::MultiChild(w) => w.layout(render_ctx, constraints),
131            WidgetKind::SingleChild(w) => w.layout(render_ctx, constraints),
132            WidgetKind::Inherited(w) => w.layout(render_ctx, constraints),
133        }
134    }
135
136    pub fn paint<'b>(
137        &self,
138        render_ctx: &'b mut AnyRenderContext,
139        piet: &mut PaintContext,
140        offset: &Offset,
141    ) {
142        match self.kind {
143            WidgetKind::View(w) => w.paint(render_ctx, piet, offset),
144            WidgetKind::Leaf(w) => w.paint(render_ctx, piet, offset),
145            WidgetKind::MultiChild(w) => w.paint(render_ctx, piet, offset),
146            WidgetKind::SingleChild(w) => w.paint(render_ctx, piet, offset),
147            WidgetKind::Inherited(w) => w.paint(render_ctx, piet, offset),
148        }
149    }
150
151    pub fn mount(&self, build_ctx: &Context) {
152        STATE_UPDATE_SUPRESSED.store(true, std::sync::atomic::Ordering::SeqCst);
153
154        match self.kind {
155            WidgetKind::View(w) => w.mount(build_ctx),
156            WidgetKind::Leaf(w) => w.mount(build_ctx),
157            WidgetKind::MultiChild(w) => w.mount(build_ctx),
158            WidgetKind::SingleChild(w) => w.mount(build_ctx),
159            WidgetKind::Inherited(w) => w.mount(build_ctx),
160        }
161
162        STATE_UPDATE_SUPRESSED.store(false, std::sync::atomic::Ordering::SeqCst);
163    }
164
165    pub fn unmount(&self, build_ctx: &Context) {
166        STATE_UPDATE_SUPRESSED.store(true, std::sync::atomic::Ordering::SeqCst);
167
168        match self.kind {
169            WidgetKind::View(w) => w.unmount(build_ctx),
170            WidgetKind::Leaf(w) => w.unmount(build_ctx),
171            WidgetKind::MultiChild(w) => w.unmount(build_ctx),
172            WidgetKind::SingleChild(w) => w.unmount(build_ctx),
173            WidgetKind::Inherited(w) => w.unmount(build_ctx),
174        }
175
176        STATE_UPDATE_SUPRESSED.store(false, std::sync::atomic::Ordering::SeqCst);
177    }
178
179    /// Returned `bool` indicates whether the event was consumed.
180    ///
181    /// # Note
182    ///
183    /// This is a bad prototype.
184    pub(crate) fn handle_event(&self, render_ctx: &mut AnyRenderContext, event: &Event) -> bool {
185        match self.kind {
186            WidgetKind::View(w) => w.handle_event(render_ctx, event),
187            WidgetKind::Leaf(w) => w.handle_event(render_ctx, event),
188            WidgetKind::SingleChild(w) => w.handle_event(render_ctx, event),
189            WidgetKind::MultiChild(w) => w.handle_event(render_ctx, event),
190            WidgetKind::Inherited(w) => w.handle_event(render_ctx, event),
191        }
192    }
193
194    //
195    //
196
197    pub fn can_update(&self, other: &WidgetPtr) -> bool {
198        self.unique_type_id() == other.unique_type_id()
199            && self.state_type_id() == other.state_type_id()
200    }
201
202    /// Compares configurations of widgets.
203    pub fn eq(&self, other: &WidgetPtr) -> bool {
204        // If widget configurations are not owned, their pointer addresses
205        // must be equal before we can compare them using `CheapEq`.
206        if self.is_borrowed() {
207            if self.widget_ptr() != other.widget_ptr() {
208                return false;
209            }
210        }
211
212        match self.kind {
213            WidgetKind::View(w) => {
214                if let WidgetKind::View(wo) = other.kind {
215                    return w.eq(wo.as_any_ext());
216                }
217            }
218            WidgetKind::Leaf(w) => {
219                if let WidgetKind::Leaf(wo) = other.kind {
220                    return w.eq(wo.as_any_ext());
221                }
222            }
223            WidgetKind::SingleChild(w) => {
224                if let WidgetKind::SingleChild(wo) = other.kind {
225                    return w.eq(wo.as_any_ext());
226                }
227            }
228            WidgetKind::MultiChild(w) => {
229                if let WidgetKind::MultiChild(wo) = other.kind {
230                    return w.eq(wo.as_any_ext());
231                }
232            }
233            WidgetKind::Inherited(w) => {
234                if let WidgetKind::Inherited(wo) = other.kind {
235                    return w.eq(wo.as_any_ext());
236                }
237            }
238        }
239
240        false
241    }
242
243    pub fn has_key(&self) -> bool {
244        self.local_key().is_some()
245    }
246
247    pub fn local_key(&self) -> Option<LocalKeyAny<'a>> {
248        match self.kind {
249            WidgetKind::View(w) => w.local_key(),
250            WidgetKind::Leaf(w) => w.local_key(),
251            WidgetKind::MultiChild(w) => w.local_key(),
252            WidgetKind::SingleChild(w) => w.local_key(),
253            WidgetKind::Inherited(w) => w.local_key(),
254        }
255    }
256
257    fn unique_type_id(&self) -> TypeId {
258        match self.kind {
259            WidgetKind::View(w) => w.unique_type(),
260            WidgetKind::Leaf(w) => w.unique_type(),
261            WidgetKind::SingleChild(w) => w.unique_type(),
262            WidgetKind::MultiChild(w) => w.unique_type(),
263            WidgetKind::Inherited(w) => w.unique_type(),
264        }
265    }
266
267    fn state_type_id(&self) -> TypeId {
268        match self.kind {
269            WidgetKind::View(w) => w.state_type_id(),
270            WidgetKind::Leaf(w) => w.state_type_id(),
271            WidgetKind::SingleChild(w) => w.state_type_id(),
272            WidgetKind::MultiChild(w) => w.state_type_id(),
273            WidgetKind::Inherited(w) => w.state_type_id(),
274        }
275    }
276
277    pub fn debug_name(&self) -> &'static str {
278        match self.kind {
279            WidgetKind::View(w) => w.debug_name(),
280            WidgetKind::Leaf(w) => w.debug_name(),
281            WidgetKind::SingleChild(w) => w.debug_name(),
282            WidgetKind::MultiChild(w) => w.debug_name(),
283            WidgetKind::Inherited(w) => w.debug_name(),
284        }
285    }
286
287    pub fn debug_name_short(&self) -> &'static str {
288        match self.kind {
289            WidgetKind::View(w) => w.debug_name_short(),
290            WidgetKind::Leaf(w) => w.debug_name_short(),
291            WidgetKind::SingleChild(w) => w.debug_name_short(),
292            WidgetKind::MultiChild(w) => w.debug_name_short(),
293            WidgetKind::Inherited(w) => w.debug_name_short(),
294        }
295    }
296
297    pub fn inherited_key(&self) -> TypeId {
298        match self.kind {
299            WidgetKind::Inherited(w) => w.inherited_key(),
300            _ => panic!("Widget::inherited_key() called on non-inherited widget"),
301        }
302    }
303
304    fn is_borrowed(&self) -> bool {
305        self.owned.is_none()
306    }
307
308    fn widget_ptr(&self) -> *const () {
309        match self.kind {
310            WidgetKind::View(w) => w as *const _ as *const (),
311            WidgetKind::Inherited(w) => w as *const _ as *const (),
312            WidgetKind::Leaf(w) => w as *const _ as *const (),
313            WidgetKind::SingleChild(w) => w as *const _ as *const (),
314            WidgetKind::MultiChild(w) => w as *const _ as *const (),
315        }
316    }
317}
318
319impl Default for WidgetPtr<'_> {
320    fn default() -> Self {
321        WidgetPtr::from_owned(Box::new(()))
322    }
323}
324
325pub trait WidgetDebug {
326    fn debug_name(&self) -> &'static str;
327    fn debug_name_short(&self) -> &'static str;
328}
329
330impl<T> WidgetDebug for T {
331    default fn debug_name(&self) -> &'static str {
332        let full_name = std::any::type_name::<T>();
333        full_name
334    }
335
336    fn debug_name_short(&self) -> &'static str {
337        let full_name = std::any::type_name::<T>();
338
339        let mut start = 0;
340        let mut end = full_name.len();
341
342        for (n, char) in full_name.chars().enumerate() {
343            if char == '<' {
344                end = n;
345                break;
346            } else if char == ':' {
347                start = n + 1;
348            }
349        }
350
351        &full_name[start..end]
352    }
353}
354
355pub trait WidgetUniqueType {
356    fn unique_type(&self) -> TypeId;
357}
358
359impl<T> WidgetUniqueType for T {
360    default fn unique_type(&self) -> TypeId {
361        unreachable!()
362    }
363}
364
365impl<T: Widget> WidgetUniqueType for T {
366    fn unique_type(&self) -> TypeId {
367        T::unique_type(self)
368    }
369}
370
371pub(crate) trait IntoWidgetPtr {
372    fn into_widget_ptr<'a>(self) -> WidgetPtr<'a>
373    where
374        Self: 'a;
375}
376
377impl<T: Widget> IntoWidgetPtr for T {
378    default fn into_widget_ptr<'a>(self) -> WidgetPtr<'a>
379    where
380        Self: 'a,
381    {
382        WidgetPtr::from_owned(Box::new(self))
383    }
384}
385
386impl<T: Widget> IntoWidgetPtr for &T {
387    default fn into_widget_ptr<'a>(self) -> WidgetPtr<'a>
388    where
389        Self: 'a,
390    {
391        WidgetPtr::from_ref(self)
392    }
393}
394
395impl IntoWidgetPtr for &dyn Widget {
396    default fn into_widget_ptr<'a>(self) -> WidgetPtr<'a>
397    where
398        Self: 'a,
399    {
400        WidgetPtr::from_ref(self)
401    }
402}
403
404pub(crate) use any_ext::*;
405
406mod any_ext {
407    use std::{
408        any::{Any, TypeId},
409        marker::PhantomData,
410    };
411
412    /// This trait allows us to acquire `TypeId` of any `T` (not just `T: 'static`),
413    /// which is used to downcast trait objects containing non-static fields to a
414    /// concrete type.
415    pub trait AnyExt: AsAny {
416        fn type_id(&self) -> TypeId;
417
418        /// Helper function.
419        fn as_any_ext<'a>(&'a self) -> &'a (dyn AnyExt + 'a);
420    }
421
422    impl<T> AnyExt for T {
423        fn type_id(&self) -> TypeId {
424            get_type_id::<T>()
425        }
426
427        fn as_any_ext<'a>(&'a self) -> &'a dyn AnyExt {
428            self
429        }
430    }
431
432    impl<'a> dyn AnyExt + 'a {
433        /// Downcasts reference `self` to `T` or returns `None`.
434        ///
435        /// # Safety
436        ///
437        /// Downcasted `&T` may contain references of lifetimes that are
438        /// different between two structures even if `TypeId`s match.
439        ///
440        /// One must ensure that this cannot cause UB.
441        ///
442        /// # Example
443        ///
444        /// Using internal mutabilty one can swap `'a` and `'static` references
445        /// causing dangling references and use-after-free.
446        ///
447        /// ```
448        /// struct Test<'a> {
449        ///     r: RefCell<&'a str>,
450        /// }
451        ///
452        /// impl<'a> Test<'a> {
453        ///     fn swap(&'a self, other: &'a Test<'a>) {
454        ///         *self.r.borrow_mut() = *other.r.borrow();
455        ///     }
456        /// }
457        ///
458        /// let string = String::from("non 'static");
459        ///
460        /// let static_ = Test {
461        ///     r: RefCell::new("'static str"),
462        /// };
463        /// let non_static = Test {
464        ///     r: RefCell::new(&string),
465        /// };
466        ///
467        /// let static_any: &dyn AnyExt = &static_;
468        /// let non_static_any: &dyn AnyExt = &non_static;
469        ///
470        /// fn uh_oh(static_: &dyn AnyExt, non_static: &dyn AnyExt) {
471        ///     unsafe {
472        ///         let static_ = static_.downcast_ref::<Test>().unwrap();
473        ///         let non_static = non_static.downcast_ref::<Test>().unwrap();
474        ///
475        ///         static_.swap(non_static);
476        ///     }
477        /// }
478        ///
479        /// uh_oh(static_any, non_static_any);
480        ///
481        /// drop(string);
482        /// println!("{}", static_.r.borrow()); // uh-oh
483        /// ```
484        pub unsafe fn downcast_ref<T>(&self) -> Option<&T> {
485            match AnyExt::type_id(self) == get_type_id::<T>() {
486                true => Some(&*(self as *const _ as *const T)),
487                false => None,
488            }
489        }
490
491        /// # Safety
492        ///
493        /// See `downcast_ref`.
494        pub unsafe fn downcast_mut<T>(&mut self) -> Option<&mut T> {
495            match AnyExt::type_id(self) == get_type_id::<T>() {
496                true => Some(&mut *(self as *mut _ as *mut T)),
497                false => None,
498            }
499        }
500    }
501
502    struct TypeIdKey<T>(PhantomData<T>);
503
504    impl<T> TypeIdKey<T> {
505        fn new() -> Self {
506            TypeIdKey(PhantomData)
507        }
508    }
509
510    fn get_type_id<T>() -> TypeId {
511        unsafe {
512            let key = <TypeIdKey<T>>::new();
513
514            // Safety: We cast &key to 'static to be able to cast it to `Any` to acquire TypeId.
515            // This is because `TypeId::of::<TypeIdKey<T>>()` won't work since T isn't 'static.
516            //
517            // That `&'static key` reference is not used any longer than it would normally be.
518            let any = std::mem::transmute::<&dyn AsAny, &'static dyn AsAny>(&key);
519            let any = any.as_any();
520            Any::type_id(any)
521        }
522    }
523
524    /// Helper trait used in [`get_type_id`] above.
525    pub trait AsAny {
526        fn as_any(&'static self) -> &dyn Any;
527    }
528
529    impl<T> AsAny for T {
530        fn as_any(&'static self) -> &dyn Any {
531            self
532        }
533    }
534
535    #[cfg(test)]
536    mod test {
537        use super::*;
538
539        #[test]
540        fn should_downcast() {
541            unsafe {
542                assert!((&16usize as &dyn AnyExt).downcast_ref::<usize>().is_some());
543                assert!((&String::new() as &dyn AnyExt)
544                    .downcast_ref::<String>()
545                    .is_some());
546                assert!((&std::sync::Mutex::new(2u8) as &dyn AnyExt)
547                    .downcast_ref::<std::sync::Mutex<u8>>()
548                    .is_some());
549            }
550        }
551
552        #[test]
553        fn should_not_downcast() {
554            unsafe {
555                assert!((&16usize as &dyn AnyExt).downcast_ref::<u8>().is_none());
556                assert!((&std::sync::Mutex::new(2u8) as &dyn AnyExt)
557                    .downcast_ref::<std::sync::Mutex<usize>>()
558                    .is_none());
559            }
560        }
561    }
562}