frui_core/api/contexts/
build_ctx.rs

1use crate::{app::tree::WidgetNodeRef, prelude::InheritedWidget};
2
3use std::{
4    any::Any,
5    cell::{Ref, RefMut},
6    marker::PhantomData,
7    ops::{Deref, DerefMut},
8    sync::atomic::{AtomicBool, Ordering},
9};
10
11/// Set by framework when accessing state mutably shouldn't register widget for
12/// state updates (e.g. in unmount/mount methods).
13pub(crate) static STATE_UPDATE_SUPRESSED: AtomicBool = AtomicBool::new(false);
14
15pub trait WidgetState: Sized {
16    type State: 'static;
17
18    fn create_state(&self) -> Self::State;
19
20    /// Called when the widget is mounted into the tree (before build).
21    ///
22    /// Accessing `state_mut` of the provided `BuildContext` will not cause a
23    /// rebuild of this widget to be scheduled.
24    fn mount<'a>(&'a self, ctx: BuildContext<'a, Self>) {
25        let _ = ctx;
26    }
27
28    /// Called when the widget is unmounted from the tree. At this point given
29    /// widget may be dropped or mounted again with its configuration updated.
30    ///
31    /// Accessing `state_mut` of the provided `BuildContext` will not cause a
32    /// rebuild of this widget to be scheduled.
33    fn unmount<'a>(&'a self, ctx: BuildContext<'a, Self>) {
34        let _ = ctx;
35    }
36}
37
38// `BuildContext` is borrowed to make it so that closures don't take ownership
39// of it, which would be inconvenient - user would have to clone `BuildContext`
40// before every closure, since otherwise the context would move.
41pub type BuildContext<'a, T> = &'a _BuildContext<'a, T>;
42
43#[repr(transparent)]
44pub struct _BuildContext<'a, T> {
45    node: WidgetNodeRef,
46    _p: PhantomData<&'a T>,
47}
48
49impl<'a, T> _BuildContext<'a, T> {
50    pub fn state(&self) -> StateGuard<T::State>
51    where
52        T: WidgetState,
53    {
54        StateGuard {
55            guard: Ref::map(self.node.borrow(), |node| node.state.deref()),
56            _p: PhantomData,
57        }
58    }
59
60    pub fn state_mut(&self) -> StateGuardMut<T::State>
61    where
62        T: WidgetState,
63    {
64        if !STATE_UPDATE_SUPRESSED.load(Ordering::SeqCst) {
65            self.node.mark_dirty();
66        }
67
68        StateGuardMut {
69            guard: RefMut::map(self.node.borrow_mut(), |node| node.state.deref_mut()),
70            _p: PhantomData,
71        }
72    }
73
74    /// This method registers the widget of this `BuildContext` as a dependency of
75    /// the closest `InheritedWidget` ancestor of type `W` in the tree. It then
76    /// returns the state of that inherited widget or `None` if inherited ancestor
77    /// doesn't exist.
78    pub fn depend_on_inherited_widget<W>(&self) -> Option<InheritedState<W::State>>
79    where
80        W: InheritedWidget + WidgetState,
81    {
82        // Register and get inherited widget of specified key.
83        let node = self
84            .node
85            .depend_on_inherited_widget_of_key::<W::UniqueTypeId>()?;
86
87        Some(InheritedState {
88            node,
89            _p: PhantomData,
90        })
91    }
92}
93
94pub struct StateGuard<'a, T: 'static> {
95    guard: Ref<'a, dyn Any>,
96    _p: PhantomData<&'a T>,
97}
98
99impl<'a, T: 'static> Deref for StateGuard<'a, T> {
100    type Target = T;
101
102    fn deref(&self) -> &Self::Target {
103        self.guard.deref().downcast_ref().unwrap()
104    }
105}
106
107pub struct StateGuardMut<'a, T: 'static> {
108    guard: RefMut<'a, dyn Any>,
109    _p: PhantomData<&'a T>,
110}
111
112impl<'a, T: 'static> Deref for StateGuardMut<'a, T> {
113    type Target = T;
114
115    fn deref(&self) -> &Self::Target {
116        self.guard.deref().downcast_ref().unwrap()
117    }
118}
119
120impl<'a, T: 'static> std::ops::DerefMut for StateGuardMut<'a, T> {
121    fn deref_mut(&mut self) -> &mut Self::Target {
122        self.guard.deref_mut().downcast_mut().unwrap()
123    }
124}
125
126pub struct InheritedState<'a, T: 'static> {
127    pub(crate) node: WidgetNodeRef,
128    pub(crate) _p: PhantomData<&'a T>,
129}
130
131impl<'a, T: 'static> InheritedState<'a, T> {
132    pub fn as_ref(&'a self) -> InheritedStateRef<'a, T> {
133        InheritedStateRef {
134            state: Ref::map(self.node.borrow(), |node| node.state.deref()),
135            _p: PhantomData,
136        }
137    }
138
139    pub fn as_mut(&'a mut self) -> InheritedStateRefMut<'a, T> {
140        if !STATE_UPDATE_SUPRESSED.load(Ordering::SeqCst) {
141            self.node.mark_dirty();
142            self.node.mark_dependent_widgets_as_dirty();
143        }
144
145        InheritedStateRefMut {
146            state: RefMut::map(self.node.borrow_mut(), |node| node.state.deref_mut()),
147            _p: PhantomData,
148        }
149    }
150}
151
152pub struct InheritedStateRef<'a, T: 'static> {
153    state: Ref<'a, dyn Any>,
154    _p: PhantomData<T>,
155}
156
157impl<'a, T> Deref for InheritedStateRef<'a, T> {
158    type Target = T;
159
160    fn deref(&self) -> &Self::Target {
161        self.state.downcast_ref().unwrap()
162    }
163}
164
165pub struct InheritedStateRefMut<'a, T: 'static> {
166    state: RefMut<'a, dyn Any>,
167    _p: PhantomData<T>,
168}
169
170impl<'a, T> Deref for InheritedStateRefMut<'a, T> {
171    type Target = T;
172
173    fn deref(&self) -> &Self::Target {
174        self.state.downcast_ref().unwrap()
175    }
176}
177
178impl<'a, T> DerefMut for InheritedStateRefMut<'a, T> {
179    fn deref_mut(&mut self) -> &mut Self::Target {
180        self.state.downcast_mut().unwrap()
181    }
182}
183
184pub(crate) use sealed::WidgetStateOS;
185
186mod sealed {
187    use std::{
188        any::{Any, TypeId},
189        cell::RefCell,
190    };
191
192    use crate::api::contexts::Context;
193
194    use super::_BuildContext;
195
196    /// `OS` stands for "object safe".
197    pub trait WidgetStateOS {
198        fn state_type_id(&self) -> TypeId;
199
200        fn create_state(&self) -> Box<dyn Any>;
201        fn mount(&self, build_ctx: &Context);
202        fn unmount(&self, build_ctx: &Context);
203    }
204
205    impl<T> WidgetStateOS for T {
206        default fn state_type_id(&self) -> TypeId {
207            struct WidgetHasNoState;
208            TypeId::of::<WidgetHasNoState>()
209        }
210
211        default fn create_state(&self) -> Box<dyn Any> {
212            Box::new(RefCell::new(()))
213        }
214
215        default fn mount(&self, _ctx: &Context) {}
216        default fn unmount(&self, _ctx: &Context) {}
217    }
218
219    impl<T: super::WidgetState> WidgetStateOS for T {
220        fn state_type_id(&self) -> TypeId {
221            TypeId::of::<T::State>()
222        }
223
224        fn create_state(&self) -> Box<dyn Any> {
225            Box::new(T::create_state(&self))
226        }
227
228        fn mount(&self, ctx: &Context) {
229            let ctx = unsafe { std::mem::transmute::<&Context, &_BuildContext<T>>(ctx) };
230
231            T::mount(&self, ctx)
232        }
233
234        fn unmount(&self, ctx: &Context) {
235            let ctx = unsafe { std::mem::transmute::<&Context, &_BuildContext<T>>(ctx) };
236
237            T::unmount(&self, ctx)
238        }
239    }
240}