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