vizia_core/
model.rs

1//! Models are used to store application data and can be bound to by views to visually display the data.
2
3use crate::{events::ViewHandler, prelude::*};
4use hashbrown::HashMap;
5use std::any::{Any, TypeId};
6
7/// A trait implemented by application data in order to respond to events and mutate state.
8///
9/// # Examples
10///
11/// ```
12/// # use vizia_core::prelude::*;
13/// #
14/// pub struct AppData {
15///     count: i32,
16/// }
17///
18/// enum AppEvent {
19///     Increment,
20///     Decrement,
21/// }
22///
23/// impl Model for AppData {
24///     fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
25///         event.map(|app_event, _| match app_event {
26///             AppEvent::Increment => {
27///                 self.count += 1;
28///             }
29///
30///             AppEvent::Decrement => {
31///                 self.count -= 1;
32///             }
33///         });
34///     }
35/// }
36/// ```
37pub trait Model: 'static + Sized {
38    /// Build the model data into the application tree.
39    ///
40    /// # Examples
41    ///
42    /// ```no_run
43    /// # use vizia_core::prelude::*;
44    /// # use vizia_winit::application::Application;
45    /// #
46    /// # #[derive(Default, Lens)]
47    /// # pub struct AppData {
48    /// #     count: i32,
49    /// # }
50    /// #
51    /// # impl Model for AppData {}
52    /// #
53    /// fn main() {
54    ///     Application::new(|cx|{
55    ///         AppData::default().build(cx);
56    ///     }).run();  
57    /// }
58    /// ```
59    fn build(self, cx: &mut Context) {
60        let current = if cx.tree.is_ignored(cx.current) {
61            cx.tree.get_layout_parent(cx.current).unwrap()
62        } else {
63            cx.current
64        };
65
66        if let Some(models) = cx.models.get_mut(&current) {
67            models.insert(TypeId::of::<Self>(), Box::new(self));
68        } else {
69            let mut models: HashMap<TypeId, Box<dyn ModelData>> = HashMap::new();
70            models.insert(TypeId::of::<Self>(), Box::new(self));
71            cx.models.insert(current, models);
72        }
73    }
74
75    /// Respond to events in order to mutate the model data.
76    ///
77    /// # Examples
78    ///
79    /// ```
80    /// # use vizia_core::prelude::*;
81    /// # use vizia_derive::*;
82    /// # use vizia_winit::application::Application;
83    /// #
84    /// # #[derive(Default, Lens)]
85    /// # pub struct AppData {
86    /// #     count: i32,
87    /// # }
88    /// #
89    /// # enum AppEvent {
90    /// #     Increment,
91    /// #     Decrement,
92    /// # }
93    /// #
94    /// impl Model for AppData {
95    ///     fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
96    ///         event.map(|app_event, _| match app_event {
97    ///             AppEvent::Increment => {
98    ///                 self.count += 1;
99    ///             }
100    ///
101    ///             AppEvent::Decrement => {
102    ///                 self.count -= 1;
103    ///             }
104    ///         });
105    ///     }
106    /// }
107    /// ```
108    #[allow(unused_variables)]
109    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {}
110
111    /// Returns the name of the [Model] if it has one.
112    #[cfg(debug_assertions)]
113    fn name(&self) -> Option<&'static str> {
114        None
115    }
116}
117
118pub(crate) trait ModelData: Any {
119    #[allow(unused_variables)]
120    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {}
121
122    fn as_any_ref(&self) -> &dyn Any;
123    #[cfg(debug_assertions)]
124    fn name(&self) -> Option<&'static str>;
125}
126
127impl dyn ModelData {
128    pub fn downcast_ref<T: Any>(&self) -> Option<&T> {
129        self.as_any_ref().downcast_ref()
130    }
131}
132
133impl<T: Model> ModelData for T {
134    fn event(&mut self, cx: &mut EventContext, event: &mut Event) {
135        <T as Model>::event(self, cx, event);
136    }
137
138    fn as_any_ref(&self) -> &dyn Any {
139        self
140    }
141
142    #[cfg(debug_assertions)]
143    fn name(&self) -> Option<&'static str> {
144        <T as Model>::name(self)
145    }
146}
147
148impl Model for () {}
149
150#[derive(Copy, Clone)]
151pub(crate) enum ModelOrView<'a> {
152    Model(&'a dyn ModelData),
153    View(&'a dyn ViewHandler),
154}
155
156impl<'a> ModelOrView<'a> {
157    pub fn downcast_ref<T: 'static>(self) -> Option<&'a T> {
158        match self {
159            ModelOrView::Model(m) => m.downcast_ref(),
160            ModelOrView::View(v) => v.downcast_ref(),
161        }
162    }
163}