diff --git a/crates/gpui3/src/app.rs b/crates/gpui3/src/app.rs index 59c62f4c17c4b571726d5bb7012d657fd6674265..ed094a24caeef487aeadbece7fba47a84c22dc90 100644 --- a/crates/gpui3/src/app.rs +++ b/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::>(); + .collect::>(); 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::>(); + 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); diff --git a/crates/gpui3/src/window.rs b/crates/gpui3/src/window.rs index a9c4dcadea09317643efbb33b8fbea742b804928..ed1ca74846d406528ce7fd383272f3727237c40d 100644 --- a/crates/gpui3/src/window.rs +++ b/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; -#[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>>, } impl FocusHandle { - pub(crate) fn new(id: FocusId) -> Self { - Self { id } + pub(crate) fn new(handles: &Arc>>) -> Self { + let id = handles.write().insert(AtomicUsize::new(1)); + Self { + id, + handles: Arc::downgrade(handles), + } + } + + pub(crate) fn for_id( + id: FocusId, + handles: &Arc>>, + ) -> 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>, @@ -109,6 +147,7 @@ pub struct Window { focus_stack: Vec, focus_parents_by_child: HashMap, pub(crate) focus_listeners: Vec, + pub(crate) focus_handles: Arc>>, propagate_event: bool, mouse_position: Point, scale_factor: f32, @@ -116,7 +155,6 @@ pub struct Window { pub(crate) dirty: bool, pub(crate) last_blur: Option>, pub(crate) focus: Option, - 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 { - 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) {