pub mod action; mod callback_collection; use crate::{ elements::ElementBox, executor::{self, Task}, geometry::rect::RectF, keymap::{self, Binding, Keystroke}, platform::{self, KeyDownEvent, Platform, PromptLevel, WindowOptions}, presenter::Presenter, util::post_inc, Appearance, AssetCache, AssetSource, ClipboardItem, FontCache, InputHandler, MouseButton, MouseRegionId, PathPromptOptions, TextLayoutCache, }; pub use action::*; use anyhow::{anyhow, Context, Result}; use callback_collection::CallbackCollection; use collections::{btree_map, hash_map::Entry, BTreeMap, HashMap, HashSet, VecDeque}; use keymap::MatchResult; use lazy_static::lazy_static; use parking_lot::Mutex; use platform::Event; use postage::oneshot; use smallvec::SmallVec; use smol::prelude::*; use std::{ any::{type_name, Any, TypeId}, cell::RefCell, fmt::{self, Debug}, hash::{Hash, Hasher}, marker::PhantomData, mem, ops::{Deref, DerefMut, Range}, path::{Path, PathBuf}, pin::Pin, rc::{self, Rc}, sync::{Arc, Weak}, time::Duration, }; use self::callback_collection::Mapping; pub trait Entity: 'static { type Event; fn release(&mut self, _: &mut MutableAppContext) {} fn app_will_quit( &mut self, _: &mut MutableAppContext, ) -> Option>>> { None } } pub trait View: Entity + Sized { fn ui_name() -> &'static str; fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox; fn on_focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext) {} fn on_focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext) {} fn keymap_context(&self, _: &AppContext) -> keymap::Context { Self::default_keymap_context() } fn default_keymap_context() -> keymap::Context { let mut cx = keymap::Context::default(); cx.set.insert(Self::ui_name().into()); cx } fn debug_json(&self, _: &AppContext) -> serde_json::Value { serde_json::Value::Null } fn text_for_range(&self, _: Range, _: &AppContext) -> Option { None } fn selected_text_range(&self, _: &AppContext) -> Option> { None } fn marked_text_range(&self, _: &AppContext) -> Option> { None } fn unmark_text(&mut self, _: &mut ViewContext) {} fn replace_text_in_range( &mut self, _: Option>, _: &str, _: &mut ViewContext, ) { } fn replace_and_mark_text_in_range( &mut self, _: Option>, _: &str, _: Option>, _: &mut ViewContext, ) { } } pub trait ReadModel { fn read_model(&self, handle: &ModelHandle) -> &T; } pub trait ReadModelWith { fn read_model_with( &self, handle: &ModelHandle, read: &mut dyn FnMut(&E, &AppContext) -> T, ) -> T; } pub trait UpdateModel { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, ) -> O; } pub trait UpgradeModelHandle { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option>; fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool; fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option; } pub trait UpgradeViewHandle { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option>; fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option; } pub trait ReadView { fn read_view(&self, handle: &ViewHandle) -> &T; } pub trait ReadViewWith { fn read_view_with( &self, handle: &ViewHandle, read: &mut dyn FnMut(&V, &AppContext) -> T, ) -> T where V: View; } pub trait UpdateView { fn update_view( &mut self, handle: &ViewHandle, update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, ) -> S where T: View; } pub struct Menu<'a> { pub name: &'a str, pub items: Vec>, } pub enum MenuItem<'a> { Separator, Submenu(Menu<'a>), Action { name: &'a str, action: Box, }, } #[derive(Clone)] pub struct App(Rc>); #[derive(Clone)] pub struct AsyncAppContext(Rc>); #[cfg(any(test, feature = "test-support"))] pub struct TestAppContext { cx: Rc>, foreground_platform: Rc, condition_duration: Option, pub function_name: String, } pub struct WindowInputHandler { app: Rc>, window_id: usize, } impl App { pub fn new(asset_source: impl AssetSource) -> Result { let platform = platform::current::platform(); let foreground_platform = platform::current::foreground_platform(); let foreground = Rc::new(executor::Foreground::platform(platform.dispatcher())?); let app = Self(Rc::new(RefCell::new(MutableAppContext::new( foreground, Arc::new(executor::Background::new()), platform.clone(), foreground_platform.clone(), Arc::new(FontCache::new(platform.fonts())), Default::default(), asset_source, )))); foreground_platform.on_quit(Box::new({ let cx = app.0.clone(); move || { cx.borrow_mut().quit(); } })); foreground_platform.on_will_open_menu(Box::new({ let cx = app.0.clone(); move || { let mut cx = cx.borrow_mut(); cx.keystroke_matcher.clear_pending(); } })); foreground_platform.on_validate_menu_command(Box::new({ let cx = app.0.clone(); move |action| { let cx = cx.borrow_mut(); !cx.keystroke_matcher.has_pending_keystrokes() && cx.is_action_available(action) } })); foreground_platform.on_menu_command(Box::new({ let cx = app.0.clone(); move |action| { let mut cx = cx.borrow_mut(); if let Some(key_window_id) = cx.cx.platform.key_window_id() { if let Some(view_id) = cx.focused_view_id(key_window_id) { cx.handle_dispatch_action_from_effect(key_window_id, Some(view_id), action); return; } } cx.dispatch_global_action_any(action); } })); app.0.borrow_mut().weak_self = Some(Rc::downgrade(&app.0)); Ok(app) } pub fn background(&self) -> Arc { self.0.borrow().background().clone() } pub fn on_become_active(self, mut callback: F) -> Self where F: 'static + FnMut(&mut MutableAppContext), { let cx = self.0.clone(); self.0 .borrow_mut() .foreground_platform .on_become_active(Box::new(move || callback(&mut *cx.borrow_mut()))); self } pub fn on_resign_active(self, mut callback: F) -> Self where F: 'static + FnMut(&mut MutableAppContext), { let cx = self.0.clone(); self.0 .borrow_mut() .foreground_platform .on_resign_active(Box::new(move || callback(&mut *cx.borrow_mut()))); self } pub fn on_quit(&mut self, mut callback: F) -> &mut Self where F: 'static + FnMut(&mut MutableAppContext), { let cx = self.0.clone(); self.0 .borrow_mut() .foreground_platform .on_quit(Box::new(move || callback(&mut *cx.borrow_mut()))); self } pub fn on_event(&mut self, mut callback: F) -> &mut Self where F: 'static + FnMut(Event, &mut MutableAppContext) -> bool, { let cx = self.0.clone(); self.0 .borrow_mut() .foreground_platform .on_event(Box::new(move |event| { callback(event, &mut *cx.borrow_mut()) })); self } pub fn on_open_urls(&mut self, mut callback: F) -> &mut Self where F: 'static + FnMut(Vec, &mut MutableAppContext), { let cx = self.0.clone(); self.0 .borrow_mut() .foreground_platform .on_open_urls(Box::new(move |paths| { callback(paths, &mut *cx.borrow_mut()) })); self } pub fn run(self, on_finish_launching: F) where F: 'static + FnOnce(&mut MutableAppContext), { let platform = self.0.borrow().foreground_platform.clone(); platform.run(Box::new(move || { let mut cx = self.0.borrow_mut(); let cx = &mut *cx; crate::views::init(cx); on_finish_launching(cx); })) } pub fn platform(&self) -> Arc { self.0.borrow().platform() } pub fn font_cache(&self) -> Arc { self.0.borrow().cx.font_cache.clone() } fn update T>(&mut self, callback: F) -> T { let mut state = self.0.borrow_mut(); let result = state.update(callback); state.pending_notifications.clear(); result } } impl WindowInputHandler { fn read_focused_view(&self, f: F) -> Option where F: FnOnce(&dyn AnyView, &AppContext) -> T, { // Input-related application hooks are sometimes called by the OS during // a call to a window-manipulation API, like prompting the user for file // paths. In that case, the AppContext will already be borrowed, so any // InputHandler methods need to fail gracefully. // // See https://github.com/zed-industries/feedback/issues/444 let app = self.app.try_borrow().ok()?; let view_id = app.focused_view_id(self.window_id)?; let view = app.cx.views.get(&(self.window_id, view_id))?; let result = f(view.as_ref(), &app); Some(result) } fn update_focused_view(&mut self, f: F) -> Option where F: FnOnce(usize, usize, &mut dyn AnyView, &mut MutableAppContext) -> T, { let mut app = self.app.try_borrow_mut().ok()?; app.update(|app| { let view_id = app.focused_view_id(self.window_id)?; let mut view = app.cx.views.remove(&(self.window_id, view_id))?; let result = f(self.window_id, view_id, view.as_mut(), &mut *app); app.cx.views.insert((self.window_id, view_id), view); Some(result) }) } } impl InputHandler for WindowInputHandler { fn text_for_range(&self, range: Range) -> Option { self.read_focused_view(|view, cx| view.text_for_range(range.clone(), cx)) .flatten() } fn selected_text_range(&self) -> Option> { self.read_focused_view(|view, cx| view.selected_text_range(cx)) .flatten() } fn replace_text_in_range(&mut self, range: Option>, text: &str) { self.update_focused_view(|window_id, view_id, view, cx| { view.replace_text_in_range(range, text, cx, window_id, view_id); }); } fn marked_text_range(&self) -> Option> { self.read_focused_view(|view, cx| view.marked_text_range(cx)) .flatten() } fn unmark_text(&mut self) { self.update_focused_view(|window_id, view_id, view, cx| { view.unmark_text(cx, window_id, view_id); }); } fn replace_and_mark_text_in_range( &mut self, range: Option>, new_text: &str, new_selected_range: Option>, ) { self.update_focused_view(|window_id, view_id, view, cx| { view.replace_and_mark_text_in_range( range, new_text, new_selected_range, cx, window_id, view_id, ); }); } fn rect_for_range(&self, range_utf16: Range) -> Option { let app = self.app.borrow(); let (presenter, _) = app.presenters_and_platform_windows.get(&self.window_id)?; let presenter = presenter.borrow(); presenter.rect_for_text_range(range_utf16, &app) } } #[cfg(any(test, feature = "test-support"))] impl TestAppContext { pub fn new( foreground_platform: Rc, platform: Arc, foreground: Rc, background: Arc, font_cache: Arc, leak_detector: Arc>, first_entity_id: usize, function_name: String, ) -> Self { let mut cx = MutableAppContext::new( foreground, background, platform, foreground_platform.clone(), font_cache, RefCounts { #[cfg(any(test, feature = "test-support"))] leak_detector, ..Default::default() }, (), ); cx.next_entity_id = first_entity_id; let cx = TestAppContext { cx: Rc::new(RefCell::new(cx)), foreground_platform, condition_duration: None, function_name, }; cx.cx.borrow_mut().weak_self = Some(Rc::downgrade(&cx.cx)); cx } pub fn dispatch_action(&self, window_id: usize, action: A) { let mut cx = self.cx.borrow_mut(); if let Some(view_id) = cx.focused_view_id(window_id) { cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action); } } pub fn dispatch_global_action(&self, action: A) { self.cx.borrow_mut().dispatch_global_action(action); } pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) { let handled = self.cx.borrow_mut().update(|cx| { let presenter = cx .presenters_and_platform_windows .get(&window_id) .unwrap() .0 .clone(); if cx.dispatch_keystroke(window_id, &keystroke) { return true; } if presenter.borrow_mut().dispatch_event( Event::KeyDown(KeyDownEvent { keystroke: keystroke.clone(), is_held, }), false, cx, ) { return true; } false }); if !handled && !keystroke.cmd && !keystroke.ctrl { WindowInputHandler { app: self.cx.clone(), window_id, } .replace_text_in_range(None, &keystroke.key) } } pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, F: FnOnce(&mut ModelContext) -> T, { self.cx.borrow_mut().add_model(build_model) } pub fn add_window(&mut self, build_root_view: F) -> (usize, ViewHandle) where T: View, F: FnOnce(&mut ViewContext) -> T, { let (window_id, view) = self .cx .borrow_mut() .add_window(Default::default(), build_root_view); self.simulate_window_activation(Some(window_id)); (window_id, view) } pub fn add_view( &mut self, parent_handle: impl Into, build_view: F, ) -> ViewHandle where T: View, F: FnOnce(&mut ViewContext) -> T, { self.cx.borrow_mut().add_view(parent_handle, build_view) } pub fn window_ids(&self) -> Vec { self.cx.borrow().window_ids().collect() } pub fn root_view(&self, window_id: usize) -> Option> { self.cx.borrow().root_view(window_id) } pub fn read T>(&self, callback: F) -> T { callback(self.cx.borrow().as_ref()) } pub fn update T>(&mut self, callback: F) -> T { let mut state = self.cx.borrow_mut(); // Don't increment pending flushes in order for effects to be flushed before the callback // completes, which is helpful in tests. let result = callback(&mut *state); // Flush effects after the callback just in case there are any. This can happen in edge // cases such as the closure dropping handles. state.flush_effects(); result } pub fn render(&mut self, handle: &ViewHandle, f: F) -> T where F: FnOnce(&mut V, &mut RenderContext) -> T, V: View, { handle.update(&mut *self.cx.borrow_mut(), |view, cx| { let mut render_cx = RenderContext { app: cx, window_id: handle.window_id(), view_id: handle.id(), view_type: PhantomData, titlebar_height: 0., hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, appearance: Appearance::Light, }; f(view, &mut render_cx) }) } pub fn to_async(&self) -> AsyncAppContext { AsyncAppContext(self.cx.clone()) } pub fn font_cache(&self) -> Arc { self.cx.borrow().cx.font_cache.clone() } pub fn foreground_platform(&self) -> Rc { self.foreground_platform.clone() } pub fn platform(&self) -> Arc { self.cx.borrow().cx.platform.clone() } pub fn foreground(&self) -> Rc { self.cx.borrow().foreground().clone() } pub fn background(&self) -> Arc { self.cx.borrow().background().clone() } pub fn spawn(&self, f: F) -> Task where F: FnOnce(AsyncAppContext) -> Fut, Fut: 'static + Future, T: 'static, { let foreground = self.foreground(); let future = f(self.to_async()); let cx = self.to_async(); foreground.spawn(async move { let result = future.await; cx.0.borrow_mut().flush_effects(); result }) } pub fn simulate_new_path_selection(&self, result: impl FnOnce(PathBuf) -> Option) { self.foreground_platform.simulate_new_path_selection(result); } pub fn did_prompt_for_new_path(&self) -> bool { self.foreground_platform.as_ref().did_prompt_for_new_path() } pub fn simulate_prompt_answer(&self, window_id: usize, answer: usize) { use postage::prelude::Sink as _; let mut done_tx = self .window_mut(window_id) .pending_prompts .borrow_mut() .pop_front() .expect("prompt was not called"); let _ = done_tx.try_send(answer); } pub fn has_pending_prompt(&self, window_id: usize) -> bool { let window = self.window_mut(window_id); let prompts = window.pending_prompts.borrow_mut(); !prompts.is_empty() } pub fn current_window_title(&self, window_id: usize) -> Option { self.window_mut(window_id).title.clone() } pub fn simulate_window_close(&self, window_id: usize) -> bool { let handler = self.window_mut(window_id).should_close_handler.take(); if let Some(mut handler) = handler { let should_close = handler(); self.window_mut(window_id).should_close_handler = Some(handler); should_close } else { false } } pub fn simulate_window_activation(&self, to_activate: Option) { let mut handlers = BTreeMap::new(); { let mut cx = self.cx.borrow_mut(); for (window_id, (_, window)) in &mut cx.presenters_and_platform_windows { let window = window .as_any_mut() .downcast_mut::() .unwrap(); handlers.insert( *window_id, mem::take(&mut window.active_status_change_handlers), ); } }; let mut handlers = handlers.into_iter().collect::>(); handlers.sort_unstable_by_key(|(window_id, _)| Some(*window_id) == to_activate); for (window_id, mut window_handlers) in handlers { for window_handler in &mut window_handlers { window_handler(Some(window_id) == to_activate); } self.window_mut(window_id) .active_status_change_handlers .extend(window_handlers); } } pub fn is_window_edited(&self, window_id: usize) -> bool { self.window_mut(window_id).edited } pub fn leak_detector(&self) -> Arc> { self.cx.borrow().leak_detector() } pub fn assert_dropped(&self, handle: impl WeakHandle) { self.cx .borrow() .leak_detector() .lock() .assert_dropped(handle.id()) } fn window_mut(&self, window_id: usize) -> std::cell::RefMut { std::cell::RefMut::map(self.cx.borrow_mut(), |state| { let (_, window) = state .presenters_and_platform_windows .get_mut(&window_id) .unwrap(); let test_window = window .as_any_mut() .downcast_mut::() .unwrap(); test_window }) } pub fn set_condition_duration(&mut self, duration: Option) { self.condition_duration = duration; } pub fn condition_duration(&self) -> Duration { self.condition_duration.unwrap_or_else(|| { if std::env::var("CI").is_ok() { Duration::from_secs(2) } else { Duration::from_millis(500) } }) } pub fn assert_clipboard_content(&mut self, expected_content: Option<&str>) { self.update(|cx| { let actual_content = cx.read_from_clipboard().map(|item| item.text().to_owned()); let expected_content = expected_content.map(|content| content.to_owned()); assert_eq!(actual_content, expected_content); }) } } impl AsyncAppContext { pub fn spawn(&self, f: F) -> Task where F: FnOnce(AsyncAppContext) -> Fut, Fut: 'static + Future, T: 'static, { self.0.borrow().foreground.spawn(f(self.clone())) } pub fn read T>(&self, callback: F) -> T { callback(self.0.borrow().as_ref()) } pub fn update T>(&mut self, callback: F) -> T { self.0.borrow_mut().update(callback) } pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, F: FnOnce(&mut ModelContext) -> T, { self.update(|cx| cx.add_model(build_model)) } pub fn add_window( &mut self, window_options: WindowOptions, build_root_view: F, ) -> (usize, ViewHandle) where T: View, F: FnOnce(&mut ViewContext) -> T, { self.update(|cx| cx.add_window(window_options, build_root_view)) } pub fn platform(&self) -> Arc { self.0.borrow().platform() } pub fn foreground(&self) -> Rc { self.0.borrow().foreground.clone() } pub fn background(&self) -> Arc { self.0.borrow().cx.background.clone() } } impl UpdateModel for AsyncAppContext { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut E, &mut ModelContext) -> O, ) -> O { self.0.borrow_mut().update_model(handle, update) } } impl UpgradeModelHandle for AsyncAppContext { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option> { self.0.borrow().upgrade_model_handle(handle) } fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool { self.0.borrow().model_handle_is_upgradable(handle) } fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option { self.0.borrow().upgrade_any_model_handle(handle) } } impl UpgradeViewHandle for AsyncAppContext { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { self.0.borrow_mut().upgrade_view_handle(handle) } fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { self.0.borrow_mut().upgrade_any_view_handle(handle) } } impl ReadModelWith for AsyncAppContext { fn read_model_with( &self, handle: &ModelHandle, read: &mut dyn FnMut(&E, &AppContext) -> T, ) -> T { let cx = self.0.borrow(); let cx = cx.as_ref(); read(handle.read(cx), cx) } } impl UpdateView for AsyncAppContext { fn update_view( &mut self, handle: &ViewHandle, update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, ) -> S where T: View, { self.0.borrow_mut().update_view(handle, update) } } impl ReadViewWith for AsyncAppContext { fn read_view_with( &self, handle: &ViewHandle, read: &mut dyn FnMut(&V, &AppContext) -> T, ) -> T where V: View, { let cx = self.0.borrow(); let cx = cx.as_ref(); read(handle.read(cx), cx) } } #[cfg(any(test, feature = "test-support"))] impl UpdateModel for TestAppContext { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, ) -> O { self.cx.borrow_mut().update_model(handle, update) } } #[cfg(any(test, feature = "test-support"))] impl ReadModelWith for TestAppContext { fn read_model_with( &self, handle: &ModelHandle, read: &mut dyn FnMut(&E, &AppContext) -> T, ) -> T { let cx = self.cx.borrow(); let cx = cx.as_ref(); read(handle.read(cx), cx) } } #[cfg(any(test, feature = "test-support"))] impl UpdateView for TestAppContext { fn update_view( &mut self, handle: &ViewHandle, update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, ) -> S where T: View, { self.cx.borrow_mut().update_view(handle, update) } } #[cfg(any(test, feature = "test-support"))] impl ReadViewWith for TestAppContext { fn read_view_with( &self, handle: &ViewHandle, read: &mut dyn FnMut(&V, &AppContext) -> T, ) -> T where V: View, { let cx = self.cx.borrow(); let cx = cx.as_ref(); read(handle.read(cx), cx) } } type ActionCallback = dyn FnMut(&mut dyn AnyView, &dyn Action, &mut MutableAppContext, usize, usize); type GlobalActionCallback = dyn FnMut(&dyn Action, &mut MutableAppContext); type SubscriptionCallback = Box bool>; type GlobalSubscriptionCallback = Box; type ObservationCallback = Box bool>; type FocusObservationCallback = Box bool>; type GlobalObservationCallback = Box; type ReleaseObservationCallback = Box; type ActionObservationCallback = Box; type WindowActivationCallback = Box bool>; type WindowFullscreenCallback = Box bool>; type DeserializeActionCallback = fn(json: &str) -> anyhow::Result>; type WindowShouldCloseSubscriptionCallback = Box bool>; pub struct MutableAppContext { weak_self: Option>>, foreground_platform: Rc, assets: Arc, cx: AppContext, action_deserializers: HashMap<&'static str, (TypeId, DeserializeActionCallback)>, capture_actions: HashMap>>>, actions: HashMap>>>, global_actions: HashMap>, keystroke_matcher: keymap::Matcher, next_entity_id: usize, next_window_id: usize, next_subscription_id: usize, frame_count: usize, focus_observations: CallbackCollection, global_subscriptions: CallbackCollection, global_observations: CallbackCollection, subscriptions: CallbackCollection, observations: CallbackCollection, window_activation_observations: CallbackCollection, window_fullscreen_observations: CallbackCollection, release_observations: Arc>>>, action_dispatch_observations: Arc>>, #[allow(clippy::type_complexity)] presenters_and_platform_windows: HashMap>, Box)>, foreground: Rc, pending_effects: VecDeque, pending_focus_index: Option, pending_notifications: HashSet, pending_global_notifications: HashSet, pending_flushes: usize, flushing_effects: bool, halt_action_dispatch: bool, } impl MutableAppContext { fn new( foreground: Rc, background: Arc, platform: Arc, foreground_platform: Rc, font_cache: Arc, ref_counts: RefCounts, asset_source: impl AssetSource, ) -> Self { Self { weak_self: None, foreground_platform, assets: Arc::new(AssetCache::new(asset_source)), cx: AppContext { models: Default::default(), views: Default::default(), parents: Default::default(), windows: Default::default(), globals: Default::default(), element_states: Default::default(), ref_counts: Arc::new(Mutex::new(ref_counts)), background, font_cache, platform, }, action_deserializers: Default::default(), capture_actions: Default::default(), actions: Default::default(), global_actions: Default::default(), keystroke_matcher: keymap::Matcher::default(), next_entity_id: 0, next_window_id: 0, next_subscription_id: 0, frame_count: 0, subscriptions: Default::default(), global_subscriptions: Default::default(), observations: Default::default(), focus_observations: Default::default(), release_observations: Default::default(), global_observations: Default::default(), window_activation_observations: Default::default(), window_fullscreen_observations: Default::default(), action_dispatch_observations: Default::default(), presenters_and_platform_windows: Default::default(), foreground, pending_effects: VecDeque::new(), pending_focus_index: None, pending_notifications: Default::default(), pending_global_notifications: Default::default(), pending_flushes: 0, flushing_effects: false, halt_action_dispatch: false, } } pub fn upgrade(&self) -> App { App(self.weak_self.as_ref().unwrap().upgrade().unwrap()) } pub fn quit(&mut self) { let mut futures = Vec::new(); for model_id in self.cx.models.keys().copied().collect::>() { let mut model = self.cx.models.remove(&model_id).unwrap(); futures.extend(model.app_will_quit(self)); self.cx.models.insert(model_id, model); } for view_id in self.cx.views.keys().copied().collect::>() { let mut view = self.cx.views.remove(&view_id).unwrap(); futures.extend(view.app_will_quit(self)); self.cx.views.insert(view_id, view); } self.remove_all_windows(); let futures = futures::future::join_all(futures); if self .background .block_with_timeout(Duration::from_millis(100), futures) .is_err() { log::error!("timed out waiting on app_will_quit"); } } pub fn remove_all_windows(&mut self) { for (window_id, _) in self.cx.windows.drain() { self.presenters_and_platform_windows.remove(&window_id); } self.flush_effects(); } pub fn platform(&self) -> Arc { self.cx.platform.clone() } pub fn font_cache(&self) -> &Arc { &self.cx.font_cache } pub fn foreground(&self) -> &Rc { &self.foreground } pub fn background(&self) -> &Arc { &self.cx.background } pub fn debug_elements(&self, window_id: usize) -> Option { self.presenters_and_platform_windows .get(&window_id) .and_then(|(presenter, _)| presenter.borrow().debug_elements(self)) } pub fn deserialize_action( &self, name: &str, argument: Option<&str>, ) -> Result> { let callback = self .action_deserializers .get(name) .ok_or_else(|| anyhow!("unknown action {}", name))? .1; callback(argument.unwrap_or("{}")) .with_context(|| format!("invalid data for action {}", name)) } pub fn add_action(&mut self, handler: F) where A: Action, V: View, F: 'static + FnMut(&mut V, &A, &mut ViewContext), { self.add_action_internal(handler, false) } pub fn capture_action(&mut self, handler: F) where A: Action, V: View, F: 'static + FnMut(&mut V, &A, &mut ViewContext), { self.add_action_internal(handler, true) } fn add_action_internal(&mut self, mut handler: F, capture: bool) where A: Action, V: View, F: 'static + FnMut(&mut V, &A, &mut ViewContext), { let handler = Box::new( move |view: &mut dyn AnyView, action: &dyn Action, cx: &mut MutableAppContext, window_id: usize, view_id: usize| { let action = action.as_any().downcast_ref().unwrap(); let mut cx = ViewContext::new(cx, window_id, view_id); handler( view.as_any_mut() .downcast_mut() .expect("downcast is type safe"), action, &mut cx, ); }, ); self.action_deserializers .entry(A::qualified_name()) .or_insert((TypeId::of::(), A::from_json_str)); let actions = if capture { &mut self.capture_actions } else { &mut self.actions }; actions .entry(TypeId::of::()) .or_default() .entry(TypeId::of::()) .or_default() .push(handler); } pub fn add_async_action(&mut self, mut handler: F) where A: Action, V: View, F: 'static + FnMut(&mut V, &A, &mut ViewContext) -> Option>>, { self.add_action(move |view, action, cx| { if let Some(task) = handler(view, action, cx) { task.detach_and_log_err(cx); } }) } pub fn add_global_action(&mut self, mut handler: F) where A: Action, F: 'static + FnMut(&A, &mut MutableAppContext), { let handler = Box::new(move |action: &dyn Action, cx: &mut MutableAppContext| { let action = action.as_any().downcast_ref().unwrap(); handler(action, cx); }); self.action_deserializers .entry(A::qualified_name()) .or_insert((TypeId::of::(), A::from_json_str)); if self .global_actions .insert(TypeId::of::(), handler) .is_some() { panic!( "registered multiple global handlers for {}", type_name::() ); } } pub fn window_ids(&self) -> impl Iterator + '_ { self.cx.windows.keys().cloned() } pub fn activate_window(&self, window_id: usize) { if let Some((_, window)) = self.presenters_and_platform_windows.get(&window_id) { window.activate() } } pub fn root_view(&self, window_id: usize) -> Option> { self.cx .windows .get(&window_id) .and_then(|window| window.root_view.clone().downcast::()) } pub fn window_is_active(&self, window_id: usize) -> bool { self.cx .windows .get(&window_id) .map_or(false, |window| window.is_active) } pub fn window_is_fullscreen(&self, window_id: usize) -> bool { self.cx .windows .get(&window_id) .map_or(false, |window| window.is_fullscreen) } pub fn window_bounds(&self, window_id: usize) -> RectF { self.presenters_and_platform_windows[&window_id].1.bounds() } pub fn render_view(&mut self, params: RenderParams) -> Result { let window_id = params.window_id; let view_id = params.view_id; let mut view = self .cx .views .remove(&(window_id, view_id)) .ok_or_else(|| anyhow!("view not found"))?; let element = view.render(params, self); self.cx.views.insert((window_id, view_id), view); Ok(element) } pub fn render_views( &mut self, window_id: usize, titlebar_height: f32, appearance: Appearance, ) -> HashMap { self.start_frame(); #[allow(clippy::needless_collect)] let view_ids = self .views .keys() .filter_map(|(win_id, view_id)| { if *win_id == window_id { Some(*view_id) } else { None } }) .collect::>(); view_ids .into_iter() .map(|view_id| { ( view_id, self.render_view(RenderParams { window_id, view_id, titlebar_height, hovered_region_ids: Default::default(), clicked_region_ids: None, refreshing: false, appearance, }) .unwrap(), ) }) .collect() } pub(crate) fn start_frame(&mut self) { self.frame_count += 1; } pub fn update T>(&mut self, callback: F) -> T { self.pending_flushes += 1; let result = callback(self); self.flush_effects(); result } pub fn set_menus(&mut self, menus: Vec) { self.foreground_platform .set_menus(menus, &self.keystroke_matcher); } fn show_character_palette(&self, window_id: usize) { let (_, window) = &self.presenters_and_platform_windows[&window_id]; window.show_character_palette(); } pub fn minimize_window(&self, window_id: usize) { let (_, window) = &self.presenters_and_platform_windows[&window_id]; window.minimize(); } pub fn zoom_window(&self, window_id: usize) { let (_, window) = &self.presenters_and_platform_windows[&window_id]; window.zoom(); } pub fn toggle_window_full_screen(&self, window_id: usize) { let (_, window) = &self.presenters_and_platform_windows[&window_id]; window.toggle_full_screen(); } fn prompt( &self, window_id: usize, level: PromptLevel, msg: &str, answers: &[&str], ) -> oneshot::Receiver { let (_, window) = &self.presenters_and_platform_windows[&window_id]; window.prompt(level, msg, answers) } pub fn prompt_for_paths( &self, options: PathPromptOptions, ) -> oneshot::Receiver>> { self.foreground_platform.prompt_for_paths(options) } pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { self.foreground_platform.prompt_for_new_path(directory) } pub fn emit_global(&mut self, payload: E) { self.pending_effects.push_back(Effect::GlobalEvent { payload: Box::new(payload), }); } pub fn subscribe(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnMut(H, &E::Event, &mut Self), { self.subscribe_internal(handle, move |handle, event, cx| { callback(handle, event, cx); true }) } pub fn subscribe_global(&mut self, mut callback: F) -> Subscription where E: Any, F: 'static + FnMut(&E, &mut Self), { let subscription_id = post_inc(&mut self.next_subscription_id); let type_id = TypeId::of::(); self.pending_effects.push_back(Effect::GlobalSubscription { type_id, subscription_id, callback: Box::new(move |payload, cx| { let payload = payload.downcast_ref().expect("downcast is type safe"); callback(payload, cx) }), }); Subscription::GlobalSubscription { id: subscription_id, type_id, subscriptions: Some(self.global_subscriptions.downgrade()), } } pub fn observe(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnMut(H, &mut Self), { self.observe_internal(handle, move |handle, cx| { callback(handle, cx); true }) } pub fn subscribe_internal(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnMut(H, &E::Event, &mut Self) -> bool, { let subscription_id = post_inc(&mut self.next_subscription_id); let emitter = handle.downgrade(); self.pending_effects.push_back(Effect::Subscription { entity_id: handle.id(), subscription_id, callback: Box::new(move |payload, cx| { if let Some(emitter) = H::upgrade_from(&emitter, cx.as_ref()) { let payload = payload.downcast_ref().expect("downcast is type safe"); callback(emitter, payload, cx) } else { false } }), }); Subscription::Subscription { id: subscription_id, entity_id: handle.id(), subscriptions: Some(self.subscriptions.downgrade()), } } fn observe_internal(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnMut(H, &mut Self) -> bool, { let subscription_id = post_inc(&mut self.next_subscription_id); let observed = handle.downgrade(); let entity_id = handle.id(); self.pending_effects.push_back(Effect::Observation { entity_id, subscription_id, callback: Box::new(move |cx| { if let Some(observed) = H::upgrade_from(&observed, cx) { callback(observed, cx) } else { false } }), }); Subscription::Observation { id: subscription_id, entity_id, observations: Some(self.observations.downgrade()), } } fn observe_focus(&mut self, handle: &ViewHandle, mut callback: F) -> Subscription where F: 'static + FnMut(ViewHandle, bool, &mut MutableAppContext) -> bool, V: View, { let subscription_id = post_inc(&mut self.next_subscription_id); let observed = handle.downgrade(); let view_id = handle.id(); self.pending_effects.push_back(Effect::FocusObservation { view_id, subscription_id, callback: Box::new(move |focused, cx| { if let Some(observed) = observed.upgrade(cx) { callback(observed, focused, cx) } else { false } }), }); Subscription::FocusObservation { id: subscription_id, view_id, observations: Some(self.focus_observations.downgrade()), } } pub fn observe_global(&mut self, mut observe: F) -> Subscription where G: Any, F: 'static + FnMut(&mut MutableAppContext), { let type_id = TypeId::of::(); let id = post_inc(&mut self.next_subscription_id); self.global_observations.add_callback( type_id, id, Box::new(move |cx: &mut MutableAppContext| observe(cx)), ); Subscription::GlobalObservation { id, type_id, observations: Some(self.global_observations.downgrade()), } } pub fn observe_release(&mut self, handle: &H, callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnOnce(&E, &mut Self), { let id = post_inc(&mut self.next_subscription_id); self.release_observations .lock() .entry(handle.id()) .or_default() .insert( id, Box::new(move |released, cx| { let released = released.downcast_ref().unwrap(); callback(released, cx) }), ); Subscription::ReleaseObservation { id, entity_id: handle.id(), observations: Some(Arc::downgrade(&self.release_observations)), } } pub fn observe_actions(&mut self, callback: F) -> Subscription where F: 'static + FnMut(TypeId, &mut MutableAppContext), { let id = post_inc(&mut self.next_subscription_id); self.action_dispatch_observations .lock() .insert(id, Box::new(callback)); Subscription::ActionObservation { id, observations: Some(Arc::downgrade(&self.action_dispatch_observations)), } } fn observe_window_activation(&mut self, window_id: usize, callback: F) -> Subscription where F: 'static + FnMut(bool, &mut MutableAppContext) -> bool, { let subscription_id = post_inc(&mut self.next_subscription_id); self.pending_effects .push_back(Effect::WindowActivationObservation { window_id, subscription_id, callback: Box::new(callback), }); Subscription::WindowActivationObservation { id: subscription_id, window_id, observations: Some(self.window_activation_observations.downgrade()), } } fn observe_fullscreen(&mut self, window_id: usize, callback: F) -> Subscription where F: 'static + FnMut(bool, &mut MutableAppContext) -> bool, { let subscription_id = post_inc(&mut self.next_subscription_id); self.pending_effects .push_back(Effect::WindowFullscreenObservation { window_id, subscription_id, callback: Box::new(callback), }); Subscription::WindowFullscreenObservation { id: subscription_id, window_id, observations: Some(self.window_activation_observations.downgrade()), } } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut MutableAppContext)) { self.pending_effects.push_back(Effect::Deferred { callback: Box::new(callback), after_window_update: false, }) } pub fn after_window_update(&mut self, callback: impl 'static + FnOnce(&mut MutableAppContext)) { self.pending_effects.push_back(Effect::Deferred { callback: Box::new(callback), after_window_update: true, }) } pub(crate) fn notify_model(&mut self, model_id: usize) { if self.pending_notifications.insert(model_id) { self.pending_effects .push_back(Effect::ModelNotification { model_id }); } } pub(crate) fn notify_view(&mut self, window_id: usize, view_id: usize) { if self.pending_notifications.insert(view_id) { self.pending_effects .push_back(Effect::ViewNotification { window_id, view_id }); } } pub(crate) fn notify_global(&mut self, type_id: TypeId) { if self.pending_global_notifications.insert(type_id) { self.pending_effects .push_back(Effect::GlobalNotification { type_id }); } } pub(crate) fn name_for_view(&self, window_id: usize, view_id: usize) -> Option<&str> { self.views .get(&(window_id, view_id)) .map(|view| view.ui_name()) } pub fn all_action_names<'a>(&'a self) -> impl Iterator + 'a { self.action_deserializers.keys().copied() } pub fn available_actions( &self, window_id: usize, view_id: usize, ) -> impl Iterator, SmallVec<[&Binding; 1]>)> { let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect(); for view_id in self.parents(window_id, view_id) { if let Some(view) = self.views.get(&(window_id, view_id)) { let view_type = view.as_any().type_id(); if let Some(actions) = self.actions.get(&view_type) { action_types.extend(actions.keys().copied()); } } } self.action_deserializers .iter() .filter_map(move |(name, (type_id, deserialize))| { if action_types.contains(type_id) { Some(( *name, deserialize("{}").ok()?, self.keystroke_matcher .bindings_for_action_type(*type_id) .collect(), )) } else { None } }) } pub fn is_action_available(&self, action: &dyn Action) -> bool { let action_type = action.as_any().type_id(); if let Some(window_id) = self.cx.platform.key_window_id() { if let Some(focused_view_id) = self.focused_view_id(window_id) { for view_id in self.parents(window_id, focused_view_id) { if let Some(view) = self.views.get(&(window_id, view_id)) { let view_type = view.as_any().type_id(); if let Some(actions) = self.actions.get(&view_type) { if actions.contains_key(&action_type) { return true; } } } } } } self.global_actions.contains_key(&action_type) } /// Return keystrokes that would dispatch the given action closest to the focused view, if there are any. pub(crate) fn keystrokes_for_action( &self, window_id: usize, dispatch_path: &[usize], action: &dyn Action, ) -> Option> { for view_id in dispatch_path.iter().rev() { let view = self .cx .views .get(&(window_id, *view_id)) .expect("view in responder chain does not exist"); let cx = view.keymap_context(self.as_ref()); let keystrokes = self.keystroke_matcher.keystrokes_for_action(action, &cx); if keystrokes.is_some() { return keystrokes; } } None } // Traverses the parent tree. Walks down the tree toward the passed // view calling visit with true. Then walks back up the tree calling visit with false. // If `visit` returns false this function will immediately return. // Returns a bool indicating if the traversal was completed early. fn visit_dispatch_path( &mut self, window_id: usize, view_id: usize, mut visit: impl FnMut(usize, bool, &mut MutableAppContext) -> bool, ) -> bool { // List of view ids from the leaf to the root of the window let path = self.parents(window_id, view_id).collect::>(); // Walk down from the root to the leaf calling visit with capture_phase = true for view_id in path.iter().rev() { if !visit(*view_id, true, self) { return false; } } // Walk up from the leaf to the root calling visit with capture_phase = false for view_id in path.iter() { if !visit(*view_id, false, self) { return false; } } true } // Returns an iterator over all of the view ids from the passed view up to the root of the window // Includes the passed view itself fn parents(&self, window_id: usize, mut view_id: usize) -> impl Iterator + '_ { std::iter::once(view_id) .into_iter() .chain(std::iter::from_fn(move || { if let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, view_id)) { view_id = *parent_id; Some(view_id) } else { None } })) } fn actions_mut( &mut self, capture_phase: bool, ) -> &mut HashMap>>> { if capture_phase { &mut self.capture_actions } else { &mut self.actions } } pub fn dispatch_global_action(&mut self, action: A) { self.dispatch_global_action_any(&action); } fn dispatch_global_action_any(&mut self, action: &dyn Action) -> bool { self.update(|this| { if let Some((name, mut handler)) = this.global_actions.remove_entry(&action.id()) { handler(action, this); this.global_actions.insert(name, handler); true } else { false } }) } pub fn add_bindings>(&mut self, bindings: T) { self.keystroke_matcher.add_bindings(bindings); } pub fn clear_bindings(&mut self) { self.keystroke_matcher.clear_bindings(); } pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool { let mut pending = false; if let Some(focused_view_id) = self.focused_view_id(window_id) { for view_id in self.parents(window_id, focused_view_id).collect::>() { let keymap_context = self .cx .views .get(&(window_id, view_id)) .unwrap() .keymap_context(self.as_ref()); match self.keystroke_matcher.push_keystroke( keystroke.clone(), view_id, &keymap_context, ) { MatchResult::None => {} MatchResult::Pending => pending = true, MatchResult::Action(action) => { if self.handle_dispatch_action_from_effect( window_id, Some(view_id), action.as_ref(), ) { self.keystroke_matcher.clear_pending(); return true; } } } } } pending } pub fn default_global(&mut self) -> &T { let type_id = TypeId::of::(); self.update(|this| { if let Entry::Vacant(entry) = this.cx.globals.entry(type_id) { entry.insert(Box::new(T::default())); this.notify_global(type_id); } }); self.globals.get(&type_id).unwrap().downcast_ref().unwrap() } pub fn set_global(&mut self, state: T) { self.update(|this| { let type_id = TypeId::of::(); this.cx.globals.insert(type_id, Box::new(state)); this.notify_global(type_id); }); } pub fn update_default_global(&mut self, update: F) -> U where T: 'static + Default, F: FnOnce(&mut T, &mut MutableAppContext) -> U, { self.update(|this| { let type_id = TypeId::of::(); let mut state = this .cx .globals .remove(&type_id) .unwrap_or_else(|| Box::new(T::default())); let result = update(state.downcast_mut().unwrap(), this); this.cx.globals.insert(type_id, state); this.notify_global(type_id); result }) } pub fn update_global(&mut self, update: F) -> U where T: 'static, F: FnOnce(&mut T, &mut MutableAppContext) -> U, { self.update(|this| { let type_id = TypeId::of::(); if let Some(mut state) = this.cx.globals.remove(&type_id) { let result = update(state.downcast_mut().unwrap(), this); this.cx.globals.insert(type_id, state); this.notify_global(type_id); result } else { panic!("No global added for {}", std::any::type_name::()); } }) } pub fn add_model(&mut self, build_model: F) -> ModelHandle where T: Entity, F: FnOnce(&mut ModelContext) -> T, { self.update(|this| { let model_id = post_inc(&mut this.next_entity_id); let handle = ModelHandle::new(model_id, &this.cx.ref_counts); let mut cx = ModelContext::new(this, model_id); let model = build_model(&mut cx); this.cx.models.insert(model_id, Box::new(model)); handle }) } pub fn add_window( &mut self, window_options: WindowOptions, build_root_view: F, ) -> (usize, ViewHandle) where T: View, F: FnOnce(&mut ViewContext) -> T, { self.update(|this| { let window_id = post_inc(&mut this.next_window_id); let root_view = this .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) .unwrap(); this.cx.windows.insert( window_id, Window { root_view: root_view.clone().into(), focused_view_id: Some(root_view.id()), is_active: false, invalidation: None, is_fullscreen: false, }, ); root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx)); let window = this.cx .platform .open_window(window_id, window_options, this.foreground.clone()); this.register_platform_window(window_id, window); (window_id, root_view) }) } pub fn add_status_bar_item(&mut self, build_root_view: F) -> (usize, ViewHandle) where T: View, F: FnOnce(&mut ViewContext) -> T, { self.update(|this| { let window_id = post_inc(&mut this.next_window_id); let root_view = this .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) .unwrap(); this.cx.windows.insert( window_id, Window { root_view: root_view.clone().into(), focused_view_id: Some(root_view.id()), is_active: false, invalidation: None, is_fullscreen: false, }, ); root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx)); let status_item = this.cx.platform.add_status_item(); this.register_platform_window(window_id, status_item); (window_id, root_view) }) } fn register_platform_window( &mut self, window_id: usize, mut window: Box, ) { let presenter = Rc::new(RefCell::new(self.build_presenter( window_id, window.titlebar_height(), window.appearance(), ))); { let mut app = self.upgrade(); let presenter = Rc::downgrade(&presenter); window.on_event(Box::new(move |event| { app.update(|cx| { if let Some(presenter) = presenter.upgrade() { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { if cx.dispatch_keystroke(window_id, keystroke) { return true; } } presenter.borrow_mut().dispatch_event(event, false, cx) } else { false } }) })); } { let mut app = self.upgrade(); window.on_active_status_change(Box::new(move |is_active| { app.update(|cx| cx.window_changed_active_status(window_id, is_active)) })); } { let mut app = self.upgrade(); window.on_resize(Box::new(move || { app.update(|cx| cx.window_was_resized(window_id)) })); } { let mut app = self.upgrade(); window.on_fullscreen(Box::new(move |is_fullscreen| { app.update(|cx| cx.window_was_fullscreen_changed(window_id, is_fullscreen)) })); } { let mut app = self.upgrade(); window.on_close(Box::new(move || { app.update(|cx| cx.remove_window(window_id)); })); } { let mut app = self.upgrade(); window.on_appearance_changed(Box::new(move || app.update(|cx| cx.refresh_windows()))); } window.set_input_handler(Box::new(WindowInputHandler { app: self.upgrade().0, window_id, })); let scene = presenter.borrow_mut().build_scene( window.content_size(), window.scale_factor(), false, self, ); window.present_scene(scene); self.presenters_and_platform_windows .insert(window_id, (presenter.clone(), window)); } pub fn replace_root_view(&mut self, window_id: usize, build_root_view: F) -> ViewHandle where T: View, F: FnOnce(&mut ViewContext) -> T, { self.update(|this| { let root_view = this .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) .unwrap(); let window = this.cx.windows.get_mut(&window_id).unwrap(); window.root_view = root_view.clone().into(); window.focused_view_id = Some(root_view.id()); root_view }) } pub fn remove_window(&mut self, window_id: usize) { self.cx.windows.remove(&window_id); self.presenters_and_platform_windows.remove(&window_id); self.flush_effects(); } pub fn build_presenter( &mut self, window_id: usize, titlebar_height: f32, appearance: Appearance, ) -> Presenter { Presenter::new( window_id, titlebar_height, appearance, self.cx.font_cache.clone(), TextLayoutCache::new(self.cx.platform.fonts()), self.assets.clone(), self, ) } pub fn add_view( &mut self, parent_handle: impl Into, build_view: F, ) -> ViewHandle where T: View, F: FnOnce(&mut ViewContext) -> T, { let parent_handle = parent_handle.into(); self.build_and_insert_view( parent_handle.window_id, ParentId::View(parent_handle.view_id), |cx| Some(build_view(cx)), ) .unwrap() } pub fn add_option_view( &mut self, parent_handle: impl Into, build_view: F, ) -> Option> where T: View, F: FnOnce(&mut ViewContext) -> Option, { let parent_handle = parent_handle.into(); self.build_and_insert_view( parent_handle.window_id, ParentId::View(parent_handle.view_id), build_view, ) } pub(crate) fn build_and_insert_view( &mut self, window_id: usize, parent_id: ParentId, build_view: F, ) -> Option> where T: View, F: FnOnce(&mut ViewContext) -> Option, { self.update(|this| { let view_id = post_inc(&mut this.next_entity_id); let mut cx = ViewContext::new(this, window_id, view_id); let handle = if let Some(view) = build_view(&mut cx) { this.cx.views.insert((window_id, view_id), Box::new(view)); this.cx.parents.insert((window_id, view_id), parent_id); if let Some(window) = this.cx.windows.get_mut(&window_id) { window .invalidation .get_or_insert_with(Default::default) .updated .insert(view_id); } Some(ViewHandle::new(window_id, view_id, &this.cx.ref_counts)) } else { None }; handle }) } fn remove_dropped_entities(&mut self) { loop { let (dropped_models, dropped_views, dropped_element_states) = self.cx.ref_counts.lock().take_dropped(); if dropped_models.is_empty() && dropped_views.is_empty() && dropped_element_states.is_empty() { break; } for model_id in dropped_models { self.subscriptions.remove(model_id); self.observations.remove(model_id); let mut model = self.cx.models.remove(&model_id).unwrap(); model.release(self); self.pending_effects .push_back(Effect::ModelRelease { model_id, model }); } for (window_id, view_id) in dropped_views { self.subscriptions.remove(view_id); self.observations.remove(view_id); let mut view = self.cx.views.remove(&(window_id, view_id)).unwrap(); view.release(self); let change_focus_to = self.cx.windows.get_mut(&window_id).and_then(|window| { window .invalidation .get_or_insert_with(Default::default) .removed .push(view_id); if window.focused_view_id == Some(view_id) { Some(window.root_view.id()) } else { None } }); self.cx.parents.remove(&(window_id, view_id)); if let Some(view_id) = change_focus_to { self.handle_focus_effect(window_id, Some(view_id)); } self.pending_effects .push_back(Effect::ViewRelease { view_id, view }); } for key in dropped_element_states { self.cx.element_states.remove(&key); } } } fn flush_effects(&mut self) { self.pending_flushes = self.pending_flushes.saturating_sub(1); let mut after_window_update_callbacks = Vec::new(); if !self.flushing_effects && self.pending_flushes == 0 { self.flushing_effects = true; let mut refreshing = false; loop { if let Some(effect) = self.pending_effects.pop_front() { if let Some(pending_focus_index) = self.pending_focus_index.as_mut() { *pending_focus_index = pending_focus_index.saturating_sub(1); } match effect { Effect::Subscription { entity_id, subscription_id, callback, } => self.subscriptions.add_or_remove_callback( entity_id, subscription_id, callback, ), Effect::Event { entity_id, payload } => { let mut subscriptions = self.subscriptions.clone(); subscriptions.emit_and_cleanup(entity_id, self, |callback, this| { callback(payload.as_ref(), this) }) } Effect::GlobalSubscription { type_id, subscription_id, callback, } => self.global_subscriptions.add_or_remove_callback( type_id, subscription_id, callback, ), Effect::GlobalEvent { payload } => self.emit_global_event(payload), Effect::Observation { entity_id, subscription_id, callback, } => self.observations.add_or_remove_callback( entity_id, subscription_id, callback, ), Effect::ModelNotification { model_id } => { let mut observations = self.observations.clone(); observations .emit_and_cleanup(model_id, self, |callback, this| callback(this)); } Effect::ViewNotification { window_id, view_id } => { self.handle_view_notification_effect(window_id, view_id) } Effect::GlobalNotification { type_id } => { let mut subscriptions = self.global_observations.clone(); subscriptions.emit_and_cleanup(type_id, self, |callback, this| { callback(this); true }); } Effect::Deferred { callback, after_window_update, } => { if after_window_update { after_window_update_callbacks.push(callback); } else { callback(self) } } Effect::ModelRelease { model_id, model } => { self.handle_entity_release_effect(model_id, model.as_any()) } Effect::ViewRelease { view_id, view } => { self.handle_entity_release_effect(view_id, view.as_any()) } Effect::Focus { window_id, view_id } => { self.handle_focus_effect(window_id, view_id); } Effect::FocusObservation { view_id, subscription_id, callback, } => { self.focus_observations.add_or_remove_callback( view_id, subscription_id, callback, ); } Effect::ResizeWindow { window_id } => { if let Some(window) = self.cx.windows.get_mut(&window_id) { window .invalidation .get_or_insert(WindowInvalidation::default()); } } Effect::WindowActivationObservation { window_id, subscription_id, callback, } => self.window_activation_observations.add_or_remove_callback( window_id, subscription_id, callback, ), Effect::ActivateWindow { window_id, is_active, } => self.handle_window_activation_effect(window_id, is_active), Effect::WindowFullscreenObservation { window_id, subscription_id, callback, } => self.window_fullscreen_observations.add_or_remove_callback( window_id, subscription_id, callback, ), Effect::FullscreenWindow { window_id, is_fullscreen, } => self.handle_fullscreen_effect(window_id, is_fullscreen), Effect::RefreshWindows => { refreshing = true; } Effect::DispatchActionFrom { window_id, view_id, action, } => { self.handle_dispatch_action_from_effect( window_id, Some(view_id), action.as_ref(), ); } Effect::ActionDispatchNotification { action_id } => { self.handle_action_dispatch_notification_effect(action_id) } Effect::WindowShouldCloseSubscription { window_id, callback, } => { self.handle_window_should_close_subscription_effect(window_id, callback) } } self.pending_notifications.clear(); self.remove_dropped_entities(); } else { self.remove_dropped_entities(); if refreshing { self.perform_window_refresh(); } else { self.update_windows(); } if self.pending_effects.is_empty() { for callback in after_window_update_callbacks.drain(..) { callback(self); } if self.pending_effects.is_empty() { self.flushing_effects = false; self.pending_notifications.clear(); self.pending_global_notifications.clear(); break; } } refreshing = false; } } } } fn update_windows(&mut self) { let mut invalidations: HashMap<_, _> = Default::default(); for (window_id, window) in &mut self.cx.windows { if let Some(invalidation) = window.invalidation.take() { invalidations.insert(*window_id, invalidation); } } for (window_id, mut invalidation) in invalidations { if let Some((presenter, mut window)) = self.presenters_and_platform_windows.remove(&window_id) { { let mut presenter = presenter.borrow_mut(); presenter.invalidate(&mut invalidation, window.appearance(), self); let scene = presenter.build_scene( window.content_size(), window.scale_factor(), false, self, ); window.present_scene(scene); } self.presenters_and_platform_windows .insert(window_id, (presenter, window)); } } } fn window_was_resized(&mut self, window_id: usize) { self.pending_effects .push_back(Effect::ResizeWindow { window_id }); } fn window_was_fullscreen_changed(&mut self, window_id: usize, is_fullscreen: bool) { self.pending_effects.push_back(Effect::FullscreenWindow { window_id, is_fullscreen, }); } fn window_changed_active_status(&mut self, window_id: usize, is_active: bool) { self.pending_effects.push_back(Effect::ActivateWindow { window_id, is_active, }); } pub fn refresh_windows(&mut self) { self.pending_effects.push_back(Effect::RefreshWindows); } pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: impl Action) { self.dispatch_any_action_at(window_id, view_id, Box::new(action)); } pub fn dispatch_any_action_at( &mut self, window_id: usize, view_id: usize, action: Box, ) { self.pending_effects.push_back(Effect::DispatchActionFrom { window_id, view_id, action, }); } fn perform_window_refresh(&mut self) { let mut presenters = mem::take(&mut self.presenters_and_platform_windows); for (window_id, (presenter, window)) in &mut presenters { let mut invalidation = self .cx .windows .get_mut(window_id) .unwrap() .invalidation .take(); let mut presenter = presenter.borrow_mut(); presenter.refresh( invalidation.as_mut().unwrap_or(&mut Default::default()), window.appearance(), self, ); let scene = presenter.build_scene(window.content_size(), window.scale_factor(), true, self); window.present_scene(scene); } self.presenters_and_platform_windows = presenters; } fn emit_global_event(&mut self, payload: Box) { let type_id = (&*payload).type_id(); let mut subscriptions = self.global_subscriptions.clone(); subscriptions.emit_and_cleanup(type_id, self, |callback, this| { callback(payload.as_ref(), this); true //Always alive }); } fn handle_view_notification_effect( &mut self, observed_window_id: usize, observed_view_id: usize, ) { if self .cx .views .contains_key(&(observed_window_id, observed_view_id)) { if let Some(window) = self.cx.windows.get_mut(&observed_window_id) { window .invalidation .get_or_insert_with(Default::default) .updated .insert(observed_view_id); } let mut observations = self.observations.clone(); observations.emit_and_cleanup(observed_view_id, self, |callback, this| callback(this)); } } fn handle_entity_release_effect(&mut self, entity_id: usize, entity: &dyn Any) { let callbacks = self.release_observations.lock().remove(&entity_id); if let Some(callbacks) = callbacks { for (_, callback) in callbacks { callback(entity, self); } } } fn handle_fullscreen_effect(&mut self, window_id: usize, is_fullscreen: bool) { //Short circuit evaluation if we're already g2g if self .cx .windows .get(&window_id) .map(|w| w.is_fullscreen == is_fullscreen) .unwrap_or(false) { return; } self.update(|this| { let window = this.cx.windows.get_mut(&window_id)?; window.is_fullscreen = is_fullscreen; let mut observations = this.window_fullscreen_observations.clone(); observations.emit_and_cleanup(window_id, this, |callback, this| { callback(is_fullscreen, this) }); Some(()) }); } fn handle_window_activation_effect(&mut self, window_id: usize, active: bool) { //Short circuit evaluation if we're already g2g if self .cx .windows .get(&window_id) .map(|w| w.is_active == active) .unwrap_or(false) { return; } self.update(|this| { let window = this.cx.windows.get_mut(&window_id)?; window.is_active = active; //Handle focus let focused_id = window.focused_view_id?; for view_id in this.parents(window_id, focused_id).collect::>() { if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { if active { view.on_focus_in(this, window_id, view_id, focused_id); } else { view.on_focus_out(this, window_id, view_id, focused_id); } this.cx.views.insert((window_id, view_id), view); } } let mut observations = this.window_activation_observations.clone(); observations.emit_and_cleanup(window_id, this, |callback, this| callback(active, this)); Some(()) }); } fn handle_focus_effect(&mut self, window_id: usize, focused_id: Option) { self.pending_focus_index.take(); if self .cx .windows .get(&window_id) .map(|w| w.focused_view_id) .map_or(false, |cur_focused| cur_focused == focused_id) { return; } self.update(|this| { let blurred_id = this.cx.windows.get_mut(&window_id).and_then(|window| { let blurred_id = window.focused_view_id; window.focused_view_id = focused_id; blurred_id }); let blurred_parents = blurred_id .map(|blurred_id| this.parents(window_id, blurred_id).collect::>()) .unwrap_or_default(); let focused_parents = focused_id .map(|focused_id| this.parents(window_id, focused_id).collect::>()) .unwrap_or_default(); if let Some(blurred_id) = blurred_id { for view_id in blurred_parents.iter().copied() { if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { view.on_focus_out(this, window_id, view_id, blurred_id); this.cx.views.insert((window_id, view_id), view); } } let mut subscriptions = this.focus_observations.clone(); subscriptions .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this)); } if let Some(focused_id) = focused_id { for view_id in focused_parents { if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { view.on_focus_in(this, window_id, view_id, focused_id); this.cx.views.insert((window_id, view_id), view); } } let mut subscriptions = this.focus_observations.clone(); subscriptions .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this)); } }) } fn handle_dispatch_action_from_effect( &mut self, window_id: usize, view_id: Option, action: &dyn Action, ) -> bool { self.update(|this| { if let Some(view_id) = view_id { this.halt_action_dispatch = false; this.visit_dispatch_path(window_id, view_id, |view_id, capture_phase, this| { if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { let type_id = view.as_any().type_id(); if let Some((name, mut handlers)) = this .actions_mut(capture_phase) .get_mut(&type_id) .and_then(|h| h.remove_entry(&action.id())) { for handler in handlers.iter_mut().rev() { this.halt_action_dispatch = true; handler(view.as_mut(), action, this, window_id, view_id); if this.halt_action_dispatch { break; } } this.actions_mut(capture_phase) .get_mut(&type_id) .unwrap() .insert(name, handlers); } this.cx.views.insert((window_id, view_id), view); } !this.halt_action_dispatch }); } if !this.halt_action_dispatch { this.halt_action_dispatch = this.dispatch_global_action_any(action); } this.pending_effects .push_back(Effect::ActionDispatchNotification { action_id: action.id(), }); this.halt_action_dispatch }) } fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) { let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock()); for callback in callbacks.values_mut() { callback(action_id, self); } self.action_dispatch_observations.lock().extend(callbacks); } fn handle_window_should_close_subscription_effect( &mut self, window_id: usize, mut callback: WindowShouldCloseSubscriptionCallback, ) { let mut app = self.upgrade(); if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) { window.on_should_close(Box::new(move || app.update(|cx| callback(cx)))) } } pub fn focus(&mut self, window_id: usize, view_id: Option) { if let Some(pending_focus_index) = self.pending_focus_index { self.pending_effects.remove(pending_focus_index); } self.pending_focus_index = Some(self.pending_effects.len()); self.pending_effects .push_back(Effect::Focus { window_id, view_id }); } pub fn spawn(&self, f: F) -> Task where F: FnOnce(AsyncAppContext) -> Fut, Fut: 'static + Future, T: 'static, { let future = f(self.to_async()); let cx = self.to_async(); self.foreground.spawn(async move { let result = future.await; cx.0.borrow_mut().flush_effects(); result }) } pub fn to_async(&self) -> AsyncAppContext { AsyncAppContext(self.weak_self.as_ref().unwrap().upgrade().unwrap()) } pub fn write_to_clipboard(&self, item: ClipboardItem) { self.cx.platform.write_to_clipboard(item); } pub fn read_from_clipboard(&self) -> Option { self.cx.platform.read_from_clipboard() } #[cfg(any(test, feature = "test-support"))] pub fn leak_detector(&self) -> Arc> { self.cx.ref_counts.lock().leak_detector.clone() } } impl ReadModel for MutableAppContext { fn read_model(&self, handle: &ModelHandle) -> &T { if let Some(model) = self.cx.models.get(&handle.model_id) { model .as_any() .downcast_ref() .expect("downcast is type safe") } else { panic!("circular model reference"); } } } impl UpdateModel for MutableAppContext { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> V, ) -> V { if let Some(mut model) = self.cx.models.remove(&handle.model_id) { self.update(|this| { let mut cx = ModelContext::new(this, handle.model_id); let result = update( model .as_any_mut() .downcast_mut() .expect("downcast is type safe"), &mut cx, ); this.cx.models.insert(handle.model_id, model); result }) } else { panic!("circular model update"); } } } impl UpgradeModelHandle for MutableAppContext { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool { self.cx.model_handle_is_upgradable(handle) } fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option { self.cx.upgrade_any_model_handle(handle) } } impl UpgradeViewHandle for MutableAppContext { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { self.cx.upgrade_view_handle(handle) } fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { self.cx.upgrade_any_view_handle(handle) } } impl ReadView for MutableAppContext { fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(view) = self.cx.views.get(&(handle.window_id, handle.view_id)) { view.as_any().downcast_ref().expect("downcast is type safe") } else { panic!("circular view reference for type {}", type_name::()); } } } impl UpdateView for MutableAppContext { fn update_view( &mut self, handle: &ViewHandle, update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, ) -> S where T: View, { self.update(|this| { let mut view = this .cx .views .remove(&(handle.window_id, handle.view_id)) .expect("circular view update"); let mut cx = ViewContext::new(this, handle.window_id, handle.view_id); let result = update( view.as_any_mut() .downcast_mut() .expect("downcast is type safe"), &mut cx, ); this.cx .views .insert((handle.window_id, handle.view_id), view); result }) } } impl AsRef for MutableAppContext { fn as_ref(&self) -> &AppContext { &self.cx } } impl Deref for MutableAppContext { type Target = AppContext; fn deref(&self) -> &Self::Target { &self.cx } } #[derive(Debug)] pub enum ParentId { View(usize), Root, } pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, pub(crate) parents: HashMap<(usize, usize), ParentId>, windows: HashMap, globals: HashMap>, element_states: HashMap>, background: Arc, ref_counts: Arc>, font_cache: Arc, platform: Arc, } impl AppContext { pub(crate) fn root_view(&self, window_id: usize) -> Option { self.windows .get(&window_id) .map(|window| window.root_view.clone()) } pub fn root_view_id(&self, window_id: usize) -> Option { self.windows .get(&window_id) .map(|window| window.root_view.id()) } pub fn focused_view_id(&self, window_id: usize) -> Option { self.windows .get(&window_id) .and_then(|window| window.focused_view_id) } pub fn background(&self) -> &Arc { &self.background } pub fn font_cache(&self) -> &Arc { &self.font_cache } pub fn platform(&self) -> &Arc { &self.platform } pub fn has_global(&self) -> bool { self.globals.contains_key(&TypeId::of::()) } pub fn global(&self) -> &T { if let Some(global) = self.globals.get(&TypeId::of::()) { global.downcast_ref().unwrap() } else { panic!("no global has been added for {}", type_name::()); } } } impl ReadModel for AppContext { fn read_model(&self, handle: &ModelHandle) -> &T { if let Some(model) = self.models.get(&handle.model_id) { model .as_any() .downcast_ref() .expect("downcast should be type safe") } else { panic!("circular model reference"); } } } impl UpgradeModelHandle for AppContext { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option> { if self.models.contains_key(&handle.model_id) { Some(ModelHandle::new(handle.model_id, &self.ref_counts)) } else { None } } fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool { self.models.contains_key(&handle.model_id) } fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option { if self.models.contains_key(&handle.model_id) { Some(AnyModelHandle::new( handle.model_id, handle.model_type, self.ref_counts.clone(), )) } else { None } } } impl UpgradeViewHandle for AppContext { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { if self.ref_counts.lock().is_entity_alive(handle.view_id) { Some(ViewHandle::new( handle.window_id, handle.view_id, &self.ref_counts, )) } else { None } } fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { if self.ref_counts.lock().is_entity_alive(handle.view_id) { Some(AnyViewHandle::new( handle.window_id, handle.view_id, handle.view_type, self.ref_counts.clone(), )) } else { None } } } impl ReadView for AppContext { fn read_view(&self, handle: &ViewHandle) -> &T { if let Some(view) = self.views.get(&(handle.window_id, handle.view_id)) { view.as_any() .downcast_ref() .expect("downcast should be type safe") } else { panic!("circular view reference"); } } } struct Window { root_view: AnyViewHandle, focused_view_id: Option, is_active: bool, is_fullscreen: bool, invalidation: Option, } #[derive(Default, Clone)] pub struct WindowInvalidation { pub updated: HashSet, pub removed: Vec, } pub enum Effect { Subscription { entity_id: usize, subscription_id: usize, callback: SubscriptionCallback, }, Event { entity_id: usize, payload: Box, }, GlobalSubscription { type_id: TypeId, subscription_id: usize, callback: GlobalSubscriptionCallback, }, GlobalEvent { payload: Box, }, Observation { entity_id: usize, subscription_id: usize, callback: ObservationCallback, }, ModelNotification { model_id: usize, }, ViewNotification { window_id: usize, view_id: usize, }, Deferred { callback: Box, after_window_update: bool, }, GlobalNotification { type_id: TypeId, }, ModelRelease { model_id: usize, model: Box, }, ViewRelease { view_id: usize, view: Box, }, Focus { window_id: usize, view_id: Option, }, FocusObservation { view_id: usize, subscription_id: usize, callback: FocusObservationCallback, }, ResizeWindow { window_id: usize, }, FullscreenWindow { window_id: usize, is_fullscreen: bool, }, ActivateWindow { window_id: usize, is_active: bool, }, WindowActivationObservation { window_id: usize, subscription_id: usize, callback: WindowActivationCallback, }, WindowFullscreenObservation { window_id: usize, subscription_id: usize, callback: WindowFullscreenCallback, }, RefreshWindows, DispatchActionFrom { window_id: usize, view_id: usize, action: Box, }, ActionDispatchNotification { action_id: TypeId, }, WindowShouldCloseSubscription { window_id: usize, callback: WindowShouldCloseSubscriptionCallback, }, } impl Debug for Effect { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Effect::Subscription { entity_id, subscription_id, .. } => f .debug_struct("Effect::Subscribe") .field("entity_id", entity_id) .field("subscription_id", subscription_id) .finish(), Effect::Event { entity_id, .. } => f .debug_struct("Effect::Event") .field("entity_id", entity_id) .finish(), Effect::GlobalSubscription { type_id, subscription_id, .. } => f .debug_struct("Effect::Subscribe") .field("type_id", type_id) .field("subscription_id", subscription_id) .finish(), Effect::GlobalEvent { payload, .. } => f .debug_struct("Effect::GlobalEvent") .field("type_id", &(&*payload).type_id()) .finish(), Effect::Observation { entity_id, subscription_id, .. } => f .debug_struct("Effect::Observation") .field("entity_id", entity_id) .field("subscription_id", subscription_id) .finish(), Effect::ModelNotification { model_id } => f .debug_struct("Effect::ModelNotification") .field("model_id", model_id) .finish(), Effect::ViewNotification { window_id, view_id } => f .debug_struct("Effect::ViewNotification") .field("window_id", window_id) .field("view_id", view_id) .finish(), Effect::GlobalNotification { type_id } => f .debug_struct("Effect::GlobalNotification") .field("type_id", type_id) .finish(), Effect::Deferred { .. } => f.debug_struct("Effect::Deferred").finish(), Effect::ModelRelease { model_id, .. } => f .debug_struct("Effect::ModelRelease") .field("model_id", model_id) .finish(), Effect::ViewRelease { view_id, .. } => f .debug_struct("Effect::ViewRelease") .field("view_id", view_id) .finish(), Effect::Focus { window_id, view_id } => f .debug_struct("Effect::Focus") .field("window_id", window_id) .field("view_id", view_id) .finish(), Effect::FocusObservation { view_id, subscription_id, .. } => f .debug_struct("Effect::FocusObservation") .field("view_id", view_id) .field("subscription_id", subscription_id) .finish(), Effect::DispatchActionFrom { window_id, view_id, .. } => f .debug_struct("Effect::DispatchActionFrom") .field("window_id", window_id) .field("view_id", view_id) .finish(), Effect::ActionDispatchNotification { action_id, .. } => f .debug_struct("Effect::ActionDispatchNotification") .field("action_id", action_id) .finish(), Effect::ResizeWindow { window_id } => f .debug_struct("Effect::RefreshWindow") .field("window_id", window_id) .finish(), Effect::WindowActivationObservation { window_id, subscription_id, .. } => f .debug_struct("Effect::WindowActivationObservation") .field("window_id", window_id) .field("subscription_id", subscription_id) .finish(), Effect::ActivateWindow { window_id, is_active, } => f .debug_struct("Effect::ActivateWindow") .field("window_id", window_id) .field("is_active", is_active) .finish(), Effect::FullscreenWindow { window_id, is_fullscreen, } => f .debug_struct("Effect::FullscreenWindow") .field("window_id", window_id) .field("is_fullscreen", is_fullscreen) .finish(), Effect::WindowFullscreenObservation { window_id, subscription_id, callback: _, } => f .debug_struct("Effect::WindowFullscreenObservation") .field("window_id", window_id) .field("subscription_id", subscription_id) .finish(), Effect::RefreshWindows => f.debug_struct("Effect::FullViewRefresh").finish(), Effect::WindowShouldCloseSubscription { window_id, .. } => f .debug_struct("Effect::WindowShouldCloseSubscription") .field("window_id", window_id) .finish(), } } } pub trait AnyModel { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn release(&mut self, cx: &mut MutableAppContext); fn app_will_quit( &mut self, cx: &mut MutableAppContext, ) -> Option>>>; } impl AnyModel for T where T: Entity, { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn release(&mut self, cx: &mut MutableAppContext) { self.release(cx); } fn app_will_quit( &mut self, cx: &mut MutableAppContext, ) -> Option>>> { self.app_will_quit(cx) } } pub trait AnyView { fn as_any(&self) -> &dyn Any; fn as_any_mut(&mut self) -> &mut dyn Any; fn release(&mut self, cx: &mut MutableAppContext); fn app_will_quit( &mut self, cx: &mut MutableAppContext, ) -> Option>>>; fn ui_name(&self) -> &'static str; fn render(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox; fn on_focus_in( &mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize, focused_id: usize, ); fn on_focus_out( &mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize, focused_id: usize, ); fn keymap_context(&self, cx: &AppContext) -> keymap::Context; fn debug_json(&self, cx: &AppContext) -> serde_json::Value; fn text_for_range(&self, range: Range, cx: &AppContext) -> Option; fn selected_text_range(&self, cx: &AppContext) -> Option>; fn marked_text_range(&self, cx: &AppContext) -> Option>; fn unmark_text(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); fn replace_text_in_range( &mut self, range: Option>, text: &str, cx: &mut MutableAppContext, window_id: usize, view_id: usize, ); fn replace_and_mark_text_in_range( &mut self, range: Option>, new_text: &str, new_selected_range: Option>, cx: &mut MutableAppContext, window_id: usize, view_id: usize, ); fn any_handle(&self, window_id: usize, view_id: usize, cx: &AppContext) -> AnyViewHandle { AnyViewHandle::new( window_id, view_id, self.as_any().type_id(), cx.ref_counts.clone(), ) } } impl AnyView for T where T: View, { fn as_any(&self) -> &dyn Any { self } fn as_any_mut(&mut self) -> &mut dyn Any { self } fn release(&mut self, cx: &mut MutableAppContext) { self.release(cx); } fn app_will_quit( &mut self, cx: &mut MutableAppContext, ) -> Option>>> { self.app_will_quit(cx) } fn ui_name(&self) -> &'static str { T::ui_name() } fn render(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox { View::render(self, &mut RenderContext::new(params, cx)) } fn on_focus_in( &mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize, focused_id: usize, ) { let mut cx = ViewContext::new(cx, window_id, view_id); let focused_view_handle: AnyViewHandle = if view_id == focused_id { cx.handle().into() } else { let focused_type = cx .views .get(&(window_id, focused_id)) .unwrap() .as_any() .type_id(); AnyViewHandle::new(window_id, focused_id, focused_type, cx.ref_counts.clone()) }; View::on_focus_in(self, focused_view_handle, &mut cx); } fn on_focus_out( &mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize, blurred_id: usize, ) { let mut cx = ViewContext::new(cx, window_id, view_id); let blurred_view_handle: AnyViewHandle = if view_id == blurred_id { cx.handle().into() } else { let blurred_type = cx .views .get(&(window_id, blurred_id)) .unwrap() .as_any() .type_id(); AnyViewHandle::new(window_id, blurred_id, blurred_type, cx.ref_counts.clone()) }; View::on_focus_out(self, blurred_view_handle, &mut cx); } fn keymap_context(&self, cx: &AppContext) -> keymap::Context { View::keymap_context(self, cx) } fn debug_json(&self, cx: &AppContext) -> serde_json::Value { View::debug_json(self, cx) } fn text_for_range(&self, range: Range, cx: &AppContext) -> Option { View::text_for_range(self, range, cx) } fn selected_text_range(&self, cx: &AppContext) -> Option> { View::selected_text_range(self, cx) } fn marked_text_range(&self, cx: &AppContext) -> Option> { View::marked_text_range(self, cx) } fn unmark_text(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) { let mut cx = ViewContext::new(cx, window_id, view_id); View::unmark_text(self, &mut cx) } fn replace_text_in_range( &mut self, range: Option>, text: &str, cx: &mut MutableAppContext, window_id: usize, view_id: usize, ) { let mut cx = ViewContext::new(cx, window_id, view_id); View::replace_text_in_range(self, range, text, &mut cx) } fn replace_and_mark_text_in_range( &mut self, range: Option>, new_text: &str, new_selected_range: Option>, cx: &mut MutableAppContext, window_id: usize, view_id: usize, ) { let mut cx = ViewContext::new(cx, window_id, view_id); View::replace_and_mark_text_in_range(self, range, new_text, new_selected_range, &mut cx) } } pub struct ModelContext<'a, T: ?Sized> { app: &'a mut MutableAppContext, model_id: usize, model_type: PhantomData, halt_stream: bool, } impl<'a, T: Entity> ModelContext<'a, T> { fn new(app: &'a mut MutableAppContext, model_id: usize) -> Self { Self { app, model_id, model_type: PhantomData, halt_stream: false, } } pub fn background(&self) -> &Arc { &self.app.cx.background } pub fn halt_stream(&mut self) { self.halt_stream = true; } pub fn model_id(&self) -> usize { self.model_id } pub fn add_model(&mut self, build_model: F) -> ModelHandle where S: Entity, F: FnOnce(&mut ModelContext) -> S, { self.app.add_model(build_model) } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ModelContext)) { let handle = self.handle(); self.app.defer(move |cx| { handle.update(cx, |model, cx| { callback(model, cx); }) }) } pub fn emit(&mut self, payload: T::Event) { self.app.pending_effects.push_back(Effect::Event { entity_id: self.model_id, payload: Box::new(payload), }); } pub fn notify(&mut self) { self.app.notify_model(self.model_id); } pub fn subscribe( &mut self, handle: &ModelHandle, mut callback: F, ) -> Subscription where S::Event: 'static, F: 'static + FnMut(&mut T, ModelHandle, &S::Event, &mut ModelContext), { let subscriber = self.weak_handle(); self.app .subscribe_internal(handle, move |emitter, event, cx| { if let Some(subscriber) = subscriber.upgrade(cx) { subscriber.update(cx, |subscriber, cx| { callback(subscriber, emitter, event, cx); }); true } else { false } }) } pub fn observe(&mut self, handle: &ModelHandle, mut callback: F) -> Subscription where S: Entity, F: 'static + FnMut(&mut T, ModelHandle, &mut ModelContext), { let observer = self.weak_handle(); self.app.observe_internal(handle, move |observed, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, observed, cx); }); true } else { false } }) } pub fn observe_global(&mut self, mut callback: F) -> Subscription where G: Any, F: 'static + FnMut(&mut T, &mut ModelContext), { let observer = self.weak_handle(); self.app.observe_global::(move |cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| callback(observer, cx)); } }) } pub fn observe_release( &mut self, handle: &ModelHandle, mut callback: F, ) -> Subscription where S: Entity, F: 'static + FnMut(&mut T, &S, &mut ModelContext), { let observer = self.weak_handle(); self.app.observe_release(handle, move |released, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, released, cx); }); } }) } pub fn handle(&self) -> ModelHandle { ModelHandle::new(self.model_id, &self.app.cx.ref_counts) } pub fn weak_handle(&self) -> WeakModelHandle { WeakModelHandle::new(self.model_id) } pub fn spawn(&self, f: F) -> Task where F: FnOnce(ModelHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, S: 'static, { let handle = self.handle(); self.app.spawn(|cx| f(handle, cx)) } pub fn spawn_weak(&self, f: F) -> Task where F: FnOnce(WeakModelHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, S: 'static, { let handle = self.weak_handle(); self.app.spawn(|cx| f(handle, cx)) } } impl AsRef for ModelContext<'_, M> { fn as_ref(&self) -> &AppContext { &self.app.cx } } impl AsMut for ModelContext<'_, M> { fn as_mut(&mut self) -> &mut MutableAppContext { self.app } } impl ReadModel for ModelContext<'_, M> { fn read_model(&self, handle: &ModelHandle) -> &T { self.app.read_model(handle) } } impl UpdateModel for ModelContext<'_, M> { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> V, ) -> V { self.app.update_model(handle, update) } } impl UpgradeModelHandle for ModelContext<'_, M> { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool { self.cx.model_handle_is_upgradable(handle) } fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option { self.cx.upgrade_any_model_handle(handle) } } impl Deref for ModelContext<'_, M> { type Target = MutableAppContext; fn deref(&self) -> &Self::Target { self.app } } impl DerefMut for ModelContext<'_, M> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.app } } pub struct ViewContext<'a, T: ?Sized> { app: &'a mut MutableAppContext, window_id: usize, view_id: usize, view_type: PhantomData, } impl<'a, T: View> ViewContext<'a, T> { fn new(app: &'a mut MutableAppContext, window_id: usize, view_id: usize) -> Self { Self { app, window_id, view_id, view_type: PhantomData, } } pub fn handle(&self) -> ViewHandle { ViewHandle::new(self.window_id, self.view_id, &self.app.cx.ref_counts) } pub fn weak_handle(&self) -> WeakViewHandle { WeakViewHandle::new(self.window_id, self.view_id) } pub fn window_id(&self) -> usize { self.window_id } pub fn view_id(&self) -> usize { self.view_id } pub fn foreground(&self) -> &Rc { self.app.foreground() } pub fn background_executor(&self) -> &Arc { &self.app.cx.background } pub fn platform(&self) -> Arc { self.app.platform() } pub fn show_character_palette(&self) { self.app.show_character_palette(self.window_id); } pub fn minimize_window(&self) { self.app.minimize_window(self.window_id) } pub fn zoom_window(&self) { self.app.zoom_window(self.window_id) } pub fn toggle_full_screen(&self) { self.app.toggle_window_full_screen(self.window_id) } pub fn window_bounds(&self) -> RectF { self.app.window_bounds(self.window_id) } pub fn prompt( &self, level: PromptLevel, msg: &str, answers: &[&str], ) -> oneshot::Receiver { self.app.prompt(self.window_id, level, msg, answers) } pub fn prompt_for_paths( &self, options: PathPromptOptions, ) -> oneshot::Receiver>> { self.app.prompt_for_paths(options) } pub fn prompt_for_new_path(&self, directory: &Path) -> oneshot::Receiver> { self.app.prompt_for_new_path(directory) } pub fn debug_elements(&self) -> crate::json::Value { self.app.debug_elements(self.window_id).unwrap() } pub fn focus(&mut self, handle: S) where S: Into, { let handle = handle.into(); self.app.focus(handle.window_id, Some(handle.view_id)); } pub fn focus_self(&mut self) { self.app.focus(self.window_id, Some(self.view_id)); } pub fn is_self_focused(&self) -> bool { self.app.focused_view_id(self.window_id) == Some(self.view_id) } pub fn blur(&mut self) { self.app.focus(self.window_id, None); } pub fn set_window_title(&mut self, title: &str) { let window_id = self.window_id(); if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) { window.set_title(title); } } pub fn set_window_edited(&mut self, edited: bool) { let window_id = self.window_id(); if let Some((_, window)) = self.presenters_and_platform_windows.get_mut(&window_id) { window.set_edited(edited); } } pub fn on_window_should_close(&mut self, mut callback: F) where F: 'static + FnMut(&mut T, &mut ViewContext) -> bool, { let window_id = self.window_id(); let view = self.weak_handle(); self.pending_effects .push_back(Effect::WindowShouldCloseSubscription { window_id, callback: Box::new(move |cx| { if let Some(view) = view.upgrade(cx) { view.update(cx, |view, cx| callback(view, cx)) } else { true } }), }); } pub fn add_model(&mut self, build_model: F) -> ModelHandle where S: Entity, F: FnOnce(&mut ModelContext) -> S, { self.app.add_model(build_model) } pub fn add_view(&mut self, build_view: F) -> ViewHandle where S: View, F: FnOnce(&mut ViewContext) -> S, { self.app .build_and_insert_view(self.window_id, ParentId::View(self.view_id), |cx| { Some(build_view(cx)) }) .unwrap() } pub fn add_option_view(&mut self, build_view: F) -> Option> where S: View, F: FnOnce(&mut ViewContext) -> Option, { self.app .build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view) } pub fn reparent(&mut self, view_handle: impl Into) { let view_handle = view_handle.into(); if self.window_id != view_handle.window_id { panic!("Can't reparent view to a view from a different window"); } self.cx .parents .remove(&(view_handle.window_id, view_handle.view_id)); let new_parent_id = self.view_id; self.cx.parents.insert( (view_handle.window_id, view_handle.view_id), ParentId::View(new_parent_id), ); } pub fn replace_root_view(&mut self, build_root_view: F) -> ViewHandle where V: View, F: FnOnce(&mut ViewContext) -> V, { let window_id = self.window_id; self.update(|this| { let root_view = this .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) .unwrap(); let window = this.cx.windows.get_mut(&window_id).unwrap(); window.root_view = root_view.clone().into(); window.focused_view_id = Some(root_view.id()); root_view }) } pub fn subscribe(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, E::Event: 'static, H: Handle, F: 'static + FnMut(&mut T, H, &E::Event, &mut ViewContext), { let subscriber = self.weak_handle(); self.app .subscribe_internal(handle, move |emitter, event, cx| { if let Some(subscriber) = subscriber.upgrade(cx) { subscriber.update(cx, |subscriber, cx| { callback(subscriber, emitter, event, cx); }); true } else { false } }) } pub fn observe(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, H: Handle, F: 'static + FnMut(&mut T, H, &mut ViewContext), { let observer = self.weak_handle(); self.app.observe_internal(handle, move |observed, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, observed, cx); }); true } else { false } }) } pub fn observe_focus(&mut self, handle: &ViewHandle, mut callback: F) -> Subscription where F: 'static + FnMut(&mut T, ViewHandle, bool, &mut ViewContext), V: View, { let observer = self.weak_handle(); self.app .observe_focus(handle, move |observed, focused, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, observed, focused, cx); }); true } else { false } }) } pub fn observe_release(&mut self, handle: &H, mut callback: F) -> Subscription where E: Entity, H: Handle, F: 'static + FnMut(&mut T, &E, &mut ViewContext), { let observer = self.weak_handle(); self.app.observe_release(handle, move |released, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, released, cx); }); } }) } pub fn observe_actions(&mut self, mut callback: F) -> Subscription where F: 'static + FnMut(&mut T, TypeId, &mut ViewContext), { let observer = self.weak_handle(); self.app.observe_actions(move |action_id, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, action_id, cx); }); } }) } pub fn observe_window_activation(&mut self, mut callback: F) -> Subscription where F: 'static + FnMut(&mut T, bool, &mut ViewContext), { let observer = self.weak_handle(); self.app .observe_window_activation(self.window_id(), move |active, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, active, cx); }); true } else { false } }) } pub fn observe_fullscreen(&mut self, mut callback: F) -> Subscription where F: 'static + FnMut(&mut T, bool, &mut ViewContext), { let observer = self.weak_handle(); self.app .observe_fullscreen(self.window_id(), move |active, cx| { if let Some(observer) = observer.upgrade(cx) { observer.update(cx, |observer, cx| { callback(observer, active, cx); }); true } else { false } }) } pub fn emit(&mut self, payload: T::Event) { self.app.pending_effects.push_back(Effect::Event { entity_id: self.view_id, payload: Box::new(payload), }); } pub fn notify(&mut self) { self.app.notify_view(self.window_id, self.view_id); } pub fn dispatch_any_action(&mut self, action: Box) { self.app .dispatch_any_action_at(self.window_id, self.view_id, action) } pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext)) { let handle = self.handle(); self.app.defer(move |cx| { handle.update(cx, |view, cx| { callback(view, cx); }) }) } pub fn after_window_update( &mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext), ) { let handle = self.handle(); self.app.after_window_update(move |cx| { handle.update(cx, |view, cx| { callback(view, cx); }) }) } pub fn propagate_action(&mut self) { self.app.halt_action_dispatch = false; } pub fn spawn(&self, f: F) -> Task where F: FnOnce(ViewHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, S: 'static, { let handle = self.handle(); self.app.spawn(|cx| f(handle, cx)) } pub fn spawn_weak(&self, f: F) -> Task where F: FnOnce(WeakViewHandle, AsyncAppContext) -> Fut, Fut: 'static + Future, S: 'static, { let handle = self.weak_handle(); self.app.spawn(|cx| f(handle, cx)) } } pub struct RenderParams { pub window_id: usize, pub view_id: usize, pub titlebar_height: f32, pub hovered_region_ids: HashSet, pub clicked_region_ids: Option<(HashSet, MouseButton)>, pub refreshing: bool, pub appearance: Appearance, } pub struct RenderContext<'a, T: View> { pub(crate) window_id: usize, pub(crate) view_id: usize, pub(crate) view_type: PhantomData, pub(crate) hovered_region_ids: HashSet, pub(crate) clicked_region_ids: Option<(HashSet, MouseButton)>, pub app: &'a mut MutableAppContext, pub titlebar_height: f32, pub appearance: Appearance, pub refreshing: bool, } #[derive(Clone, Copy, Default)] pub struct MouseState { pub hovered: bool, pub clicked: Option, } impl<'a, V: View> RenderContext<'a, V> { fn new(params: RenderParams, app: &'a mut MutableAppContext) -> Self { Self { app, window_id: params.window_id, view_id: params.view_id, view_type: PhantomData, titlebar_height: params.titlebar_height, hovered_region_ids: params.hovered_region_ids.clone(), clicked_region_ids: params.clicked_region_ids.clone(), refreshing: params.refreshing, appearance: params.appearance, } } pub fn handle(&self) -> WeakViewHandle { WeakViewHandle::new(self.window_id, self.view_id) } pub fn window_id(&self) -> usize { self.window_id } pub fn view_id(&self) -> usize { self.view_id } pub fn mouse_state(&self, region_id: usize) -> MouseState { let region_id = MouseRegionId::new::(self.view_id, region_id); MouseState { hovered: self.hovered_region_ids.contains(®ion_id), clicked: self.clicked_region_ids.as_ref().and_then(|(ids, button)| { if ids.contains(®ion_id) { Some(*button) } else { None } }), } } pub fn element_state( &mut self, element_id: usize, initial: T, ) -> ElementStateHandle { let id = ElementStateId { view_id: self.view_id(), element_id, tag: TypeId::of::(), }; self.cx .element_states .entry(id) .or_insert_with(|| Box::new(initial)); ElementStateHandle::new(id, self.frame_count, &self.cx.ref_counts) } pub fn default_element_state( &mut self, element_id: usize, ) -> ElementStateHandle { self.element_state::(element_id, T::default()) } } impl AsRef for &AppContext { fn as_ref(&self) -> &AppContext { self } } impl Deref for RenderContext<'_, V> { type Target = MutableAppContext; fn deref(&self) -> &Self::Target { self.app } } impl DerefMut for RenderContext<'_, V> { fn deref_mut(&mut self) -> &mut Self::Target { self.app } } impl ReadModel for RenderContext<'_, V> { fn read_model(&self, handle: &ModelHandle) -> &T { self.app.read_model(handle) } } impl UpdateModel for RenderContext<'_, V> { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, ) -> O { self.app.update_model(handle, update) } } impl ReadView for RenderContext<'_, V> { fn read_view(&self, handle: &ViewHandle) -> &T { self.app.read_view(handle) } } impl AsRef for ViewContext<'_, M> { fn as_ref(&self) -> &AppContext { &self.app.cx } } impl Deref for ViewContext<'_, M> { type Target = MutableAppContext; fn deref(&self) -> &Self::Target { self.app } } impl DerefMut for ViewContext<'_, M> { fn deref_mut(&mut self) -> &mut Self::Target { &mut self.app } } impl AsMut for ViewContext<'_, M> { fn as_mut(&mut self) -> &mut MutableAppContext { self.app } } impl ReadModel for ViewContext<'_, V> { fn read_model(&self, handle: &ModelHandle) -> &T { self.app.read_model(handle) } } impl UpgradeModelHandle for ViewContext<'_, V> { fn upgrade_model_handle( &self, handle: &WeakModelHandle, ) -> Option> { self.cx.upgrade_model_handle(handle) } fn model_handle_is_upgradable(&self, handle: &WeakModelHandle) -> bool { self.cx.model_handle_is_upgradable(handle) } fn upgrade_any_model_handle(&self, handle: &AnyWeakModelHandle) -> Option { self.cx.upgrade_any_model_handle(handle) } } impl UpgradeViewHandle for ViewContext<'_, V> { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { self.cx.upgrade_view_handle(handle) } fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { self.cx.upgrade_any_view_handle(handle) } } impl UpgradeViewHandle for RenderContext<'_, V> { fn upgrade_view_handle(&self, handle: &WeakViewHandle) -> Option> { self.cx.upgrade_view_handle(handle) } fn upgrade_any_view_handle(&self, handle: &AnyWeakViewHandle) -> Option { self.cx.upgrade_any_view_handle(handle) } } impl UpdateModel for ViewContext<'_, V> { fn update_model( &mut self, handle: &ModelHandle, update: &mut dyn FnMut(&mut T, &mut ModelContext) -> O, ) -> O { self.app.update_model(handle, update) } } impl ReadView for ViewContext<'_, V> { fn read_view(&self, handle: &ViewHandle) -> &T { self.app.read_view(handle) } } impl UpdateView for ViewContext<'_, V> { fn update_view( &mut self, handle: &ViewHandle, update: &mut dyn FnMut(&mut T, &mut ViewContext) -> S, ) -> S where T: View, { self.app.update_view(handle, update) } } pub trait Handle { type Weak: 'static; fn id(&self) -> usize; fn location(&self) -> EntityLocation; fn downgrade(&self) -> Self::Weak; fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option where Self: Sized; } pub trait WeakHandle { fn id(&self) -> usize; } #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] pub enum EntityLocation { Model(usize), View(usize, usize), } pub struct ModelHandle { model_id: usize, model_type: PhantomData, ref_counts: Arc>, #[cfg(any(test, feature = "test-support"))] handle_id: usize, } impl ModelHandle { fn new(model_id: usize, ref_counts: &Arc>) -> Self { ref_counts.lock().inc_model(model_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts .lock() .leak_detector .lock() .handle_created(Some(type_name::()), model_id); Self { model_id, model_type: PhantomData, ref_counts: ref_counts.clone(), #[cfg(any(test, feature = "test-support"))] handle_id, } } pub fn downgrade(&self) -> WeakModelHandle { WeakModelHandle::new(self.model_id) } pub fn id(&self) -> usize { self.model_id } pub fn read<'a, C: ReadModel>(&self, cx: &'a C) -> &'a T { cx.read_model(self) } pub fn read_with(&self, cx: &C, read: F) -> S where C: ReadModelWith, F: FnOnce(&T, &AppContext) -> S, { let mut read = Some(read); cx.read_model_with(self, &mut |model, cx| { let read = read.take().unwrap(); read(model, cx) }) } pub fn update(&self, cx: &mut C, update: F) -> S where C: UpdateModel, F: FnOnce(&mut T, &mut ModelContext) -> S, { let mut update = Some(update); cx.update_model(self, &mut |model, cx| { let update = update.take().unwrap(); update(model, cx) }) } #[cfg(any(test, feature = "test-support"))] pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { let (tx, mut rx) = futures::channel::mpsc::unbounded(); let mut cx = cx.cx.borrow_mut(); let subscription = cx.observe(self, move |_, _| { tx.unbounded_send(()).ok(); }); let duration = if std::env::var("CI").is_ok() { Duration::from_secs(5) } else { Duration::from_secs(1) }; async move { let notification = crate::util::timeout(duration, rx.next()) .await .expect("next notification timed out"); drop(subscription); notification.expect("model dropped while test was waiting for its next notification") } } #[cfg(any(test, feature = "test-support"))] pub fn next_event(&self, cx: &TestAppContext) -> impl Future where T::Event: Clone, { let (tx, mut rx) = futures::channel::mpsc::unbounded(); let mut cx = cx.cx.borrow_mut(); let subscription = cx.subscribe(self, move |_, event, _| { tx.unbounded_send(event.clone()).ok(); }); let duration = if std::env::var("CI").is_ok() { Duration::from_secs(5) } else { Duration::from_secs(1) }; cx.foreground.start_waiting(); async move { let event = crate::util::timeout(duration, rx.next()) .await .expect("next event timed out"); drop(subscription); event.expect("model dropped while test was waiting for its next event") } } #[cfg(any(test, feature = "test-support"))] pub fn condition( &self, cx: &TestAppContext, mut predicate: impl FnMut(&T, &AppContext) -> bool, ) -> impl Future { let (tx, mut rx) = futures::channel::mpsc::unbounded(); let mut cx = cx.cx.borrow_mut(); let subscriptions = ( cx.observe(self, { let tx = tx.clone(); move |_, _| { tx.unbounded_send(()).ok(); } }), cx.subscribe(self, { move |_, _, _| { tx.unbounded_send(()).ok(); } }), ); let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); let handle = self.downgrade(); let duration = if std::env::var("CI").is_ok() { Duration::from_secs(5) } else { Duration::from_secs(1) }; async move { crate::util::timeout(duration, async move { loop { { let cx = cx.borrow(); let cx = cx.as_ref(); if predicate( handle .upgrade(cx) .expect("model dropped with pending condition") .read(cx), cx, ) { break; } } cx.borrow().foreground().start_waiting(); rx.next() .await .expect("model dropped with pending condition"); cx.borrow().foreground().finish_waiting(); } }) .await .expect("condition timed out"); drop(subscriptions); } } } impl Clone for ModelHandle { fn clone(&self) -> Self { Self::new(self.model_id, &self.ref_counts) } } impl PartialEq for ModelHandle { fn eq(&self, other: &Self) -> bool { self.model_id == other.model_id } } impl Eq for ModelHandle {} impl PartialEq> for ModelHandle { fn eq(&self, other: &WeakModelHandle) -> bool { self.model_id == other.model_id } } impl Hash for ModelHandle { fn hash(&self, state: &mut H) { self.model_id.hash(state); } } impl std::borrow::Borrow for ModelHandle { fn borrow(&self) -> &usize { &self.model_id } } impl Debug for ModelHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_tuple(&format!("ModelHandle<{}>", type_name::())) .field(&self.model_id) .finish() } } unsafe impl Send for ModelHandle {} unsafe impl Sync for ModelHandle {} impl Drop for ModelHandle { fn drop(&mut self) { let mut ref_counts = self.ref_counts.lock(); ref_counts.dec_model(self.model_id); #[cfg(any(test, feature = "test-support"))] ref_counts .leak_detector .lock() .handle_dropped(self.model_id, self.handle_id); } } impl Handle for ModelHandle { type Weak = WeakModelHandle; fn id(&self) -> usize { self.model_id } fn location(&self) -> EntityLocation { EntityLocation::Model(self.model_id) } fn downgrade(&self) -> Self::Weak { self.downgrade() } fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option where Self: Sized, { weak.upgrade(cx) } } pub struct WeakModelHandle { model_id: usize, model_type: PhantomData, } impl WeakHandle for WeakModelHandle { fn id(&self) -> usize { self.model_id } } unsafe impl Send for WeakModelHandle {} unsafe impl Sync for WeakModelHandle {} impl WeakModelHandle { fn new(model_id: usize) -> Self { Self { model_id, model_type: PhantomData, } } pub fn id(&self) -> usize { self.model_id } pub fn is_upgradable(&self, cx: &impl UpgradeModelHandle) -> bool { cx.model_handle_is_upgradable(self) } pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option> { cx.upgrade_model_handle(self) } } impl Hash for WeakModelHandle { fn hash(&self, state: &mut H) { self.model_id.hash(state) } } impl PartialEq for WeakModelHandle { fn eq(&self, other: &Self) -> bool { self.model_id == other.model_id } } impl Eq for WeakModelHandle {} impl Clone for WeakModelHandle { fn clone(&self) -> Self { Self { model_id: self.model_id, model_type: PhantomData, } } } impl Copy for WeakModelHandle {} pub struct ViewHandle { window_id: usize, view_id: usize, view_type: PhantomData, ref_counts: Arc>, #[cfg(any(test, feature = "test-support"))] handle_id: usize, } impl ViewHandle { fn new(window_id: usize, view_id: usize, ref_counts: &Arc>) -> Self { ref_counts.lock().inc_view(window_id, view_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts .lock() .leak_detector .lock() .handle_created(Some(type_name::()), view_id); Self { window_id, view_id, view_type: PhantomData, ref_counts: ref_counts.clone(), #[cfg(any(test, feature = "test-support"))] handle_id, } } pub fn downgrade(&self) -> WeakViewHandle { WeakViewHandle::new(self.window_id, self.view_id) } pub fn window_id(&self) -> usize { self.window_id } pub fn id(&self) -> usize { self.view_id } pub fn read<'a, C: ReadView>(&self, cx: &'a C) -> &'a T { cx.read_view(self) } pub fn read_with(&self, cx: &C, read: F) -> S where C: ReadViewWith, F: FnOnce(&T, &AppContext) -> S, { let mut read = Some(read); cx.read_view_with(self, &mut |view, cx| { let read = read.take().unwrap(); read(view, cx) }) } pub fn update(&self, cx: &mut C, update: F) -> S where C: UpdateView, F: FnOnce(&mut T, &mut ViewContext) -> S, { let mut update = Some(update); cx.update_view(self, &mut |view, cx| { let update = update.take().unwrap(); update(view, cx) }) } pub fn defer(&self, cx: &mut C, update: F) where C: AsMut, F: 'static + FnOnce(&mut T, &mut ViewContext), { let this = self.clone(); cx.as_mut().defer(move |cx| { this.update(cx, |view, cx| update(view, cx)); }); } pub fn is_focused(&self, cx: &AppContext) -> bool { cx.focused_view_id(self.window_id) .map_or(false, |focused_id| focused_id == self.view_id) } #[cfg(any(test, feature = "test-support"))] pub fn next_notification(&self, cx: &TestAppContext) -> impl Future { use postage::prelude::{Sink as _, Stream as _}; let (mut tx, mut rx) = postage::mpsc::channel(1); let mut cx = cx.cx.borrow_mut(); let subscription = cx.observe(self, move |_, _| { tx.try_send(()).ok(); }); let duration = if std::env::var("CI").is_ok() { Duration::from_secs(5) } else { Duration::from_secs(1) }; async move { let notification = crate::util::timeout(duration, rx.recv()) .await .expect("next notification timed out"); drop(subscription); notification.expect("model dropped while test was waiting for its next notification") } } #[cfg(any(test, feature = "test-support"))] pub fn condition( &self, cx: &TestAppContext, mut predicate: impl FnMut(&T, &AppContext) -> bool, ) -> impl Future { use postage::prelude::{Sink as _, Stream as _}; let (tx, mut rx) = postage::mpsc::channel(1024); let timeout_duration = cx.condition_duration(); let mut cx = cx.cx.borrow_mut(); let subscriptions = self.update(&mut *cx, |_, cx| { ( cx.observe(self, { let mut tx = tx.clone(); move |_, _, _| { tx.blocking_send(()).ok(); } }), cx.subscribe(self, { let mut tx = tx.clone(); move |_, _, _, _| { tx.blocking_send(()).ok(); } }), ) }); let cx = cx.weak_self.as_ref().unwrap().upgrade().unwrap(); let handle = self.downgrade(); async move { crate::util::timeout(timeout_duration, async move { loop { { let cx = cx.borrow(); let cx = cx.as_ref(); if predicate( handle .upgrade(cx) .expect("view dropped with pending condition") .read(cx), cx, ) { break; } } cx.borrow().foreground().start_waiting(); rx.recv() .await .expect("view dropped with pending condition"); cx.borrow().foreground().finish_waiting(); } }) .await .expect("condition timed out"); drop(subscriptions); } } } impl Clone for ViewHandle { fn clone(&self) -> Self { ViewHandle::new(self.window_id, self.view_id, &self.ref_counts) } } impl PartialEq for ViewHandle { fn eq(&self, other: &Self) -> bool { self.window_id == other.window_id && self.view_id == other.view_id } } impl PartialEq> for ViewHandle { fn eq(&self, other: &WeakViewHandle) -> bool { self.window_id == other.window_id && self.view_id == other.view_id } } impl PartialEq> for WeakViewHandle { fn eq(&self, other: &ViewHandle) -> bool { self.window_id == other.window_id && self.view_id == other.view_id } } impl Eq for ViewHandle {} impl Hash for ViewHandle { fn hash(&self, state: &mut H) { self.window_id.hash(state); self.view_id.hash(state); } } impl Debug for ViewHandle { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { f.debug_struct(&format!("ViewHandle<{}>", type_name::())) .field("window_id", &self.window_id) .field("view_id", &self.view_id) .finish() } } impl Drop for ViewHandle { fn drop(&mut self) { self.ref_counts .lock() .dec_view(self.window_id, self.view_id); #[cfg(any(test, feature = "test-support"))] self.ref_counts .lock() .leak_detector .lock() .handle_dropped(self.view_id, self.handle_id); } } impl Handle for ViewHandle { type Weak = WeakViewHandle; fn id(&self) -> usize { self.view_id } fn location(&self) -> EntityLocation { EntityLocation::View(self.window_id, self.view_id) } fn downgrade(&self) -> Self::Weak { self.downgrade() } fn upgrade_from(weak: &Self::Weak, cx: &AppContext) -> Option where Self: Sized, { weak.upgrade(cx) } } pub struct AnyViewHandle { window_id: usize, view_id: usize, view_type: TypeId, ref_counts: Arc>, #[cfg(any(test, feature = "test-support"))] handle_id: usize, } impl AnyViewHandle { fn new( window_id: usize, view_id: usize, view_type: TypeId, ref_counts: Arc>, ) -> Self { ref_counts.lock().inc_view(window_id, view_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts .lock() .leak_detector .lock() .handle_created(None, view_id); Self { window_id, view_id, view_type, ref_counts, #[cfg(any(test, feature = "test-support"))] handle_id, } } pub fn id(&self) -> usize { self.view_id } pub fn is(&self) -> bool { TypeId::of::() == self.view_type } pub fn is_focused(&self, cx: &AppContext) -> bool { cx.focused_view_id(self.window_id) .map_or(false, |focused_id| focused_id == self.view_id) } pub fn downcast(self) -> Option> { if self.is::() { let result = Some(ViewHandle { window_id: self.window_id, view_id: self.view_id, ref_counts: self.ref_counts.clone(), view_type: PhantomData, #[cfg(any(test, feature = "test-support"))] handle_id: self.handle_id, }); unsafe { Arc::decrement_strong_count(Arc::as_ptr(&self.ref_counts)); } std::mem::forget(self); result } else { None } } pub fn downgrade(&self) -> AnyWeakViewHandle { AnyWeakViewHandle { window_id: self.window_id, view_id: self.view_id, view_type: self.view_type, } } pub fn view_type(&self) -> TypeId { self.view_type } pub fn debug_json(&self, cx: &AppContext) -> serde_json::Value { cx.views .get(&(self.window_id, self.view_id)) .map_or_else(|| serde_json::Value::Null, |view| view.debug_json(cx)) } } impl Clone for AnyViewHandle { fn clone(&self) -> Self { Self::new( self.window_id, self.view_id, self.view_type, self.ref_counts.clone(), ) } } impl From<&AnyViewHandle> for AnyViewHandle { fn from(handle: &AnyViewHandle) -> Self { handle.clone() } } impl From<&ViewHandle> for AnyViewHandle { fn from(handle: &ViewHandle) -> Self { Self::new( handle.window_id, handle.view_id, TypeId::of::(), handle.ref_counts.clone(), ) } } impl From> for AnyViewHandle { fn from(handle: ViewHandle) -> Self { let any_handle = AnyViewHandle { window_id: handle.window_id, view_id: handle.view_id, view_type: TypeId::of::(), ref_counts: handle.ref_counts.clone(), #[cfg(any(test, feature = "test-support"))] handle_id: handle.handle_id, }; unsafe { Arc::decrement_strong_count(Arc::as_ptr(&handle.ref_counts)); } std::mem::forget(handle); any_handle } } impl Drop for AnyViewHandle { fn drop(&mut self) { self.ref_counts .lock() .dec_view(self.window_id, self.view_id); #[cfg(any(test, feature = "test-support"))] self.ref_counts .lock() .leak_detector .lock() .handle_dropped(self.view_id, self.handle_id); } } pub struct AnyModelHandle { model_id: usize, model_type: TypeId, ref_counts: Arc>, #[cfg(any(test, feature = "test-support"))] handle_id: usize, } impl AnyModelHandle { fn new(model_id: usize, model_type: TypeId, ref_counts: Arc>) -> Self { ref_counts.lock().inc_model(model_id); #[cfg(any(test, feature = "test-support"))] let handle_id = ref_counts .lock() .leak_detector .lock() .handle_created(None, model_id); Self { model_id, model_type, ref_counts, #[cfg(any(test, feature = "test-support"))] handle_id, } } pub fn downcast(self) -> Option> { if self.is::() { let result = Some(ModelHandle { model_id: self.model_id, model_type: PhantomData, ref_counts: self.ref_counts.clone(), #[cfg(any(test, feature = "test-support"))] handle_id: self.handle_id, }); unsafe { Arc::decrement_strong_count(Arc::as_ptr(&self.ref_counts)); } std::mem::forget(self); result } else { None } } pub fn downgrade(&self) -> AnyWeakModelHandle { AnyWeakModelHandle { model_id: self.model_id, model_type: self.model_type, } } pub fn is(&self) -> bool { self.model_type == TypeId::of::() } pub fn model_type(&self) -> TypeId { self.model_type } } impl From> for AnyModelHandle { fn from(handle: ModelHandle) -> Self { Self::new( handle.model_id, TypeId::of::(), handle.ref_counts.clone(), ) } } impl Clone for AnyModelHandle { fn clone(&self) -> Self { Self::new(self.model_id, self.model_type, self.ref_counts.clone()) } } impl Drop for AnyModelHandle { fn drop(&mut self) { let mut ref_counts = self.ref_counts.lock(); ref_counts.dec_model(self.model_id); #[cfg(any(test, feature = "test-support"))] ref_counts .leak_detector .lock() .handle_dropped(self.model_id, self.handle_id); } } #[derive(Hash, PartialEq, Eq, Debug)] pub struct AnyWeakModelHandle { model_id: usize, model_type: TypeId, } impl AnyWeakModelHandle { pub fn upgrade(&self, cx: &impl UpgradeModelHandle) -> Option { cx.upgrade_any_model_handle(self) } pub fn model_type(&self) -> TypeId { self.model_type } fn is(&self) -> bool { TypeId::of::() == self.model_type } pub fn downcast(&self) -> Option> { if self.is::() { let result = Some(WeakModelHandle { model_id: self.model_id, model_type: PhantomData, }); result } else { None } } } impl From> for AnyWeakModelHandle { fn from(handle: WeakModelHandle) -> Self { AnyWeakModelHandle { model_id: handle.model_id, model_type: TypeId::of::(), } } } #[derive(Debug)] pub struct WeakViewHandle { window_id: usize, view_id: usize, view_type: PhantomData, } impl WeakHandle for WeakViewHandle { fn id(&self) -> usize { self.view_id } } impl WeakViewHandle { fn new(window_id: usize, view_id: usize) -> Self { Self { window_id, view_id, view_type: PhantomData, } } pub fn id(&self) -> usize { self.view_id } pub fn window_id(&self) -> usize { self.window_id } pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option> { cx.upgrade_view_handle(self) } } impl Clone for WeakViewHandle { fn clone(&self) -> Self { Self { window_id: self.window_id, view_id: self.view_id, view_type: PhantomData, } } } impl PartialEq for WeakViewHandle { fn eq(&self, other: &Self) -> bool { self.window_id == other.window_id && self.view_id == other.view_id } } impl Eq for WeakViewHandle {} impl Hash for WeakViewHandle { fn hash(&self, state: &mut H) { self.window_id.hash(state); self.view_id.hash(state); } } pub struct AnyWeakViewHandle { window_id: usize, view_id: usize, view_type: TypeId, } impl AnyWeakViewHandle { pub fn upgrade(&self, cx: &impl UpgradeViewHandle) -> Option { cx.upgrade_any_view_handle(self) } } impl From> for AnyWeakViewHandle { fn from(handle: WeakViewHandle) -> Self { AnyWeakViewHandle { window_id: handle.window_id, view_id: handle.view_id, view_type: TypeId::of::(), } } } #[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub struct ElementStateId { view_id: usize, element_id: usize, tag: TypeId, } pub struct ElementStateHandle { value_type: PhantomData, id: ElementStateId, ref_counts: Weak>, } impl ElementStateHandle { fn new(id: ElementStateId, frame_id: usize, ref_counts: &Arc>) -> Self { ref_counts.lock().inc_element_state(id, frame_id); Self { value_type: PhantomData, id, ref_counts: Arc::downgrade(ref_counts), } } pub fn id(&self) -> ElementStateId { self.id } pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T { cx.element_states .get(&self.id) .unwrap() .downcast_ref() .unwrap() } pub fn update(&self, cx: &mut C, f: impl FnOnce(&mut T, &mut C) -> R) -> R where C: DerefMut, { let mut element_state = cx.deref_mut().cx.element_states.remove(&self.id).unwrap(); let result = f(element_state.downcast_mut().unwrap(), cx); cx.deref_mut() .cx .element_states .insert(self.id, element_state); result } } impl Drop for ElementStateHandle { fn drop(&mut self) { if let Some(ref_counts) = self.ref_counts.upgrade() { ref_counts.lock().dec_element_state(self.id); } } } #[must_use] pub enum Subscription { Subscription { id: usize, entity_id: usize, subscriptions: Option>>, }, GlobalSubscription { id: usize, type_id: TypeId, subscriptions: Option>>, }, Observation { id: usize, entity_id: usize, observations: Option>>, }, GlobalObservation { id: usize, type_id: TypeId, observations: Option>>, }, FocusObservation { id: usize, view_id: usize, observations: Option>>, }, WindowActivationObservation { id: usize, window_id: usize, observations: Option>>, }, WindowFullscreenObservation { id: usize, window_id: usize, observations: Option>>, }, ReleaseObservation { id: usize, entity_id: usize, #[allow(clippy::type_complexity)] observations: Option>>>>, }, ActionObservation { id: usize, observations: Option>>>, }, } impl Subscription { pub fn detach(&mut self) { match self { Subscription::Subscription { subscriptions, .. } => { subscriptions.take(); } Subscription::GlobalSubscription { subscriptions, .. } => { subscriptions.take(); } Subscription::Observation { observations, .. } => { observations.take(); } Subscription::GlobalObservation { observations, .. } => { observations.take(); } Subscription::ReleaseObservation { observations, .. } => { observations.take(); } Subscription::FocusObservation { observations, .. } => { observations.take(); } Subscription::ActionObservation { observations, .. } => { observations.take(); } Subscription::WindowActivationObservation { observations, .. } => { observations.take(); } Subscription::WindowFullscreenObservation { observations, .. } => { observations.take(); } } } } impl Drop for Subscription { fn drop(&mut self) { match self { Subscription::Subscription { id, entity_id, subscriptions, } => { if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) { match subscriptions .lock() .entry(*entity_id) .or_default() .entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::GlobalSubscription { id, type_id, subscriptions, } => { if let Some(subscriptions) = subscriptions.as_ref().and_then(Weak::upgrade) { match subscriptions.lock().entry(*type_id).or_default().entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::Observation { id, entity_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { match observations .lock() .entry(*entity_id) .or_default() .entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::GlobalObservation { id, type_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { match observations.lock().entry(*type_id).or_default().entry(*id) { collections::btree_map::Entry::Vacant(entry) => { entry.insert(None); } collections::btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::ReleaseObservation { id, entity_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { if let Some(observations) = observations.lock().get_mut(entity_id) { observations.remove(id); } } } Subscription::FocusObservation { id, view_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { match observations.lock().entry(*view_id).or_default().entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::ActionObservation { id, observations } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { observations.lock().remove(id); } } Subscription::WindowActivationObservation { id, window_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { match observations .lock() .entry(*window_id) .or_default() .entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } Subscription::WindowFullscreenObservation { id, window_id, observations, } => { if let Some(observations) = observations.as_ref().and_then(Weak::upgrade) { match observations .lock() .entry(*window_id) .or_default() .entry(*id) { btree_map::Entry::Vacant(entry) => { entry.insert(None); } btree_map::Entry::Occupied(entry) => { entry.remove(); } } } } } } } lazy_static! { static ref LEAK_BACKTRACE: bool = std::env::var("LEAK_BACKTRACE").map_or(false, |b| !b.is_empty()); } #[cfg(any(test, feature = "test-support"))] #[derive(Default)] pub struct LeakDetector { next_handle_id: usize, #[allow(clippy::type_complexity)] handle_backtraces: HashMap< usize, ( Option<&'static str>, HashMap>, ), >, } #[cfg(any(test, feature = "test-support"))] impl LeakDetector { fn handle_created(&mut self, type_name: Option<&'static str>, entity_id: usize) -> usize { let handle_id = post_inc(&mut self.next_handle_id); let entry = self.handle_backtraces.entry(entity_id).or_default(); let backtrace = if *LEAK_BACKTRACE { Some(backtrace::Backtrace::new_unresolved()) } else { None }; if let Some(type_name) = type_name { entry.0.get_or_insert(type_name); } entry.1.insert(handle_id, backtrace); handle_id } fn handle_dropped(&mut self, entity_id: usize, handle_id: usize) { if let Some((_, backtraces)) = self.handle_backtraces.get_mut(&entity_id) { assert!(backtraces.remove(&handle_id).is_some()); if backtraces.is_empty() { self.handle_backtraces.remove(&entity_id); } } } pub fn assert_dropped(&mut self, entity_id: usize) { if let Some((type_name, backtraces)) = self.handle_backtraces.get_mut(&entity_id) { for trace in backtraces.values_mut().flatten() { trace.resolve(); eprintln!("{:?}", crate::util::CwdBacktrace(trace)); } let hint = if *LEAK_BACKTRACE { "" } else { " – set LEAK_BACKTRACE=1 for more information" }; panic!( "{} handles to {} {} still exist{}", backtraces.len(), type_name.unwrap_or("entity"), entity_id, hint ); } } pub fn detect(&mut self) { let mut found_leaks = false; for (id, (type_name, backtraces)) in self.handle_backtraces.iter_mut() { eprintln!( "leaked {} handles to {} {}", backtraces.len(), type_name.unwrap_or("entity"), id ); for trace in backtraces.values_mut().flatten() { trace.resolve(); eprintln!("{:?}", crate::util::CwdBacktrace(trace)); } found_leaks = true; } let hint = if *LEAK_BACKTRACE { "" } else { " – set LEAK_BACKTRACE=1 for more information" }; assert!(!found_leaks, "detected leaked handles{}", hint); } } #[derive(Default)] struct RefCounts { entity_counts: HashMap, element_state_counts: HashMap, dropped_models: HashSet, dropped_views: HashSet<(usize, usize)>, dropped_element_states: HashSet, #[cfg(any(test, feature = "test-support"))] leak_detector: Arc>, } struct ElementStateRefCount { ref_count: usize, frame_id: usize, } impl RefCounts { fn inc_model(&mut self, model_id: usize) { match self.entity_counts.entry(model_id) { Entry::Occupied(mut entry) => { *entry.get_mut() += 1; } Entry::Vacant(entry) => { entry.insert(1); self.dropped_models.remove(&model_id); } } } fn inc_view(&mut self, window_id: usize, view_id: usize) { match self.entity_counts.entry(view_id) { Entry::Occupied(mut entry) => *entry.get_mut() += 1, Entry::Vacant(entry) => { entry.insert(1); self.dropped_views.remove(&(window_id, view_id)); } } } fn inc_element_state(&mut self, id: ElementStateId, frame_id: usize) { match self.element_state_counts.entry(id) { Entry::Occupied(mut entry) => { let entry = entry.get_mut(); if entry.frame_id == frame_id || entry.ref_count >= 2 { panic!("used the same element state more than once in the same frame"); } entry.ref_count += 1; entry.frame_id = frame_id; } Entry::Vacant(entry) => { entry.insert(ElementStateRefCount { ref_count: 1, frame_id, }); self.dropped_element_states.remove(&id); } } } fn dec_model(&mut self, model_id: usize) { let count = self.entity_counts.get_mut(&model_id).unwrap(); *count -= 1; if *count == 0 { self.entity_counts.remove(&model_id); self.dropped_models.insert(model_id); } } fn dec_view(&mut self, window_id: usize, view_id: usize) { let count = self.entity_counts.get_mut(&view_id).unwrap(); *count -= 1; if *count == 0 { self.entity_counts.remove(&view_id); self.dropped_views.insert((window_id, view_id)); } } fn dec_element_state(&mut self, id: ElementStateId) { let entry = self.element_state_counts.get_mut(&id).unwrap(); entry.ref_count -= 1; if entry.ref_count == 0 { self.element_state_counts.remove(&id); self.dropped_element_states.insert(id); } } fn is_entity_alive(&self, entity_id: usize) -> bool { self.entity_counts.contains_key(&entity_id) } fn take_dropped( &mut self, ) -> ( HashSet, HashSet<(usize, usize)>, HashSet, ) { ( std::mem::take(&mut self.dropped_models), std::mem::take(&mut self.dropped_views), std::mem::take(&mut self.dropped_element_states), ) } } #[cfg(test)] mod tests { use super::*; use crate::{actions, elements::*, impl_actions, MouseButton, MouseButtonEvent}; use serde::Deserialize; use smol::future::poll_once; use std::{ cell::Cell, sync::atomic::{AtomicBool, AtomicUsize, Ordering::SeqCst}, }; #[crate::test(self)] fn test_model_handles(cx: &mut MutableAppContext) { struct Model { other: Option>, events: Vec, } impl Entity for Model { type Event = usize; } impl Model { fn new(other: Option>, cx: &mut ModelContext) -> Self { if let Some(other) = other.as_ref() { cx.observe(other, |me, _, _| { me.events.push("notified".into()); }) .detach(); cx.subscribe(other, |me, _, event, _| { me.events.push(format!("observed event {}", event)); }) .detach(); } Self { other, events: Vec::new(), } } } let handle_1 = cx.add_model(|cx| Model::new(None, cx)); let handle_2 = cx.add_model(|cx| Model::new(Some(handle_1.clone()), cx)); assert_eq!(cx.cx.models.len(), 2); handle_1.update(cx, |model, cx| { model.events.push("updated".into()); cx.emit(1); cx.notify(); cx.emit(2); }); assert_eq!(handle_1.read(cx).events, vec!["updated".to_string()]); assert_eq!( handle_2.read(cx).events, vec![ "observed event 1".to_string(), "notified".to_string(), "observed event 2".to_string(), ] ); handle_2.update(cx, |model, _| { drop(handle_1); model.other.take(); }); assert_eq!(cx.cx.models.len(), 1); assert!(cx.subscriptions.is_empty()); assert!(cx.observations.is_empty()); } #[crate::test(self)] fn test_model_events(cx: &mut MutableAppContext) { #[derive(Default)] struct Model { events: Vec, } impl Entity for Model { type Event = usize; } let handle_1 = cx.add_model(|_| Model::default()); let handle_2 = cx.add_model(|_| Model::default()); handle_1.update(cx, |_, cx| { cx.subscribe(&handle_2, move |model: &mut Model, emitter, event, cx| { model.events.push(*event); cx.subscribe(&emitter, |model, _, event, _| { model.events.push(*event * 2); }) .detach(); }) .detach(); }); handle_2.update(cx, |_, c| c.emit(7)); assert_eq!(handle_1.read(cx).events, vec![7]); handle_2.update(cx, |_, c| c.emit(5)); assert_eq!(handle_1.read(cx).events, vec![7, 5, 10]); } #[crate::test(self)] fn test_model_emit_before_subscribe_in_same_update_cycle(cx: &mut MutableAppContext) { #[derive(Default)] struct Model; impl Entity for Model { type Event = (); } let events = Rc::new(RefCell::new(Vec::new())); cx.add_model(|cx| { drop(cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("dropped before flush") })); cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("before emit") }) .detach(); cx.emit(()); cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("after emit") }) .detach(); Model }); assert_eq!(*events.borrow(), ["before emit"]); } #[crate::test(self)] fn test_observe_and_notify_from_model(cx: &mut MutableAppContext) { #[derive(Default)] struct Model { count: usize, events: Vec, } impl Entity for Model { type Event = (); } let handle_1 = cx.add_model(|_| Model::default()); let handle_2 = cx.add_model(|_| Model::default()); handle_1.update(cx, |_, c| { c.observe(&handle_2, move |model, observed, c| { model.events.push(observed.read(c).count); c.observe(&observed, |model, observed, c| { model.events.push(observed.read(c).count * 2); }) .detach(); }) .detach(); }); handle_2.update(cx, |model, c| { model.count = 7; c.notify() }); assert_eq!(handle_1.read(cx).events, vec![7]); handle_2.update(cx, |model, c| { model.count = 5; c.notify() }); assert_eq!(handle_1.read(cx).events, vec![7, 5, 10]) } #[crate::test(self)] fn test_model_notify_before_observe_in_same_update_cycle(cx: &mut MutableAppContext) { #[derive(Default)] struct Model; impl Entity for Model { type Event = (); } let events = Rc::new(RefCell::new(Vec::new())); cx.add_model(|cx| { drop(cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("dropped before flush") })); cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("before notify") }) .detach(); cx.notify(); cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("after notify") }) .detach(); Model }); assert_eq!(*events.borrow(), ["before notify"]); } #[crate::test(self)] fn test_defer_and_after_window_update(cx: &mut MutableAppContext) { struct View { render_count: usize, } impl Entity for View { type Event = usize; } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { post_inc(&mut self.render_count); Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } let (_, view) = cx.add_window(Default::default(), |_| View { render_count: 0 }); let called_defer = Rc::new(AtomicBool::new(false)); let called_after_window_update = Rc::new(AtomicBool::new(false)); view.update(cx, |this, cx| { assert_eq!(this.render_count, 1); cx.defer({ let called_defer = called_defer.clone(); move |this, _| { assert_eq!(this.render_count, 1); called_defer.store(true, SeqCst); } }); cx.after_window_update({ let called_after_window_update = called_after_window_update.clone(); move |this, cx| { assert_eq!(this.render_count, 2); called_after_window_update.store(true, SeqCst); cx.notify(); } }); assert!(!called_defer.load(SeqCst)); assert!(!called_after_window_update.load(SeqCst)); cx.notify(); }); assert!(called_defer.load(SeqCst)); assert!(called_after_window_update.load(SeqCst)); assert_eq!(view.read(cx).render_count, 3); } #[crate::test(self)] fn test_view_handles(cx: &mut MutableAppContext) { struct View { other: Option>, events: Vec, } impl Entity for View { type Event = usize; } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } impl View { fn new(other: Option>, cx: &mut ViewContext) -> Self { if let Some(other) = other.as_ref() { cx.subscribe(other, |me, _, event, _| { me.events.push(format!("observed event {}", event)); }) .detach(); } Self { other, events: Vec::new(), } } } let (_, root_view) = cx.add_window(Default::default(), |cx| View::new(None, cx)); let handle_1 = cx.add_view(&root_view, |cx| View::new(None, cx)); let handle_2 = cx.add_view(&root_view, |cx| View::new(Some(handle_1.clone()), cx)); assert_eq!(cx.cx.views.len(), 3); handle_1.update(cx, |view, cx| { view.events.push("updated".into()); cx.emit(1); cx.emit(2); }); assert_eq!(handle_1.read(cx).events, vec!["updated".to_string()]); assert_eq!( handle_2.read(cx).events, vec![ "observed event 1".to_string(), "observed event 2".to_string(), ] ); handle_2.update(cx, |view, _| { drop(handle_1); view.other.take(); }); assert_eq!(cx.cx.views.len(), 2); assert!(cx.subscriptions.is_empty()); assert!(cx.observations.is_empty()); } #[crate::test(self)] fn test_add_window(cx: &mut MutableAppContext) { struct View { mouse_down_count: Arc, } impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, cx: &mut RenderContext) -> ElementBox { enum Handler {} let mouse_down_count = self.mouse_down_count.clone(); MouseEventHandler::::new(0, cx, |_, _| Empty::new().boxed()) .on_down(MouseButton::Left, move |_, _| { mouse_down_count.fetch_add(1, SeqCst); }) .boxed() } fn ui_name() -> &'static str { "View" } } let mouse_down_count = Arc::new(AtomicUsize::new(0)); let (window_id, _) = cx.add_window(Default::default(), |_| View { mouse_down_count: mouse_down_count.clone(), }); let presenter = cx.presenters_and_platform_windows[&window_id].0.clone(); // Ensure window's root element is in a valid lifecycle state. presenter.borrow_mut().dispatch_event( Event::MouseDown(MouseButtonEvent { position: Default::default(), button: MouseButton::Left, ctrl: false, alt: false, shift: false, cmd: false, click_count: 1, }), false, cx, ); assert_eq!(mouse_down_count.load(SeqCst), 1); } #[crate::test(self)] fn test_entity_release_hooks(cx: &mut MutableAppContext) { struct Model { released: Rc>, } struct View { released: Rc>, } impl Entity for Model { type Event = (); fn release(&mut self, _: &mut MutableAppContext) { self.released.set(true); } } impl Entity for View { type Event = (); fn release(&mut self, _: &mut MutableAppContext) { self.released.set(true); } } impl super::View for View { fn ui_name() -> &'static str { "View" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let model_released = Rc::new(Cell::new(false)); let model_release_observed = Rc::new(Cell::new(false)); let view_released = Rc::new(Cell::new(false)); let view_release_observed = Rc::new(Cell::new(false)); let model = cx.add_model(|_| Model { released: model_released.clone(), }); let (window_id, view) = cx.add_window(Default::default(), |_| View { released: view_released.clone(), }); assert!(!model_released.get()); assert!(!view_released.get()); cx.observe_release(&model, { let model_release_observed = model_release_observed.clone(); move |_, _| model_release_observed.set(true) }) .detach(); cx.observe_release(&view, { let view_release_observed = view_release_observed.clone(); move |_, _| view_release_observed.set(true) }) .detach(); cx.update(move |_| { drop(model); }); assert!(model_released.get()); assert!(model_release_observed.get()); drop(view); cx.remove_window(window_id); assert!(view_released.get()); assert!(view_release_observed.get()); } #[crate::test(self)] fn test_view_events(cx: &mut MutableAppContext) { #[derive(Default)] struct View { events: Vec, } impl Entity for View { type Event = usize; } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } struct Model; impl Entity for Model { type Event = usize; } let (_, handle_1) = cx.add_window(Default::default(), |_| View::default()); let handle_2 = cx.add_view(&handle_1, |_| View::default()); let handle_3 = cx.add_model(|_| Model); handle_1.update(cx, |_, cx| { cx.subscribe(&handle_2, move |me, emitter, event, cx| { me.events.push(*event); cx.subscribe(&emitter, |me, _, event, _| { me.events.push(*event * 2); }) .detach(); }) .detach(); cx.subscribe(&handle_3, |me, _, event, _| { me.events.push(*event); }) .detach(); }); handle_2.update(cx, |_, c| c.emit(7)); assert_eq!(handle_1.read(cx).events, vec![7]); handle_2.update(cx, |_, c| c.emit(5)); assert_eq!(handle_1.read(cx).events, vec![7, 5, 10]); handle_3.update(cx, |_, c| c.emit(9)); assert_eq!(handle_1.read(cx).events, vec![7, 5, 10, 9]); } #[crate::test(self)] fn test_global_events(cx: &mut MutableAppContext) { #[derive(Clone, Debug, Eq, PartialEq)] struct GlobalEvent(u64); let events = Rc::new(RefCell::new(Vec::new())); let first_subscription; let second_subscription; { let events = events.clone(); first_subscription = cx.subscribe_global(move |e: &GlobalEvent, _| { events.borrow_mut().push(("First", e.clone())); }); } { let events = events.clone(); second_subscription = cx.subscribe_global(move |e: &GlobalEvent, _| { events.borrow_mut().push(("Second", e.clone())); }); } cx.update(|cx| { cx.emit_global(GlobalEvent(1)); cx.emit_global(GlobalEvent(2)); }); drop(first_subscription); cx.update(|cx| { cx.emit_global(GlobalEvent(3)); }); drop(second_subscription); cx.update(|cx| { cx.emit_global(GlobalEvent(4)); }); assert_eq!( &*events.borrow(), &[ ("First", GlobalEvent(1)), ("Second", GlobalEvent(1)), ("First", GlobalEvent(2)), ("Second", GlobalEvent(2)), ("Second", GlobalEvent(3)), ] ); } #[crate::test(self)] fn test_global_events_emitted_before_subscription_in_same_update_cycle( cx: &mut MutableAppContext, ) { let events = Rc::new(RefCell::new(Vec::new())); cx.update(|cx| { { let events = events.clone(); drop(cx.subscribe_global(move |_: &(), _| { events.borrow_mut().push("dropped before emit"); })); } { let events = events.clone(); cx.subscribe_global(move |_: &(), _| { events.borrow_mut().push("before emit"); }) .detach(); } cx.emit_global(()); { let events = events.clone(); cx.subscribe_global(move |_: &(), _| { events.borrow_mut().push("after emit"); }) .detach(); } }); assert_eq!(*events.borrow(), ["before emit"]); } #[crate::test(self)] fn test_global_nested_events(cx: &mut MutableAppContext) { #[derive(Clone, Debug, Eq, PartialEq)] struct GlobalEvent(u64); let events = Rc::new(RefCell::new(Vec::new())); { let events = events.clone(); cx.subscribe_global(move |e: &GlobalEvent, cx| { events.borrow_mut().push(("Outer", e.clone())); if e.0 == 1 { let events = events.clone(); cx.subscribe_global(move |e: &GlobalEvent, _| { events.borrow_mut().push(("Inner", e.clone())); }) .detach(); } }) .detach(); } cx.update(|cx| { cx.emit_global(GlobalEvent(1)); cx.emit_global(GlobalEvent(2)); cx.emit_global(GlobalEvent(3)); }); cx.update(|cx| { cx.emit_global(GlobalEvent(4)); }); assert_eq!( &*events.borrow(), &[ ("Outer", GlobalEvent(1)), ("Outer", GlobalEvent(2)), ("Outer", GlobalEvent(3)), ("Outer", GlobalEvent(4)), ("Inner", GlobalEvent(4)), ] ); } #[crate::test(self)] fn test_global(cx: &mut MutableAppContext) { type Global = usize; let observation_count = Rc::new(RefCell::new(0)); let subscription = cx.observe_global::({ let observation_count = observation_count.clone(); move |_| { *observation_count.borrow_mut() += 1; } }); assert!(!cx.has_global::()); assert_eq!(cx.default_global::(), &0); assert_eq!(*observation_count.borrow(), 1); assert!(cx.has_global::()); assert_eq!( cx.update_global::(|global, _| { *global = 1; "Update Result" }), "Update Result" ); assert_eq!(*observation_count.borrow(), 2); assert_eq!(cx.global::(), &1); drop(subscription); cx.update_global::(|global, _| { *global = 2; }); assert_eq!(*observation_count.borrow(), 2); type OtherGlobal = f32; let observation_count = Rc::new(RefCell::new(0)); cx.observe_global::({ let observation_count = observation_count.clone(); move |_| { *observation_count.borrow_mut() += 1; } }) .detach(); assert_eq!( cx.update_default_global::(|global, _| { assert_eq!(global, &0.0); *global = 2.0; "Default update result" }), "Default update result" ); assert_eq!(cx.global::(), &2.0); assert_eq!(*observation_count.borrow(), 1); } #[crate::test(self)] fn test_dropping_subscribers(cx: &mut MutableAppContext) { struct View; impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } struct Model; impl Entity for Model { type Event = (); } let (_, root_view) = cx.add_window(Default::default(), |_| View); let observing_view = cx.add_view(&root_view, |_| View); let emitting_view = cx.add_view(&root_view, |_| View); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); observing_view.update(cx, |_, cx| { cx.subscribe(&emitting_view, |_, _, _, _| {}).detach(); cx.subscribe(&observed_model, |_, _, _, _| {}).detach(); }); observing_model.update(cx, |_, cx| { cx.subscribe(&observed_model, |_, _, _, _| {}).detach(); }); cx.update(|_| { drop(observing_view); drop(observing_model); }); emitting_view.update(cx, |_, cx| cx.emit(())); observed_model.update(cx, |_, cx| cx.emit(())); } #[crate::test(self)] fn test_view_emit_before_subscribe_in_same_update_cycle(cx: &mut MutableAppContext) { #[derive(Default)] struct TestView; impl Entity for TestView { type Event = (); } impl View for TestView { fn ui_name() -> &'static str { "TestView" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let events = Rc::new(RefCell::new(Vec::new())); cx.add_window(Default::default(), |cx| { drop(cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("dropped before flush") })); cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("before emit") }) .detach(); cx.emit(()); cx.subscribe(&cx.handle(), { let events = events.clone(); move |_, _, _, _| events.borrow_mut().push("after emit") }) .detach(); TestView }); assert_eq!(*events.borrow(), ["before emit"]); } #[crate::test(self)] fn test_observe_and_notify_from_view(cx: &mut MutableAppContext) { #[derive(Default)] struct View { events: Vec, } impl Entity for View { type Event = usize; } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } #[derive(Default)] struct Model { count: usize, } impl Entity for Model { type Event = (); } let (_, view) = cx.add_window(Default::default(), |_| View::default()); let model = cx.add_model(|_| Model::default()); view.update(cx, |_, c| { c.observe(&model, |me, observed, c| { me.events.push(observed.read(c).count) }) .detach(); }); model.update(cx, |model, c| { model.count = 11; c.notify(); }); assert_eq!(view.read(cx).events, vec![11]); } #[crate::test(self)] fn test_view_notify_before_observe_in_same_update_cycle(cx: &mut MutableAppContext) { #[derive(Default)] struct TestView; impl Entity for TestView { type Event = (); } impl View for TestView { fn ui_name() -> &'static str { "TestView" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let events = Rc::new(RefCell::new(Vec::new())); cx.add_window(Default::default(), |cx| { drop(cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("dropped before flush") })); cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("before notify") }) .detach(); cx.notify(); cx.observe(&cx.handle(), { let events = events.clone(); move |_, _, _| events.borrow_mut().push("after notify") }) .detach(); TestView }); assert_eq!(*events.borrow(), ["before notify"]); } #[crate::test(self)] fn test_dropping_observers(cx: &mut MutableAppContext) { struct View; impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } struct Model; impl Entity for Model { type Event = (); } let (_, root_view) = cx.add_window(Default::default(), |_| View); let observing_view = cx.add_view(root_view, |_| View); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); observing_view.update(cx, |_, cx| { cx.observe(&observed_model, |_, _, _| {}).detach(); }); observing_model.update(cx, |_, cx| { cx.observe(&observed_model, |_, _, _| {}).detach(); }); cx.update(|_| { drop(observing_view); drop(observing_model); }); observed_model.update(cx, |_, cx| cx.notify()); } #[crate::test(self)] fn test_dropping_subscriptions_during_callback(cx: &mut MutableAppContext) { struct Model; impl Entity for Model { type Event = u64; } // Events let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); let events = Rc::new(RefCell::new(Vec::new())); observing_model.update(cx, |_, cx| { let events = events.clone(); let subscription = Rc::new(RefCell::new(None)); *subscription.borrow_mut() = Some(cx.subscribe(&observed_model, { let subscription = subscription.clone(); move |_, _, e, _| { subscription.borrow_mut().take(); events.borrow_mut().push(*e); } })); }); observed_model.update(cx, |_, cx| { cx.emit(1); cx.emit(2); }); assert_eq!(*events.borrow(), [1]); // Global Events #[derive(Clone, Debug, Eq, PartialEq)] struct GlobalEvent(u64); let events = Rc::new(RefCell::new(Vec::new())); { let events = events.clone(); let subscription = Rc::new(RefCell::new(None)); *subscription.borrow_mut() = Some(cx.subscribe_global({ let subscription = subscription.clone(); move |e: &GlobalEvent, _| { subscription.borrow_mut().take(); events.borrow_mut().push(e.clone()); } })); } cx.update(|cx| { cx.emit_global(GlobalEvent(1)); cx.emit_global(GlobalEvent(2)); }); assert_eq!(*events.borrow(), [GlobalEvent(1)]); // Model Observation let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); let observation_count = Rc::new(RefCell::new(0)); observing_model.update(cx, |_, cx| { let observation_count = observation_count.clone(); let subscription = Rc::new(RefCell::new(None)); *subscription.borrow_mut() = Some(cx.observe(&observed_model, { let subscription = subscription.clone(); move |_, _, _| { subscription.borrow_mut().take(); *observation_count.borrow_mut() += 1; } })); }); observed_model.update(cx, |_, cx| { cx.notify(); }); observed_model.update(cx, |_, cx| { cx.notify(); }); assert_eq!(*observation_count.borrow(), 1); // View Observation struct View; impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } let (_, root_view) = cx.add_window(Default::default(), |_| View); let observing_view = cx.add_view(&root_view, |_| View); let observed_view = cx.add_view(&root_view, |_| View); let observation_count = Rc::new(RefCell::new(0)); observing_view.update(cx, |_, cx| { let observation_count = observation_count.clone(); let subscription = Rc::new(RefCell::new(None)); *subscription.borrow_mut() = Some(cx.observe(&observed_view, { let subscription = subscription.clone(); move |_, _, _| { subscription.borrow_mut().take(); *observation_count.borrow_mut() += 1; } })); }); observed_view.update(cx, |_, cx| { cx.notify(); }); observed_view.update(cx, |_, cx| { cx.notify(); }); assert_eq!(*observation_count.borrow(), 1); // Global Observation let observation_count = Rc::new(RefCell::new(0)); let subscription = Rc::new(RefCell::new(None)); *subscription.borrow_mut() = Some(cx.observe_global::<(), _>({ let observation_count = observation_count.clone(); let subscription = subscription.clone(); move |_| { subscription.borrow_mut().take(); *observation_count.borrow_mut() += 1; } })); cx.default_global::<()>(); cx.set_global(()); assert_eq!(*observation_count.borrow(), 1); } #[crate::test(self)] fn test_focus(cx: &mut MutableAppContext) { struct View { name: String, events: Arc>>, } impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { if cx.handle().id() == focused.id() { self.events.lock().push(format!("{} focused", &self.name)); } } fn on_focus_out(&mut self, blurred: AnyViewHandle, cx: &mut ViewContext) { if cx.handle().id() == blurred.id() { self.events.lock().push(format!("{} blurred", &self.name)); } } } let view_events: Arc>> = Default::default(); let (_, view_1) = cx.add_window(Default::default(), |_| View { events: view_events.clone(), name: "view 1".to_string(), }); let view_2 = cx.add_view(&view_1, |_| View { events: view_events.clone(), name: "view 2".to_string(), }); let observed_events: Arc>> = Default::default(); view_1.update(cx, |_, cx| { cx.observe_focus(&view_2, { let observed_events = observed_events.clone(); move |this, view, focused, cx| { let label = if focused { "focus" } else { "blur" }; observed_events.lock().push(format!( "{} observed {}'s {}", this.name, view.read(cx).name, label )) } }) .detach(); }); view_2.update(cx, |_, cx| { cx.observe_focus(&view_1, { let observed_events = observed_events.clone(); move |this, view, focused, cx| { let label = if focused { "focus" } else { "blur" }; observed_events.lock().push(format!( "{} observed {}'s {}", this.name, view.read(cx).name, label )) } }) .detach(); }); assert_eq!(mem::take(&mut *view_events.lock()), ["view 1 focused"]); assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new()); view_1.update(cx, |_, cx| { // Ensure only the latest focus is honored. cx.focus(&view_2); cx.focus(&view_1); cx.focus(&view_2); }); assert_eq!( mem::take(&mut *view_events.lock()), ["view 1 blurred", "view 2 focused"], ); assert_eq!( mem::take(&mut *observed_events.lock()), [ "view 2 observed view 1's blur", "view 1 observed view 2's focus" ] ); view_1.update(cx, |_, cx| cx.focus(&view_1)); assert_eq!( mem::take(&mut *view_events.lock()), ["view 2 blurred", "view 1 focused"], ); assert_eq!( mem::take(&mut *observed_events.lock()), [ "view 1 observed view 2's blur", "view 2 observed view 1's focus" ] ); view_1.update(cx, |_, cx| cx.focus(&view_2)); assert_eq!( mem::take(&mut *view_events.lock()), ["view 1 blurred", "view 2 focused"], ); assert_eq!( mem::take(&mut *observed_events.lock()), [ "view 2 observed view 1's blur", "view 1 observed view 2's focus" ] ); view_1.update(cx, |_, _| drop(view_2)); assert_eq!(mem::take(&mut *view_events.lock()), ["view 1 focused"]); assert_eq!(mem::take(&mut *observed_events.lock()), Vec::<&str>::new()); } #[crate::test(self)] fn test_deserialize_actions(cx: &mut MutableAppContext) { #[derive(Clone, Debug, Deserialize, PartialEq, Eq)] pub struct ComplexAction { arg: String, count: usize, } actions!(test::something, [SimpleAction]); impl_actions!(test::something, [ComplexAction]); cx.add_global_action(move |_: &SimpleAction, _: &mut MutableAppContext| {}); cx.add_global_action(move |_: &ComplexAction, _: &mut MutableAppContext| {}); let action1 = cx .deserialize_action( "test::something::ComplexAction", Some(r#"{"arg": "a", "count": 5}"#), ) .unwrap(); let action2 = cx .deserialize_action("test::something::SimpleAction", None) .unwrap(); assert_eq!( action1.as_any().downcast_ref::().unwrap(), &ComplexAction { arg: "a".to_string(), count: 5, } ); assert_eq!( action2.as_any().downcast_ref::().unwrap(), &SimpleAction ); } #[crate::test(self)] fn test_dispatch_action(cx: &mut MutableAppContext) { struct ViewA { id: usize, } impl Entity for ViewA { type Event = (); } impl View for ViewA { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } struct ViewB { id: usize, } impl Entity for ViewB { type Event = (); } impl View for ViewB { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } } #[derive(Clone, Default, Deserialize, PartialEq)] pub struct Action(pub String); impl_actions!(test, [Action]); let actions = Rc::new(RefCell::new(Vec::new())); cx.add_global_action({ let actions = actions.clone(); move |_: &Action, _: &mut MutableAppContext| { actions.borrow_mut().push("global".to_string()); } }); cx.add_action({ let actions = actions.clone(); move |view: &mut ViewA, action: &Action, cx| { assert_eq!(action.0, "bar"); cx.propagate_action(); actions.borrow_mut().push(format!("{} a", view.id)); } }); cx.add_action({ let actions = actions.clone(); move |view: &mut ViewA, _: &Action, cx| { if view.id != 1 { cx.add_view(|cx| { cx.propagate_action(); // Still works on a nested ViewContext ViewB { id: 5 } }); } actions.borrow_mut().push(format!("{} b", view.id)); } }); cx.add_action({ let actions = actions.clone(); move |view: &mut ViewB, _: &Action, cx| { cx.propagate_action(); actions.borrow_mut().push(format!("{} c", view.id)); } }); cx.add_action({ let actions = actions.clone(); move |view: &mut ViewB, _: &Action, cx| { cx.propagate_action(); actions.borrow_mut().push(format!("{} d", view.id)); } }); cx.capture_action({ let actions = actions.clone(); move |view: &mut ViewA, _: &Action, cx| { cx.propagate_action(); actions.borrow_mut().push(format!("{} capture", view.id)); } }); let observed_actions = Rc::new(RefCell::new(Vec::new())); cx.observe_actions({ let observed_actions = observed_actions.clone(); move |action_id, _| observed_actions.borrow_mut().push(action_id) }) .detach(); let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 }); let view_2 = cx.add_view(&view_1, |_| ViewB { id: 2 }); let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 }); let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 }); cx.handle_dispatch_action_from_effect( window_id, Some(view_4.id()), &Action("bar".to_string()), ); assert_eq!( *actions.borrow(), vec![ "1 capture", "3 capture", "4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "1 b" ] ); assert_eq!(*observed_actions.borrow(), [Action::default().id()]); // Remove view_1, which doesn't propagate the action let (window_id, view_2) = cx.add_window(Default::default(), |_| ViewB { id: 2 }); let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 }); let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 }); actions.borrow_mut().clear(); cx.handle_dispatch_action_from_effect( window_id, Some(view_4.id()), &Action("bar".to_string()), ); assert_eq!( *actions.borrow(), vec![ "3 capture", "4 d", "4 c", "3 b", "3 a", "2 d", "2 c", "global" ] ); assert_eq!( *observed_actions.borrow(), [Action::default().id(), Action::default().id()] ); } #[crate::test(self)] fn test_dispatch_keystroke(cx: &mut MutableAppContext) { #[derive(Clone, Deserialize, PartialEq)] pub struct Action(String); impl_actions!(test, [Action]); struct View { id: usize, keymap_context: keymap::Context, } impl Entity for View { type Event = (); } impl super::View for View { fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } fn ui_name() -> &'static str { "View" } fn keymap_context(&self, _: &AppContext) -> keymap::Context { self.keymap_context.clone() } } impl View { fn new(id: usize) -> Self { View { id, keymap_context: keymap::Context::default(), } } } let mut view_1 = View::new(1); let mut view_2 = View::new(2); let mut view_3 = View::new(3); view_1.keymap_context.set.insert("a".into()); view_2.keymap_context.set.insert("a".into()); view_2.keymap_context.set.insert("b".into()); view_3.keymap_context.set.insert("a".into()); view_3.keymap_context.set.insert("b".into()); view_3.keymap_context.set.insert("c".into()); let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1); let view_2 = cx.add_view(&view_1, |_| view_2); let _view_3 = cx.add_view(&view_2, |cx| { cx.focus_self(); view_3 }); // This keymap's only binding dispatches an action on view 2 because that view will have // "a" and "b" in its context, but not "c". cx.add_bindings(vec![keymap::Binding::new( "a", Action("a".to_string()), Some("a && b && !c"), )]); cx.add_bindings(vec![keymap::Binding::new( "b", Action("b".to_string()), None, )]); let actions = Rc::new(RefCell::new(Vec::new())); cx.add_action({ let actions = actions.clone(); move |view: &mut View, action: &Action, cx| { if action.0 == "a" { actions.borrow_mut().push(format!("{} a", view.id)); } else { actions .borrow_mut() .push(format!("{} {}", view.id, action.0)); cx.propagate_action(); } } }); cx.add_global_action({ let actions = actions.clone(); move |action: &Action, _| { actions.borrow_mut().push(format!("global {}", action.0)); } }); cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap()); assert_eq!(&*actions.borrow(), &["2 a"]); actions.borrow_mut().clear(); cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap()); assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]); } #[crate::test(self)] async fn test_model_condition(cx: &mut TestAppContext) { struct Counter(usize); impl super::Entity for Counter { type Event = (); } impl Counter { fn inc(&mut self, cx: &mut ModelContext) { self.0 += 1; cx.notify(); } } let model = cx.add_model(|_| Counter(0)); let condition1 = model.condition(cx, |model, _| model.0 == 2); let condition2 = model.condition(cx, |model, _| model.0 == 3); smol::pin!(condition1, condition2); model.update(cx, |model, cx| model.inc(cx)); assert_eq!(poll_once(&mut condition1).await, None); assert_eq!(poll_once(&mut condition2).await, None); model.update(cx, |model, cx| model.inc(cx)); assert_eq!(poll_once(&mut condition1).await, Some(())); assert_eq!(poll_once(&mut condition2).await, None); model.update(cx, |model, cx| model.inc(cx)); assert_eq!(poll_once(&mut condition2).await, Some(())); model.update(cx, |_, cx| cx.notify()); } #[crate::test(self)] #[should_panic] async fn test_model_condition_timeout(cx: &mut TestAppContext) { struct Model; impl super::Entity for Model { type Event = (); } let model = cx.add_model(|_| Model); model.condition(cx, |_, _| false).await; } #[crate::test(self)] #[should_panic(expected = "model dropped with pending condition")] async fn test_model_condition_panic_on_drop(cx: &mut TestAppContext) { struct Model; impl super::Entity for Model { type Event = (); } let model = cx.add_model(|_| Model); let condition = model.condition(cx, |_, _| false); cx.update(|_| drop(model)); condition.await; } #[crate::test(self)] async fn test_view_condition(cx: &mut TestAppContext) { struct Counter(usize); impl super::Entity for Counter { type Event = (); } impl super::View for Counter { fn ui_name() -> &'static str { "test view" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } impl Counter { fn inc(&mut self, cx: &mut ViewContext) { self.0 += 1; cx.notify(); } } let (_, view) = cx.add_window(|_| Counter(0)); let condition1 = view.condition(cx, |view, _| view.0 == 2); let condition2 = view.condition(cx, |view, _| view.0 == 3); smol::pin!(condition1, condition2); view.update(cx, |view, cx| view.inc(cx)); assert_eq!(poll_once(&mut condition1).await, None); assert_eq!(poll_once(&mut condition2).await, None); view.update(cx, |view, cx| view.inc(cx)); assert_eq!(poll_once(&mut condition1).await, Some(())); assert_eq!(poll_once(&mut condition2).await, None); view.update(cx, |view, cx| view.inc(cx)); assert_eq!(poll_once(&mut condition2).await, Some(())); view.update(cx, |_, cx| cx.notify()); } #[crate::test(self)] #[should_panic] async fn test_view_condition_timeout(cx: &mut TestAppContext) { struct View; impl super::Entity for View { type Event = (); } impl super::View for View { fn ui_name() -> &'static str { "test view" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let (_, view) = cx.add_window(|_| View); view.condition(cx, |_, _| false).await; } #[crate::test(self)] #[should_panic(expected = "view dropped with pending condition")] async fn test_view_condition_panic_on_drop(cx: &mut TestAppContext) { struct View; impl super::Entity for View { type Event = (); } impl super::View for View { fn ui_name() -> &'static str { "test view" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let (_, root_view) = cx.add_window(|_| View); let view = cx.add_view(&root_view, |_| View); let condition = view.condition(cx, |_, _| false); cx.update(|_| drop(view)); condition.await; } #[crate::test(self)] fn test_refresh_windows(cx: &mut MutableAppContext) { struct View(usize); impl super::Entity for View { type Event = (); } impl super::View for View { fn ui_name() -> &'static str { "test view" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().named(format!("render count: {}", post_inc(&mut self.0))) } } let (window_id, root_view) = cx.add_window(Default::default(), |_| View(0)); let presenter = cx.presenters_and_platform_windows[&window_id].0.clone(); assert_eq!( presenter.borrow().rendered_views[&root_view.id()].name(), Some("render count: 0") ); let view = cx.add_view(&root_view, |cx| { cx.refresh_windows(); View(0) }); assert_eq!( presenter.borrow().rendered_views[&root_view.id()].name(), Some("render count: 1") ); assert_eq!( presenter.borrow().rendered_views[&view.id()].name(), Some("render count: 0") ); cx.update(|cx| cx.refresh_windows()); assert_eq!( presenter.borrow().rendered_views[&root_view.id()].name(), Some("render count: 2") ); assert_eq!( presenter.borrow().rendered_views[&view.id()].name(), Some("render count: 1") ); cx.update(|cx| { cx.refresh_windows(); drop(view); }); assert_eq!( presenter.borrow().rendered_views[&root_view.id()].name(), Some("render count: 3") ); assert_eq!(presenter.borrow().rendered_views.len(), 1); } #[crate::test(self)] async fn test_window_activation(cx: &mut TestAppContext) { struct View(&'static str); impl super::Entity for View { type Event = (); } impl super::View for View { fn ui_name() -> &'static str { "test view" } fn render(&mut self, _: &mut RenderContext) -> ElementBox { Empty::new().boxed() } } let events = Rc::new(RefCell::new(Vec::new())); let (window_1, _) = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) }) .detach(); View("window 1") }); assert_eq!(mem::take(&mut *events.borrow_mut()), [("window 1", true)]); let (window_2, _) = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) }) .detach(); View("window 2") }); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 2", true)] ); let (window_3, _) = cx.add_window(|cx: &mut ViewContext| { cx.observe_window_activation({ let events = events.clone(); move |this, active, _| events.borrow_mut().push((this.0, active)) }) .detach(); View("window 3") }); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 3", true)] ); cx.simulate_window_activation(Some(window_2)); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 3", false), ("window 2", true)] ); cx.simulate_window_activation(Some(window_1)); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 2", false), ("window 1", true)] ); cx.simulate_window_activation(Some(window_3)); assert_eq!( mem::take(&mut *events.borrow_mut()), [("window 1", false), ("window 3", true)] ); cx.simulate_window_activation(Some(window_3)); assert_eq!(mem::take(&mut *events.borrow_mut()), []); } }