Checkpoint

Antonio Scandurra created

Change summary

crates/gpui3/src/app.rs                 |  85 ++++++++++--
crates/gpui3/src/elements/stateless.rs  |   3 
crates/gpui3/src/gpui3.rs               |  21 ++
crates/gpui3/src/platform.rs            |  16 +-
crates/gpui3/src/platform/mac/window.rs | 184 +++++++++++++-------------
crates/gpui3/src/scene.rs               |  11 +
crates/gpui3/src/taffy.rs               |  55 +++++--
crates/gpui3/src/view.rs                |  34 ++--
crates/gpui3/src/window.rs              |  70 ++++++++-
9 files changed, 313 insertions(+), 166 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -31,13 +31,13 @@ impl App {
         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 + Send>));
+        let unit_entity = Handle::new(entities.insert(Some(Box::new(()) as Box<dyn Any + Send>)));
         Self(Arc::new_cyclic(|this| {
             Mutex::new(AppContext {
                 this: this.clone(),
                 platform: MainThreadOnly::new(platform, dispatcher),
                 text_system,
-                unit_entity_id,
+                unit_entity,
                 entities,
                 windows: SlotMap::with_key(),
                 pending_updates: 0,
@@ -67,7 +67,7 @@ pub struct AppContext {
     this: Weak<Mutex<AppContext>>,
     platform: MainThreadOnly<dyn Platform>,
     text_system: Arc<TextSystem>,
-    pub(crate) unit_entity_id: EntityId,
+    pub(crate) unit_entity: Handle<()>,
     pub(crate) entities: SlotMap<EntityId, Option<Box<dyn Any + Send>>>,
     pub(crate) windows: SlotMap<WindowId, Option<Window>>,
     pending_updates: usize,
@@ -82,8 +82,12 @@ impl AppContext {
         &self.text_system
     }
 
+    pub fn to_async(&self) -> AsyncContext {
+        AsyncContext(self.this.clone())
+    }
+
     pub fn spawn_on_main<F, R>(
-        &mut self,
+        &self,
         f: impl FnOnce(&dyn Platform, &mut Self) -> F + Send + 'static,
     ) -> impl Future<Output = R>
     where
@@ -97,7 +101,7 @@ impl AppContext {
         })
     }
 
-    pub fn open_window<S: 'static + Send>(
+    pub fn open_window<S: 'static + Send + Sync>(
         &mut self,
         options: crate::WindowOptions,
         build_root_view: impl FnOnce(&mut WindowContext) -> RootView<S> + Send + 'static,
@@ -105,9 +109,9 @@ impl AppContext {
         let id = self.windows.insert(None);
         let handle = WindowHandle::new(id);
         self.spawn_on_main(move |platform, cx| {
-            let mut window = Window::new(handle.into(), options, platform);
+            let mut window = Window::new(handle.into(), options, platform, cx);
             let root_view = build_root_view(&mut WindowContext::mutable(cx, &mut window));
-            window.root_view.replace(Box::new(root_view));
+            window.root_view.replace(root_view.into_any());
             cx.windows.get_mut(id).unwrap().replace(window);
             future::ready(handle)
         })
@@ -184,6 +188,7 @@ impl AppContext {
 
 impl Context for AppContext {
     type EntityContext<'a, 'w, T: Send + Sync + 'static> = ModelContext<'a, T>;
+    type Result<T> = T;
 
     fn entity<T: Send + Sync + 'static>(
         &mut self,
@@ -216,6 +221,54 @@ impl Context for AppContext {
     }
 }
 
+#[derive(Clone)]
+pub struct AsyncContext(Weak<Mutex<AppContext>>);
+
+impl Context for AsyncContext {
+    type EntityContext<'a, 'b, T: Send + Sync + 'static> = ModelContext<'a, T>;
+    type Result<T> = Result<T>;
+
+    fn entity<T: Send + Sync + 'static>(
+        &mut self,
+        build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
+    ) -> Result<Handle<T>> {
+        let app = self
+            .0
+            .upgrade()
+            .ok_or_else(|| anyhow!("app was released"))?;
+        let mut lock = app.lock();
+        Ok(lock.entity(build_entity))
+    }
+
+    fn update_entity<T: Send + Sync + 'static, R>(
+        &mut self,
+        handle: &Handle<T>,
+        update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
+    ) -> Result<R> {
+        let app = self
+            .0
+            .upgrade()
+            .ok_or_else(|| anyhow!("app was released"))?;
+        let mut lock = app.lock();
+        Ok(lock.update_entity(handle, update))
+    }
+}
+
+impl AsyncContext {
+    pub fn update_window<T>(
+        &self,
+        handle: AnyWindowHandle,
+        update: impl FnOnce(&mut WindowContext) -> T + Send + Sync,
+    ) -> Result<T> {
+        let app = self
+            .0
+            .upgrade()
+            .ok_or_else(|| anyhow!("app was released"))?;
+        let mut app_context = app.lock();
+        app_context.update_window(handle.id, update)
+    }
+}
+
 pub struct ModelContext<'a, T> {
     app: Reference<'a, AppContext>,
     entity_type: PhantomData<T>,
@@ -293,6 +346,7 @@ impl<'a, T: Send + Sync + 'static> ModelContext<'a, T> {
 
 impl<'a, T: 'static> Context for ModelContext<'a, T> {
     type EntityContext<'b, 'c, U: Send + Sync + 'static> = ModelContext<'b, U>;
+    type Result<U> = U;
 
     fn entity<U: Send + Sync + 'static>(
         &mut self,
@@ -341,7 +395,7 @@ impl<T: Send + Sync + 'static> Handle<T> {
         &self,
         cx: &mut C,
         update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
-    ) -> R {
+    ) -> C::Result<R> {
         cx.update_entity(self, update)
     }
 }
@@ -380,12 +434,15 @@ impl<T: Send + Sync + 'static> WeakHandle<T> {
         &self,
         cx: &mut C,
         update: impl FnOnce(&mut T, &mut C::EntityContext<'_, '_, T>) -> R,
-    ) -> Result<R> {
-        if let Some(this) = self.upgrade(cx) {
-            Ok(cx.update_entity(&this, update))
-        } else {
-            Err(anyhow!("entity released"))
-        }
+    ) -> Result<R>
+    where
+        Result<C::Result<R>>: crate::Flatten<R>,
+    {
+        crate::Flatten::flatten(
+            self.upgrade(cx)
+                .ok_or_else(|| anyhow!("entity release"))
+                .map(|this| cx.update_entity(&this, update)),
+        )
     }
 }
 

crates/gpui3/src/gpui3.rs 🔗

@@ -47,17 +47,34 @@ pub use window::*;
 
 pub trait Context {
     type EntityContext<'a, 'w, T: Send + Sync + 'static>;
+    type Result<T>;
 
     fn entity<T: Send + Sync + 'static>(
         &mut self,
         build_entity: impl FnOnce(&mut Self::EntityContext<'_, '_, T>) -> T,
-    ) -> Handle<T>;
+    ) -> Self::Result<Handle<T>>;
 
     fn update_entity<T: Send + Sync + 'static, R>(
         &mut self,
         handle: &Handle<T>,
         update: impl FnOnce(&mut T, &mut Self::EntityContext<'_, '_, T>) -> R,
-    ) -> R;
+    ) -> Self::Result<R>;
+}
+
+pub trait Flatten<T> {
+    fn flatten(self) -> Result<T>;
+}
+
+impl<T> Flatten<T> for Result<Result<T>> {
+    fn flatten(self) -> Result<T> {
+        self?
+    }
+}
+
+impl<T> Flatten<T> for Result<T> {
+    fn flatten(self) -> Result<T> {
+        self
+    }
 }
 
 #[derive(Clone, Eq, PartialEq)]

crates/gpui3/src/platform.rs 🔗

@@ -136,14 +136,14 @@ pub trait PlatformWindow {
     fn minimize(&self);
     fn zoom(&self);
     fn toggle_full_screen(&self);
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>);
-    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>);
-    fn on_resize(&mut self, callback: Box<dyn FnMut()>);
-    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>);
-    fn on_moved(&mut self, callback: Box<dyn FnMut()>);
-    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>);
-    fn on_close(&mut self, callback: Box<dyn FnOnce()>);
-    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>);
+    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>);
+    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>);
+    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>);
+    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>);
+    fn on_moved(&self, callback: Box<dyn FnMut()>);
+    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>);
+    fn on_close(&self, callback: Box<dyn FnOnce()>);
+    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>);
     fn is_topmost_for_position(&self, position: Point<Pixels>) -> bool;
     fn draw(&self, scene: Scene);
 }

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

@@ -286,7 +286,7 @@ struct MacWindowState {
     kind: WindowKind,
     event_callback: Option<Box<dyn FnMut(Event) -> bool>>,
     activate_callback: Option<Box<dyn FnMut(bool)>>,
-    resize_callback: Option<Box<dyn FnMut()>>,
+    resize_callback: Option<Box<dyn FnMut(Size<Pixels>, f32)>>,
     fullscreen_callback: Option<Box<dyn FnMut(bool)>>,
     moved_callback: Option<Box<dyn FnMut()>>,
     should_close_callback: Option<Box<dyn FnMut() -> bool>>,
@@ -821,35 +821,35 @@ impl PlatformWindow for MacWindow {
         });
     }
 
-    fn on_event(&mut self, callback: Box<dyn FnMut(Event) -> bool>) {
+    fn on_event(&self, callback: Box<dyn FnMut(Event) -> bool>) {
         self.0.as_ref().lock().event_callback = Some(callback);
     }
 
-    fn on_active_status_change(&mut self, callback: Box<dyn FnMut(bool)>) {
+    fn on_active_status_change(&self, callback: Box<dyn FnMut(bool)>) {
         self.0.as_ref().lock().activate_callback = Some(callback);
     }
 
-    fn on_resize(&mut self, callback: Box<dyn FnMut()>) {
+    fn on_resize(&self, callback: Box<dyn FnMut(Size<Pixels>, f32)>) {
         self.0.as_ref().lock().resize_callback = Some(callback);
     }
 
-    fn on_fullscreen(&mut self, callback: Box<dyn FnMut(bool)>) {
+    fn on_fullscreen(&self, callback: Box<dyn FnMut(bool)>) {
         self.0.as_ref().lock().fullscreen_callback = Some(callback);
     }
 
-    fn on_moved(&mut self, callback: Box<dyn FnMut()>) {
+    fn on_moved(&self, callback: Box<dyn FnMut()>) {
         self.0.as_ref().lock().moved_callback = Some(callback);
     }
 
-    fn on_should_close(&mut self, callback: Box<dyn FnMut() -> bool>) {
+    fn on_should_close(&self, callback: Box<dyn FnMut() -> bool>) {
         self.0.as_ref().lock().should_close_callback = Some(callback);
     }
 
-    fn on_close(&mut self, callback: Box<dyn FnOnce()>) {
+    fn on_close(&self, callback: Box<dyn FnOnce()>) {
         self.0.as_ref().lock().close_callback = Some(callback);
     }
 
-    fn on_appearance_changed(&mut self, callback: Box<dyn FnMut()>) {
+    fn on_appearance_changed(&self, callback: Box<dyn FnMut()>) {
         self.0.lock().appearance_changed_callback = Some(callback);
     }
 
@@ -935,9 +935,9 @@ 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().lock();
+    let mut lock = window_state.as_ref().lock();
 
-    let window_height = window_state_borrow.content_size().height;
+    let window_height = lock.content_size().height;
     let event = unsafe { Event::from_native(native_event, Some(window_height)) };
 
     if let Some(Event::KeyDown(event)) = event {
@@ -946,8 +946,8 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
         // makes no distinction between these two types of events, so we need to ignore
         // the "key down" event if we've already just processed its "key equivalent" version.
         if key_equivalent {
-            window_state_borrow.last_key_equivalent = Some(event.clone());
-        } else if window_state_borrow.last_key_equivalent.take().as_ref() == Some(&event) {
+            lock.last_key_equivalent = Some(event.clone());
+        } else if lock.last_key_equivalent.take().as_ref() == Some(&event) {
             return NO;
         }
 
@@ -956,14 +956,14 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
         // Ignore events from held-down keys after some of the initially-pressed keys
         // were released.
         if event.is_held {
-            if window_state_borrow.last_fresh_keydown.as_ref() != Some(&keydown) {
+            if lock.last_fresh_keydown.as_ref() != Some(&keydown) {
                 return YES;
             }
         } else {
-            window_state_borrow.last_fresh_keydown = Some(keydown);
+            lock.last_fresh_keydown = Some(keydown);
         }
-        window_state_borrow.pending_key_down = Some((event, None));
-        drop(window_state_borrow);
+        lock.pending_key_down = Some((event, None));
+        drop(lock);
 
         // Send the event to the input context for IME handling, unless the `fn` modifier is
         // being pressed.
@@ -975,12 +975,12 @@ extern "C" fn handle_key_event(this: &Object, native_event: id, key_equivalent:
         }
 
         let mut handled = false;
-        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 mut lock = window_state.lock();
+        let ime_text = lock.ime_text.clone();
+        if let Some((event, insert_text)) = lock.pending_key_down.take() {
             let is_held = event.is_held;
-            if let Some(mut callback) = window_state_borrow.event_callback.take() {
-                drop(window_state_borrow);
+            if let Some(mut callback) = lock.event_callback.take() {
+                drop(lock);
 
                 let is_composing =
                     with_input_handler(this, |input_handler| input_handler.marked_text_range())
@@ -1056,10 +1056,10 @@ 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 = 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 mut lock = window_state.as_ref().lock();
+    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
 
-    let window_height = window_state_borrow.content_size().height;
+    let window_height = lock.content_size().height;
     let event = unsafe { Event::from_native(native_event, Some(window_height)) };
 
     if let Some(mut event) = event {
@@ -1095,7 +1095,7 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                 modifiers: Modifiers { control: true, .. },
                 ..
             }) => {
-                window_state_borrow.synthetic_drag_counter += 1;
+                lock.synthetic_drag_counter += 1;
                 return;
             }
 
@@ -1109,50 +1109,46 @@ extern "C" fn handle_view_event(this: &Object, _: Sel, native_event: id) {
                     ..
                 },
             ) => {
-                window_state_borrow.synthetic_drag_counter += 1;
-                let dispatcher = window_state_borrow.dispatcher.clone();
+                lock.synthetic_drag_counter += 1;
+                let dispatcher = lock.dispatcher.clone();
                 let _ = crate::spawn_on_main_local(
                     dispatcher,
                     synthetic_drag(
                         weak_window_state,
-                        window_state_borrow.synthetic_drag_counter,
+                        lock.synthetic_drag_counter,
                         event.clone(),
                     ),
                 );
             }
 
-            Event::MouseMoved(_)
-                if !(is_active || window_state_borrow.kind == WindowKind::PopUp) =>
-            {
-                return
-            }
+            Event::MouseMoved(_) if !(is_active || lock.kind == WindowKind::PopUp) => return,
 
             Event::MouseUp(MouseUpEvent {
                 button: MouseButton::Left,
                 ..
             }) => {
-                window_state_borrow.synthetic_drag_counter += 1;
+                lock.synthetic_drag_counter += 1;
             }
 
             Event::ModifiersChanged(ModifiersChangedEvent { modifiers }) => {
                 // Only raise modifiers changed event when they have actually changed
                 if let Some(Event::ModifiersChanged(ModifiersChangedEvent {
                     modifiers: prev_modifiers,
-                })) = &window_state_borrow.previous_modifiers_changed_event
+                })) = &lock.previous_modifiers_changed_event
                 {
                     if prev_modifiers == modifiers {
                         return;
                     }
                 }
 
-                window_state_borrow.previous_modifiers_changed_event = Some(event.clone());
+                lock.previous_modifiers_changed_event = Some(event.clone());
             }
 
             _ => {}
         }
 
-        if let Some(mut callback) = window_state_borrow.event_callback.take() {
-            drop(window_state_borrow);
+        if let Some(mut callback) = lock.event_callback.take() {
+            drop(lock);
             callback(event);
             if let Some(event) = synthesized_second_event {
                 callback(event);
@@ -1166,7 +1162,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().lock();
+    let mut lock = window_state.as_ref().lock();
 
     let keystroke = Keystroke {
         modifiers: Default::default(),
@@ -1177,9 +1173,9 @@ extern "C" fn cancel_operation(this: &Object, _sel: Sel, _sender: id) {
         is_held: false,
     });
 
-    window_state_borrow.last_fresh_keydown = Some(keystroke);
-    if let Some(mut callback) = window_state_borrow.event_callback.take() {
-        drop(window_state_borrow);
+    lock.last_fresh_keydown = Some(keystroke);
+    if let Some(mut callback) = lock.event_callback.take() {
+        drop(lock);
         callback(event);
         window_state.lock().event_callback = Some(callback);
     }
@@ -1200,9 +1196,9 @@ 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().lock();
-    if let Some(mut callback) = window_state_borrow.fullscreen_callback.take() {
-        drop(window_state_borrow);
+    let mut lock = window_state.as_ref().lock();
+    if let Some(mut callback) = lock.fullscreen_callback.take() {
+        drop(lock);
         callback(is_fullscreen);
         window_state.lock().fullscreen_callback = Some(callback);
     }
@@ -1210,9 +1206,9 @@ fn window_fullscreen_changed(this: &Object, is_fullscreen: bool) {
 
 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().lock();
-    if let Some(mut callback) = window_state_borrow.moved_callback.take() {
-        drop(window_state_borrow);
+    let mut lock = window_state.as_ref().lock();
+    if let Some(mut callback) = lock.moved_callback.take() {
+        drop(lock);
         callback();
         window_state.lock().moved_callback = Some(callback);
     }
@@ -1220,8 +1216,8 @@ extern "C" fn window_did_move(this: &Object, _: Sel, _: id) {
 
 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.lock();
-    let is_active = unsafe { window_state_borrow.native_window.isKeyWindow() == YES };
+    let lock = window_state.lock();
+    let is_active = unsafe { lock.native_window.isKeyWindow() == YES };
 
     // When opening a pop-up while the application isn't active, Cocoa sends a spurious
     // `windowDidBecomeKey` message to the previous key window even though that window
@@ -1235,18 +1231,18 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
     if selector == sel!(windowDidBecomeKey:) {
         if !is_active {
             unsafe {
-                let _: () = msg_send![window_state_borrow.native_window, resignKeyWindow];
+                let _: () = msg_send![lock.native_window, resignKeyWindow];
                 return;
             }
         }
     }
 
-    let dispatcher = window_state_borrow.dispatcher.clone();
-    drop(window_state_borrow);
+    let dispatcher = lock.dispatcher.clone();
+    drop(lock);
     let _ = crate::spawn_on_main_local(dispatcher, async move {
-        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);
+        let mut lock = window_state.as_ref().lock();
+        if let Some(mut callback) = lock.activate_callback.take() {
+            drop(lock);
             callback(is_active);
             window_state.lock().activate_callback = Some(callback);
         };
@@ -1255,9 +1251,9 @@ extern "C" fn window_did_change_key_status(this: &Object, selector: Sel, _: id)
 
 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().lock();
-    if let Some(mut callback) = window_state_borrow.should_close_callback.take() {
-        drop(window_state_borrow);
+    let mut lock = window_state.as_ref().lock();
+    if let Some(mut callback) = lock.should_close_callback.take() {
+        drop(lock);
         let should_close = callback();
         window_state.lock().should_close_callback = Some(callback);
         should_close as BOOL
@@ -1292,38 +1288,40 @@ extern "C" fn make_backing_layer(this: &Object, _: Sel) -> 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().lock();
+    let mut lock = window_state.as_ref().lock();
 
     unsafe {
-        let scale_factor = window_state_borrow.scale_factor() as f64;
-        let size = window_state_borrow.content_size();
+        let scale_factor = lock.scale_factor() as f64;
+        let size = lock.content_size();
         let drawable_size: NSSize = NSSize {
             width: f64::from(size.width) * scale_factor,
             height: f64::from(size.height) * scale_factor,
         };
 
         let _: () = msg_send![
-            window_state_borrow.renderer.layer(),
+            lock.renderer.layer(),
             setContentsScale: scale_factor
         ];
         let _: () = msg_send![
-            window_state_borrow.renderer.layer(),
+            lock.renderer.layer(),
             setDrawableSize: drawable_size
         ];
     }
 
-    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
-        drop(window_state_borrow);
-        callback();
+    if let Some(mut callback) = lock.resize_callback.take() {
+        let content_size = lock.content_size();
+        let scale_factor = lock.scale_factor();
+        drop(lock);
+        callback(content_size, scale_factor);
         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().lock();
+    let lock = window_state.as_ref().lock();
 
-    if window_state_borrow.content_size() == size.into() {
+    if lock.content_size() == size.into() {
         return;
     }
 
@@ -1331,7 +1329,7 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
         let _: () = msg_send![super(this, class!(NSView)), setFrameSize: size];
     }
 
-    let scale_factor = window_state_borrow.scale_factor() as f64;
+    let scale_factor = lock.scale_factor() as f64;
     let drawable_size: NSSize = NSSize {
         width: size.width * scale_factor,
         height: size.height * scale_factor,
@@ -1339,16 +1337,18 @@ extern "C" fn set_frame_size(this: &Object, _: Sel, size: NSSize) {
 
     unsafe {
         let _: () = msg_send![
-            window_state_borrow.renderer.layer(),
+            lock.renderer.layer(),
             setDrawableSize: drawable_size
         ];
     }
 
-    drop(window_state_borrow);
-    let mut window_state_borrow = window_state.lock();
-    if let Some(mut callback) = window_state_borrow.resize_callback.take() {
-        drop(window_state_borrow);
-        callback();
+    drop(lock);
+    let mut lock = window_state.lock();
+    if let Some(mut callback) = lock.resize_callback.take() {
+        let content_size = lock.content_size();
+        let scale_factor = lock.scale_factor();
+        drop(lock);
+        callback(content_size, scale_factor);
         window_state.lock().resize_callback = Some(callback);
     };
 }
@@ -1416,9 +1416,9 @@ 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.lock();
-        let pending_key_down = window_state_borrow.pending_key_down.take();
-        drop(window_state_borrow);
+        let mut lock = window_state.lock();
+        let pending_key_down = lock.pending_key_down.take();
+        drop(lock);
 
         let is_attributed_string: BOOL =
             msg_send![text, isKindOfClass: [class!(NSAttributedString)]];
@@ -1534,9 +1534,9 @@ 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().lock();
-        if let Some(mut callback) = state_borrow.appearance_changed_callback.take() {
-            drop(state_borrow);
+        let mut lock = state.as_ref().lock();
+        if let Some(mut callback) = lock.appearance_changed_callback.take() {
+            drop(lock);
             callback();
             state.lock().appearance_changed_callback = Some(callback);
         }
@@ -1546,8 +1546,8 @@ 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().lock();
-        return if state_borrow.kind == WindowKind::PopUp {
+        let lock = state.as_ref().lock();
+        return if lock.kind == WindowKind::PopUp {
             YES
         } else {
             NO
@@ -1563,10 +1563,10 @@ async fn synthetic_drag(
     loop {
         Timer::after(Duration::from_millis(16)).await;
         if let Some(window_state) = window_state.upgrade() {
-            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);
+            let mut lock = window_state.lock();
+            if lock.synthetic_drag_counter == drag_id {
+                if let Some(mut callback) = lock.event_callback.take() {
+                    drop(lock);
                     callback(Event::MouseMoved(event.clone()));
                     window_state.lock().event_callback = Some(callback);
                 }
@@ -1582,9 +1582,9 @@ where
     F: FnOnce(&mut dyn InputHandler) -> R,
 {
     let window_state = unsafe { get_window_state(window) };
-    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 mut lock = window_state.as_ref().lock();
+    if let Some(mut input_handler) = lock.input_handler.take() {
+        drop(lock);
         let result = f(input_handler.as_mut());
         window_state.lock().input_handler = Some(input_handler);
         Some(result)

crates/gpui3/src/scene.rs 🔗

@@ -1,3 +1,5 @@
+use std::mem;
+
 use super::{Bounds, Hsla, Pixels, Point};
 use crate::{Corners, Edges};
 use bytemuck::{Pod, Zeroable};
@@ -8,7 +10,7 @@ pub type PointF = Point<f32>;
 
 pub struct Scene {
     layers: BTreeMap<u32, SceneLayer>,
-    scale_factor: f32,
+    pub(crate) scale_factor: f32,
 }
 
 #[derive(Default)]
@@ -24,6 +26,13 @@ impl Scene {
         }
     }
 
+    pub fn take(&mut self) -> Scene {
+        Scene {
+            layers: mem::take(&mut self.layers),
+            scale_factor: self.scale_factor,
+        }
+    }
+
     pub fn insert(&mut self, primitive: impl Into<Primitive>) {
         let mut primitive = primitive.into();
         primitive.scale(self.scale_factor);

crates/gpui3/src/taffy.rs 🔗

@@ -66,6 +66,16 @@ impl TaffyLayoutEngine {
             .into())
     }
 
+    pub fn compute_layout(
+        &mut self,
+        id: LayoutId,
+        available_space: Size<AvailableSpace>,
+    ) -> Result<()> {
+        self.taffy
+            .compute_layout(id.into(), available_space.into())?;
+        Ok(())
+    }
+
     pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
         if let Some(layout) = self.absolute_layouts.get(&id).cloned() {
             return Ok(layout);
@@ -104,16 +114,6 @@ impl From<LayoutId> for NodeId {
     }
 }
 
-#[derive(Copy, Clone, Debug)]
-pub enum AvailableSpace {
-    /// The amount of space available is the specified number of pixels
-    Definite(Pixels),
-    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
-    MinContent,
-    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
-    MaxContent,
-}
-
 struct Measureable<F>(F);
 
 impl<F> taffy::tree::Measurable for Measureable<F>
@@ -334,17 +334,28 @@ impl<T: Into<U> + Clone + Debug, U> From<Size<T>> for taffy::geometry::Size<U> {
 //     }
 // }
 
-// impl From<TaffySize<TaffyAvailableSpace>> for Size<AvailableSpace> {
-//     fn from(taffy_size: TaffySize<TaffyAvailableSpace>) -> Self {
-//         Size {
-//             width: From::from(taffy_size.width),
-//             height: From::from(taffy_size.height),
-//         }
-//     }
-// }
+#[derive(Copy, Clone, Debug)]
+pub enum AvailableSpace {
+    /// The amount of space available is the specified number of pixels
+    Definite(Pixels),
+    /// The amount of space available is indefinite and the node should be laid out under a min-content constraint
+    MinContent,
+    /// The amount of space available is indefinite and the node should be laid out under a max-content constraint
+    MaxContent,
+}
+
+impl From<AvailableSpace> for TaffyAvailableSpace {
+    fn from(space: AvailableSpace) -> TaffyAvailableSpace {
+        match space {
+            AvailableSpace::Definite(Pixels(value)) => TaffyAvailableSpace::Definite(value),
+            AvailableSpace::MinContent => TaffyAvailableSpace::MinContent,
+            AvailableSpace::MaxContent => TaffyAvailableSpace::MaxContent,
+        }
+    }
+}
 
 impl From<TaffyAvailableSpace> for AvailableSpace {
-    fn from(space: TaffyAvailableSpace) -> Self {
+    fn from(space: TaffyAvailableSpace) -> AvailableSpace {
         match space {
             TaffyAvailableSpace::Definite(value) => AvailableSpace::Definite(Pixels(value)),
             TaffyAvailableSpace::MinContent => AvailableSpace::MinContent,
@@ -353,6 +364,12 @@ impl From<TaffyAvailableSpace> for AvailableSpace {
     }
 }
 
+impl From<Pixels> for AvailableSpace {
+    fn from(pixels: Pixels) -> Self {
+        AvailableSpace::Definite(pixels)
+    }
+}
+
 impl From<&taffy::tree::Layout> for Layout {
     fn from(layout: &taffy::tree::Layout) -> Self {
         Layout {

crates/gpui3/src/view.rs 🔗

@@ -1,8 +1,10 @@
+use parking_lot::Mutex;
+
 use crate::{
     AnyElement, Element, Handle, IntoAnyElement, Layout, LayoutId, Result, ViewContext,
     WindowContext,
 };
-use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc, sync::Arc};
+use std::{any::Any, marker::PhantomData, sync::Arc};
 
 pub struct View<S, P> {
     state: Handle<S>,
@@ -10,6 +12,15 @@ pub struct View<S, P> {
     parent_state_type: PhantomData<P>,
 }
 
+impl<S: 'static + Send + Sync, P: 'static + Send> View<S, P> {
+    pub fn into_any(self) -> AnyView<P> {
+        AnyView {
+            view: Arc::new(Mutex::new(self)),
+            parent_state_type: PhantomData,
+        }
+    }
+}
+
 impl<S, P> Clone for View<S, P> {
     fn clone(&self) -> Self {
         Self {
@@ -33,15 +44,6 @@ pub fn view<S: 'static, P: 'static, E: Element<State = S>>(
     }
 }
 
-impl<S: Send + Sync + 'static, P: 'static> View<S, P> {
-    pub fn into_any<ParentState>(self) -> AnyView<ParentState> {
-        AnyView {
-            view: Rc::new(RefCell::new(self)),
-            parent_state_type: PhantomData,
-        }
-    }
-}
-
 impl<S: Send + Sync + 'static, P: Send + 'static> Element for View<S, P> {
     type State = P;
     type FrameState = AnyElement<S>;
@@ -70,7 +72,7 @@ impl<S: Send + Sync + 'static, P: Send + 'static> Element for View<S, P> {
     }
 }
 
-trait ViewObject {
+trait ViewObject: Send + 'static {
     fn layout(&mut self, cx: &mut WindowContext) -> Result<(LayoutId, Box<dyn Any>)>;
     fn paint(
         &mut self,
@@ -80,7 +82,7 @@ trait ViewObject {
     ) -> Result<()>;
 }
 
-impl<S: Send + Sync + 'static, P> ViewObject for View<S, P> {
+impl<S: Send + Sync + 'static, P: Send + 'static> 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);
@@ -106,12 +108,12 @@ impl<S: Send + Sync + 'static, P> ViewObject for View<S, P> {
 }
 
 pub struct AnyView<S> {
-    view: Rc<RefCell<dyn ViewObject>>,
+    view: Arc<Mutex<dyn ViewObject>>,
     parent_state_type: PhantomData<S>,
 }
 
 impl<S: 'static> Element for AnyView<S> {
-    type State = S;
+    type State = ();
     type FrameState = Box<dyn Any>;
 
     fn layout(
@@ -119,7 +121,7 @@ impl<S: 'static> Element for AnyView<S> {
         _: &mut Self::State,
         cx: &mut ViewContext<Self::State>,
     ) -> Result<(LayoutId, Self::FrameState)> {
-        self.view.borrow_mut().layout(cx)
+        self.view.lock().layout(cx)
     }
 
     fn paint(
@@ -129,7 +131,7 @@ impl<S: 'static> Element for AnyView<S> {
         element: &mut Self::FrameState,
         cx: &mut ViewContext<Self::State>,
     ) -> Result<()> {
-        self.view.borrow_mut().paint(layout, element, cx)
+        self.view.lock().paint(layout, element, cx)
     }
 }
 

crates/gpui3/src/window.rs 🔗

@@ -1,16 +1,13 @@
 use crate::{
-    px, AppContext, AvailableSpace, Bounds, Context, Effect, EntityId, Handle, LayoutId,
-    MainThreadOnly, Pixels, Platform, PlatformWindow, Point, Reference, Size, Style,
-    TaffyLayoutEngine, TextStyle, TextStyleRefinement, WeakHandle, WindowOptions,
+    px, AnyView, AppContext, AvailableSpace, Bounds, Context, Effect, Element, EntityId, Handle,
+    LayoutId, MainThreadOnly, Pixels, Platform, PlatformWindow, Point, Reference, Scene, Size,
+    Style, TaffyLayoutEngine, TextStyle, TextStyleRefinement, WeakHandle, WindowOptions,
 };
 use anyhow::Result;
 use derive_more::{Deref, DerefMut};
 use refineable::Refineable;
-use std::{
-    any::{Any, TypeId},
-    marker::PhantomData,
-    sync::Arc,
-};
+use std::{any::TypeId, future, marker::PhantomData, sync::Arc};
+use util::ResultExt;
 
 pub struct AnyWindow {}
 
@@ -18,26 +15,51 @@ pub struct Window {
     handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
     rem_size: Pixels,
+    content_size: Size<Pixels>,
     layout_engine: TaffyLayoutEngine,
     text_style_stack: Vec<TextStyleRefinement>,
-    pub(crate) root_view: Option<Box<dyn Any + Send>>,
+    pub(crate) root_view: Option<AnyView<()>>,
     mouse_position: Point<Pixels>,
+    pub(crate) scene: Scene,
     pub(crate) dirty: bool,
 }
 
 impl Window {
-    pub fn new(handle: AnyWindowHandle, options: WindowOptions, platform: &dyn Platform) -> Self {
+    pub fn new(
+        handle: AnyWindowHandle,
+        options: WindowOptions,
+        platform: &dyn Platform,
+        cx: &mut AppContext,
+    ) -> Self {
         let platform_window = platform.open_window(handle, options);
         let mouse_position = platform_window.mouse_position();
+        let content_size = platform_window.content_size();
+        let scale_factor = platform_window.scale_factor();
+        platform_window.on_resize(Box::new({
+            let handle = handle;
+            let cx = cx.to_async();
+            move |content_size, scale_factor| {
+                cx.update_window(handle, |cx| {
+                    cx.window.scene = Scene::new(scale_factor);
+                    cx.window.content_size = content_size;
+                    cx.window.dirty = true;
+                })
+                .log_err();
+            }
+        }));
+
         let platform_window = MainThreadOnly::new(Arc::new(platform_window), platform.dispatcher());
+
         Window {
             handle,
             platform_window,
             rem_size: px(16.),
+            content_size,
             layout_engine: TaffyLayoutEngine::new(),
             text_style_stack: Vec::new(),
             root_view: None,
             mouse_position,
+            scene: Scene::new(scale_factor),
             dirty: true,
         }
     }
@@ -66,6 +88,28 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         }
     }
 
+    pub(crate) fn draw(&mut self) -> Result<()> {
+        let unit_entity = self.unit_entity.clone();
+        self.update_entity(&unit_entity, |_, cx| {
+            let mut root_view = cx.window.root_view.take().unwrap();
+            let (root_layout_id, mut frame_state) = root_view.layout(&mut (), cx)?;
+            let available_space = cx.window.content_size.map(Into::into);
+            cx.window
+                .layout_engine
+                .compute_layout(root_layout_id, available_space)?;
+            let layout = cx.window.layout_engine.layout(root_layout_id)?;
+            root_view.paint(layout, &mut (), &mut frame_state, cx)?;
+            cx.window.root_view = Some(root_view);
+            let scene = cx.window.scene.take();
+            let _ = cx.window.platform_window.read(|platform_window| {
+                platform_window.draw(scene);
+                future::ready(())
+            });
+
+            Ok(())
+        })
+    }
+
     pub fn request_layout(
         &mut self,
         style: Style,
@@ -140,6 +184,7 @@ impl<'a, 'w> WindowContext<'a, 'w> {
 
 impl Context for WindowContext<'_, '_> {
     type EntityContext<'a, 'w, T: Send + Sync + 'static> = ViewContext<'a, 'w, T>;
+    type Result<T> = T;
 
     fn entity<T: Send + Sync + 'static>(
         &mut self,
@@ -229,11 +274,11 @@ impl<'a, 'w, T: Send + Sync + 'static> ViewContext<'a, 'w, T> {
     }
 
     pub fn erase_state<R>(&mut self, f: impl FnOnce(&mut ViewContext<()>) -> R) -> R {
-        let unit_entity_id = self.unit_entity_id;
+        let entity_id = self.unit_entity.id;
         let mut cx = ViewContext::mutable(
             &mut *self.window_cx.app,
             &mut *self.window_cx.window,
-            unit_entity_id,
+            entity_id,
         );
         f(&mut cx)
     }
@@ -281,6 +326,7 @@ impl<'a, 'w, T: Send + Sync + 'static> ViewContext<'a, 'w, T> {
 
 impl<'a, 'w, T: 'static> Context for ViewContext<'a, 'w, T> {
     type EntityContext<'b, 'c, U: Send + Sync + 'static> = ViewContext<'b, 'c, U>;
+    type Result<U> = U;
 
     fn entity<T2: Send + Sync + 'static>(
         &mut self,