Skip to content

Commit

Permalink
Store focus handles in app
Browse files Browse the repository at this point in the history
This commit moves the responsibility of storing focus handles from individual
windows to the app level. This will allow new focus handles to be created without
the need to reference the window, which paves the way for a simplification of
gpui's context types I'm working on.
  • Loading branch information
nathansobo committed Dec 17, 2024
1 parent d11deff commit 4d7cf44
Show file tree
Hide file tree
Showing 3 changed files with 34 additions and 33 deletions.
55 changes: 29 additions & 26 deletions crates/gpui/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ use std::{
ops::{Deref, DerefMut},
path::{Path, PathBuf},
rc::{Rc, Weak},
sync::{atomic::Ordering::SeqCst, Arc},
sync::{
atomic::{AtomicUsize, Ordering::SeqCst},
Arc,
},
time::Duration,
};

Expand All @@ -16,6 +19,7 @@ use futures::{
future::{LocalBoxFuture, Shared},
Future, FutureExt,
};
use parking_lot::RwLock;
use slotmap::SlotMap;

pub use async_context::*;
Expand All @@ -30,9 +34,9 @@ use util::ResultExt;
use crate::{
current_platform, hash, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
Asset, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId,
Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point,
PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
Entity, EventEmitter, FocusId, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke,
LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay,
Point, PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem,
View, ViewContext, Window, WindowAppearance, WindowContext, WindowHandle, WindowId,
};
Expand Down Expand Up @@ -242,6 +246,7 @@ pub struct AppContext {
pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
pub(crate) keymap: Rc<RefCell<Keymap>>,
pub(crate) keyboard_layout: SharedString,
pub(crate) global_action_listeners:
Expand Down Expand Up @@ -302,8 +307,9 @@ impl AppContext {
entities,
new_view_observers: SubscriberSet::new(),
new_model_observers: SubscriberSet::new(),
window_handles: FxHashMap::default(),
windows: SlotMap::with_key(),
window_handles: FxHashMap::default(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
keymap: Rc::new(RefCell::new(Keymap::default())),
keyboard_layout,
global_action_listeners: FxHashMap::default(),
Expand Down Expand Up @@ -844,28 +850,25 @@ impl AppContext {

/// Repeatedly called during `flush_effects` to handle a focused handle being dropped.
fn release_dropped_focus_handles(&mut self) {
for window_handle in self.windows() {
window_handle
.update(self, |_, 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.blur();
self.focus_handles
.clone()
.write()
.retain(|handle_id, count| {
if count.load(SeqCst) == 0 {
for window_handle in self.windows() {
window_handle
.update(self, |_, cx| {
if cx.window.focus == Some(handle_id) {
cx.blur();
}
})
.unwrap();
}
})
.unwrap();
}
false
} else {
true
}
});
}

fn apply_notify_effect(&mut self, emitter: EntityId) {
Expand Down
2 changes: 1 addition & 1 deletion crates/gpui/src/element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -500,7 +500,7 @@ impl AnyElement {

if !focus_assigned {
if let Some(focus_id) = cx.window.next_frame.focus {
return FocusHandle::for_id(focus_id, &cx.window.focus_handles);
return FocusHandle::for_id(focus_id, &cx.focus_handles);
}
}

Expand Down
10 changes: 4 additions & 6 deletions crates/gpui/src/window.rs
Original file line number Diff line number Diff line change
Expand Up @@ -531,7 +531,6 @@ pub struct Window {
pub(crate) tooltip_bounds: Option<TooltipBounds>,
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
pub(crate) dirty_views: FxHashSet<EntityId>,
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
focus_lost_listeners: SubscriberSet<(), AnyObserver>,
default_prevented: bool,
Expand Down Expand Up @@ -809,7 +808,6 @@ impl Window {
next_tooltip_id: TooltipId::default(),
tooltip_bounds: None,
dirty_views: FxHashSet::default(),
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
focus_listeners: SubscriberSet::new(),
focus_lost_listeners: SubscriberSet::new(),
default_prevented: true,
Expand Down Expand Up @@ -934,14 +932,14 @@ impl<'a> WindowContext<'a> {
/// Obtain a new [`FocusHandle`], which allows you to track and manipulate the keyboard focus
/// for elements rendered within this window.
pub fn focus_handle(&self) -> FocusHandle {
FocusHandle::new(&self.window.focus_handles)
FocusHandle::new(&self.app.focus_handles)
}

/// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`.
pub fn focused(&self) -> Option<FocusHandle> {
self.window
.focus
.and_then(|id| FocusHandle::for_id(id, &self.window.focus_handles))
.and_then(|id| FocusHandle::for_id(id, &self.app.focus_handles))
}

/// Move focus to the element associated with the given [`FocusHandle`].
Expand Down Expand Up @@ -3023,7 +3021,7 @@ impl<'a> WindowContext<'a> {
let event = FocusOutEvent {
blurred: WeakFocusHandle {
id: blurred_id,
handles: Arc::downgrade(&cx.window.focus_handles),
handles: Arc::downgrade(&cx.app.focus_handles),
},
};
listener(event, cx)
Expand Down Expand Up @@ -4441,7 +4439,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
let event = FocusOutEvent {
blurred: WeakFocusHandle {
id: blurred_id,
handles: Arc::downgrade(&cx.window.focus_handles),
handles: Arc::downgrade(&cx.app.focus_handles),
},
};
listener(view, event, cx)
Expand Down

0 comments on commit 4d7cf44

Please sign in to comment.