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