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