Store focus handles in AppContext instead of Window (#22158)
Previously, each window stored its own collection of focus handles. This meant that to create a focus handle, you needed to have access to a Window. I'm working on a simplification to gpui's context types that removes `WindowContext` and `ViewContext` in favor of passing a window reference explicitly when rendering or handling events. You'll still need a window to manipulate focus, but it will be helpful to be able to create focus handles without a window. cc @mgsloan Release Notes: - N/A
This commit is contained in:
parent
e1ca5ed836
commit
81c118d67d
3 changed files with 43 additions and 40 deletions
|
@ -5,7 +5,10 @@ use std::{
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
rc::{Rc, Weak},
|
rc::{Rc, Weak},
|
||||||
sync::{atomic::Ordering::SeqCst, Arc},
|
sync::{
|
||||||
|
atomic::{AtomicUsize, Ordering::SeqCst},
|
||||||
|
Arc,
|
||||||
|
},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -16,6 +19,7 @@ use futures::{
|
||||||
future::{LocalBoxFuture, Shared},
|
future::{LocalBoxFuture, Shared},
|
||||||
Future, FutureExt,
|
Future, FutureExt,
|
||||||
};
|
};
|
||||||
|
use parking_lot::RwLock;
|
||||||
use slotmap::SlotMap;
|
use slotmap::SlotMap;
|
||||||
|
|
||||||
pub use async_context::*;
|
pub use async_context::*;
|
||||||
|
@ -30,11 +34,12 @@ use util::ResultExt;
|
||||||
use crate::{
|
use crate::{
|
||||||
current_platform, hash, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
|
current_platform, hash, init_app_menus, Action, ActionRegistry, Any, AnyView, AnyWindowHandle,
|
||||||
Asset, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
|
Asset, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId,
|
||||||
Entity, EventEmitter, ForegroundExecutor, Global, KeyBinding, Keymap, Keystroke, LayoutId,
|
Entity, EventEmitter, FocusHandle, FocusId, ForegroundExecutor, Global, KeyBinding, Keymap,
|
||||||
Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point,
|
Keystroke, LayoutId, Menu, MenuItem, OwnedMenu, PathPromptOptions, Pixels, Platform,
|
||||||
PromptBuilder, PromptHandle, PromptLevel, Render, RenderablePromptHandle, Reservation,
|
PlatformDisplay, Point, PromptBuilder, PromptHandle, PromptLevel, Render,
|
||||||
ScreenCaptureSource, SharedString, SubscriberSet, Subscription, SvgRenderer, Task, TextSystem,
|
RenderablePromptHandle, Reservation, ScreenCaptureSource, SharedString, SubscriberSet,
|
||||||
View, ViewContext, Window, WindowAppearance, WindowContext, WindowHandle, WindowId,
|
Subscription, SvgRenderer, Task, TextSystem, View, ViewContext, Window, WindowAppearance,
|
||||||
|
WindowContext, WindowHandle, WindowId,
|
||||||
};
|
};
|
||||||
|
|
||||||
mod async_context;
|
mod async_context;
|
||||||
|
@ -242,6 +247,7 @@ pub struct AppContext {
|
||||||
pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
|
pub(crate) new_view_observers: SubscriberSet<TypeId, NewViewListener>,
|
||||||
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
pub(crate) windows: SlotMap<WindowId, Option<Window>>,
|
||||||
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
|
pub(crate) window_handles: FxHashMap<WindowId, AnyWindowHandle>,
|
||||||
|
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
||||||
pub(crate) keymap: Rc<RefCell<Keymap>>,
|
pub(crate) keymap: Rc<RefCell<Keymap>>,
|
||||||
pub(crate) keyboard_layout: SharedString,
|
pub(crate) keyboard_layout: SharedString,
|
||||||
pub(crate) global_action_listeners:
|
pub(crate) global_action_listeners:
|
||||||
|
@ -302,8 +308,9 @@ impl AppContext {
|
||||||
entities,
|
entities,
|
||||||
new_view_observers: SubscriberSet::new(),
|
new_view_observers: SubscriberSet::new(),
|
||||||
new_model_observers: SubscriberSet::new(),
|
new_model_observers: SubscriberSet::new(),
|
||||||
window_handles: FxHashMap::default(),
|
|
||||||
windows: SlotMap::with_key(),
|
windows: SlotMap::with_key(),
|
||||||
|
window_handles: FxHashMap::default(),
|
||||||
|
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
||||||
keymap: Rc::new(RefCell::new(Keymap::default())),
|
keymap: Rc::new(RefCell::new(Keymap::default())),
|
||||||
keyboard_layout,
|
keyboard_layout,
|
||||||
global_action_listeners: FxHashMap::default(),
|
global_action_listeners: FxHashMap::default(),
|
||||||
|
@ -439,6 +446,7 @@ impl AppContext {
|
||||||
self.defer(move |_| activate());
|
self.defer(move |_| activate());
|
||||||
subscription
|
subscription
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn observe_internal<W, E>(
|
pub(crate) fn observe_internal<W, E>(
|
||||||
&mut self,
|
&mut self,
|
||||||
entity: &E,
|
entity: &E,
|
||||||
|
@ -569,6 +577,12 @@ impl AppContext {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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.focus_handles)
|
||||||
|
}
|
||||||
|
|
||||||
/// Instructs the platform to activate the application by bringing it to the foreground.
|
/// Instructs the platform to activate the application by bringing it to the foreground.
|
||||||
pub fn activate(&self, ignoring_other_apps: bool) {
|
pub fn activate(&self, ignoring_other_apps: bool) {
|
||||||
self.platform.activate(ignoring_other_apps);
|
self.platform.activate(ignoring_other_apps);
|
||||||
|
@ -844,28 +858,25 @@ impl AppContext {
|
||||||
|
|
||||||
/// Repeatedly called during `flush_effects` to handle a focused handle being dropped.
|
/// Repeatedly called during `flush_effects` to handle a focused handle being dropped.
|
||||||
fn release_dropped_focus_handles(&mut self) {
|
fn release_dropped_focus_handles(&mut self) {
|
||||||
for window_handle in self.windows() {
|
self.focus_handles
|
||||||
window_handle
|
.clone()
|
||||||
.update(self, |_, cx| {
|
.write()
|
||||||
let mut blur_window = false;
|
.retain(|handle_id, count| {
|
||||||
let focus = cx.window.focus;
|
if count.load(SeqCst) == 0 {
|
||||||
cx.window.focus_handles.write().retain(|handle_id, count| {
|
for window_handle in self.windows() {
|
||||||
if count.load(SeqCst) == 0 {
|
window_handle
|
||||||
if focus == Some(handle_id) {
|
.update(self, |_, cx| {
|
||||||
blur_window = true;
|
if cx.window.focus == Some(handle_id) {
|
||||||
}
|
cx.blur();
|
||||||
false
|
}
|
||||||
} else {
|
})
|
||||||
true
|
.unwrap();
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if blur_window {
|
|
||||||
cx.blur();
|
|
||||||
}
|
}
|
||||||
})
|
false
|
||||||
.unwrap();
|
} else {
|
||||||
}
|
true
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn apply_notify_effect(&mut self, emitter: EntityId) {
|
fn apply_notify_effect(&mut self, emitter: EntityId) {
|
||||||
|
|
|
@ -500,7 +500,7 @@ impl AnyElement {
|
||||||
|
|
||||||
if !focus_assigned {
|
if !focus_assigned {
|
||||||
if let Some(focus_id) = cx.window.next_frame.focus {
|
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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -531,7 +531,6 @@ pub struct Window {
|
||||||
pub(crate) tooltip_bounds: Option<TooltipBounds>,
|
pub(crate) tooltip_bounds: Option<TooltipBounds>,
|
||||||
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
|
next_frame_callbacks: Rc<RefCell<Vec<FrameCallback>>>,
|
||||||
pub(crate) dirty_views: FxHashSet<EntityId>,
|
pub(crate) dirty_views: FxHashSet<EntityId>,
|
||||||
pub(crate) focus_handles: Arc<RwLock<SlotMap<FocusId, AtomicUsize>>>,
|
|
||||||
focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
|
focus_listeners: SubscriberSet<(), AnyWindowFocusListener>,
|
||||||
focus_lost_listeners: SubscriberSet<(), AnyObserver>,
|
focus_lost_listeners: SubscriberSet<(), AnyObserver>,
|
||||||
default_prevented: bool,
|
default_prevented: bool,
|
||||||
|
@ -809,7 +808,6 @@ impl Window {
|
||||||
next_tooltip_id: TooltipId::default(),
|
next_tooltip_id: TooltipId::default(),
|
||||||
tooltip_bounds: None,
|
tooltip_bounds: None,
|
||||||
dirty_views: FxHashSet::default(),
|
dirty_views: FxHashSet::default(),
|
||||||
focus_handles: Arc::new(RwLock::new(SlotMap::with_key())),
|
|
||||||
focus_listeners: SubscriberSet::new(),
|
focus_listeners: SubscriberSet::new(),
|
||||||
focus_lost_listeners: SubscriberSet::new(),
|
focus_lost_listeners: SubscriberSet::new(),
|
||||||
default_prevented: true,
|
default_prevented: true,
|
||||||
|
@ -931,17 +929,11 @@ impl<'a> WindowContext<'a> {
|
||||||
self.window.removed = true;
|
self.window.removed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`.
|
/// Obtain the currently focused [`FocusHandle`]. If no elements are focused, returns `None`.
|
||||||
pub fn focused(&self) -> Option<FocusHandle> {
|
pub fn focused(&self) -> Option<FocusHandle> {
|
||||||
self.window
|
self.window
|
||||||
.focus
|
.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`].
|
/// Move focus to the element associated with the given [`FocusHandle`].
|
||||||
|
@ -3021,7 +3013,7 @@ impl<'a> WindowContext<'a> {
|
||||||
let event = FocusOutEvent {
|
let event = FocusOutEvent {
|
||||||
blurred: WeakFocusHandle {
|
blurred: WeakFocusHandle {
|
||||||
id: blurred_id,
|
id: blurred_id,
|
||||||
handles: Arc::downgrade(&cx.window.focus_handles),
|
handles: Arc::downgrade(&cx.app.focus_handles),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
listener(event, cx)
|
listener(event, cx)
|
||||||
|
@ -4439,7 +4431,7 @@ impl<'a, V: 'static> ViewContext<'a, V> {
|
||||||
let event = FocusOutEvent {
|
let event = FocusOutEvent {
|
||||||
blurred: WeakFocusHandle {
|
blurred: WeakFocusHandle {
|
||||||
id: blurred_id,
|
id: blurred_id,
|
||||||
handles: Arc::downgrade(&cx.window.focus_handles),
|
handles: Arc::downgrade(&cx.app.focus_handles),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
listener(view, event, cx)
|
listener(view, event, cx)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue