Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/app.rs                      | 109 +++++----
crates/gpui3/src/element.rs                  |   1 
crates/gpui3/src/executor.rs                 |  49 ++++
crates/gpui3/src/platform.rs                 |   8 
crates/gpui3/src/platform/mac/platform.rs    | 160 +++++++-------
crates/gpui3/src/platform/mac/window.rs      | 193 ++++++++---------
crates/gpui3/src/platform/test.rs            |   2 
crates/gpui3/src/text_system/line_wrapper.rs | 233 +++++++++++----------
crates/gpui3/src/window.rs                   |  16 
crates/storybook2/src/storybook2.rs          |   2 
10 files changed, 403 insertions(+), 370 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -3,13 +3,12 @@ use crate::{
     WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
-use async_task::Runnable;
-use futures::Future;
 use parking_lot::RwLock;
 use slotmap::SlotMap;
 use std::{
     any::Any,
     marker::PhantomData,
+    mem,
     sync::{Arc, Weak},
 };
 
@@ -19,17 +18,43 @@ pub struct App(Arc<RwLock<AppContext<()>>>);
 pub struct MainThread;
 
 impl App {
-    pub fn new() -> Self {
-        Self(Arc::new(RwLock::new(AppContext::new(current_platform()))))
+    pub fn production() -> Self {
+        Self::new(current_platform())
+    }
+
+    #[cfg(any(test, feature = "test"))]
+    pub fn test() -> Self {
+        Self::new(Arc::new(super::TestPlatform::new()))
+    }
+
+    fn new(platform: Arc<dyn Platform>) -> Self {
+        let text_system = Arc::new(TextSystem::new(platform.text_system()));
+        let mut entities = SlotMap::with_key();
+        let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any>));
+        Self(Arc::new_cyclic(|this| {
+            RwLock::new(AppContext {
+                this: this.clone(),
+                thread: PhantomData,
+                platform,
+                text_system,
+                unit_entity_id,
+                entities,
+                windows: SlotMap::with_key(),
+                layout_id_buffer: Default::default(),
+            })
+        }))
     }
 
     pub fn run<F>(self, on_finish_launching: F)
     where
         F: 'static + FnOnce(&mut AppContext<MainThread>),
     {
+        let platform = self.0.read().platform.clone();
         platform.run(Box::new(move || {
             let mut cx = self.0.write();
-            on_finish_launching(&mut *cx);
+            let cx: &mut AppContext<()> = &mut cx;
+            let cx: &mut AppContext<MainThread> = unsafe { mem::transmute(cx) };
+            on_finish_launching(cx);
         }));
     }
 }
@@ -37,7 +62,7 @@ impl App {
 pub struct AppContext<Thread = ()> {
     this: Weak<RwLock<AppContext>>,
     thread: PhantomData<Thread>,
-    platform: Box<dyn Platform>,
+    platform: Arc<dyn Platform>,
     text_system: Arc<TextSystem>,
     pub(crate) unit_entity_id: EntityId,
     pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
@@ -47,53 +72,33 @@ pub struct AppContext<Thread = ()> {
 }
 
 impl AppContext<()> {
-    pub fn run_on_main<F: 'static, T: 'static>(
-        &self,
-        to_call: F,
-    ) -> Result<T, impl Future<Output = T>>
-    where
-        F: Fn(&mut AppContext<MainThread>) -> T + Send + Sync,
-    {
-        let dispatcher = self.platform().dispatcher();
-        if dispatcher.is_main_thread() {
-        } else {
-            let future = async move {
-                // let cx = unsafe {  };
-            };
-            let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
-
-            let (runnable, task) = async_task::spawn_local();
-            runnable.schedule();
-        }
-
-        let (runnable, task) = async_task::spawn_local(future, schedule);
-        runnable.schedule();
-        task
-    }
+    // pub fn run_on_main<F: 'static, T: 'static>(
+    //     &self,
+    //     to_call: F,
+    // ) -> Result<T, impl Future<Output = T>>
+    // where
+    //     F: Fn(&mut AppContext<MainThread>) -> T + Send + Sync,
+    // {
+    //     todo!();
+
+    //     // let dispatcher = self.platform().dispatcher();
+    //     // if dispatcher.is_main_thread() {
+    //     // } else {
+    //     //     let future = async move {
+    //     //         // let cx = unsafe {  };
+    //     //     };
+    //     //     let schedule = move |runnable: Runnable| dispatcher.run_on_main_thread(runnable);
+    //     //     // let (runnable, task) = async_task::spawn_local();
+    //     //     // runnable.schedule();
+    //     // }
+
+    //     // let (runnable, task) = async_task::spawn_local(future, schedule);
+    //     // runnable.schedule();
+    //     // task
+    // }
 }
 
 impl<Thread> AppContext<Thread> {
-    pub fn new(platform: Arc<dyn Platform>) -> Self {
-        let text_system = Arc::new(TextSystem::new(platform.text_system()));
-        let mut entities = SlotMap::with_key();
-        let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any>));
-
-        AppContext {
-            thread: PhantomData,
-            platform,
-            text_system,
-            unit_entity_id,
-            entities,
-            windows: SlotMap::with_key(),
-            layout_id_buffer: Default::default(),
-        }
-    }
-
-    #[cfg(any(test, feature = "test"))]
-    pub fn test() -> Self {
-        Self::new(Arc::new(super::TestPlatform::new()))
-    }
-
     pub fn text_system(&self) -> &Arc<TextSystem> {
         &self.text_system
     }
@@ -101,7 +106,7 @@ impl<Thread> AppContext<Thread> {
     pub fn open_window<S: 'static>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S>,
+        build_root_view: impl FnOnce(&mut WindowContext<Thread>) -> RootView<S>,
     ) -> WindowHandle<S> {
         let id = self.windows.insert(None);
         let handle = WindowHandle::new(id);
@@ -118,7 +123,7 @@ impl<Thread> AppContext<Thread> {
     pub(crate) fn update_window<R>(
         &mut self,
         window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext) -> R,
+        update: impl FnOnce(&mut WindowContext<Thread>) -> R,
     ) -> Result<R> {
         let mut window = self
             .windows

crates/gpui3/src/element.rs 🔗

@@ -1,6 +1,5 @@
 use super::{Layout, LayoutId, Pixels, Point, Result, ViewContext};
 pub(crate) use smallvec::SmallVec;
-use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
 
 pub trait Element: 'static {
     type State;

crates/gpui3/src/executor.rs 🔗

@@ -1,7 +1,8 @@
 use crate::util;
+use crate::PlatformDispatcher;
 use anyhow::{anyhow, Result};
 use async_task::Runnable;
-use futures::channel::mpsc;
+use futures::channel::{mpsc, oneshot};
 use smol::{channel, prelude::*, Executor};
 use std::{
     any::Any,
@@ -16,7 +17,51 @@ use std::{
     time::Duration,
 };
 
-use crate::PlatformDispatcher;
+/// Enqueues the given closure to be run on the application's event loop. Can be
+/// called on any thread.
+pub(crate) fn spawn_on_main<R>(
+    dispatcher: Arc<dyn PlatformDispatcher>,
+    future: impl Future<Output = R> + Send + 'static,
+) -> impl Future<Output = R>
+where
+    R: Send + 'static,
+{
+    let (tx, rx) = oneshot::channel();
+    let (runnable, task) = async_task::spawn(
+        async move {
+            let result = future.await;
+            let _ = tx.send(result);
+        },
+        move |runnable| dispatcher.run_on_main_thread(runnable),
+    );
+    runnable.schedule();
+    task.detach();
+    async move { rx.await.unwrap() }
+}
+
+/// Enqueues the given closure to be run on the application's event loop. Must
+/// be called on the main thread.
+pub(crate) fn spawn_on_main_local<R>(
+    dispatcher: Arc<dyn PlatformDispatcher>,
+    future: impl Future<Output = R> + 'static,
+) -> impl Future<Output = R>
+where
+    R: 'static,
+{
+    assert!(dispatcher.is_main_thread(), "must be called on main thread");
+
+    let (tx, rx) = oneshot::channel();
+    let (runnable, task) = async_task::spawn_local(
+        async move {
+            let result = future.await;
+            let _ = tx.send(result);
+        },
+        move |runnable| dispatcher.run_on_main_thread(runnable),
+    );
+    runnable.schedule();
+    task.detach();
+    async move { rx.await.unwrap() }
+}
 
 pub enum ForegroundExecutor {
     Platform {

crates/gpui3/src/platform.rs 🔗

@@ -6,8 +6,8 @@ mod mac;
 mod test;
 
 use crate::{
-    AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight,
-    ForegroundExecutor, GlyphId, LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size,
+    AnyWindowHandle, Bounds, FontFeatures, FontId, FontMetrics, FontStyle, FontWeight, GlyphId,
+    LineLayout, Pixels, Point, Result, RunStyle, SharedString, Size,
 };
 use anyhow::anyhow;
 use async_task::Runnable;
@@ -37,14 +37,14 @@ pub use test::*;
 
 #[cfg(target_os = "macos")]
 pub(crate) fn current_platform() -> Arc<dyn Platform> {
-    Rc::new(MacPlatform::new())
+    Arc::new(MacPlatform::new())
 }
 
 pub trait Platform {
     fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
     fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 
-    fn run(&self, on_finish_launching: Box<dyn FnOnce()>);
+    fn run(&self, on_finish_launching: Box<dyn 'static + FnOnce()>);
     fn quit(&self);
     fn restart(&self);
     fn activate(&self, ignoring_other_apps: bool);

crates/gpui3/src/platform/mac/platform.rs 🔗

@@ -1,8 +1,8 @@
 use super::BoolExt;
 use crate::{
-    AnyWindowHandle, ClipboardItem, CursorStyle, Event, ForegroundExecutor, MacDispatcher,
-    MacScreen, MacTextSystem, MacWindow, PathPromptOptions, Platform, PlatformScreen,
-    PlatformTextSystem, PlatformWindow, Result, SemanticVersion, WindowOptions,
+    AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
+    MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
+    Result, SemanticVersion, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -33,9 +33,10 @@ use objc::{
     runtime::{Class, Object, Sel},
     sel, sel_impl,
 };
+use parking_lot::Mutex;
 use ptr::null_mut;
 use std::{
-    cell::{Cell, RefCell},
+    cell::Cell,
     convert::TryInto,
     ffi::{c_void, CStr, OsStr},
     os::{raw::c_char, unix::ffi::OsStrExt},
@@ -138,10 +139,10 @@ unsafe fn build_classes() {
     }
 }
 
-pub struct MacPlatform(RefCell<MacPlatformState>);
+pub struct MacPlatform(Mutex<MacPlatformState>);
 
 pub struct MacPlatformState {
-    executor: Rc<ForegroundExecutor>,
+    dispatcher: Arc<MacDispatcher>,
     text_system: Arc<MacTextSystem>,
     pasteboard: id,
     text_hash_pasteboard_type: id,
@@ -161,8 +162,8 @@ pub struct MacPlatformState {
 
 impl MacPlatform {
     pub fn new() -> Self {
-        Self(RefCell::new(MacPlatformState {
-            executor: Rc::new(ForegroundExecutor::new(Arc::new(MacDispatcher)).unwrap()),
+        Self(Mutex::new(MacPlatformState {
+            dispatcher: Arc::new(MacDispatcher),
             text_system: Arc::new(MacTextSystem::new()),
             pasteboard: unsafe { NSPasteboard::generalPasteboard(nil) },
             text_hash_pasteboard_type: unsafe { ns_string("zed-text-hash") },
@@ -182,7 +183,7 @@ impl MacPlatform {
     }
 
     unsafe fn read_from_pasteboard(&self, kind: id) -> Option<&[u8]> {
-        let pasteboard = self.0.borrow().pasteboard;
+        let pasteboard = self.0.lock().pasteboard;
         let data = pasteboard.dataForType(kind);
         if data == nil {
             None
@@ -342,16 +343,16 @@ impl MacPlatform {
 }
 
 impl Platform for MacPlatform {
-    fn executor(&self) -> Rc<ForegroundExecutor> {
-        self.0.borrow().executor.clone()
+    fn dispatcher(&self) -> Arc<dyn crate::PlatformDispatcher> {
+        Arc::new(MacDispatcher)
     }
 
     fn text_system(&self) -> Arc<dyn PlatformTextSystem> {
-        self.0.borrow().text_system.clone()
+        self.0.lock().text_system.clone()
     }
 
     fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
-        self.0.borrow_mut().finish_launching = Some(on_finish_launching);
+        self.0.lock().finish_launching = Some(on_finish_launching);
 
         unsafe {
             let app: id = msg_send![APP_CLASS, sharedApplication];
@@ -465,25 +466,21 @@ impl Platform for MacPlatform {
         MacScreen::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
     }
 
-    fn main_window(&self) -> Option<AnyWindowHandle> {
-        MacWindow::main_window()
-    }
-
     // fn add_status_item(&self, _handle: AnyWindowHandle) -> Box<dyn platform::Window> {
     //     Box::new(StatusItem::add(self.fonts()))
     // }
 
+    fn main_window(&self) -> Option<AnyWindowHandle> {
+        MacWindow::main_window()
+    }
+
     fn open_window(
         &self,
         handle: AnyWindowHandle,
         options: WindowOptions,
     ) -> Box<dyn PlatformWindow> {
-        Box::new(MacWindow::open(
-            handle,
-            options,
-            self.executor(),
-            self.text_system(),
-        ))
+        let dispatcher = self.0.lock().dispatcher.clone();
+        Box::new(MacWindow::open(handle, options, dispatcher))
     }
 
     fn open_url(&self, url: &str) {
@@ -497,7 +494,7 @@ impl Platform for MacPlatform {
     }
 
     fn on_open_urls(&self, callback: Box<dyn FnMut(Vec<String>)>) {
-        self.0.borrow_mut().open_urls = Some(callback);
+        self.0.lock().open_urls = Some(callback);
     }
 
     fn prompt_for_paths(
@@ -570,41 +567,38 @@ impl Platform for MacPlatform {
     fn reveal_path(&self, path: &Path) {
         unsafe {
             let path = path.to_path_buf();
-            self.0
-                .borrow()
-                .executor
-                .spawn(async move {
-                    let full_path = ns_string(path.to_str().unwrap_or(""));
-                    let root_full_path = ns_string("");
-                    let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
-                    let _: BOOL = msg_send![
-                        workspace,
-                        selectFile: full_path
-                        inFileViewerRootedAtPath: root_full_path
-                    ];
-                })
-                .detach();
+            let dispatcher = self.0.lock().dispatcher.clone();
+            let _ = crate::spawn_on_main_local(dispatcher, async move {
+                let full_path = ns_string(path.to_str().unwrap_or(""));
+                let root_full_path = ns_string("");
+                let workspace: id = msg_send![class!(NSWorkspace), sharedWorkspace];
+                let _: BOOL = msg_send![
+                    workspace,
+                    selectFile: full_path
+                    inFileViewerRootedAtPath: root_full_path
+                ];
+            });
         }
     }
 
     fn on_become_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().become_active = Some(callback);
+        self.0.lock().become_active = Some(callback);
     }
 
     fn on_resign_active(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().resign_active = Some(callback);
+        self.0.lock().resign_active = Some(callback);
     }
 
     fn on_quit(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().quit = Some(callback);
+        self.0.lock().quit = Some(callback);
     }
 
     fn on_reopen(&self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().reopen = Some(callback);
+        self.0.lock().reopen = Some(callback);
     }
 
     fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
-        self.0.borrow_mut().event = Some(callback);
+        self.0.lock().event = Some(callback);
     }
 
     fn os_name(&self) -> &'static str {
@@ -704,7 +698,7 @@ impl Platform for MacPlatform {
     }
 
     fn write_to_clipboard(&self, item: ClipboardItem) {
-        let state = self.0.borrow();
+        let state = self.0.lock();
         unsafe {
             state.pasteboard.clearContents();
 
@@ -741,7 +735,7 @@ impl Platform for MacPlatform {
     }
 
     fn read_from_clipboard(&self) -> Option<ClipboardItem> {
-        let state = self.0.borrow();
+        let state = self.0.lock();
         unsafe {
             if let Some(text_bytes) = self.read_from_pasteboard(NSPasteboardTypeString) {
                 let text = String::from_utf8_lossy(text_bytes).to_string();
@@ -777,6 +771,32 @@ impl Platform for MacPlatform {
         }
     }
 
+    // fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
+    //     self.0.lock().menu_command = Some(callback);
+    // }
+
+    // fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
+    //     self.0.lock().will_open_menu = Some(callback);
+    // }
+
+    // fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
+    //     self.0.lock().validate_menu_command = Some(callback);
+    // }
+
+    // fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
+    //     unsafe {
+    //         let app: id = msg_send![APP_CLASS, sharedApplication];
+    //         let mut state = self.0.lock();
+    //         let actions = &mut state.menu_actions;
+    //         app.setMainMenu_(self.create_menu_bar(
+    //             menus,
+    //             app.delegate(),
+    //             actions,
+    //             keystroke_matcher,
+    //         ));
+    //     }
+    // }
+
     fn write_credentials(&self, url: &str, username: &str, password: &[u8]) -> Result<()> {
         let url = CFString::from(url);
         let username = CFString::from(username);
@@ -816,32 +836,6 @@ impl Platform for MacPlatform {
         Ok(())
     }
 
-    // fn on_menu_command(&self, callback: Box<dyn FnMut(&dyn Action)>) {
-    //     self.0.borrow_mut().menu_command = Some(callback);
-    // }
-
-    // fn on_will_open_menu(&self, callback: Box<dyn FnMut()>) {
-    //     self.0.borrow_mut().will_open_menu = Some(callback);
-    // }
-
-    // fn on_validate_menu_command(&self, callback: Box<dyn FnMut(&dyn Action) -> bool>) {
-    //     self.0.borrow_mut().validate_menu_command = Some(callback);
-    // }
-
-    // fn set_menus(&self, menus: Vec<Menu>, keystroke_matcher: &KeymapMatcher) {
-    //     unsafe {
-    //         let app: id = msg_send![APP_CLASS, sharedApplication];
-    //         let mut state = self.0.borrow_mut();
-    //         let actions = &mut state.menu_actions;
-    //         app.setMainMenu_(self.create_menu_bar(
-    //             menus,
-    //             app.delegate(),
-    //             actions,
-    //             keystroke_matcher,
-    //         ));
-    //     }
-    // }
-
     fn read_credentials(&self, url: &str) -> Result<Option<(String, Vec<u8>)>> {
         let url = CFString::from(url);
         let cf_true = CFBoolean::true_value().as_CFTypeRef();
@@ -921,7 +915,7 @@ extern "C" fn send_event(this: &mut Object, _sel: Sel, native_event: id) {
     unsafe {
         if let Some(event) = Event::from_native(native_event, None) {
             let platform = get_foreground_platform(this);
-            if let Some(callback) = platform.0.borrow_mut().event.as_mut() {
+            if let Some(callback) = platform.0.lock().event.as_mut() {
                 if callback(event) {
                     return;
                 }
@@ -937,7 +931,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
         app.setActivationPolicy_(NSApplicationActivationPolicyRegular);
 
         let platform = get_foreground_platform(this);
-        let callback = platform.0.borrow_mut().finish_launching.take();
+        let callback = platform.0.lock().finish_launching.take();
         if let Some(callback) = callback {
             callback();
         }
@@ -947,7 +941,7 @@ extern "C" fn did_finish_launching(this: &mut Object, _: Sel, _: id) {
 extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_windows: bool) {
     if !has_open_windows {
         let platform = unsafe { get_foreground_platform(this) };
-        if let Some(callback) = platform.0.borrow_mut().reopen.as_mut() {
+        if let Some(callback) = platform.0.lock().reopen.as_mut() {
             callback();
         }
     }
@@ -955,21 +949,21 @@ extern "C" fn should_handle_reopen(this: &mut Object, _: Sel, _: id, has_open_wi
 
 extern "C" fn did_become_active(this: &mut Object, _: Sel, _: id) {
     let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().become_active.as_mut() {
+    if let Some(callback) = platform.0.lock().become_active.as_mut() {
         callback();
     }
 }
 
 extern "C" fn did_resign_active(this: &mut Object, _: Sel, _: id) {
     let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().resign_active.as_mut() {
+    if let Some(callback) = platform.0.lock().resign_active.as_mut() {
         callback();
     }
 }
 
 extern "C" fn will_terminate(this: &mut Object, _: Sel, _: id) {
     let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().quit.as_mut() {
+    if let Some(callback) = platform.0.lock().quit.as_mut() {
         callback();
     }
 }
@@ -991,7 +985,7 @@ extern "C" fn open_urls(this: &mut Object, _: Sel, _: id, urls: id) {
             .collect::<Vec<_>>()
     };
     let platform = unsafe { get_foreground_platform(this) };
-    if let Some(callback) = platform.0.borrow_mut().open_urls.as_mut() {
+    if let Some(callback) = platform.0.lock().open_urls.as_mut() {
         callback(urls);
     }
 }
@@ -1000,7 +994,7 @@ extern "C" fn handle_menu_item(__this: &mut Object, _: Sel, __item: id) {
     todo!()
     // unsafe {
     //     let platform = get_foreground_platform(this);
-    //     let mut platform = platform.0.borrow_mut();
+    //     let mut platform = platform.0.lock();
     //     if let Some(mut callback) = platform.menu_command.take() {
     //         let tag: NSInteger = msg_send![item, tag];
     //         let index = tag as usize;
@@ -1017,7 +1011,7 @@ extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> boo
     // unsafe {
     //     let mut result = false;
     //     let platform = get_foreground_platform(this);
-    //     let mut platform = platform.0.borrow_mut();
+    //     let mut platform = platform.0.lock();
     //     if let Some(mut callback) = platform.validate_menu_command.take() {
     //         let tag: NSInteger = msg_send![item, tag];
     //         let index = tag as usize;
@@ -1033,7 +1027,7 @@ extern "C" fn validate_menu_item(__this: &mut Object, _: Sel, __item: id) -> boo
 extern "C" fn menu_will_open(this: &mut Object, _: Sel, _: id) {
     unsafe {
         let platform = get_foreground_platform(this);
-        let mut platform = platform.0.borrow_mut();
+        let mut platform = platform.0.lock();
         if let Some(mut callback) = platform.will_open_menu.take() {
             callback();
             platform.will_open_menu = Some(callback);
@@ -1112,7 +1106,7 @@ mod tests {
             );
             platform
                 .0
-                .borrow_mut()
+                .lock()
                 .pasteboard
                 .setData_forType(bytes, NSPasteboardTypeString);
         }
@@ -1124,7 +1118,7 @@ mod tests {
 
     fn build_platform() -> MacPlatform {
         let platform = MacPlatform::new();
-        platform.0.borrow_mut().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
+        platform.0.lock().pasteboard = unsafe { NSPasteboard::pasteboardWithUniqueName(nil) };
         platform
     }
 }

crates/gpui3/src/platform/mac/window.rs 🔗

@@ -1,9 +1,9 @@
 use crate::{
-    point, px, size, AnyWindowHandle, Bounds, Event, ForegroundExecutor, InputHandler,
-    KeyDownEvent, Keystroke, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton,
-    MouseDownEvent, MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, PlatformScreen,
-    PlatformTextSystem, PlatformWindow, Point, Size, Timer, WindowAppearance, WindowBounds,
-    WindowKind, WindowOptions, WindowPromptLevel,
+    point, px, size, AnyWindowHandle, Bounds, Event, InputHandler, KeyDownEvent, Keystroke,
+    MacDispatcher, MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent,
+    MouseMovedEvent, MouseUpEvent, NSRectExt, Pixels, PlatformDispatcher, PlatformScreen,
+    PlatformWindow, Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
+    WindowPromptLevel,
 };
 use block::ConcreteBlock;
 use cocoa::{
@@ -25,6 +25,7 @@ use objc::{
     runtime::{Class, Object, Protocol, Sel, BOOL, NO, YES},
     sel, sel_impl,
 };
+use parking_lot::Mutex;
 use raw_window_handle::{
     AppKitDisplayHandle, AppKitWindowHandle, HasRawDisplayHandle, HasRawWindowHandle,
     RawDisplayHandle, RawWindowHandle,
@@ -282,6 +283,7 @@ struct InsertText {
 
 struct WindowState {
     handle: AnyWindowHandle,
+    dispatcher: Arc<dyn PlatformDispatcher>,
     native_window: id,
     kind: WindowKind,
     event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
@@ -296,7 +298,6 @@ struct WindowState {
     pending_key_down: Option<(KeyDownEvent, Option<InsertText>)>,
     last_key_equivalent: Option<KeyDownEvent>,
     synthetic_drag_counter: usize,
-    executor: Rc<ForegroundExecutor>,
     last_fresh_keydown: Option<Keystroke>,
     traffic_light_position: Option<Point<Pixels>>,
     previous_modifiers_changed_event: Option<Event>,
@@ -411,14 +412,15 @@ impl WindowState {
     }
 }
 
-pub struct MacWindow(Rc<RefCell<WindowState>>);
+unsafe impl Send for WindowState {}
+
+pub struct MacWindow(Arc<Mutex<WindowState>>);
 
 impl MacWindow {
     pub fn open(
         handle: AnyWindowHandle,
         options: WindowOptions,
-        executor: Rc<ForegroundExecutor>,
-        text_system: Arc<dyn PlatformTextSystem>,
+        dispatcher: Arc<MacDispatcher>,
     ) -> Self {
         unsafe {
             let pool = NSAutoreleasePool::new(nil);
@@ -483,8 +485,9 @@ impl MacWindow {
 
             assert!(!native_view.is_null());
 
-            let window = Self(Rc::new(RefCell::new(WindowState {
+            let window = Self(Arc::new(Mutex::new(WindowState {
                 handle,
+                dispatcher,
                 native_window,
                 kind: options.kind,
                 event_callback: None,
@@ -499,7 +502,6 @@ impl MacWindow {
                 pending_key_down: None,
                 last_key_equivalent: None,
                 synthetic_drag_counter: 0,
-                executor,
                 last_fresh_keydown: None,
                 traffic_light_position: options
                     .titlebar
@@ -512,12 +514,12 @@ impl MacWindow {
 
             (*native_window).set_ivar(
                 WINDOW_STATE_IVAR,
-                Rc::into_raw(window.0.clone()) as *const c_void,
+                Arc::into_raw(window.0.clone()) as *const c_void,
             );
             native_window.setDelegate_(native_window);
             (*native_view).set_ivar(
                 WINDOW_STATE_IVAR,
-                Rc::into_raw(window.0.clone()) as *const c_void,
+                Arc::into_raw(window.0.clone()) as *const c_void,
             );
 
             if let Some(title) = options
@@ -596,7 +598,7 @@ impl MacWindow {
                 native_window.orderFront_(nil);
             }
 
-            window.0.borrow().move_traffic_light();
+            window.0.lock().move_traffic_light();
             pool.drain();
 
             window
@@ -619,21 +621,19 @@ impl MacWindow {
 
 impl Drop for MacWindow {
     fn drop(&mut self) {
-        let this = self.0.borrow();
-        let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    window.close();
-                }
-            })
-            .detach();
+        let this = self.0.clone();
+        let dispatcher = self.0.lock().dispatcher.clone();
+        let _ = crate::spawn_on_main(dispatcher, async move {
+            unsafe {
+                this.lock().native_window.close();
+            }
+        });
     }
 }
 
 unsafe impl HasRawWindowHandle for MacWindow {
     fn raw_window_handle(&self) -> RawWindowHandle {
-        let ns_window = self.0.borrow().native_window;
+        let ns_window = self.0.lock().native_window;
         let ns_view = unsafe { ns_window.contentView() };
         let mut handle = AppKitWindowHandle::empty();
         handle.ns_window = ns_window as *mut c_void;
@@ -650,24 +650,24 @@ unsafe impl HasRawDisplayHandle for MacWindow {
 
 impl PlatformWindow for MacWindow {
     fn bounds(&self) -> WindowBounds {
-        self.0.as_ref().borrow().bounds()
+        self.0.as_ref().lock().bounds()
     }
 
     fn content_size(&self) -> Size<Pixels> {
-        self.0.as_ref().borrow().content_size().into()
+        self.0.as_ref().lock().content_size().into()
     }
 
     fn scale_factor(&self) -> f32 {
-        self.0.as_ref().borrow().scale_factor()
+        self.0.as_ref().lock().scale_factor()
     }
 
     fn titlebar_height(&self) -> Pixels {
-        self.0.as_ref().borrow().titlebar_height()
+        self.0.as_ref().lock().titlebar_height()
     }
 
     fn appearance(&self) -> WindowAppearance {
         unsafe {
-            let appearance: id = msg_send![self.0.borrow().native_window, effectiveAppearance];
+            let appearance: id = msg_send![self.0.lock().native_window, effectiveAppearance];
             WindowAppearance::from_native(appearance)
         }
     }
@@ -675,7 +675,7 @@ impl PlatformWindow for MacWindow {
     fn screen(&self) -> Rc<dyn PlatformScreen> {
         unsafe {
             Rc::new(MacScreen {
-                native_screen: self.0.as_ref().borrow().native_window.screen(),
+                native_screen: self.0.as_ref().lock().native_window.screen(),
             })
         }
     }
@@ -683,7 +683,7 @@ impl PlatformWindow for MacWindow {
     fn mouse_position(&self) -> Point<Pixels> {
         let position = unsafe {
             self.0
-                .borrow()
+                .lock()
                 .native_window
                 .mouseLocationOutsideOfEventStream()
         };
@@ -695,7 +695,7 @@ impl PlatformWindow for MacWindow {
     }
 
     fn set_input_handler(&mut self, input_handler: Box<dyn InputHandler>) {
-        self.0.as_ref().borrow_mut().input_handler = Some(input_handler);
+        self.0.as_ref().lock().input_handler = Some(input_handler);
     }
 
     fn prompt(
@@ -761,131 +761,123 @@ impl PlatformWindow for MacWindow {
                 }
             });
             let block = block.copy();
-            let native_window = self.0.borrow().native_window;
-            self.0
-                .borrow()
-                .executor
-                .spawn(async move {
-                    let _: () = msg_send![
-                        alert,
-                        beginSheetModalForWindow: native_window
-                        completionHandler: block
-                    ];
-                })
-                .detach();
+            let native_window = self.0.lock().native_window;
+            let dispatcher = self.0.lock().dispatcher.clone();
+            let _ = crate::spawn_on_main_local(dispatcher, async move {
+                let _: () = msg_send![
+                    alert,
+                    beginSheetModalForWindow: native_window
+                    completionHandler: block
+                ];
+            });
 
             done_rx
         }
     }
 
     fn activate(&self) {
-        let window = self.0.borrow().native_window;
-        self.0
-            .borrow()
-            .executor
-            .spawn(async move {
-                unsafe {
-                    let _: () = msg_send![window, makeKeyAndOrderFront: nil];
-                }
-            })
-            .detach();
+        let window = self.0.lock().native_window;
+        let dispatcher = self.0.lock().dispatcher.clone();
+        let _ = crate::spawn_on_main_local(dispatcher.clone(), async move {
+            unsafe {
+                let _: () = msg_send![window, makeKeyAndOrderFront: nil];
+            }
+        });
     }
 
     fn set_title(&mut self, title: &str) {
         unsafe {
             let app = NSApplication::sharedApplication(nil);
-            let window = self.0.borrow().native_window;
+            let window = self.0.lock().native_window;
             let title = ns_string(title);
             let _: () = msg_send![app, changeWindowsItem:window title:title filename:false];
             let _: () = msg_send![window, setTitle: title];
-            self.0.borrow().move_traffic_light();
+            self.0.lock().move_traffic_light();
         }
     }
 
     fn set_edited(&mut self, edited: bool) {
         unsafe {
-            let window = self.0.borrow().native_window;
+            let window = self.0.lock().native_window;
             msg_send![window, setDocumentEdited: edited as BOOL]
         }
 
         // Changing the document edited state resets the traffic light position,
         // so we have to move it again.
-        self.0.borrow().move_traffic_light();
+        self.0.lock().move_traffic_light();
     }
 
     fn show_character_palette(&self) {
         unsafe {
             let app = NSApplication::sharedApplication(nil);
-            let window = self.0.borrow().native_window;
+            let window = self.0.lock().native_window;
             let _: () = msg_send![app, orderFrontCharacterPalette: window];
         }
     }
 
     fn minimize(&self) {
-        let window = self.0.borrow().native_window;
+        let window = self.0.lock().native_window;
         unsafe {
             window.miniaturize_(nil);
         }
     }
 
     fn zoom(&self) {
-        let this = self.0.borrow();
+        let this = self.0.lock();
         let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    window.zoom_(nil);
-                }
-            })
-            .detach();
+        let dispatcher = this.dispatcher.clone();
+        let _ = crate::spawn_on_main_local(dispatcher, async move {
+            unsafe {
+                window.zoom_(nil);
+            }
+        });
     }
 
     fn toggle_full_screen(&self) {
-        let this = self.0.borrow();
+        let this = self.0.lock();
         let window = this.native_window;
-        this.executor
-            .spawn(async move {
-                unsafe {
-                    window.toggleFullScreen_(nil);
-                }
-            })
-            .detach();
+        let dispatcher = this.dispatcher.clone();
+        let _ = crate::spawn_on_main_local(dispatcher, async move {
+            unsafe {
+                window.toggleFullScreen_(nil);
+            }
+        });
     }
 
     fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
-        self.0.as_ref().borrow_mut().event_callback = Some(callback);
+        self.0.as_ref().lock().event_callback = Some(callback);
     }
 
     fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().borrow_mut().activate_callback = Some(callback);
+        self.0.as_ref().lock().activate_callback = Some(callback);
     }
 
     fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.as_ref().borrow_mut().resize_callback = Some(callback);
+        self.0.as_ref().lock().resize_callback = Some(callback);
     }
 
     fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
-        self.0.as_ref().borrow_mut().fullscreen_callback = Some(callback);
+        self.0.as_ref().lock().fullscreen_callback = Some(callback);
     }
 
     fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.as_ref().borrow_mut().moved_callback = Some(callback);
+        self.0.as_ref().lock().moved_callback = Some(callback);
     }
 
     fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
-        self.0.as_ref().borrow_mut().should_close_callback = Some(callback);
+        self.0.as_ref().lock().should_close_callback = Some(callback);
     }
 
     fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
-        self.0.as_ref().borrow_mut().close_callback = Some(callback);
+        self.0.as_ref().lock().close_callback = Some(callback);
     }
 
     fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
-        self.0.borrow_mut().appearance_changed_callback = Some(callback);
+        self.0.lock().appearance_changed_callback = Some(callback);
     }
 
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool {
-        let self_borrow = self.0.borrow();
+        let self_borrow = self.0.lock();
         let self_handle = self_borrow.handle;
 
         unsafe {
@@ -1133,14 +1125,15 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                 },
             ) => {
                 window_state_borrow.synthetic_drag_counter += 1;
-                window_state_borrow
-                    .executor
-                    .spawn(synthetic_drag(
+                let dispatcher = window_state_borrow.dispatcher.clone();
+                let _ = crate::spawn_on_main_local(
+                    dispatcher,
+                    synthetic_drag(
                         weak_window_state,
                         window_state_borrow.synthetic_drag_counter,
                         event.clone(),
-                    ))
-                    .detach();
+                    ),
+                );
             }
 
             Event::MouseMoved(_)
@@ -1263,18 +1256,16 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
         }
     }
 
-    let executor = window_state_borrow.executor.clone();
+    let dispatcher = window_state_borrow.dispatcher.clone();
     drop(window_state_borrow);
-    executor
-        .spawn(async move {
-            let mut window_state_borrow = window_state.as_ref().borrow_mut();
-            if let Some(mut callback) = window_state_borrow.activate_callback.take() {
-                drop(window_state_borrow);
-                callback(is_active);
-                window_state.borrow_mut().activate_callback = Some(callback);
-            };
-        })
-        .detach();
+    let _ = crate::spawn_on_main_local(dispatcher, async move {
+        let mut window_state_borrow = window_state.as_ref().borrow_mut();
+        if let Some(mut callback) = window_state_borrow.activate_callback.take() {
+            drop(window_state_borrow);
+            callback(is_active);
+            window_state.borrow_mut().activate_callback = Some(callback);
+        };
+    });
 }
 
 extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {

crates/gpui3/src/platform/test.rs 🔗

@@ -9,7 +9,7 @@ impl TestPlatform {
 }
 
 impl Platform for TestPlatform {
-    fn executor(&self) -> std::rc::Rc<crate::ForegroundExecutor> {
+    fn dispatcher(&self) -> std::sync::Arc<dyn crate::PlatformDispatcher> {
         todo!()
     }
 

crates/gpui3/src/text_system/line_wrapper.rs 🔗

@@ -215,133 +215,134 @@ impl Boundary {
 #[cfg(test)]
 mod tests {
     use super::*;
-    use crate::{AppContext, FontWeight};
+    use crate::{App, AppContext, FontWeight};
 
     #[test]
     fn test_wrap_line() {
-        let cx = AppContext::test();
+        App::test().run(|cx| {
+            let text_system = cx.text_system().clone();
+            let family = text_system
+                .load_font_family(&["Courier"], &Default::default())
+                .unwrap();
+            let font_id = text_system
+                .select_font(family, Default::default(), Default::default())
+                .unwrap();
 
-        let text_system = cx.text_system().clone();
-        let family = text_system
-            .load_font_family(&["Courier"], &Default::default())
-            .unwrap();
-        let font_id = text_system
-            .select_font(family, Default::default(), Default::default())
-            .unwrap();
-
-        let mut wrapper =
-            LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
-        assert_eq!(
-            wrapper
-                .wrap_line("aa bbb cccc ddddd eeee", px(72.))
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(12, 0),
-                Boundary::new(18, 0)
-            ],
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("aaa aaaaaaaaaaaaaaaaaa", px(72.0))
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(4, 0),
-                Boundary::new(11, 0),
-                Boundary::new(18, 0)
-            ],
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("     aaaaaaa", px(72.))
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 5),
-                Boundary::new(9, 5),
-                Boundary::new(11, 5),
-            ]
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("                            ", px(72.))
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(14, 0),
-                Boundary::new(21, 0)
-            ]
-        );
-        assert_eq!(
-            wrapper
-                .wrap_line("          aaaaaaaaaaaaaa", px(72.))
-                .collect::<Vec<_>>(),
-            &[
-                Boundary::new(7, 0),
-                Boundary::new(14, 3),
-                Boundary::new(18, 3),
-                Boundary::new(22, 3),
-            ]
-        );
+            let mut wrapper =
+                LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+            assert_eq!(
+                wrapper
+                    .wrap_line("aa bbb cccc ddddd eeee", px(72.))
+                    .collect::<Vec<_>>(),
+                &[
+                    Boundary::new(7, 0),
+                    Boundary::new(12, 0),
+                    Boundary::new(18, 0)
+                ],
+            );
+            assert_eq!(
+                wrapper
+                    .wrap_line("aaa aaaaaaaaaaaaaaaaaa", px(72.0))
+                    .collect::<Vec<_>>(),
+                &[
+                    Boundary::new(4, 0),
+                    Boundary::new(11, 0),
+                    Boundary::new(18, 0)
+                ],
+            );
+            assert_eq!(
+                wrapper
+                    .wrap_line("     aaaaaaa", px(72.))
+                    .collect::<Vec<_>>(),
+                &[
+                    Boundary::new(7, 5),
+                    Boundary::new(9, 5),
+                    Boundary::new(11, 5),
+                ]
+            );
+            assert_eq!(
+                wrapper
+                    .wrap_line("                            ", px(72.))
+                    .collect::<Vec<_>>(),
+                &[
+                    Boundary::new(7, 0),
+                    Boundary::new(14, 0),
+                    Boundary::new(21, 0)
+                ]
+            );
+            assert_eq!(
+                wrapper
+                    .wrap_line("          aaaaaaaaaaaaaa", px(72.))
+                    .collect::<Vec<_>>(),
+                &[
+                    Boundary::new(7, 0),
+                    Boundary::new(14, 3),
+                    Boundary::new(18, 3),
+                    Boundary::new(22, 3),
+                ]
+            );
+        });
     }
 
     // todo! repeat this test
     #[test]
     fn test_wrap_shaped_line() {
-        let cx = AppContext::test();
-        let text_system = cx.text_system().clone();
+        App::test().run(|cx| {
+            let text_system = cx.text_system().clone();
 
-        let family = text_system
-            .load_font_family(&["Helvetica"], &Default::default())
-            .unwrap();
-        let font_id = text_system
-            .select_font(family, Default::default(), Default::default())
-            .unwrap();
-        let normal = RunStyle {
-            font_id,
-            color: Default::default(),
-            underline: Default::default(),
-        };
-        let bold = RunStyle {
-            font_id: text_system
-                .select_font(family, FontWeight::BOLD, Default::default())
-                .unwrap(),
-            color: Default::default(),
-            underline: Default::default(),
-        };
+            let family = text_system
+                .load_font_family(&["Helvetica"], &Default::default())
+                .unwrap();
+            let font_id = text_system
+                .select_font(family, Default::default(), Default::default())
+                .unwrap();
+            let normal = RunStyle {
+                font_id,
+                color: Default::default(),
+                underline: Default::default(),
+            };
+            let bold = RunStyle {
+                font_id: text_system
+                    .select_font(family, FontWeight::BOLD, Default::default())
+                    .unwrap(),
+                color: Default::default(),
+                underline: Default::default(),
+            };
 
-        let text = "aa bbb cccc ddddd eeee";
-        let line = text_system.layout_str(
-            text,
-            px(16.),
-            &[
-                (4, normal.clone()),
-                (5, bold.clone()),
-                (6, normal.clone()),
-                (1, bold),
-                (7, normal),
-            ],
-        );
+            let text = "aa bbb cccc ddddd eeee";
+            let line = text_system.layout_str(
+                text,
+                px(16.),
+                &[
+                    (4, normal.clone()),
+                    (5, bold.clone()),
+                    (6, normal.clone()),
+                    (1, bold),
+                    (7, normal),
+                ],
+            );
 
-        let mut wrapper =
-            LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
-        assert_eq!(
-            wrapper
-                .wrap_shaped_line(text, &line, px(72.))
-                .collect::<Vec<_>>(),
-            &[
-                ShapedBoundary {
-                    run_ix: 1,
-                    glyph_ix: 3
-                },
-                ShapedBoundary {
-                    run_ix: 2,
-                    glyph_ix: 3
-                },
-                ShapedBoundary {
-                    run_ix: 4,
-                    glyph_ix: 2
-                }
-            ],
-        );
+            let mut wrapper =
+                LineWrapper::new(font_id, px(16.), text_system.platform_text_system.clone());
+            assert_eq!(
+                wrapper
+                    .wrap_shaped_line(text, &line, px(72.))
+                    .collect::<Vec<_>>(),
+                &[
+                    ShapedBoundary {
+                        run_ix: 1,
+                        glyph_ix: 3
+                    },
+                    ShapedBoundary {
+                        run_ix: 2,
+                        glyph_ix: 3
+                    },
+                    ShapedBoundary {
+                        run_ix: 4,
+                        glyph_ix: 2
+                    }
+                ],
+            );
+        });
     }
 }

crates/gpui3/src/window.rs 🔗

@@ -1,6 +1,4 @@
-use crate::{
-    AvailableSpace, Bg, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement,
-};
+use crate::{AvailableSpace, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
 
 use super::{
     px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference,
@@ -39,22 +37,22 @@ impl Window {
 }
 
 #[derive(Deref, DerefMut)]
-pub struct WindowContext<'a, 'b> {
+pub struct WindowContext<'a, 'b, Thread = ()> {
     #[deref]
     #[deref_mut]
-    app: Reference<'a, AppContext>,
+    app: Reference<'a, AppContext<Thread>>,
     window: Reference<'b, Window>,
 }
 
-impl<'a, 'w> WindowContext<'a, 'w> {
-    pub(crate) fn mutable(app: &'a mut AppContext<Bg>, window: &'w mut Window) -> Self {
+impl<'a, 'w, Thread> WindowContext<'a, 'w, Thread> {
+    pub(crate) fn mutable(app: &'a mut AppContext<Thread>, window: &'w mut Window) -> Self {
         Self {
             app: Reference::Mutable(app),
             window: Reference::Mutable(window),
         }
     }
 
-    pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
+    pub(crate) fn immutable(app: &'a AppContext<Thread>, window: &'w Window) -> Self {
         Self {
             app: Reference::Immutable(app),
             window: Reference::Immutable(window),
@@ -123,7 +121,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     fn update_window<R>(
         &mut self,
         window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext) -> R,
+        update: impl FnOnce(&mut WindowContext<Thread>) -> R,
     ) -> Result<R> {
         if window_id == self.window.id {
             Ok(update(self))

crates/storybook2/src/storybook2.rs 🔗

@@ -21,7 +21,7 @@ mod workspace;
 fn main() {
     SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
 
-    gpui3::App::new().run(|cx| {
+    gpui3::App::production().run(|cx| {
         let window: gpui3::WindowHandle<()> = cx.open_window(Default::default(), |cx| todo!());
     });