WIP

Antonio Scandurra created

Change summary

crates/gpui2/src/app/entity_map.rs | 25 +++++++++++-
crates/gpui2/src/gpui2.rs          | 10 +++++
crates/gpui2/src/view.rs           | 36 ++++++++++++------
crates/gpui2/src/window.rs         | 64 ++++++++++++++++++++++++--------
crates/workspace2/src/item.rs      | 51 +++++++++++--------------
5 files changed, 127 insertions(+), 59 deletions(-)

Detailed changes

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

@@ -1,4 +1,4 @@
-use crate::{AnyBox, AppContext, Context};
+use crate::{AnyBox, AppContext, Context, EntityHandle};
 use anyhow::{anyhow, Result};
 use derive_more::{Deref, DerefMut};
 use parking_lot::{RwLock, RwLockUpgradableReadGuard};
@@ -329,7 +329,26 @@ impl<T> Eq for Handle<T> {}
 
 impl<T> PartialEq<WeakHandle<T>> for Handle<T> {
     fn eq(&self, other: &WeakHandle<T>) -> bool {
-        self.entity_id() == other.entity_id()
+        self.entity_id == other.entity_id
+    }
+}
+
+impl<T: 'static> EntityHandle<T> for Handle<T> {
+    type Weak = WeakHandle<T>;
+
+    fn entity_id(&self) -> EntityId {
+        self.entity_id
+    }
+
+    fn downgrade(&self) -> Self::Weak {
+        self.downgrade()
+    }
+
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        weak.upgrade()
     }
 }
 
@@ -457,6 +476,6 @@ impl<T> Eq for WeakHandle<T> {}
 
 impl<T> PartialEq<Handle<T>> for WeakHandle<T> {
     fn eq(&self, other: &Handle<T>) -> bool {
-        self.entity_id() == other.entity_id()
+        self.entity_id == other.entity_id
     }
 }

crates/gpui2/src/gpui2.rs 🔗

@@ -106,6 +106,16 @@ pub trait VisualContext: Context {
     ) -> Self::Result<R>;
 }
 
+pub trait EntityHandle<T> {
+    type Weak: 'static + Send;
+
+    fn entity_id(&self) -> EntityId;
+    fn downgrade(&self) -> Self::Weak;
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized;
+}
+
 pub enum GlobalKey {
     Numeric(usize),
     View(EntityId),

crates/gpui2/src/view.rs 🔗

@@ -1,7 +1,7 @@
 use crate::{
-    AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
-    EntityId, Flatten, Handle, LayoutId, Pixels, Size, ViewContext, VisualContext, WeakHandle,
-    WindowContext,
+    AnyBox, AnyElement, AppContext, AvailableSpace, BorrowWindow, Bounds, Component, Element,
+    ElementId, EntityHandle, EntityId, Flatten, Handle, LayoutId, Pixels, Size, ViewContext,
+    VisualContext, WeakHandle, WindowContext,
 };
 use anyhow::{Context, Result};
 use parking_lot::Mutex;
@@ -31,9 +31,7 @@ impl<V: 'static> View<V> {
             )),
         }
     }
-}
 
-impl<V: 'static> View<V> {
     pub fn into_any(self) -> AnyView {
         AnyView(Arc::new(self))
     }
@@ -44,9 +42,7 @@ impl<V: 'static> View<V> {
             render: Arc::downgrade(&self.render),
         }
     }
-}
 
-impl<V: 'static> View<V> {
     pub fn update<C, R>(
         &self,
         cx: &mut C,
@@ -58,11 +54,8 @@ impl<V: 'static> View<V> {
         cx.update_view(self, f)
     }
 
-    pub fn read<C>(&self, cx: &mut C) -> &V
-    where
-        C: VisualContext,
-    {
-        todo!()
+    pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
+        cx.entities.read(&self.state)
     }
 }
 
@@ -124,6 +117,25 @@ impl<V: 'static> Element<()> for View<V> {
     }
 }
 
+impl<T: 'static> EntityHandle<T> for View<T> {
+    type Weak = WeakView<T>;
+
+    fn entity_id(&self) -> EntityId {
+        self.state.entity_id
+    }
+
+    fn downgrade(&self) -> Self::Weak {
+        self.downgrade()
+    }
+
+    fn upgrade_from(weak: &Self::Weak) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        weak.upgrade()
+    }
+}
+
 pub struct WeakView<V> {
     pub(crate) state: WeakHandle<V>,
     render: Weak<Mutex<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + 'static>>,

crates/gpui2/src/window.rs 🔗

@@ -1,14 +1,14 @@
 use crate::{
     px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
     Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect,
-    EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, GlobalElementId,
-    GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher,
-    Keystroke, LayoutId, MainThread, MainThreadOnly, ModelContext, Modifiers, MonochromeSprite,
-    MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
-    PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
-    RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
-    TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakHandle, WeakView,
-    WindowOptions, SUBPIXEL_VARIANTS,
+    EntityHandle, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId,
+    GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch,
+    KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, ModelContext, Modifiers,
+    MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels,
+    PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams,
+    RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size,
+    Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
+    WeakHandle, WeakView, WindowOptions, SUBPIXEL_VARIANTS,
 };
 use anyhow::Result;
 use collections::HashMap;
@@ -376,6 +376,35 @@ impl<'a, 'w> WindowContext<'a, 'w> {
         self.notify();
     }
 
+    pub fn subscribe<E, H>(
+        &mut self,
+        handle: &H,
+        mut on_event: impl FnMut(H, &E::Event, &mut WindowContext<'_, '_>) + Send + 'static,
+    ) -> Subscription
+    where
+        E: EventEmitter,
+        H: EntityHandle<E>,
+    {
+        let entity_id = handle.entity_id();
+        let handle = handle.downgrade();
+        let window_handle = self.window.handle;
+        self.app.event_listeners.insert(
+            entity_id,
+            Box::new(move |event, cx| {
+                cx.update_window(window_handle, |cx| {
+                    if let Some(handle) = H::upgrade_from(&handle) {
+                        let event = event.downcast_ref().expect("invalid event type");
+                        on_event(handle, event, cx);
+                        true
+                    } else {
+                        false
+                    }
+                })
+                .unwrap_or(false)
+            }),
+        )
+    }
+
     /// Schedule the given closure to be run on the main thread. It will be invoked with
     /// a `MainThread<WindowContext>`, which provides access to platform-specific functionality
     /// of the window.
@@ -1600,21 +1629,24 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
         )
     }
 
-    pub fn subscribe<E: EventEmitter>(
+    pub fn subscribe<E, H>(
         &mut self,
-        handle: &Handle<E>,
-        mut on_event: impl FnMut(&mut V, Handle<E>, &E::Event, &mut ViewContext<'_, '_, V>)
-            + Send
-            + 'static,
-    ) -> Subscription {
+        handle: &H,
+        mut on_event: impl FnMut(&mut V, H, &E::Event, &mut ViewContext<'_, '_, V>) + Send + 'static,
+    ) -> Subscription
+    where
+        E: EventEmitter,
+        H: EntityHandle<E>,
+    {
         let view = self.view();
+        let entity_id = handle.entity_id();
         let handle = handle.downgrade();
         let window_handle = self.window.handle;
         self.app.event_listeners.insert(
-            handle.entity_id,
+            entity_id,
             Box::new(move |event, cx| {
                 cx.update_window(window_handle, |cx| {
-                    if let Some(handle) = handle.upgrade() {
+                    if let Some(handle) = H::upgrade_from(&handle) {
                         let event = event.downcast_ref().expect("invalid event type");
                         view.update(cx, |this, cx| on_event(this, handle, event, cx))
                             .is_ok()

crates/workspace2/src/item.rs 🔗

@@ -104,30 +104,26 @@ pub trait Item: EventEmitter + Sized {
     //     fn navigate(&mut self, _: Box<dyn Any>, _: &mut ViewContext<Self>) -> bool {
     //         false
     //     }
-    //     fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
-    //         None
-    //     }
-    //     fn tab_description<'a>(&'a self, _: usize, _: &'a AppContext) -> Option<Cow<str>> {
-    //         None
-    //     }
-    //     fn tab_content<V: 'static>(
-    //         &self,
-    //         detail: Option<usize>,
-    //         style: &theme2::Tab,
-    //         cx: &AppContext,
-    //     ) -> AnyElement<V>;
-    //     fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) {
-    //     } // (model id, Item)
+    fn tab_tooltip_text(&self, _: &AppContext) -> Option<SharedString> {
+        None
+    }
+    fn tab_description(&self, _: usize, _: &AppContext) -> Option<SharedString> {
+        None
+    }
+    fn tab_content<V: 'static>(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<V>;
+
+    fn for_each_project_item(&self, _: &AppContext, _: &mut dyn FnMut(usize, &dyn project2::Item)) {
+    } // (model id, Item)
     fn is_singleton(&self, _cx: &AppContext) -> bool {
         false
     }
     //     fn set_nav_history(&mut self, _: ItemNavHistory, _: &mut ViewContext<Self>) {}
-    //     fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
-    //     where
-    //         Self: Sized,
-    //     {
-    //         None
-    //     }
+    fn clone_on_split(&self, _workspace_id: WorkspaceId, _: &mut ViewContext<Self>) -> Option<Self>
+    where
+        Self: Sized,
+    {
+        None
+    }
     //     fn is_dirty(&self, _: &AppContext) -> bool {
     //         false
     //     }
@@ -221,7 +217,6 @@ pub trait Item: EventEmitter + Sized {
 
 use std::{
     any::Any,
-    borrow::Cow,
     cell::RefCell,
     ops::Range,
     path::PathBuf,
@@ -235,7 +230,7 @@ use std::{
 
 use gpui2::{
     AnyElement, AnyWindowHandle, AppContext, EventEmitter, Handle, HighlightStyle, Pixels, Point,
-    Task, View, ViewContext, WindowContext,
+    SharedString, Task, View, ViewContext, VisualContext, WindowContext,
 };
 use project2::{Project, ProjectEntryId, ProjectPath};
 use smallvec::SmallVec;
@@ -252,10 +247,10 @@ pub trait ItemHandle: 'static + Send {
     fn subscribe_to_item_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+        handler: Box<dyn Fn(ItemEvent, &mut WindowContext) + Send>,
     ) -> gpui2::Subscription;
-    fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>>;
-    fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>>;
+    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString>;
+    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString>;
     fn tab_content(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<Pane>;
     fn dragged_tab_content(&self, detail: Option<usize>, cx: &AppContext) -> AnyElement<Workspace>;
     fn project_path(&self, cx: &AppContext) -> Option<ProjectPath>;
@@ -329,7 +324,7 @@ impl<T: Item> ItemHandle for View<T> {
     fn subscribe_to_item_events(
         &self,
         cx: &mut WindowContext,
-        handler: Box<dyn Fn(ItemEvent, &mut WindowContext)>,
+        handler: Box<dyn Fn(ItemEvent, &mut WindowContext) + Send>,
     ) -> gpui2::Subscription {
         cx.subscribe(self, move |_, event, cx| {
             for item_event in T::to_item_events(event) {
@@ -338,11 +333,11 @@ impl<T: Item> ItemHandle for View<T> {
         })
     }
 
-    fn tab_tooltip_text<'a>(&self, cx: &'a AppContext) -> Option<Cow<'a, str>> {
+    fn tab_tooltip_text(&self, cx: &AppContext) -> Option<SharedString> {
         self.read(cx).tab_tooltip_text(cx)
     }
 
-    fn tab_description<'a>(&'a self, detail: usize, cx: &'a AppContext) -> Option<Cow<'a, str>> {
+    fn tab_description(&self, detail: usize, cx: &AppContext) -> Option<SharedString> {
         self.read(cx).tab_description(detail, cx)
     }