1use anyhow::{anyhow, Result};
2use slotmap::SlotMap;
3use std::{any::Any, marker::PhantomData, rc::Rc, sync::Arc};
4
5use crate::FontCache;
6
7use super::{
8 platform::Platform,
9 window::{Window, WindowHandle, WindowId},
10 Context, LayoutId, Reference, View, WindowContext,
11};
12
13pub struct AppContext {
14 platform: Rc<dyn Platform>,
15 font_cache: Arc<FontCache>,
16 pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
17 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
18 // We recycle this memory across layout requests.
19 pub(crate) layout_id_buffer: Vec<LayoutId>,
20}
21
22impl AppContext {
23 pub fn new(platform: Rc<dyn Platform>) -> Self {
24 let font_cache = Arc::new(FontCache::new(platform.font_system()));
25 AppContext {
26 platform,
27 font_cache,
28 entities: SlotMap::with_key(),
29 windows: SlotMap::with_key(),
30 layout_id_buffer: Default::default(),
31 }
32 }
33
34 #[cfg(any(test, feature = "test"))]
35 pub fn test() -> Self {
36 Self::new(Rc::new(super::TestPlatform::new()))
37 }
38
39 pub fn platform(&self) -> &Rc<dyn Platform> {
40 &self.platform
41 }
42
43 pub fn font_cache(&self) -> &Arc<FontCache> {
44 &self.font_cache
45 }
46
47 pub fn open_window<S: 'static>(
48 &mut self,
49 options: crate::WindowOptions,
50 build_root_view: impl FnOnce(&mut WindowContext) -> View<S>,
51 ) -> WindowHandle<S> {
52 let id = self.windows.insert(None);
53 let handle = WindowHandle::new(id);
54 let platform_window = self.platform.open_window(handle.into(), options);
55
56 let mut window = Window::new(id, platform_window);
57 let root_view = build_root_view(&mut WindowContext::mutable(self, &mut window));
58 window.root_view.replace(Box::new(root_view));
59
60 self.windows.get_mut(id).unwrap().replace(window);
61 handle
62 }
63
64 pub(crate) fn update_window<R>(
65 &mut self,
66 window_id: WindowId,
67 update: impl FnOnce(&mut WindowContext) -> R,
68 ) -> Result<R> {
69 let mut window = self
70 .windows
71 .get_mut(window_id)
72 .ok_or_else(|| anyhow!("window not found"))?
73 .take()
74 .unwrap();
75
76 let result = update(&mut WindowContext::mutable(self, &mut window));
77
78 self.windows
79 .get_mut(window_id)
80 .ok_or_else(|| anyhow!("window not found"))?
81 .replace(window);
82
83 Ok(result)
84 }
85}
86
87impl Context for AppContext {
88 type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, T>;
89
90 fn entity<T: 'static>(
91 &mut self,
92 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
93 ) -> Handle<T> {
94 let id = self.entities.insert(None);
95 let entity = Box::new(build_entity(&mut ModelContext::mutable(self, id)));
96 self.entities.get_mut(id).unwrap().replace(entity);
97
98 Handle::new(id)
99 }
100
101 fn update_entity<T: 'static, R>(
102 &mut self,
103 handle: &Handle<T>,
104 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
105 ) -> R {
106 let mut entity = self
107 .entities
108 .get_mut(handle.id)
109 .unwrap()
110 .take()
111 .unwrap()
112 .downcast::<T>()
113 .unwrap();
114
115 let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
116 self.entities.get_mut(handle.id).unwrap().replace(entity);
117 result
118 }
119}
120
121pub struct ModelContext<'a, T> {
122 app: Reference<'a, AppContext>,
123 entity_type: PhantomData<T>,
124 entity_id: EntityId,
125}
126
127impl<'a, T: 'static> ModelContext<'a, T> {
128 pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
129 Self {
130 app: Reference::Mutable(app),
131 entity_type: PhantomData,
132 entity_id,
133 }
134 }
135
136 fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
137 Self {
138 app: Reference::Immutable(app),
139 entity_type: PhantomData,
140 entity_id,
141 }
142 }
143
144 fn update<R>(&mut self, update: impl FnOnce(&mut T, &mut Self) -> R) -> R {
145 let mut entity = self
146 .app
147 .entities
148 .get_mut(self.entity_id)
149 .unwrap()
150 .take()
151 .unwrap();
152 let result = update(entity.downcast_mut::<T>().unwrap(), self);
153 self.app
154 .entities
155 .get_mut(self.entity_id)
156 .unwrap()
157 .replace(entity);
158 result
159 }
160}
161
162impl<'a, T: 'static> Context for ModelContext<'a, T> {
163 type EntityContext<'b, 'c, U: 'static> = ModelContext<'b, U>;
164
165 fn entity<U: 'static>(
166 &mut self,
167 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
168 ) -> Handle<U> {
169 self.app.entity(build_entity)
170 }
171
172 fn update_entity<U: 'static, R>(
173 &mut self,
174 handle: &Handle<U>,
175 update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
176 ) -> R {
177 self.app.update_entity(handle, update)
178 }
179}
180
181pub struct Handle<T> {
182 pub(crate) id: EntityId,
183 pub(crate) entity_type: PhantomData<T>,
184}
185
186slotmap::new_key_type! { pub struct EntityId; }
187
188impl<T: 'static> Handle<T> {
189 fn new(id: EntityId) -> Self {
190 Self {
191 id,
192 entity_type: PhantomData,
193 }
194 }
195
196 /// Update the entity referenced by this handle with the given function.
197 ///
198 /// The update function receives a context appropriate for its environment.
199 /// When updating in an `AppContext`, it receives a `ModelContext`.
200 /// When updating an a `WindowContext`, it receives a `ViewContext`.
201 pub fn update<C: Context, R>(
202 &self,
203 cx: &mut C,
204 update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
205 ) -> R {
206 cx.update_entity(self, update)
207 }
208}
209
210impl<T> Clone for Handle<T> {
211 fn clone(&self) -> Self {
212 Self {
213 id: self.id,
214 entity_type: PhantomData,
215 }
216 }
217}