Checkpoint: Reproduction with `Avatar` story

Marshall Bowers created

Change summary

crates/gpui2/src/app.rs                     | 46 +++++++++++++++++-----
crates/gpui2/src/app/async_context.rs       | 35 ++++++++++-------
crates/gpui2/src/app/test_context.rs        | 20 +++++-----
crates/gpui2/src/elements/img.rs            |  6 ++
crates/gpui2/src/platform/mac/dispatcher.rs |  2 +
crates/ui2/src/elements/avatar.rs           | 20 ++++++++--
crates/ui2/src/elements/player.rs           |  4 +-
7 files changed, 91 insertions(+), 42 deletions(-)

Detailed changes

crates/gpui2/src/app.rs 🔗

@@ -5,6 +5,7 @@ mod model_context;
 mod test_context;
 
 pub use async_context::*;
+use derive_more::{Deref, DerefMut};
 pub use entity_map::*;
 pub use model_context::*;
 use refineable::Refineable;
@@ -27,7 +28,7 @@ use parking_lot::Mutex;
 use slotmap::SlotMap;
 use std::{
     any::{type_name, Any, TypeId},
-    cell::RefCell,
+    cell::{Ref, RefCell, RefMut},
     marker::PhantomData,
     mem,
     ops::{Deref, DerefMut},
@@ -38,7 +39,29 @@ use std::{
 };
 use util::http::{self, HttpClient};
 
-pub struct App(Rc<RefCell<AppContext>>);
+pub struct AppCell {
+    app: RefCell<AppContext>,
+}
+impl AppCell {
+    pub fn borrow(&self) -> AppRef {
+        AppRef(self.app.borrow())
+    }
+
+    pub fn borrow_mut(&self, label: &str) -> AppRefMut {
+        let thread_id = std::thread::current().id();
+
+        eprintln!(">>> borrowing {thread_id:?}: {label}");
+        AppRefMut(self.app.borrow_mut())
+    }
+}
+
+#[derive(Deref, DerefMut)]
+pub struct AppRef<'a>(Ref<'a, AppContext>);
+
+#[derive(Deref, DerefMut)]
+pub struct AppRefMut<'a>(RefMut<'a, AppContext>);
+
+pub struct App(Rc<AppCell>);
 
 /// Represents an application before it is fully launched. Once your app is
 /// configured, you'll start the app with `App::run`.
@@ -61,7 +84,8 @@ impl App {
         let this = self.0.clone();
         let platform = self.0.borrow().platform.clone();
         platform.run(Box::new(move || {
-            let cx = &mut *this.borrow_mut();
+            dbg!("run callback");
+            let cx = &mut *this.borrow_mut("app::borrow_mut");
             on_finish_launching(cx);
         }));
     }
@@ -75,7 +99,7 @@ impl App {
         let this = Rc::downgrade(&self.0);
         self.0.borrow().platform.on_open_urls(Box::new(move |urls| {
             if let Some(app) = this.upgrade() {
-                callback(urls, &mut *app.borrow_mut());
+                callback(urls, &mut *app.borrow_mut("app.rs::on_open_urls"));
             }
         }));
         self
@@ -86,9 +110,9 @@ impl App {
         F: 'static + FnMut(&mut AppContext),
     {
         let this = Rc::downgrade(&self.0);
-        self.0.borrow_mut().platform.on_reopen(Box::new(move || {
+        self.0.borrow_mut("app.rs::on_reopen").platform.on_reopen(Box::new(move || {
             if let Some(app) = this.upgrade() {
-                callback(&mut app.borrow_mut());
+                callback(&mut app.borrow_mut("app.rs::on_reopen(callback)"));
             }
         }));
         self
@@ -119,7 +143,7 @@ type QuitHandler = Box<dyn FnOnce(&mut AppContext) -> LocalBoxFuture<'static, ()
 type ReleaseListener = Box<dyn FnOnce(&mut dyn Any, &mut AppContext) + 'static>;
 
 pub struct AppContext {
-    this: Weak<RefCell<AppContext>>,
+    this: Weak<AppCell>,
     pub(crate) platform: Rc<dyn Platform>,
     app_metadata: AppMetadata,
     text_system: Arc<TextSystem>,
@@ -157,7 +181,7 @@ impl AppContext {
         platform: Rc<dyn Platform>,
         asset_source: Arc<dyn AssetSource>,
         http_client: Arc<dyn HttpClient>,
-    ) -> Rc<RefCell<Self>> {
+    ) -> Rc<AppCell> {
         let executor = platform.background_executor();
         let foreground_executor = platform.foreground_executor();
         assert!(
@@ -174,8 +198,8 @@ impl AppContext {
             app_version: platform.app_version().ok(),
         };
 
-        Rc::new_cyclic(|this| {
-            RefCell::new(AppContext {
+        Rc::new_cyclic(|this| AppCell {
+            app: RefCell::new(AppContext {
                 this: this.clone(),
                 text_system,
                 platform,
@@ -206,7 +230,7 @@ impl AppContext {
                 layout_id_buffer: Default::default(),
                 propagate_event: true,
                 active_drag: None,
-            })
+            }),
         })
     }
 

crates/gpui2/src/app/async_context.rs 🔗

@@ -1,15 +1,15 @@
 use crate::{
-    AnyView, AnyWindowHandle, AppContext, BackgroundExecutor, Context, ForegroundExecutor, Model,
-    ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
+    AnyView, AnyWindowHandle, AppCell, AppContext, BackgroundExecutor, Context, ForegroundExecutor,
+    Model, ModelContext, Render, Result, Task, View, ViewContext, VisualContext, WindowContext,
     WindowHandle,
 };
 use anyhow::{anyhow, Context as _};
 use derive_more::{Deref, DerefMut};
-use std::{cell::RefCell, future::Future, rc::Weak};
+use std::{future::Future, rc::Weak};
 
 #[derive(Clone)]
 pub struct AsyncAppContext {
-    pub(crate) app: Weak<RefCell<AppContext>>,
+    pub(crate) app: Weak<AppCell>,
     pub(crate) background_executor: BackgroundExecutor,
     pub(crate) foreground_executor: ForegroundExecutor,
 }
@@ -28,7 +28,8 @@ impl Context for AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut app = app.borrow_mut();
+        dbg!("BUILD MODEL A");
+        let mut app = app.borrow_mut("gpui2/async_context.rs::build_model");
         Ok(app.build_model(build_model))
     }
 
@@ -41,7 +42,8 @@ impl Context for AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut app = app.borrow_mut();
+        dbg!("UPDATE MODEL B");
+        let mut app = app.borrow_mut("gpui2/async_context.rs::update_model");
         Ok(app.update_model(handle, update))
     }
 
@@ -50,7 +52,8 @@ impl Context for AsyncAppContext {
         F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
     {
         let app = self.app.upgrade().context("app was released")?;
-        let mut lock = app.borrow_mut();
+        dbg!("UPDATE WINDOW C");
+        let mut lock = app.borrow_mut("gpui2/async_context::update_window");
         lock.update_window(window, f)
     }
 }
@@ -61,7 +64,8 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.borrow_mut();
+        dbg!("REFRESH");
+        let mut lock = app.borrow_mut("async_context.rs::refresh");
         lock.refresh();
         Ok(())
     }
@@ -79,7 +83,7 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.borrow_mut();
+        let mut lock = app.borrow_mut("async_context.rs::update");
         Ok(f(&mut *lock))
     }
 
@@ -95,7 +99,7 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut lock = app.borrow_mut();
+        let mut lock = app.borrow_mut("open_window");
         Ok(lock.open_window(options, build_root_view))
     }
 
@@ -112,7 +116,7 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let app = app.borrow_mut();
+        let app = app.borrow_mut("has_global");
         Ok(app.has_global::<G>())
     }
 
@@ -121,7 +125,8 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let app = app.borrow_mut(); // Need this to compile
+        dbg!("read global");
+        let app = app.borrow_mut("async_context.rs::read_global");
         Ok(read(app.global(), &app))
     }
 
@@ -130,7 +135,8 @@ impl AsyncAppContext {
         read: impl FnOnce(&G, &AppContext) -> R,
     ) -> Option<R> {
         let app = self.app.upgrade()?;
-        let app = app.borrow_mut();
+        dbg!("try read global");
+        let app = app.borrow_mut("async_context.rs::try_read_global");
         Some(read(app.try_global()?, &app))
     }
 
@@ -142,7 +148,8 @@ impl AsyncAppContext {
             .app
             .upgrade()
             .ok_or_else(|| anyhow!("app was released"))?;
-        let mut app = app.borrow_mut();
+        dbg!("update global");
+        let mut app = app.borrow_mut("async_context.rs::update_global");
         Ok(app.update_global(update))
     }
 }

crates/gpui2/src/app/test_context.rs 🔗

@@ -1,15 +1,15 @@
 use crate::{
     AnyView, AnyWindowHandle, AppContext, AsyncAppContext, BackgroundExecutor, Context,
     EventEmitter, ForegroundExecutor, Model, ModelContext, Result, Task, TestDispatcher,
-    TestPlatform, WindowContext,
+    TestPlatform, WindowContext, AppCell,
 };
 use anyhow::{anyhow, bail};
 use futures::{Stream, StreamExt};
-use std::{cell::RefCell, future::Future, rc::Rc, sync::Arc, time::Duration};
+use std::{future::Future, rc::Rc, sync::Arc, time::Duration};
 
 #[derive(Clone)]
 pub struct TestAppContext {
-    pub app: Rc<RefCell<AppContext>>,
+    pub app: Rc<AppCell>,
     pub background_executor: BackgroundExecutor,
     pub foreground_executor: ForegroundExecutor,
 }
@@ -24,7 +24,7 @@ impl Context for TestAppContext {
     where
         T: 'static,
     {
-        let mut app = self.app.borrow_mut();
+        let mut app = self.app.borrow_mut("test_context.rs::build_model");
         app.build_model(build_model)
     }
 
@@ -33,7 +33,7 @@ impl Context for TestAppContext {
         handle: &Model<T>,
         update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
     ) -> Self::Result<R> {
-        let mut app = self.app.borrow_mut();
+        let mut app = self.app.borrow_mut("test_context::update_model");
         app.update_model(handle, update)
     }
 
@@ -41,7 +41,7 @@ impl Context for TestAppContext {
     where
         F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
     {
-        let mut lock = self.app.borrow_mut();
+        let mut lock = self.app.borrow_mut("test_context::update_window");
         lock.update_window(window, f)
     }
 }
@@ -65,11 +65,11 @@ impl TestAppContext {
     }
 
     pub fn quit(&self) {
-        self.app.borrow_mut().quit();
+        self.app.borrow_mut("test_context.rs::quit").quit();
     }
 
     pub fn refresh(&mut self) -> Result<()> {
-        let mut app = self.app.borrow_mut();
+        let mut app = self.app.borrow_mut("test_context.rs::refresh");
         app.refresh();
         Ok(())
     }
@@ -83,7 +83,7 @@ impl TestAppContext {
     }
 
     pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
-        let mut cx = self.app.borrow_mut();
+        let mut cx = self.app.borrow_mut("test_context::update");
         cx.update(f)
     }
 
@@ -117,7 +117,7 @@ impl TestAppContext {
         &mut self,
         update: impl FnOnce(&mut G, &mut AppContext) -> R,
     ) -> R {
-        let mut lock = self.app.borrow_mut();
+        let mut lock = self.app.borrow_mut("test_context.rs::update_global");
         lock.update_global(update)
     }
 

crates/gpui2/src/elements/img.rs 🔗

@@ -109,7 +109,9 @@ where
         let corner_radii = style.corner_radii;
 
         if let Some(uri) = self.uri.clone() {
-            let image_future = cx.image_cache.get(uri);
+            // eprintln!(">>> image_cache.get({uri}");
+            let image_future = cx.image_cache.get(uri.clone());
+            // eprintln!("<<< image_cache.get({uri}");
             if let Some(data) = image_future
                 .clone()
                 .now_or_never()
@@ -123,7 +125,9 @@ where
             } else {
                 cx.spawn(|_, mut cx| async move {
                     if image_future.await.log_err().is_some() {
+                        eprintln!(">>> on_next_frame");
                         cx.on_next_frame(|cx| cx.notify());
+                        eprintln!("<<< on_next_frame")
                     }
                 })
                 .detach()

crates/gpui2/src/platform/mac/dispatcher.rs 🔗

@@ -42,6 +42,7 @@ impl PlatformDispatcher for MacDispatcher {
     }
 
     fn dispatch(&self, runnable: Runnable) {
+        println!("DISPATCH");
         unsafe {
             dispatch_async_f(
                 dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.try_into().unwrap(), 0),
@@ -52,6 +53,7 @@ impl PlatformDispatcher for MacDispatcher {
     }
 
     fn dispatch_on_main_thread(&self, runnable: Runnable) {
+        println!("DISPATCH ON MAIN THREAD");
         unsafe {
             dispatch_async_f(
                 dispatch_get_main_queue(),

crates/ui2/src/elements/avatar.rs 🔗

@@ -58,11 +58,23 @@ mod stories {
                 .child(Avatar::new(
                     "https://avatars.githubusercontent.com/u/1714999?v=4",
                 ))
+                .child(Avatar::new(
+                    "https://avatars.githubusercontent.com/u/326587?v=4",
+                ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/482957?v=4",
+                // ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/1714999?v=4",
+                // ))
+                // .child(Avatar::new(
+                //     "https://avatars.githubusercontent.com/u/1486634?v=4",
+                // ))
                 .child(Story::label(cx, "Rounded rectangle"))
-                .child(
-                    Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
-                        .shape(Shape::RoundedRectangle),
-                )
+                // .child(
+                //     Avatar::new("https://avatars.githubusercontent.com/u/1714999?v=4")
+                //         .shape(Shape::RoundedRectangle),
+                // )
         }
     }
 }

crates/ui2/src/elements/player.rs 🔗

@@ -139,11 +139,11 @@ impl Player {
     }
 
     pub fn cursor_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
-        cx.theme().styles.player.0[self.index].cursor
+        cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].cursor
     }
 
     pub fn selection_color<V: 'static>(&self, cx: &mut ViewContext<V>) -> Hsla {
-        cx.theme().styles.player.0[self.index].selection
+        cx.theme().styles.player.0[self.index % cx.theme().styles.player.0.len()].selection
     }
 
     pub fn avatar_src(&self) -> &str {