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