1mod async_context;
2mod entity_map;
3mod model_context;
4
5pub use async_context::*;
6pub use entity_map::*;
7pub use model_context::*;
8use refineable::Refineable;
9
10use crate::{
11 current_platform, run_on_main, spawn_on_main, Context, LayoutId, MainThread, MainThreadOnly,
12 Platform, PlatformDispatcher, RootView, TextStyle, TextStyleRefinement, TextSystem, Window,
13 WindowContext, WindowHandle, WindowId,
14};
15use anyhow::{anyhow, Result};
16use collections::{HashMap, VecDeque};
17use futures::Future;
18use parking_lot::Mutex;
19use slotmap::SlotMap;
20use smallvec::SmallVec;
21use std::{
22 any::{type_name, Any, TypeId},
23 mem,
24 sync::{Arc, Weak},
25};
26use util::ResultExt;
27
28#[derive(Clone)]
29pub struct App(Arc<Mutex<MainThread<AppContext>>>);
30
31impl App {
32 pub fn production() -> Self {
33 Self::new(current_platform())
34 }
35
36 #[cfg(any(test, feature = "test"))]
37 pub fn test() -> Self {
38 Self::new(Arc::new(super::TestPlatform::new()))
39 }
40
41 fn new(platform: Arc<dyn Platform>) -> Self {
42 let dispatcher = platform.dispatcher();
43 let text_system = Arc::new(TextSystem::new(platform.text_system()));
44 let entities = EntityMap::new();
45 let unit_entity = entities.redeem(entities.reserve(), ());
46 Self(Arc::new_cyclic(|this| {
47 Mutex::new(MainThread::new(AppContext {
48 this: this.clone(),
49 platform: MainThreadOnly::new(platform, dispatcher.clone()),
50 dispatcher,
51 text_system,
52 pending_updates: 0,
53 text_style_stack: Vec::new(),
54 state_stacks_by_type: HashMap::default(),
55 unit_entity,
56 entities,
57 windows: SlotMap::with_key(),
58 pending_effects: Default::default(),
59 observers: Default::default(),
60 layout_id_buffer: Default::default(),
61 }))
62 }))
63 }
64
65 pub fn run<F>(self, on_finish_launching: F)
66 where
67 F: 'static + FnOnce(&mut MainThread<AppContext>),
68 {
69 let this = self.clone();
70 let platform = self.0.lock().platform.clone();
71 platform.borrow_on_main_thread().run(Box::new(move || {
72 let cx = &mut *this.0.lock();
73 on_finish_launching(cx);
74 }));
75 }
76}
77
78type Handlers = SmallVec<[Arc<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>; 2]>;
79
80pub struct AppContext {
81 this: Weak<Mutex<MainThread<AppContext>>>,
82 platform: MainThreadOnly<dyn Platform>,
83 dispatcher: Arc<dyn PlatformDispatcher>,
84 text_system: Arc<TextSystem>,
85 pending_updates: usize,
86 pub(crate) text_style_stack: Vec<TextStyleRefinement>,
87 pub(crate) state_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
88 pub(crate) unit_entity: Handle<()>,
89 pub(crate) entities: EntityMap,
90 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
91 pub(crate) pending_effects: VecDeque<Effect>,
92 pub(crate) observers: HashMap<EntityId, Handlers>,
93 pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
94}
95
96impl AppContext {
97 fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
98 self.pending_updates += 1;
99 let result = update(self);
100 if self.pending_updates == 1 {
101 self.flush_effects();
102 }
103 self.pending_updates -= 1;
104 result
105 }
106
107 pub(crate) fn update_window<R>(
108 &mut self,
109 id: WindowId,
110 update: impl FnOnce(&mut WindowContext) -> R,
111 ) -> Result<R> {
112 self.update(|cx| {
113 let mut window = cx
114 .windows
115 .get_mut(id)
116 .ok_or_else(|| anyhow!("window not found"))?
117 .take()
118 .unwrap();
119
120 let result = update(&mut WindowContext::mutable(cx, &mut window));
121
122 cx.windows
123 .get_mut(id)
124 .ok_or_else(|| anyhow!("window not found"))?
125 .replace(window);
126
127 Ok(result)
128 })
129 }
130
131 fn flush_effects(&mut self) {
132 while let Some(effect) = self.pending_effects.pop_front() {
133 match effect {
134 Effect::Notify(entity_id) => self.apply_notify_effect(entity_id),
135 }
136 }
137
138 let dirty_window_ids = self
139 .windows
140 .iter()
141 .filter_map(|(window_id, window)| {
142 let window = window.as_ref().unwrap();
143 if window.dirty {
144 Some(window_id)
145 } else {
146 None
147 }
148 })
149 .collect::<Vec<_>>();
150
151 for dirty_window_id in dirty_window_ids {
152 self.update_window(dirty_window_id, |cx| cx.draw())
153 .unwrap()
154 .log_err();
155 }
156 }
157
158 fn apply_notify_effect(&mut self, updated_entity: EntityId) {
159 if let Some(mut handlers) = self.observers.remove(&updated_entity) {
160 handlers.retain(|handler| handler(self));
161 if let Some(new_handlers) = self.observers.remove(&updated_entity) {
162 handlers.extend(new_handlers);
163 }
164 self.observers.insert(updated_entity, handlers);
165 }
166 }
167
168 pub fn to_async(&self) -> AsyncContext {
169 AsyncContext(unsafe { mem::transmute(self.this.clone()) })
170 }
171
172 pub fn run_on_main<R>(
173 &self,
174 f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
175 ) -> impl Future<Output = R>
176 where
177 R: Send + 'static,
178 {
179 let this = self.this.upgrade().unwrap();
180 run_on_main(self.dispatcher.clone(), move || {
181 let cx = &mut *this.lock();
182 cx.update(|cx| {
183 f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
184 })
185 })
186 }
187
188 pub fn spawn_on_main<F, R>(
189 &self,
190 f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
191 ) -> impl Future<Output = R>
192 where
193 F: Future<Output = R> + 'static,
194 R: Send + 'static,
195 {
196 let this = self.this.upgrade().unwrap();
197 spawn_on_main(self.dispatcher.clone(), move || {
198 let cx = &mut *this.lock();
199 cx.update(|cx| {
200 f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
201 })
202 })
203 }
204
205 pub fn text_system(&self) -> &Arc<TextSystem> {
206 &self.text_system
207 }
208
209 pub fn text_style(&self) -> TextStyle {
210 let mut style = TextStyle::default();
211 for refinement in &self.text_style_stack {
212 style.refine(refinement);
213 }
214 style
215 }
216
217 pub fn state<S: 'static>(&self) -> &S {
218 self.state_stacks_by_type
219 .get(&TypeId::of::<S>())
220 .and_then(|stack| stack.last())
221 .and_then(|any_state| any_state.downcast_ref::<S>())
222 .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<S>()))
223 .unwrap()
224 }
225
226 pub fn state_mut<S: 'static>(&mut self) -> &mut S {
227 self.state_stacks_by_type
228 .get_mut(&TypeId::of::<S>())
229 .and_then(|stack| stack.last_mut())
230 .and_then(|any_state| any_state.downcast_mut::<S>())
231 .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<S>()))
232 .unwrap()
233 }
234
235 pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
236 self.text_style_stack.push(text_style);
237 }
238
239 pub(crate) fn pop_text_style(&mut self) {
240 self.text_style_stack.pop();
241 }
242
243 pub(crate) fn push_state<T: Send + Sync + 'static>(&mut self, state: T) {
244 self.state_stacks_by_type
245 .entry(TypeId::of::<T>())
246 .or_default()
247 .push(Box::new(state));
248 }
249
250 pub(crate) fn pop_state<T: 'static>(&mut self) {
251 self.state_stacks_by_type
252 .get_mut(&TypeId::of::<T>())
253 .and_then(|stack| stack.pop())
254 .expect("state stack underflow");
255 }
256}
257
258impl Context for AppContext {
259 type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
260 type Result<T> = T;
261
262 fn entity<T: Send + Sync + 'static>(
263 &mut self,
264 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
265 ) -> Handle<T> {
266 let slot = self.entities.reserve();
267 let entity = build_entity(&mut ModelContext::mutable(self, slot.id));
268 self.entities.redeem(slot, entity)
269 }
270
271 fn update_entity<T: Send + Sync + 'static, R>(
272 &mut self,
273 handle: &Handle<T>,
274 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
275 ) -> R {
276 let mut entity = self.entities.lease(handle);
277 let result = update(&mut *entity, &mut ModelContext::mutable(self, handle.id));
278 self.entities.end_lease(entity);
279 result
280 }
281}
282
283impl MainThread<AppContext> {
284 fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
285 self.0.update(|cx| {
286 update(unsafe {
287 std::mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx)
288 })
289 })
290 }
291
292 pub(crate) fn update_window<R>(
293 &mut self,
294 id: WindowId,
295 update: impl FnOnce(&mut WindowContext) -> R,
296 ) -> Result<R> {
297 self.0.update_window(id, |cx| {
298 update(unsafe {
299 std::mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
300 })
301 })
302 }
303
304 pub(crate) fn platform(&self) -> &dyn Platform {
305 self.platform.borrow_on_main_thread()
306 }
307
308 pub fn activate(&mut self, ignoring_other_apps: bool) {
309 self.platform().activate(ignoring_other_apps);
310 }
311
312 pub fn open_window<S: 'static + Send + Sync>(
313 &mut self,
314 options: crate::WindowOptions,
315 build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
316 ) -> WindowHandle<S> {
317 self.update(|cx| {
318 let id = cx.windows.insert(None);
319 let handle = WindowHandle::new(id);
320 let mut window = Window::new(handle.into(), options, cx);
321 let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
322 window.root_view.replace(root_view.into_any());
323 cx.windows.get_mut(id).unwrap().replace(window);
324 handle
325 })
326 }
327}
328
329pub(crate) enum Effect {
330 Notify(EntityId),
331}
332
333#[cfg(test)]
334mod tests {
335 use super::AppContext;
336
337 #[test]
338 fn test_app_context_send_sync() {
339 // This will not compile if `AppContext` does not implement `Send`
340 fn assert_send<T: Send>() {}
341 assert_send::<AppContext>();
342 }
343}