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 164 165 166 167 168 169 170
//! Models are used to store application data and can be bound to by views to visually display the data.
use std::any::{Any, TypeId};
use crate::binding::Store;
use crate::binding::StoreId;
use crate::{events::ViewHandler, prelude::*};
use hashbrown::HashMap;
/// 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(model_data_store) = cx.data.get_mut(¤t) {
model_data_store.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.data.insert(current, ModelDataStore { models, stores: HashMap::default() });
}
}
/// 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) {}
#[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)
}
}
#[derive(Default)]
pub(crate) struct ModelDataStore {
pub models: HashMap<TypeId, Box<dyn ModelData>>,
pub stores: HashMap<StoreId, Box<dyn Store>>,
}
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(),
}
}
}