WIP

Antonio Scandurra created

Change summary

crates/gpui3/src/app.rs    | 40 ++++++++++++++++++++++--
crates/gpui3/src/window.rs | 63 ++++++++++++++++++++++++++++++++-------
2 files changed, 87 insertions(+), 16 deletions(-)

Detailed changes

crates/gpui3/src/app.rs 🔗

@@ -6,6 +6,7 @@ pub use async_context::*;
 pub use entity_map::*;
 pub use model_context::*;
 use refineable::Refineable;
+use smallvec::SmallVec;
 
 use crate::{
     current_platform, image_cache::ImageCache, AssetSource, Context, DisplayId, Executor,
@@ -21,7 +22,7 @@ use slotmap::SlotMap;
 use std::{
     any::{type_name, Any, TypeId},
     mem,
-    sync::{Arc, Weak},
+    sync::{atomic::Ordering::SeqCst, Arc, Weak},
 };
 use util::http::{self, HttpClient};
 
@@ -170,6 +171,7 @@ impl AppContext {
     fn flush_effects(&mut self) {
         loop {
             self.release_dropped_entities();
+            self.release_dropped_focus_handles();
             if let Some(effect) = self.pending_effects.pop_front() {
                 match effect {
                     Effect::Notify { emitter } => self.apply_notify_effect(emitter),
@@ -194,7 +196,7 @@ impl AppContext {
                     None
                 }
             })
-            .collect::<Vec<_>>();
+            .collect::<SmallVec<[_; 8]>>();
 
         for dirty_window_id in dirty_window_ids {
             self.update_window(dirty_window_id, |cx| cx.draw()).unwrap();
@@ -218,6 +220,32 @@ impl AppContext {
         }
     }
 
+    fn release_dropped_focus_handles(&mut self) {
+        let window_ids = self.windows.keys().collect::<SmallVec<[_; 8]>>();
+        for window_id in window_ids {
+            self.update_window(window_id, |cx| {
+                let mut blur_window = false;
+                let focus = cx.window.focus;
+                cx.window.focus_handles.write().retain(|handle_id, count| {
+                    if count.load(SeqCst) == 0 {
+                        if focus == Some(handle_id) {
+                            blur_window = true;
+                        }
+                        false
+                    } else {
+                        true
+                    }
+                });
+
+                if blur_window {
+                    cx.window.focus = None;
+                    cx.blur();
+                }
+            })
+            .unwrap();
+        }
+    }
+
     fn apply_notify_effect(&mut self, emitter: EntityId) {
         self.pending_notifications.remove(&emitter);
         self.observers
@@ -235,8 +263,12 @@ impl AppContext {
         self.update_window(window_id, |cx| {
             if cx.window.focus == focused {
                 let mut listeners = mem::take(&mut cx.window.focus_listeners);
-                let focused = focused.map(FocusHandle::new);
-                let blurred = cx.window.last_blur.unwrap().map(FocusHandle::new);
+                let focused = focused.map(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
+                let blurred = cx
+                    .window
+                    .last_blur
+                    .unwrap()
+                    .map(|id| FocusHandle::for_id(id, &cx.window.focus_handles));
                 let event = FocusEvent { focused, blurred };
                 for listener in &listeners {
                     listener(&event, cx);

crates/gpui3/src/window.rs 🔗

@@ -12,6 +12,8 @@ use crate::{
 use anyhow::Result;
 use collections::HashMap;
 use derive_more::{Deref, DerefMut};
+use parking_lot::{RwLock, RwLockUpgradableReadGuard};
+use slotmap::SlotMap;
 use smallvec::SmallVec;
 use std::{
     any::{Any, TypeId},
@@ -20,9 +22,12 @@ use std::{
     future::Future,
     marker::PhantomData,
     mem,
-    sync::Arc,
+    sync::{
+        atomic::{AtomicUsize, Ordering::SeqCst},
+        Arc, Weak,
+    },
 };
-use util::{post_inc, ResultExt};
+use util::ResultExt;
 
 #[derive(Deref, DerefMut, Ord, PartialOrd, Eq, PartialEq, Clone, Default)]
 pub struct StackingOrder(pub(crate) SmallVec<[u32; 16]>);
@@ -50,17 +55,34 @@ type AnyKeyDownListener =
 type AnyKeyUpListener =
     Box<dyn Fn(&KeyUpEvent, DispatchPhase, &mut WindowContext) + Send + Sync + 'static>;
 
-#[derive(Copy, Clone, PartialEq, Eq, Hash)]
-pub struct FocusId(usize);
+slotmap::new_key_type! { pub struct FocusId; }
 
-#[derive(Clone, PartialEq, Eq)]
+#[derive(Clone)]
 pub struct FocusHandle {
     pub(crate) id: FocusId,
+    handles: Weak<RwLock<SlotMap<FocusId, AtomicUsize>>>,
 }
 
 impl FocusHandle {
-    pub(crate) fn new(id: FocusId) -> Self {
-        Self { id }
+    pub(crate) fn new(handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>) -> Self {
+        let id = handles.write().insert(AtomicUsize::new(1));
+        Self {
+            id,
+            handles: Arc::downgrade(handles),
+        }
+    }
+
+    pub(crate) fn for_id(
+        id: FocusId,
+        handles: &Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
+    ) -> Self {
+        let lock = handles.upgradable_read();
+        let ref_count = lock.get(id).expect("all focus handles dropped for id");
+        ref_count.fetch_add(1, SeqCst);
+        Self {
+            id,
+            handles: Arc::downgrade(handles),
+        }
     }
 
     pub fn is_focused(&self, cx: &WindowContext) -> bool {
@@ -90,6 +112,22 @@ impl FocusHandle {
     }
 }
 
+impl PartialEq for FocusHandle {
+    fn eq(&self, other: &Self) -> bool {
+        self.id == other.id
+    }
+}
+
+impl Eq for FocusHandle {}
+
+impl Drop for FocusHandle {
+    fn drop(&mut self) {
+        if let Some(handles) = self.handles.upgrade() {
+            handles.read().get(self.id).unwrap().fetch_sub(1, SeqCst);
+        }
+    }
+}
+
 pub struct Window {
     handle: AnyWindowHandle,
     platform_window: MainThreadOnly<Box<dyn PlatformWindow>>,
@@ -109,6 +147,7 @@ pub struct Window {
     focus_stack: Vec<FocusStackFrame>,
     focus_parents_by_child: HashMap<FocusId, FocusId>,
     pub(crate) focus_listeners: Vec<AnyFocusListener>,
+    pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
     propagate_event: bool,
     mouse_position: Point<Pixels>,
     scale_factor: f32,
@@ -116,7 +155,6 @@ pub struct Window {
     pub(crate) dirty: bool,
     pub(crate) last_blur: Option<Option<FocusId>>,
     pub(crate) focus: Option<FocusId>,
-    next_focus_id: FocusId,
 }
 
 impl Window {
@@ -185,9 +223,9 @@ impl Window {
             scale_factor,
             scene_builder: SceneBuilder::new(),
             dirty: true,
+            focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
             last_blur: None,
             focus: None,
-            next_focus_id: FocusId(0),
         }
     }
 }
@@ -235,12 +273,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
     }
 
     pub fn focus_handle(&mut self) -> FocusHandle {
-        let id = FocusId(post_inc(&mut self.window.next_focus_id.0));
-        FocusHandle { id }
+        FocusHandle::new(&self.window.focus_handles)
     }
 
     pub fn focused(&self) -> Option<FocusHandle> {
-        self.window.focus.map(|id| FocusHandle::new(id))
+        self.window
+            .focus
+            .map(|id| FocusHandle::for_id(id, &self.window.focus_handles))
     }
 
     pub fn focus(&mut self, handle: &FocusHandle) {