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, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor, LayoutId,
12 MainThread, MainThreadOnly, Platform, SubscriberSet, SvgRenderer, Task, TextStyle,
13 TextStyleRefinement, TextSystem, View, Window, WindowContext, WindowHandle, WindowId,
14};
15use anyhow::{anyhow, Result};
16use collections::{HashMap, HashSet, VecDeque};
17use futures::Future;
18use parking_lot::Mutex;
19use slotmap::SlotMap;
20use std::{
21 any::{type_name, Any, TypeId},
22 mem,
23 sync::{Arc, Weak},
24};
25use util::http::{self, HttpClient};
26
27#[derive(Clone)]
28pub struct App(Arc<Mutex<AppContext>>);
29
30impl App {
31 pub fn production(asset_source: Arc<dyn AssetSource>) -> Self {
32 let http_client = http::client();
33 Self::new(current_platform(), asset_source, http_client)
34 }
35
36 #[cfg(any(test, feature = "test"))]
37 pub fn test() -> Self {
38 let platform = Arc::new(super::TestPlatform::new());
39 let asset_source = Arc::new(());
40 let http_client = util::http::FakeHttpClient::with_404_response();
41 Self::new(platform, asset_source, http_client)
42 }
43
44 fn new(
45 platform: Arc<dyn Platform>,
46 asset_source: Arc<dyn AssetSource>,
47 http_client: Arc<dyn HttpClient>,
48 ) -> Self {
49 let executor = platform.executor();
50 let entities = EntityMap::new();
51 let unit_entity = entities.insert(entities.reserve(), ());
52 Self(Arc::new_cyclic(|this| {
53 Mutex::new(AppContext {
54 this: this.clone(),
55 text_system: Arc::new(TextSystem::new(platform.text_system())),
56 pending_updates: 0,
57 next_frame_callbacks: Default::default(),
58 platform: MainThreadOnly::new(platform, executor.clone()),
59 executor,
60 svg_renderer: SvgRenderer::new(asset_source),
61 image_cache: ImageCache::new(http_client),
62 text_style_stack: Vec::new(),
63 global_stacks_by_type: HashMap::default(),
64 unit_entity,
65 entities,
66 windows: SlotMap::with_key(),
67 pending_notifications: Default::default(),
68 pending_effects: Default::default(),
69 observers: SubscriberSet::new(),
70 event_handlers: SubscriberSet::new(),
71 release_handlers: SubscriberSet::new(),
72 layout_id_buffer: Default::default(),
73 })
74 }))
75 }
76
77 pub fn run<F>(self, on_finish_launching: F)
78 where
79 F: 'static + FnOnce(&mut MainThread<AppContext>),
80 {
81 let this = self.clone();
82 let platform = self.0.lock().platform.clone();
83 platform.borrow_on_main_thread().run(Box::new(move || {
84 let cx = &mut *this.0.lock();
85 let cx = unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) };
86 on_finish_launching(cx);
87 }));
88 }
89}
90
91type Handler = Box<dyn Fn(&mut AppContext) -> bool + Send + Sync + 'static>;
92type EventHandler = Box<dyn Fn(&dyn Any, &mut AppContext) -> bool + Send + Sync + 'static>;
93type ReleaseHandler = Box<dyn Fn(&mut dyn Any, &mut AppContext) + Send + Sync + 'static>;
94type FrameCallback = Box<dyn FnOnce(&mut WindowContext) + Send>;
95
96pub struct AppContext {
97 this: Weak<Mutex<AppContext>>,
98 pub(crate) platform: MainThreadOnly<dyn Platform>,
99 text_system: Arc<TextSystem>,
100 pending_updates: usize,
101 pub(crate) next_frame_callbacks: HashMap<DisplayId, Vec<FrameCallback>>,
102 pub(crate) executor: Executor,
103 pub(crate) svg_renderer: SvgRenderer,
104 pub(crate) image_cache: ImageCache,
105 pub(crate) text_style_stack: Vec<TextStyleRefinement>,
106 pub(crate) global_stacks_by_type: HashMap<TypeId, Vec<Box<dyn Any + Send + Sync>>>,
107 pub(crate) unit_entity: Handle<()>,
108 pub(crate) entities: EntityMap,
109 pub(crate) windows: SlotMap<WindowId, Option<Window>>,
110 pub(crate) pending_notifications: HashSet<EntityId>,
111 pending_effects: VecDeque<Effect>,
112 pub(crate) observers: SubscriberSet<EntityId, Handler>,
113 pub(crate) event_handlers: SubscriberSet<EntityId, EventHandler>,
114 pub(crate) release_handlers: SubscriberSet<EntityId, ReleaseHandler>,
115 pub(crate) layout_id_buffer: Vec<LayoutId>, // We recycle this memory across layout requests.
116}
117
118impl AppContext {
119 pub(crate) fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
120 self.pending_updates += 1;
121 let result = update(self);
122 if self.pending_updates == 1 {
123 self.flush_effects();
124 }
125 self.pending_updates -= 1;
126 result
127 }
128
129 pub(crate) fn update_window<R>(
130 &mut self,
131 id: WindowId,
132 update: impl FnOnce(&mut WindowContext) -> R,
133 ) -> Result<R> {
134 self.update(|cx| {
135 let mut window = cx
136 .windows
137 .get_mut(id)
138 .ok_or_else(|| anyhow!("window not found"))?
139 .take()
140 .unwrap();
141
142 let result = update(&mut WindowContext::mutable(cx, &mut window));
143
144 cx.windows
145 .get_mut(id)
146 .ok_or_else(|| anyhow!("window not found"))?
147 .replace(window);
148
149 Ok(result)
150 })
151 }
152
153 pub(crate) fn push_effect(&mut self, effect: Effect) {
154 match &effect {
155 Effect::Notify { emitter } => {
156 if self.pending_notifications.insert(*emitter) {
157 self.pending_effects.push_back(effect);
158 }
159 }
160 Effect::Emit { .. } => self.pending_effects.push_back(effect),
161 }
162 }
163
164 fn flush_effects(&mut self) {
165 loop {
166 self.release_dropped_entities();
167 if let Some(effect) = self.pending_effects.pop_front() {
168 match effect {
169 Effect::Notify { emitter } => self.apply_notify_effect(emitter),
170 Effect::Emit { emitter, event } => self.apply_emit_effect(emitter, event),
171 }
172 } else {
173 break;
174 }
175 }
176
177 let dirty_window_ids = self
178 .windows
179 .iter()
180 .filter_map(|(window_id, window)| {
181 let window = window.as_ref().unwrap();
182 if window.dirty {
183 Some(window_id)
184 } else {
185 None
186 }
187 })
188 .collect::<Vec<_>>();
189
190 for dirty_window_id in dirty_window_ids {
191 self.update_window(dirty_window_id, |cx| cx.draw()).unwrap();
192 }
193 }
194
195 fn release_dropped_entities(&mut self) {
196 loop {
197 let dropped = self.entities.take_dropped();
198 if dropped.is_empty() {
199 break;
200 }
201
202 for (entity_id, mut entity) in dropped {
203 self.observers.remove(&entity_id);
204 self.event_handlers.remove(&entity_id);
205 for release_callback in self.release_handlers.remove(&entity_id) {
206 release_callback(&mut entity, self);
207 }
208 }
209 }
210 }
211
212 fn apply_notify_effect(&mut self, emitter: EntityId) {
213 self.pending_notifications.remove(&emitter);
214 self.observers
215 .clone()
216 .retain(&emitter, |handler| handler(self));
217 }
218
219 fn apply_emit_effect(&mut self, emitter: EntityId, event: Box<dyn Any>) {
220 self.event_handlers
221 .clone()
222 .retain(&emitter, |handler| handler(&event, self));
223 }
224
225 pub fn to_async(&self) -> AsyncAppContext {
226 AsyncAppContext(unsafe { mem::transmute(self.this.clone()) })
227 }
228
229 pub fn executor(&self) -> &Executor {
230 &self.executor
231 }
232
233 pub fn run_on_main<R>(
234 &mut self,
235 f: impl FnOnce(&mut MainThread<AppContext>) -> R + Send + 'static,
236 ) -> Task<R>
237 where
238 R: Send + 'static,
239 {
240 if self.executor.is_main_thread() {
241 Task::ready(f(unsafe {
242 mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(self)
243 }))
244 } else {
245 let this = self.this.upgrade().unwrap();
246 self.executor.run_on_main(move || {
247 let cx = &mut *this.lock();
248 cx.update(|cx| f(unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(cx) }))
249 })
250 }
251 }
252
253 pub fn spawn_on_main<F, R>(
254 &self,
255 f: impl FnOnce(&mut MainThread<AppContext>) -> F + Send + 'static,
256 ) -> Task<R>
257 where
258 F: Future<Output = R> + 'static,
259 R: Send + 'static,
260 {
261 let this = self.this.upgrade().unwrap();
262 self.executor.spawn_on_main(move || {
263 let cx = &mut *this.lock();
264 cx.update(|cx| {
265 f(unsafe { mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx) })
266 })
267 })
268 }
269
270 pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut + Send + 'static) -> Task<R>
271 where
272 Fut: Future<Output = R> + Send + 'static,
273 R: Send + 'static,
274 {
275 let cx = self.to_async();
276 self.executor.spawn(async move {
277 let future = f(cx);
278 future.await
279 })
280 }
281
282 pub fn text_system(&self) -> &Arc<TextSystem> {
283 &self.text_system
284 }
285
286 pub fn text_style(&self) -> TextStyle {
287 let mut style = TextStyle::default();
288 for refinement in &self.text_style_stack {
289 style.refine(refinement);
290 }
291 style
292 }
293
294 pub fn global<G: 'static>(&self) -> &G {
295 self.global_stacks_by_type
296 .get(&TypeId::of::<G>())
297 .and_then(|stack| stack.last())
298 .and_then(|any_state| any_state.downcast_ref::<G>())
299 .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
300 .unwrap()
301 }
302
303 pub fn global_mut<G: 'static>(&mut self) -> &mut G {
304 self.global_stacks_by_type
305 .get_mut(&TypeId::of::<G>())
306 .and_then(|stack| stack.last_mut())
307 .and_then(|any_state| any_state.downcast_mut::<G>())
308 .ok_or_else(|| anyhow!("no state of type {} exists", type_name::<G>()))
309 .unwrap()
310 }
311
312 pub fn default_global<G: 'static + Default + Sync + Send>(&mut self) -> &mut G {
313 let stack = self
314 .global_stacks_by_type
315 .entry(TypeId::of::<G>())
316 .or_default();
317 if stack.is_empty() {
318 stack.push(Box::new(G::default()));
319 }
320 stack.last_mut().unwrap().downcast_mut::<G>().unwrap()
321 }
322
323 pub(crate) fn push_global<T: Send + Sync + 'static>(&mut self, state: T) {
324 self.global_stacks_by_type
325 .entry(TypeId::of::<T>())
326 .or_default()
327 .push(Box::new(state));
328 }
329
330 pub(crate) fn pop_global<T: 'static>(&mut self) {
331 self.global_stacks_by_type
332 .get_mut(&TypeId::of::<T>())
333 .and_then(|stack| stack.pop())
334 .expect("state stack underflow");
335 }
336
337 pub(crate) fn push_text_style(&mut self, text_style: TextStyleRefinement) {
338 self.text_style_stack.push(text_style);
339 }
340
341 pub(crate) fn pop_text_style(&mut self) {
342 self.text_style_stack.pop();
343 }
344}
345
346impl Context for AppContext {
347 type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
348 type Result<T> = T;
349
350 fn entity<T: Send + Sync + 'static>(
351 &mut self,
352 build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
353 ) -> Handle<T> {
354 self.update(|cx| {
355 let slot = cx.entities.reserve();
356 let entity = build_entity(&mut ModelContext::mutable(cx, slot.id));
357 cx.entities.insert(slot, entity)
358 })
359 }
360
361 fn update_entity<T: Send + Sync + 'static, R>(
362 &mut self,
363 handle: &Handle<T>,
364 update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
365 ) -> R {
366 self.update(|cx| {
367 let mut entity = cx.entities.lease(handle);
368 let result = update(&mut entity, &mut ModelContext::mutable(cx, handle.id));
369 cx.entities.end_lease(entity);
370 result
371 })
372 }
373}
374
375impl MainThread<AppContext> {
376 fn update<R>(&mut self, update: impl FnOnce(&mut Self) -> R) -> R {
377 self.0.update(|cx| {
378 update(unsafe {
379 std::mem::transmute::<&mut AppContext, &mut MainThread<AppContext>>(cx)
380 })
381 })
382 }
383
384 pub(crate) fn update_window<R>(
385 &mut self,
386 id: WindowId,
387 update: impl FnOnce(&mut MainThread<WindowContext>) -> R,
388 ) -> Result<R> {
389 self.0.update_window(id, |cx| {
390 update(unsafe {
391 std::mem::transmute::<&mut WindowContext, &mut MainThread<WindowContext>>(cx)
392 })
393 })
394 }
395
396 pub(crate) fn platform(&self) -> &dyn Platform {
397 self.platform.borrow_on_main_thread()
398 }
399
400 pub fn activate(&mut self, ignoring_other_apps: bool) {
401 self.platform().activate(ignoring_other_apps);
402 }
403
404 pub fn open_window<S: 'static + Send + Sync>(
405 &mut self,
406 options: crate::WindowOptions,
407 build_root_view: impl FnOnce(&mut WindowContext) -> View<S> + Send + 'static,
408 ) -> WindowHandle<S> {
409 self.update(|cx| {
410 let id = cx.windows.insert(None);
411 let handle = WindowHandle::new(id);
412 let mut window = Window::new(handle.into(), options, cx);
413 let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
414 window.root_view.replace(root_view.into_any());
415 cx.windows.get_mut(id).unwrap().replace(window);
416 handle
417 })
418 }
419}
420
421pub(crate) enum Effect {
422 Notify {
423 emitter: EntityId,
424 },
425 Emit {
426 emitter: EntityId,
427 event: Box<dyn Any + Send + Sync + 'static>,
428 },
429}
430
431#[cfg(test)]
432mod tests {
433 use super::AppContext;
434
435 #[test]
436 fn test_app_context_send_sync() {
437 // This will not compile if `AppContext` does not implement `Send`
438 fn assert_send<T: Send>() {}
439 assert_send::<AppContext>();
440 }
441}