Checkpoint

Antonio Scandurra and Nathan Sobo created

Co-Authored-By: Nathan Sobo <nathan@zed.dev>

Change summary

crates/gpui3/src/app.rs                      | 155 ++++++++++-----------
crates/gpui3/src/elements/div.rs             |  17 -
crates/gpui3/src/gpui3.rs                    |  64 ++++++++
crates/gpui3/src/platform.rs                 |  23 ++
crates/gpui3/src/platform/mac/platform.rs    |   7 
crates/gpui3/src/platform/mac/screen.rs      |  38 +++-
crates/gpui3/src/platform/mac/window.rs      | 119 +++++++--------
crates/gpui3/src/platform/test.rs            |   3 
crates/gpui3/src/text_system/line_wrapper.rs |   2 
crates/gpui3/src/view.rs                     |  14 
crates/gpui3/src/window.rs                   |  50 ++++--
crates/storybook2/src/storybook2.rs          |   2 
12 files changed, 282 insertions(+), 212 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -1,21 +1,19 @@
 use crate::{
-    current_platform, Context, LayoutId, Platform, Reference, RootView, TextSystem, Window,
-    WindowContext, WindowHandle, WindowId,
+    current_platform, Context, LayoutId, MainThreadOnly, Platform, Reference, RootView, TextSystem,
+    Window, WindowContext, WindowHandle, WindowId,
 };
 use anyhow::{anyhow, Result};
-use parking_lot::RwLock;
+use futures::Future;
+use parking_lot::Mutex;
 use slotmap::SlotMap;
 use std::{
     any::Any,
     marker::PhantomData,
-    mem,
     sync::{Arc, Weak},
 };
 
 #[derive(Clone)]
-pub struct App(Arc<RwLock<AppContext<()>>>);
-
-pub struct MainThread;
+pub struct App(Arc<Mutex<AppContext>>);
 
 impl App {
     pub fn production() -> Self {
@@ -28,14 +26,14 @@ impl App {
     }
 
     fn new(platform: Arc<dyn Platform>) -> Self {
+        let dispatcher = platform.dispatcher();
         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>));
+        let unit_entity_id = entities.insert(Some(Box::new(()) as Box<dyn Any + Send>));
         Self(Arc::new_cyclic(|this| {
-            RwLock::new(AppContext {
+            Mutex::new(AppContext {
                 this: this.clone(),
-                thread: PhantomData,
-                platform,
+                platform: MainThreadOnly::new(platform, dispatcher),
                 text_system,
                 unit_entity_id,
                 entities,
@@ -47,83 +45,65 @@ impl App {
 
     pub fn run<F>(self, on_finish_launching: F)
     where
-        F: 'static + FnOnce(&mut AppContext<MainThread>),
+        F: 'static + FnOnce(&mut AppContext),
     {
-        let platform = self.0.read().platform.clone();
-        platform.run(Box::new(move || {
-            let mut cx = self.0.write();
-            let cx: &mut AppContext<()> = &mut cx;
-            let cx: &mut AppContext<MainThread> = unsafe { mem::transmute(cx) };
+        let platform = self.0.lock().platform.clone();
+        platform.borrow_on_main_thread().run(Box::new(move || {
+            let cx = &mut *self.0.lock();
             on_finish_launching(cx);
         }));
     }
 }
 
-pub struct AppContext<Thread = ()> {
-    this: Weak<RwLock<AppContext>>,
-    thread: PhantomData<Thread>,
-    platform: Arc<dyn Platform>,
+pub struct AppContext {
+    this: Weak<Mutex<AppContext>>,
+    platform: MainThreadOnly<dyn Platform>,
     text_system: Arc<TextSystem>,
     pub(crate) unit_entity_id: EntityId,
-    pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any>>>,
+    pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     // We recycle this memory across layout requests.
     pub(crate) layout_id_buffer: Vec<LayoutId>,
 }
 
-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,
-    // {
-    //     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> {
+impl AppContext {
     pub fn text_system(&self) -> &Arc<TextSystem> {
         &self.text_system
     }
 
-    pub fn open_window<S: 'static>(
+    pub fn with_platform<R: Send + 'static>(
+        &mut self,
+        f: impl FnOnce(&dyn Platform, &mut Self) -> R + Send + 'static,
+    ) -> impl Future<Output = R> {
+        let this = self.this.upgrade().unwrap();
+        self.platform.read(move |platform| {
+            let cx = &mut *this.lock();
+            f(platform, cx)
+        })
+    }
+
+    pub fn open_window<S: 'static + Send>(
         &mut self,
         options: crate::WindowOptions,
-        build_root_view: impl FnOnce(&mut WindowContext<Thread>) -> RootView<S>,
-    ) -> WindowHandle<S> {
-        let id = self.windows.insert(None);
-        let handle = WindowHandle::new(id);
-        let platform_window = self.platform.open_window(handle.into(), options);
-
-        let mut window = Window::new(id, platform_window);
-        let root_view = build_root_view(&mut WindowContext::mutable(self, &mut window));
-        window.root_view.replace(Box::new(root_view));
-
-        self.windows.get_mut(id).unwrap().replace(window);
-        handle
+        build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
+    ) -> impl Future<Output = WindowHandle<S>> {
+        self.with_platform(move |platform, cx| {
+            let id = cx.windows.insert(None);
+            let handle = WindowHandle::new(id);
+
+            let mut window = Window::new(handle.into(), options, platform);
+            let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
+            window.root_view.replace(Box::new(root_view));
+
+            cx.windows.get_mut(id).unwrap().replace(window);
+            handle
+        })
     }
 
     pub(crate) fn update_window<R>(
         &mut self,
         window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext<Thread>) -> R,
+        update: impl FnOnce(&mut WindowContext) -> R,
     ) -> Result<R> {
         let mut window = self
             .windows
@@ -143,16 +123,10 @@ impl<Thread> AppContext<Thread> {
     }
 }
 
-impl AppContext<MainThread> {
-    pub fn platform(&self) -> &dyn Platform {
-        self.platform.as_ref()
-    }
-}
-
-impl<Thread: 'static> Context for AppContext<Thread> {
-    type EntityContext<'a, 'w, T: 'static> = ModelContext<'a, Thread, T>;
+impl Context for AppContext {
+    type EntityContext<'a, 'w, T: Send + 'static> = ModelContext<'a, T>;
 
-    fn entity<T: 'static>(
+    fn entity<T: Send + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
     ) -> Handle<T> {
@@ -163,7 +137,7 @@ impl<Thread: 'static> Context for AppContext<Thread> {
         Handle::new(id)
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_entity<T: Send + 'static, R>(
         &mut self,
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
@@ -183,14 +157,14 @@ impl<Thread: 'static> Context for AppContext<Thread> {
     }
 }
 
-pub struct ModelContext<'a, Thread: 'static, T> {
-    app: Reference<'a, AppContext<Thread>>,
+pub struct ModelContext<'a, T> {
+    app: Reference<'a, AppContext>,
     entity_type: PhantomData<T>,
     entity_id: EntityId,
 }
 
-impl<'a, Thread, T: 'static> ModelContext<'a, Thread, T> {
-    pub(crate) fn mutable(app: &'a mut AppContext<Thread>, entity_id: EntityId) -> Self {
+impl<'a, T: 'static> ModelContext<'a, T> {
+    pub(crate) fn mutable(app: &'a mut AppContext, entity_id: EntityId) -> Self {
         Self {
             app: Reference::Mutable(app),
             entity_type: PhantomData,
@@ -198,7 +172,7 @@ impl<'a, Thread, T: 'static> ModelContext<'a, Thread, T> {
         }
     }
 
-    fn immutable(app: &'a AppContext<Thread>, entity_id: EntityId) -> Self {
+    fn immutable(app: &'a AppContext, entity_id: EntityId) -> Self {
         Self {
             app: Reference::Immutable(app),
             entity_type: PhantomData,
@@ -224,16 +198,17 @@ impl<'a, Thread, T: 'static> ModelContext<'a, Thread, T> {
     }
 }
 
-impl<'a, Thread, T: 'static> Context for ModelContext<'a, Thread, T> {
-    type EntityContext<'b, 'c, U: 'static> = ModelContext<'b, Thread, U>;
-    fn entity<U: 'static>(
+impl<'a, T: 'static> Context for ModelContext<'a, T> {
+    type EntityContext<'b, 'c, U: Send + 'static> = ModelContext<'b, U>;
+
+    fn entity<U: Send + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, U>) -> U,
     ) -> Handle<U> {
         self.app.entity(build_entity)
     }
 
-    fn update_entity<U: 'static, R>(
+    fn update_entity<U: Send + 'static, R>(
         &mut self,
         handle: &Handle<U>,
         update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,
@@ -249,7 +224,7 @@ pub struct Handle<T> {
 
 slotmap::new_key_type! { pub struct EntityId; }
 
-impl<T: 'static> Handle<T> {
+impl<T: Send + 'static> Handle<T> {
     fn new(id: EntityId) -> Self {
         Self {
             id,
@@ -279,3 +254,15 @@ impl<T> Clone for Handle<T> {
         }
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::AppContext;
+
+    #[test]
+    fn test_app_context_send_sync() {
+        // This will not compile if `AppContext` does not implement `Send`
+        fn assert_send<T: Send>() {}
+        assert_send::<AppContext>();
+    }
+}

crates/gpui3/src/elements/div.rs 🔗

@@ -2,8 +2,9 @@ use crate::{
     AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point,
     Refineable, RefinementCascade, Result, Style, StyleHelpers, Styled, ViewContext,
 };
+use parking_lot::Mutex;
 use smallvec::SmallVec;
-use std::{cell::Cell, rc::Rc};
+use std::sync::Arc;
 use util::ResultExt;
 
 pub struct Div<S: 'static> {
@@ -271,26 +272,22 @@ impl<V: 'static> ParentElement<V> for Div<V> {
 }
 
 #[derive(Default, Clone)]
-pub struct ScrollState(Rc<Cell<Point<Pixels>>>);
+pub struct ScrollState(Arc<Mutex<Point<Pixels>>>);
 
 impl ScrollState {
     pub fn x(&self) -> Pixels {
-        self.0.get().x
+        self.0.lock().x
     }
 
     pub fn set_x(&self, value: Pixels) {
-        let mut current_value = self.0.get();
-        current_value.x = value;
-        self.0.set(current_value);
+        self.0.lock().x = value;
     }
 
     pub fn y(&self) -> Pixels {
-        self.0.get().y
+        self.0.lock().y
     }
 
     pub fn set_y(&self, value: Pixels) {
-        let mut current_value = self.0.get();
-        current_value.y = value;
-        self.0.set(current_value);
+        self.0.lock().y = value;
     }
 }

crates/gpui3/src/gpui3.rs 🔗

@@ -22,6 +22,7 @@ pub use color::*;
 pub use element::*;
 pub use elements::*;
 pub use executor::*;
+use futures::channel::oneshot;
 pub use geometry::*;
 pub use gpui3_macros::*;
 pub use platform::*;
@@ -31,7 +32,11 @@ pub use serde;
 pub use serde_json;
 pub use smallvec;
 pub use smol::Timer;
-use std::ops::{Deref, DerefMut};
+use std::{
+    future::Future,
+    ops::{Deref, DerefMut},
+    sync::Arc,
+};
 pub use style::*;
 pub use style_helpers::*;
 pub use styled::*;
@@ -43,14 +48,14 @@ pub use view::*;
 pub use window::*;
 
 pub trait Context {
-    type EntityContext<'a, 'w, T: 'static>;
+    type EntityContext<'a, 'w, T: Send + 'static>;
 
-    fn entity<T: 'static>(
+    fn entity<T: 'static + Send>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
     ) -> Handle<T>;
 
-    fn update_entity<T: 'static, R>(
+    fn update_entity<T: 'static + Send, R>(
         &mut self,
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
@@ -110,3 +115,54 @@ impl<'a, T> DerefMut for Reference<'a, T> {
         }
     }
 }
+
+pub(crate) struct MainThreadOnly<T: ?Sized> {
+    dispatcher: Arc<dyn PlatformDispatcher>,
+    value: Arc<T>,
+}
+
+impl<T: ?Sized> Clone for MainThreadOnly<T> {
+    fn clone(&self) -> Self {
+        Self {
+            dispatcher: self.dispatcher.clone(),
+            value: self.value.clone(),
+        }
+    }
+}
+
+/// Allows a value to be accessed only on the main thread, allowing a non-`Send` type
+/// to become `Send`.
+impl<T: 'static + ?Sized> MainThreadOnly<T> {
+    pub(crate) fn new(value: Arc<T>, dispatcher: Arc<dyn PlatformDispatcher>) -> Self {
+        Self { dispatcher, value }
+    }
+
+    pub(crate) fn borrow_on_main_thread(&self) -> &T {
+        assert!(self.dispatcher.is_main_thread());
+        &self.value
+    }
+
+    pub(crate) fn read<R>(
+        &self,
+        f: impl FnOnce(&T) -> R + Send + 'static,
+    ) -> impl Future<Output = R>
+    where
+        R: Send + 'static,
+    {
+        let (tx, rx) = oneshot::channel();
+        if self.dispatcher.is_main_thread() {
+            let _ = tx.send(f(&self.value));
+        } else {
+            let this = self.clone();
+            let _ = crate::spawn_on_main(self.dispatcher.clone(), async move {
+                // Required so we move `this` instead of this.value. Only `this` is `Send`.
+                let this = this;
+                let _ = tx.send(f(&this.value));
+            });
+        }
+
+        async move { rx.await.unwrap() }
+    }
+}
+
+unsafe impl<T: ?Sized> Send for MainThreadOnly<T> {}

crates/gpui3/src/platform.rs 🔗

@@ -15,6 +15,7 @@ use futures::channel::oneshot;
 use raw_window_handle::{HasRawDisplayHandle, HasRawWindowHandle};
 use seahash::SeaHasher;
 use serde::{Deserialize, Serialize};
+use std::ffi::c_void;
 use std::hash::{Hash, Hasher};
 use std::{
     any::Any,
@@ -40,7 +41,7 @@ pub(crate) fn current_platform() -> Arc<dyn Platform> {
     Arc::new(MacPlatform::new())
 }
 
-pub trait Platform {
+pub trait Platform: 'static {
     fn dispatcher(&self) -> Arc<dyn PlatformDispatcher>;
     fn text_system(&self) -> Arc<dyn PlatformTextSystem>;
 
@@ -53,7 +54,7 @@ pub trait Platform {
     fn unhide_other_apps(&self);
 
     fn screens(&self) -> Vec<Rc<dyn PlatformScreen>>;
-    fn screen_by_id(&self, id: uuid::Uuid) -> Option<Rc<dyn PlatformScreen>>;
+    fn screen_by_id(&self, id: ScreenId) -> Option<Rc<dyn PlatformScreen>>;
     fn main_window(&self) -> Option<AnyWindowHandle>;
     fn open_window(
         &self,
@@ -96,12 +97,23 @@ pub trait Platform {
 }
 
 pub trait PlatformScreen: Debug {
+    fn id(&self) -> Option<ScreenId>;
+    fn handle(&self) -> PlatformScreenHandle;
     fn as_any(&self) -> &dyn Any;
     fn bounds(&self) -> Bounds<Pixels>;
     fn content_bounds(&self) -> Bounds<Pixels>;
-    fn display_uuid(&self) -> Option<Uuid>;
 }
 
+pub struct PlatformScreenHandle(pub *mut c_void);
+
+impl Debug for PlatformScreenHandle {
+    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+        write!(f, "PlatformScreenHandle({:p})", self.0)
+    }
+}
+
+unsafe impl Send for PlatformScreenHandle {}
+
 pub trait PlatformWindow: HasRawWindowHandle + HasRawDisplayHandle {
     fn bounds(&self) -> WindowBounds;
     fn content_size(&self) -> Size<Pixels>;
@@ -190,6 +202,9 @@ pub trait InputHandler {
     fn bounds_for_range(&self, range_utf16: Range<usize>) -> Option<Bounds<f32>>;
 }
 
+#[derive(Copy, Clone, Debug, PartialEq)]
+pub struct ScreenId(pub(crate) Uuid);
+
 #[derive(Copy, Clone, Debug)]
 pub enum RasterizationOptions {
     Alpha,
@@ -205,7 +220,7 @@ pub struct WindowOptions {
     pub show: bool,
     pub kind: WindowKind,
     pub is_movable: bool,
-    pub screen: Option<Rc<dyn PlatformScreen>>,
+    pub screen: Option<PlatformScreenHandle>,
 }
 
 impl Default for WindowOptions {

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

@@ -2,7 +2,7 @@ use super::BoolExt;
 use crate::{
     AnyWindowHandle, ClipboardItem, CursorStyle, Event, MacDispatcher, MacScreen, MacTextSystem,
     MacWindow, PathPromptOptions, Platform, PlatformScreen, PlatformTextSystem, PlatformWindow,
-    Result, SemanticVersion, WindowOptions,
+    Result, ScreenId, SemanticVersion, WindowOptions,
 };
 use anyhow::anyhow;
 use block::ConcreteBlock;
@@ -462,7 +462,7 @@ impl Platform for MacPlatform {
             .collect()
     }
 
-    fn screen_by_id(&self, id: uuid::Uuid) -> Option<Rc<dyn PlatformScreen>> {
+    fn screen_by_id(&self, id: ScreenId) -> Option<Rc<dyn PlatformScreen>> {
         MacScreen::find_by_id(id).map(|screen| Rc::new(screen) as Rc<_>)
     }
 
@@ -479,8 +479,7 @@ impl Platform for MacPlatform {
         handle: AnyWindowHandle,
         options: WindowOptions,
     ) -> Box<dyn PlatformWindow> {
-        let dispatcher = self.0.lock().dispatcher.clone();
-        Box::new(MacWindow::open(handle, options, dispatcher))
+        Box::new(MacWindow::open(handle, options, self))
     }
 
     fn open_url(&self, url: &str) {

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

@@ -1,5 +1,8 @@
 use super::ns_string;
-use crate::{platform, point, px, size, Bounds, Pixels, PlatformScreen};
+use crate::{
+    platform, point, px, size, Bounds, MainThreadOnly, Pixels, PlatformScreen,
+    PlatformScreenHandle, ScreenId,
+};
 use cocoa::{
     appkit::NSScreen,
     base::{id, nil},
@@ -10,6 +13,7 @@ use core_foundation::{
     uuid::{CFUUIDGetUUIDBytes, CFUUIDRef},
 };
 use core_graphics::display::CGDirectDisplayID;
+use objc::runtime::Object;
 use std::{any::Any, ffi::c_void};
 use uuid::Uuid;
 
@@ -23,10 +27,18 @@ pub struct MacScreen {
     pub(crate) native_screen: id,
 }
 
+unsafe impl Send for MacScreen {}
+
 impl MacScreen {
+    pub(crate) fn from_handle(handle: PlatformScreenHandle) -> Self {
+        Self {
+            native_screen: handle.0 as *mut Object,
+        }
+    }
+
     /// Get the screen with the given UUID.
-    pub fn find_by_id(uuid: Uuid) -> Option<Self> {
-        Self::all().find(|screen| platform::MacScreen::display_uuid(screen) == Some(uuid))
+    pub fn find_by_id(id: ScreenId) -> Option<Self> {
+        Self::all().find(|screen| screen.id() == Some(id))
     }
 
     /// Get the primary screen - the one with the menu bar, and whose bottom left
@@ -82,14 +94,8 @@ impl MacScreen {
 }
 
 impl PlatformScreen for MacScreen {
-    fn as_any(&self) -> &dyn Any {
-        self
-    }
-
-    fn display_uuid(&self) -> Option<uuid::Uuid> {
+    fn id(&self) -> Option<ScreenId> {
         unsafe {
-            // Screen ids are not stable. Further, the default device id is also unstable across restarts.
-            // CGDisplayCreateUUIDFromDisplayID is stable but not exposed in the bindings we use.
             // This approach is similar to that which winit takes
             // https://github.com/rust-windowing/winit/blob/402cbd55f932e95dbfb4e8b5e8551c49e56ff9ac/src/platform_impl/macos/monitor.rs#L99
             let device_description = self.native_screen.deviceDescription();
@@ -114,7 +120,7 @@ impl PlatformScreen for MacScreen {
             }
 
             let bytes = CFUUIDGetUUIDBytes(cfuuid);
-            Some(Uuid::from_bytes([
+            Some(ScreenId(Uuid::from_bytes([
                 bytes.byte0,
                 bytes.byte1,
                 bytes.byte2,
@@ -131,10 +137,18 @@ impl PlatformScreen for MacScreen {
                 bytes.byte13,
                 bytes.byte14,
                 bytes.byte15,
-            ]))
+            ])))
         }
     }
 
+    fn handle(&self) -> PlatformScreenHandle {
+        PlatformScreenHandle(self.native_screen as *mut c_void)
+    }
+
+    fn as_any(&self) -> &dyn Any {
+        self
+    }
+
     fn bounds(&self) -> Bounds<Pixels> {
         unsafe { Self::screen_bounds_from_native(self.native_screen.frame()) }
     }

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

@@ -1,8 +1,8 @@
 use crate::{
     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,
+    MacScreen, Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMovedEvent,
+    MouseUpEvent, NSRectExt, Pixels, Platform, PlatformDispatcher, PlatformScreen, PlatformWindow,
+    Point, Size, Timer, WindowAppearance, WindowBounds, WindowKind, WindowOptions,
     WindowPromptLevel,
 };
 use block::ConcreteBlock;
@@ -38,8 +38,8 @@ use std::{
     ops::Range,
     os::raw::c_char,
     ptr,
-    rc::{Rc, Weak},
-    sync::Arc,
+    rc::Rc,
+    sync::{Arc, Weak},
     time::Duration,
 };
 
@@ -417,11 +417,7 @@ unsafe impl Send for WindowState {}
 pub struct MacWindow(Arc<Mutex<WindowState>>);
 
 impl MacWindow {
-    pub fn open(
-        handle: AnyWindowHandle,
-        options: WindowOptions,
-        dispatcher: Arc<MacDispatcher>,
-    ) -> Self {
+    pub fn open(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
         unsafe {
             let pool = NSAutoreleasePool::new(nil);
 
@@ -454,9 +450,7 @@ impl MacWindow {
                 NO,
                 options
                     .screen
-                    .and_then(|screen| {
-                        Some(screen.as_any().downcast_ref::<MacScreen>()?.native_screen)
-                    })
+                    .map(|screen| MacScreen::from_handle(screen).native_screen)
                     .unwrap_or(nil),
             );
             assert!(!native_window.is_null());
@@ -487,7 +481,7 @@ impl MacWindow {
 
             let window = Self(Arc::new(Mutex::new(WindowState {
                 handle,
-                dispatcher,
+                dispatcher: platform.dispatcher(),
                 native_window,
                 kind: options.kind,
                 event_callback: None,
@@ -610,7 +604,7 @@ impl MacWindow {
             let app = NSApplication::sharedApplication(nil);
             let main_window: id = msg_send![app, mainWindow];
             if msg_send![main_window, isKindOfClass: WINDOW_CLASS] {
-                let handle = get_window_state(&*main_window).borrow().handle;
+                let handle = get_window_state(&*main_window).lock().handle;
                 Some(handle)
             } else {
                 None
@@ -892,7 +886,7 @@ impl PlatformWindow for MacWindow {
             let is_panel: BOOL = msg_send![top_most_window, isKindOfClass: PANEL_CLASS];
             let is_window: BOOL = msg_send![top_most_window, isKindOfClass: WINDOW_CLASS];
             if is_panel == YES || is_window == YES {
-                let topmost_window = get_window_state(&*top_most_window).borrow().handle;
+                let topmost_window = get_window_state(&*top_most_window).lock().handle;
                 topmost_window == self_handle
             } else {
                 // Someone else's window is on top
@@ -909,9 +903,9 @@ fn get_scale_factor(native_window: id) -> f32 {
     }
 }
 
-unsafe fn get_window_state(object: &Object) -> Rc<RefCell<WindowState>> {
+unsafe fn get_window_state(object: &Object) -> Arc<Mutex<WindowState>> {
     let raw: *mut c_void = *object.get_ivar(WINDOW_STATE_IVAR);
-    let rc1 = Rc::from_raw(raw as *mut RefCell<WindowState>);
+    let rc1 = Arc::from_raw(raw as *mut Mutex<WindowState>);
     let rc2 = rc1.clone();
     mem::forget(rc1);
     rc2
@@ -950,7 +944,7 @@ extern "C" fn handle_key_down(this: &Object, _: Sel, native_event: id) {
 
 extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent: bool) -> BOOL {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
 
     let window_height = window_state_borrow.content_size().height;
     let event = unsafe { Event::from_native(native_event, Some(window_height)) };
@@ -990,7 +984,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
         }
 
         let mut handled = false;
-        let mut window_state_borrow = window_state.borrow_mut();
+        let mut window_state_borrow = window_state.lock();
         let ime_text = window_state_borrow.ime_text.clone();
         if let Some((event, insert_text)) = window_state_borrow.pending_key_down.take() {
             let is_held = event.is_held;
@@ -1056,7 +1050,7 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
                     }
                 }
 
-                window_state.borrow_mut().event_callback = Some(callback);
+                window_state.lock().event_callback = Some(callback);
             }
         } else {
             handled = true;
@@ -1070,8 +1064,8 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
 
 extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
     let window_state = unsafe { get_window_state(this) };
-    let weak_window_state = Rc::downgrade(&window_state);
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let weak_window_state = Arc::downgrade(&window_state);
+    let mut window_state_borrow = window_state.as_ref().lock();
     let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
 
     let window_height = window_state_borrow.content_size().height;
@@ -1172,7 +1166,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
             if let Some(event) = synthesized_second_event {
                 callback(event);
             }
-            window_state.borrow_mut().event_callback = Some(callback);
+            window_state.lock().event_callback = Some(callback);
         }
     }
 }
@@ -1181,7 +1175,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
 // https://bugs.eclipse.org/bugs/show_bug.cgi?id=300620#c6
 extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
 
     let keystroke = Keystroke {
         modifiers: Default::default(),
@@ -1196,13 +1190,13 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
     if let Some(mut callback) = window_state_borrow.event_callback.take() {
         drop(window_state_borrow);
         callback(event);
-        window_state.borrow_mut().event_callback = Some(callback);
+        window_state.lock().event_callback = Some(callback);
     }
 }
 
 extern "C" fn window_did_resize(this: &Object, _: Sel, _: id) {
     let window_state = unsafe { get_window_state(this) };
-    window_state.as_ref().borrow().move_traffic_light();
+    window_state.as_ref().lock().move_traffic_light();
 }
 
 extern "C" fn window_will_enter_fullscreen(this: &Object, _: Sel, _: id) {
@@ -1215,27 +1209,27 @@ extern "C" fn window_will_exit_fullscreen(this: &Object, _: Sel, _: id) {
 
 fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
     if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
         drop(window_state_borrow);
         callback(is_fullscreen);
-        window_state.borrow_mut().fullscreen_callback = Some(callback);
+        window_state.lock().fullscreen_callback = Some(callback);
     }
 }
 
 extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
     if let Some(mut callback) = window_state_borrow.moved_callback.take() {
         drop(window_state_borrow);
         callback();
-        window_state.borrow_mut().moved_callback = Some(callback);
+        window_state.lock().moved_callback = Some(callback);
     }
 }
 
 extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id) {
     let window_state = unsafe { get_window_state(this) };
-    let window_state_borrow = window_state.borrow();
+    let window_state_borrow = window_state.lock();
     let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
 
     // When opening a pop-up while the application isn't active, Cocoa sends a spurious
@@ -1259,22 +1253,22 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
     let dispatcher = window_state_borrow.dispatcher.clone();
     drop(window_state_borrow);
     let _ = crate::spawn_on_main_local(dispatcher, async move {
-        let mut window_state_borrow = window_state.as_ref().borrow_mut();
+        let mut window_state_borrow = window_state.as_ref().lock();
         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);
+            window_state.lock().activate_callback = Some(callback);
         };
     });
 }
 
 extern "C" fn window_should_close(this: &Object, _: Sel, _: id) -> BOOL {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
     if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
         drop(window_state_borrow);
         let should_close = callback();
-        window_state.borrow_mut().should_close_callback = Some(callback);
+        window_state.lock().should_close_callback = Some(callback);
         should_close as BOOL
     } else {
         YES
@@ -1287,8 +1281,7 @@ extern "C" fn close_window(this: &Object, _: Sel) {
             let window_state = get_window_state(this);
             window_state
                 .as_ref()
-                .try_borrow_mut()
-                .ok()
+                .try_lock()
                 .and_then(|mut window_state| window_state.close_callback.take())
         };
 
@@ -1302,13 +1295,13 @@ extern "C" fn close_window(this: &Object, _: Sel) {
 
 // extern "C" fn make_backing_layer(this: &Object, _: Sel) -> id {
 //     let window_state = unsafe { get_window_state(this) };
-//     let window_state = window_state.as_ref().borrow();
+//     let window_state = window_state.as_ref().lock();
 //     window_state.renderer.layer().as_ptr() as id
 // }
 
 extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
     let window_state = unsafe { get_window_state(this) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
 
     // unsafe {
     //     let scale_factor = window_state_borrow.scale_factor() as f64;
@@ -1331,13 +1324,13 @@ extern "C" fn view_did_change_backing_properties(this: &Object, _: Sel) {
     if let Some(mut callback) = window_state_borrow.resize_callback.take() {
         drop(window_state_borrow);
         callback();
-        window_state.as_ref().borrow_mut().resize_callback = Some(callback);
+        window_state.as_ref().lock().resize_callback = Some(callback);
     };
 }
 
 extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     let window_state = unsafe { get_window_state(this) };
-    let window_state_borrow = window_state.as_ref().borrow();
+    let window_state_borrow = window_state.as_ref().lock();
 
     if window_state_borrow.content_size() == size.into() {
         return;
@@ -1361,18 +1354,18 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
     // }
 
     drop(window_state_borrow);
-    let mut window_state_borrow = window_state.borrow_mut();
+    let mut window_state_borrow = window_state.lock();
     if let Some(mut callback) = window_state_borrow.resize_callback.take() {
         drop(window_state_borrow);
         callback();
-        window_state.borrow_mut().resize_callback = Some(callback);
+        window_state.lock().resize_callback = Some(callback);
     };
 }
 
 extern "C" fn display_layer(_this: &Object, _: Sel, _: id) {
     // unsafe {
     // let window_state = get_window_state(this);
-    // let mut window_state = window_state.as_ref().borrow_mut();
+    // let mut window_state = window_state.as_ref().lock();
     // if let Some(scene) = window_state.scene_to_render.take() {
     //     window_state.renderer.render(&scene);
     // };
@@ -1408,7 +1401,7 @@ extern "C" fn first_rect_for_character_range(
     _: id,
 ) -> NSRect {
     let frame = unsafe {
-        let window = get_window_state(this).borrow().native_window;
+        let window = get_window_state(this).lock().native_window;
         NSView::frame(window)
     };
     with_input_handler(this, |input_handler| {
@@ -1432,7 +1425,7 @@ extern "C" fn first_rect_for_character_range(
 extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NSRange) {
     unsafe {
         let window_state = get_window_state(this);
-        let mut window_state_borrow = window_state.borrow_mut();
+        let mut window_state_borrow = window_state.lock();
         let pending_key_down = window_state_borrow.pending_key_down.take();
         drop(window_state_borrow);
 
@@ -1448,8 +1441,8 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
             .unwrap();
         let replacement_range = replacement_range.to_range();
 
-        window_state.borrow_mut().ime_text = Some(text.to_string());
-        window_state.borrow_mut().ime_state = ImeState::Acted;
+        window_state.lock().ime_text = Some(text.to_string());
+        window_state.lock().ime_state = ImeState::Acted;
 
         let is_composing =
             with_input_handler(this, |input_handler| input_handler.marked_text_range())
@@ -1466,7 +1459,7 @@ extern "C" fn insert_text(this: &Object, _: Sel, text: id, replacement_range: NS
                 replacement_range,
                 text: text.to_string(),
             });
-            window_state.borrow_mut().pending_key_down = Some(pending_key_down);
+            window_state.lock().pending_key_down = Some(pending_key_down);
         }
     }
 }
@@ -1480,7 +1473,7 @@ extern "C" fn set_marked_text(
 ) {
     unsafe {
         let window_state = get_window_state(this);
-        window_state.borrow_mut().pending_key_down.take();
+        window_state.lock().pending_key_down.take();
 
         let is_attributed_string: BOOL =
             msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
@@ -1495,8 +1488,8 @@ extern "C" fn set_marked_text(
             .to_str()
             .unwrap();
 
-        window_state.borrow_mut().ime_state = ImeState::Acted;
-        window_state.borrow_mut().ime_text = Some(text.to_string());
+        window_state.lock().ime_state = ImeState::Acted;
+        window_state.lock().ime_text = Some(text.to_string());
 
         with_input_handler(this, |input_handler| {
             input_handler.replace_and_mark_text_in_range(replacement_range, text, selected_range);
@@ -1507,7 +1500,7 @@ extern "C" fn set_marked_text(
 extern "C" fn unmark_text(this: &Object, _: Sel) {
     unsafe {
         let state = get_window_state(this);
-        let mut borrow = state.borrow_mut();
+        let mut borrow = state.lock();
         borrow.ime_state = ImeState::Acted;
         borrow.ime_text.take();
     }
@@ -1541,7 +1534,7 @@ extern "C" fn attributed_substring_for_proposed_range(
 extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
     unsafe {
         let state = get_window_state(this);
-        let mut borrow = state.borrow_mut();
+        let mut borrow = state.lock();
         borrow.ime_state = ImeState::Continue;
         borrow.ime_text.take();
     }
@@ -1550,11 +1543,11 @@ extern "C" fn do_command_by_selector(this: &Object, _: Sel, _: Sel) {
 extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
     unsafe {
         let state = get_window_state(this);
-        let mut state_borrow = state.as_ref().borrow_mut();
+        let mut state_borrow = state.as_ref().lock();
         if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
             drop(state_borrow);
             callback();
-            state.borrow_mut().appearance_changed_callback = Some(callback);
+            state.lock().appearance_changed_callback = Some(callback);
         }
     }
 }
@@ -1562,7 +1555,7 @@ extern "C" fn view_did_change_effective_appearance(this: &Object, _: Sel) {
 extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
     unsafe {
         let state = get_window_state(this);
-        let state_borrow = state.as_ref().borrow();
+        let state_borrow = state.as_ref().lock();
         return if state_borrow.kind == WindowKind::PopUp {
             YES
         } else {
@@ -1572,19 +1565,19 @@ extern "C" fn accepts_first_mouse(this: &Object, _: Sel, _: id) -> BOOL {
 }
 
 async fn synthetic_drag(
-    window_state: Weak<RefCell<WindowState>>,
+    window_state: Weak<Mutex<WindowState>>,
     drag_id: usize,
     event: MouseMovedEvent,
 ) {
     loop {
         Timer::after(Duration::from_millis(16)).await;
         if let Some(window_state) = window_state.upgrade() {
-            let mut window_state_borrow = window_state.borrow_mut();
+            let mut window_state_borrow = window_state.lock();
             if window_state_borrow.synthetic_drag_counter == drag_id {
                 if let Some(mut callback) = window_state_borrow.event_callback.take() {
                     drop(window_state_borrow);
                     callback(Event::MouseMoved(event.clone()));
-                    window_state.borrow_mut().event_callback = Some(callback);
+                    window_state.lock().event_callback = Some(callback);
                 }
             } else {
                 break;
@@ -1598,11 +1591,11 @@ where
     F: FnOnce(&mut dyn InputHandler) -> R,
 {
     let window_state = unsafe { get_window_state(window) };
-    let mut window_state_borrow = window_state.as_ref().borrow_mut();
+    let mut window_state_borrow = window_state.as_ref().lock();
     if let Some(mut input_handler) = window_state_borrow.input_handler.take() {
         drop(window_state_borrow);
         let result = f(input_handler.as_mut());
-        window_state.borrow_mut().input_handler = Some(input_handler);
+        window_state.lock().input_handler = Some(input_handler);
         Some(result)
     } else {
         None

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

@@ -1,4 +1,5 @@
 use super::Platform;
+use crate::ScreenId;
 
 pub struct TestPlatform;
 
@@ -49,7 +50,7 @@ impl Platform for TestPlatform {
         todo!()
     }
 
-    fn screen_by_id(&self, _id: uuid::Uuid) -> Option<std::rc::Rc<dyn crate::PlatformScreen>> {
+    fn screen_by_id(&self, _id: ScreenId) -> Option<std::rc::Rc<dyn crate::PlatformScreen>> {
         todo!()
     }
 

crates/gpui3/src/view.rs 🔗

@@ -2,11 +2,11 @@ use crate::{
     AnyElement, Element, Handle, IntoAnyElement, Layout, LayoutId, Result, ViewContext,
     WindowContext,
 };
-use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
+use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc, sync::Arc};
 
 pub struct View<S, P> {
     state: Handle<S>,
-    render: Rc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S>>,
+    render: Arc<dyn Fn(&mut S, &mut ViewContext<S>) -> AnyElement<S> + Send + Sync + 'static>,
     parent_state_type: PhantomData<P>,
 }
 
@@ -24,16 +24,16 @@ pub type RootView<S> = View<S, ()>;
 
 pub fn view<S: 'static, P: 'static, E: Element<State = S>>(
     state: Handle<S>,
-    render: impl 'static + Fn(&mut S, &mut ViewContext<S>) -> E,
+    render: impl Fn(&mut S, &mut ViewContext<S>) -> E + Send + Sync + 'static,
 ) -> View<S, P> {
     View {
         state,
-        render: Rc::new(move |state, cx| render(state, cx).into_any()),
+        render: Arc::new(move |state, cx| render(state, cx).into_any()),
         parent_state_type: PhantomData,
     }
 }
 
-impl<S: 'static, P: 'static> View<S, P> {
+impl<S: Send + 'static, P: 'static> View<S, P> {
     pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
         AnyView {
             view: Rc::new(RefCell::new(self)),
@@ -42,7 +42,7 @@ impl<S: 'static, P: 'static> View<S, P> {
     }
 }
 
-impl<S: 'static, P: 'static> Element for View<S, P> {
+impl<S: Send + 'static, P: Send + 'static> Element for View<S, P> {
     type State = P;
     type FrameState = AnyElement<S>;
 
@@ -80,7 +80,7 @@ trait ViewObject {
     ) -> Result<()>;
 }
 
-impl<S: 'static, P> ViewObject for View<S, P> {
+impl<S: Send + 'static, P> ViewObject for View<S, P> {
     fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)> {
         self.state.update(cx, |state, cx| {
             let mut element = (self.render)(state, cx);

crates/gpui3/src/window.rs 🔗

@@ -1,4 +1,7 @@
-use crate::{AvailableSpace, PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
+use crate::{
+    AvailableSpace, MainThreadOnly, Platform, PlatformWindow, Point, Size, Style, TextStyle,
+    TextStyleRefinement, WindowOptions,
+};
 
 use super::{
     px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference,
@@ -10,49 +13,54 @@ use refineable::Refineable;
 use std::{
     any::{Any, TypeId},
     marker::PhantomData,
+    sync::Arc,
 };
 
 pub struct AnyWindow {}
 
 pub struct Window {
-    id: WindowId,
-    platform_window: Box<dyn PlatformWindow>,
+    handle: AnyWindowHandle,
+    platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
     rem_size: Pixels,
     layout_engine: TaffyLayoutEngine,
     text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) root_view: Option<Box<dyn Any>>,
+    pub(crate) root_view: Option<Box<dyn Any + Send>>,
+    mouse_position: Point<Pixels>,
 }
 
 impl Window {
-    pub fn new(id: WindowId, platform_window: Box<dyn PlatformWindow>) -> Window {
+    pub fn new(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Window {
+        let platform_window = Arc::new(platform.open_window(handle, options));
+        let mouse_position = platform_window.mouse_position();
         Window {
-            id,
-            platform_window,
+            handle,
+            platform_window: MainThreadOnly::new(platform_window, platform.dispatcher()),
             rem_size: px(16.),
             layout_engine: TaffyLayoutEngine::new(),
             text_style_stack: Vec::new(),
             root_view: None,
+            mouse_position,
         }
     }
 }
 
 #[derive(Deref, DerefMut)]
-pub struct WindowContext<'a, 'b, Thread = ()> {
+pub struct WindowContext<'a, 'b> {
     #[deref]
     #[deref_mut]
-    app: Reference<'a, AppContext<Thread>>,
+    app: Reference<'a, AppContext>,
     window: Reference<'b, Window>,
 }
 
-impl<'a, 'w, Thread> WindowContext<'a, 'w, Thread> {
-    pub(crate) fn mutable(app: &'a mut AppContext<Thread>, window: &'w mut Window) -> Self {
+impl<'a, 'w> WindowContext<'a, 'w> {
+    pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window) -> Self {
         Self {
             app: Reference::Mutable(app),
             window: Reference::Mutable(window),
         }
     }
 
-    pub(crate) fn immutable(app: &'a AppContext<Thread>, window: &'w Window) -> Self {
+    pub(crate) fn immutable(app: &'a AppContext, window: &'w Window) -> Self {
         Self {
             app: Reference::Immutable(app),
             window: Reference::Immutable(window),
@@ -115,15 +123,15 @@ impl<'a, 'w, Thread> WindowContext<'a, 'w, Thread> {
     }
 
     pub fn mouse_position(&self) -> Point<Pixels> {
-        self.window.platform_window.mouse_position()
+        self.window.mouse_position
     }
 
     fn update_window<R>(
         &mut self,
         window_id: WindowId,
-        update: impl FnOnce(&mut WindowContext<Thread>) -> R,
+        update: impl FnOnce(&mut WindowContext) -> R,
     ) -> Result<R> {
-        if window_id == self.window.id {
+        if window_id == self.window.handle.id {
             Ok(update(self))
         } else {
             self.app.update_window(window_id, update)
@@ -132,9 +140,9 @@ impl<'a, 'w, Thread> WindowContext<'a, 'w, Thread> {
 }
 
 impl Context for WindowContext<'_, '_> {
-    type EntityContext<'a, 'w, T: 'static> = ViewContext<'a, 'w, T>;
+    type EntityContext<'a, 'w, T: Send + 'static> = ViewContext<'a, 'w, T>;
 
-    fn entity<T: 'static>(
+    fn entity<T: Send + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
     ) -> Handle<T> {
@@ -152,7 +160,7 @@ impl Context for WindowContext<'_, '_> {
         }
     }
 
-    fn update_entity<T: 'static, R>(
+    fn update_entity<T: Send + 'static, R>(
         &mut self,
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
@@ -233,16 +241,16 @@ impl<'a, 'w, T: 'static> ViewContext<'a, 'w, T> {
 }
 
 impl<'a, 'w, T: 'static> Context for ViewContext<'a, 'w, T> {
-    type EntityContext<'b, 'c, U: 'static> = ViewContext<'b, 'c, U>;
+    type EntityContext<'b, 'c, U: Send + 'static> = ViewContext<'b, 'c, U>;
 
-    fn entity<T2: 'static>(
+    fn entity<T2: Send + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T2>) -> T2,
     ) -> Handle<T2> {
         self.window_cx.entity(build_entity)
     }
 
-    fn update_entity<U: 'static, R>(
+    fn update_entity<U: Send + 'static, R>(
         &mut self,
         handle: &Handle<U>,
         update: impl FnOnce(&mut U, &mut Self::EntityContext<'_, '_, U>) -> R,

crates/storybook2/src/storybook2.rs 🔗

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