Detailed changes
@@ -1477,7 +1477,7 @@ async fn test_host_disconnect(
.unwrap()
.downcast::<Editor>()
.unwrap();
- assert!(cx_b.read(|cx| editor_b.is_focused(cx)));
+ assert!(cx_b.read_window(window_id_b, |cx| editor_b.is_focused(cx)).unwrap());
editor_b.update(cx_b, |editor, cx| editor.insert("X", cx));
assert!(cx_b.is_window_edited(workspace_b.window_id()));
@@ -43,7 +43,7 @@ impl CommandPalette {
pub fn new(focused_view_id: usize, cx: &mut ViewContext<Self>) -> Self {
let this = cx.weak_handle();
let actions = cx
- .available_actions(cx.window_id(), focused_view_id)
+ .available_actions(focused_view_id)
.filter_map(|(name, action, bindings)| {
if cx.has_global::<CommandPaletteFilter>() {
let filter = cx.global::<CommandPaletteFilter>();
@@ -42,7 +42,7 @@ use gpui::{
platform::{CursorStyle, MouseButton},
serde_json::{self, json},
AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Drawable, Element, Entity,
- ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle,
+ ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, WindowContext,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
use hover_popover::{hide_hover, HideHover, HoverState};
@@ -1317,7 +1317,7 @@ impl Editor {
self.buffer().read(cx).title(cx)
}
- pub fn snapshot(&mut self, cx: &mut AppContext) -> EditorSnapshot {
+ pub fn snapshot(&mut self, cx: &mut WindowContext) -> EditorSnapshot {
EditorSnapshot {
mode: self.mode,
display_snapshot: self.display_map.update(cx, |map, cx| map.snapshot(cx)),
@@ -25,7 +25,6 @@ use std::{
use anyhow::{anyhow, Context, Result};
use parking_lot::Mutex;
use postage::oneshot;
-use smallvec::SmallVec;
use smol::prelude::*;
use uuid::Uuid;
@@ -825,47 +824,6 @@ impl AppContext {
.map(|view| view.as_any().type_id())
}
- /// Returns an iterator over all of the view ids from the passed view up to the root of the window
- /// Includes the passed view itself
- fn ancestors(&self, window_id: usize, mut view_id: usize) -> impl Iterator<Item = usize> + '_ {
- std::iter::once(view_id)
- .into_iter()
- .chain(std::iter::from_fn(move || {
- if let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, view_id)) {
- view_id = *parent_id;
- Some(view_id)
- } else {
- None
- }
- }))
- }
-
- /// Returns the id of the parent of the given view, or none if the given
- /// view is the root.
- pub fn parent(&self, window_id: usize, view_id: usize) -> Option<usize> {
- if let Some(ParentId::View(view_id)) = self.parents.get(&(window_id, view_id)) {
- Some(*view_id)
- } else {
- None
- }
- }
-
- fn focused_view_id(&self, window_id: usize) -> Option<usize> {
- self.windows
- .get(&window_id)
- .and_then(|window| window.focused_view_id)
- }
-
- pub fn is_child_focused(&self, view: &AnyViewHandle) -> bool {
- if let Some(focused_view_id) = self.focused_view_id(view.window_id) {
- self.ancestors(view.window_id, focused_view_id)
- .skip(1) // Skip self id
- .any(|parent| parent == view.view_id)
- } else {
- false
- }
- }
-
pub fn active_labeled_tasks<'a>(
&'a self,
) -> impl DoubleEndedIterator<Item = &'static str> + 'a {
@@ -1231,141 +1189,29 @@ impl AppContext {
self.action_deserializers.keys().copied()
}
- /// Return keystrokes that would dispatch the given action on the given view.
- pub(crate) fn keystrokes_for_action(
- &mut self,
- window_id: usize,
- view_id: usize,
- action: &dyn Action,
- ) -> Option<SmallVec<[Keystroke; 2]>> {
- let mut contexts = Vec::new();
- let mut handler_depth = None;
- for (i, view_id) in self.ancestors(window_id, view_id).enumerate() {
- if let Some(view) = self.views.get(&(window_id, view_id)) {
- if let Some(actions) = self.actions.get(&view.as_any().type_id()) {
- if actions.contains_key(&action.as_any().type_id()) {
- handler_depth = Some(i);
- }
- }
- contexts.push(view.keymap_context(self));
- }
- }
-
- if self.global_actions.contains_key(&action.as_any().type_id()) {
- handler_depth = Some(contexts.len())
- }
-
- self.keystroke_matcher
- .bindings_for_action_type(action.as_any().type_id())
- .find_map(|b| {
- handler_depth
- .map(|highest_handler| {
- if (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..])) {
- Some(b.keystrokes().into())
- } else {
- None
- }
- })
- .flatten()
- })
- }
-
- pub fn available_actions(
- &self,
- window_id: usize,
- view_id: usize,
- ) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
- let mut contexts = Vec::new();
- let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
- for (depth, view_id) in self.ancestors(window_id, view_id).enumerate() {
- if let Some(view) = self.views.get(&(window_id, view_id)) {
- contexts.push(view.keymap_context(self));
- let view_type = view.as_any().type_id();
- if let Some(actions) = self.actions.get(&view_type) {
- handler_depths_by_action_type.extend(
- actions
- .keys()
- .copied()
- .map(|action_type| (action_type, depth)),
- );
- }
- }
- }
-
- handler_depths_by_action_type.extend(
- self.global_actions
- .keys()
- .copied()
- .map(|action_type| (action_type, contexts.len())),
- );
-
- self.action_deserializers
- .iter()
- .filter_map(move |(name, (type_id, deserialize))| {
- if let Some(action_depth) = handler_depths_by_action_type.get(type_id).copied() {
- Some((
- *name,
- deserialize("{}").ok()?,
- self.keystroke_matcher
- .bindings_for_action_type(*type_id)
- .filter(|b| {
- (0..=action_depth).any(|depth| b.match_context(&contexts[depth..]))
- })
- .collect(),
- ))
- } else {
- None
- }
- })
- }
-
pub fn is_action_available(&self, action: &dyn Action) -> bool {
+ let mut available_in_window = false;
let action_type = action.as_any().type_id();
if let Some(window_id) = self.platform.main_window_id() {
- if let Some(focused_view_id) = self.focused_view_id(window_id) {
- for view_id in self.ancestors(window_id, focused_view_id) {
- if let Some(view) = self.views.get(&(window_id, view_id)) {
- let view_type = view.as_any().type_id();
- if let Some(actions) = self.actions.get(&view_type) {
- if actions.contains_key(&action_type) {
- return true;
+ available_in_window = self
+ .read_window(window_id, |cx| {
+ if let Some(focused_view_id) = cx.focused_view_id() {
+ for view_id in cx.ancestors(focused_view_id) {
+ if let Some(view) = cx.views.get(&(window_id, view_id)) {
+ let view_type = view.as_any().type_id();
+ if let Some(actions) = cx.actions.get(&view_type) {
+ if actions.contains_key(&action_type) {
+ return true;
+ }
+ }
}
}
}
- }
- }
- }
- self.global_actions.contains_key(&action_type)
- }
-
- // Traverses the parent tree. Walks down the tree toward the passed
- // view calling visit with true. Then walks back up the tree calling visit with false.
- // If `visit` returns false this function will immediately return.
- // Returns a bool indicating if the traversal was completed early.
- fn visit_dispatch_path(
- &mut self,
- window_id: usize,
- view_id: usize,
- mut visit: impl FnMut(usize, bool, &mut AppContext) -> bool,
- ) -> bool {
- // List of view ids from the leaf to the root of the window
- let path = self.ancestors(window_id, view_id).collect::<Vec<_>>();
-
- // Walk down from the root to the leaf calling visit with capture_phase = true
- for view_id in path.iter().rev() {
- if !visit(*view_id, true, self) {
- return false;
- }
- }
-
- // Walk up from the leaf to the root calling visit with capture_phase = false
- for view_id in path.iter() {
- if !visit(*view_id, false, self) {
- return false;
- }
+ false
+ })
+ .unwrap_or(false);
}
-
- true
+ available_in_window || self.global_actions.contains_key(&action_type)
}
fn actions_mut(
@@ -2077,7 +1923,7 @@ impl AppContext {
cx.window.is_active = active;
if let Some(focused_id) = cx.window.focused_view_id {
- for view_id in cx.ancestors(window_id, focused_id).collect::<Vec<_>>() {
+ for view_id in cx.ancestors(focused_id).collect::<Vec<_>>() {
cx.update_any_view(focused_id, |view, cx| {
if active {
view.focus_in(focused_id, cx, view_id);
@@ -2104,10 +1950,10 @@ impl AppContext {
cx.window.focused_view_id = focused_id;
let blurred_parents = blurred_id
- .map(|blurred_id| cx.ancestors(window_id, blurred_id).collect::<Vec<_>>())
+ .map(|blurred_id| cx.ancestors(blurred_id).collect::<Vec<_>>())
.unwrap_or_default();
let focused_parents = focused_id
- .map(|focused_id| cx.ancestors(window_id, focused_id).collect::<Vec<_>>())
+ .map(|focused_id| cx.ancestors(focused_id).collect::<Vec<_>>())
.unwrap_or_default();
if let Some(blurred_id) = blurred_id {
@@ -2141,48 +1987,10 @@ impl AppContext {
window_id: usize,
view_id: Option<usize>,
action: &dyn Action,
- ) -> bool {
- self.update(|this| {
- if let Some(view_id) = view_id {
- this.halt_action_dispatch = false;
- this.visit_dispatch_path(window_id, view_id, |view_id, capture_phase, this| {
- this.update_window(window_id, |cx| {
- cx.update_any_view(view_id, |view, cx| {
- let type_id = view.as_any().type_id();
- if let Some((name, mut handlers)) = cx
- .actions_mut(capture_phase)
- .get_mut(&type_id)
- .and_then(|h| h.remove_entry(&action.id()))
- {
- for handler in handlers.iter_mut().rev() {
- cx.halt_action_dispatch = true;
- handler(view, action, cx, view_id);
- if cx.halt_action_dispatch {
- break;
- }
- }
- cx.actions_mut(capture_phase)
- .get_mut(&type_id)
- .unwrap()
- .insert(name, handlers);
- }
- })
- });
-
- !this.halt_action_dispatch
- });
- }
-
- if !this.halt_action_dispatch {
- this.halt_action_dispatch = this.dispatch_global_action_any(action);
- }
-
- this.pending_effects
- .push_back(Effect::ActionDispatchNotification {
- action_id: action.id(),
- });
- this.halt_action_dispatch
- })
+ ) {
+ self.update_window(window_id, |cx| {
+ cx.handle_dispatch_action_from_effect(view_id, action)
+ });
}
fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) {
@@ -3215,7 +3023,7 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
}
pub fn parent(&self) -> Option<usize> {
- self.window_context.parent(self.window_id, self.view_id)
+ self.window_context.parent(self.view_id)
}
pub fn window_id(&self) -> usize {
@@ -3269,7 +3077,7 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
}
pub fn is_parent_view_focused(&self) -> bool {
- if let Some(parent_view_id) = self.ancestors(self.window_id, self.view_id).next().clone() {
+ if let Some(parent_view_id) = self.ancestors(self.view_id).next().clone() {
self.focused_view_id() == Some(parent_view_id)
} else {
false
@@ -3277,7 +3085,7 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
}
pub fn focus_parent_view(&mut self) {
- let next = self.ancestors(self.window_id, self.view_id).next().clone();
+ let next = self.ancestors(self.view_id).next().clone();
if let Some(parent_view_id) = next {
let window_id = self.window_id;
self.window_context.focus(window_id, Some(parent_view_id));
@@ -3289,7 +3097,7 @@ impl<'a, 'b, 'c, V: View> ViewContext<'a, 'b, 'c, V> {
if self.window_id != view.window_id {
return false;
}
- self.ancestors(view.window_id, view.view_id)
+ self.ancestors(view.view_id)
.skip(1) // Skip self id
.any(|parent| parent == self.view_id)
}
@@ -4193,9 +4001,12 @@ impl<T: View> ViewHandle<T> {
});
}
+ #[cfg(any(test, feature = "test-support"))]
pub fn is_focused(&self, cx: &AppContext) -> bool {
- cx.focused_view_id(self.window_id)
- .map_or(false, |focused_id| focused_id == self.view_id)
+ cx.read_window(self.window_id, |cx| {
+ cx.focused_view_id() == Some(self.view_id)
+ })
+ .unwrap_or(false)
}
}
@@ -4312,11 +4123,6 @@ impl AnyViewHandle {
TypeId::of::<T>() == self.view_type
}
- pub fn is_focused(&self, cx: &AppContext) -> bool {
- cx.focused_view_id(self.window_id)
- .map_or(false, |focused_id| focused_id == self.view_id)
- }
-
pub fn downcast<T: View>(self) -> Option<ViewHandle<T>> {
if self.is::<T>() {
Some(ViewHandle {
@@ -5713,7 +5519,7 @@ mod tests {
}
let view_events: Arc<Mutex<Vec<String>>> = Default::default();
- let (_, view_1) = cx.add_window(Default::default(), |_| View {
+ let (window_id, view_1) = cx.add_window(Default::default(), |_| View {
events: view_events.clone(),
name: "view 1".to_string(),
});
@@ -5763,8 +5569,10 @@ mod tests {
cx.focus(&view_2);
});
- assert!(cx.is_child_focused(&view_1));
- assert!(!cx.is_child_focused(&view_2));
+ cx.read_window(window_id, |cx| {
+ assert!(cx.is_child_focused(&view_1));
+ assert!(!cx.is_child_focused(&view_2));
+ });
assert_eq!(
mem::take(&mut *view_events.lock()),
[
@@ -5789,8 +5597,10 @@ mod tests {
);
view_1.update(cx, |_, cx| cx.focus(&view_1));
- assert!(!cx.is_child_focused(&view_1));
- assert!(!cx.is_child_focused(&view_2));
+ cx.read_window(window_id, |cx| {
+ assert!(!cx.is_child_focused(&view_1));
+ assert!(!cx.is_child_focused(&view_2));
+ });
assert_eq!(
mem::take(&mut *view_events.lock()),
["view 2 blurred", "view 1 focused"],
@@ -5967,11 +5777,9 @@ mod tests {
let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 });
let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 });
- cx.handle_dispatch_action_from_effect(
- window_id,
- Some(view_4.id()),
- &Action("bar".to_string()),
- );
+ cx.update_window(window_id, |cx| {
+ cx.handle_dispatch_action_from_effect(Some(view_4.id()), &Action("bar".to_string()))
+ });
assert_eq!(
*actions.borrow(),
@@ -5996,11 +5804,9 @@ mod tests {
let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 });
actions.borrow_mut().clear();
- cx.handle_dispatch_action_from_effect(
- window_id,
- Some(view_4.id()),
- &Action("bar".to_string()),
- );
+ cx.update_window(window_id, |cx| {
+ cx.handle_dispatch_action_from_effect(Some(view_4.id()), &Action("bar".to_string()))
+ });
assert_eq!(
*actions.borrow(),
@@ -6197,48 +6003,62 @@ mod tests {
Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
]);
- // Sanity check
- assert_eq!(
- cx.keystrokes_for_action(window_id, view_1.id(), &Action1)
- .unwrap()
- .as_slice(),
- &[Keystroke::parse("a").unwrap()]
- );
- assert_eq!(
- cx.keystrokes_for_action(window_id, view_2.id(), &Action2)
- .unwrap()
- .as_slice(),
- &[Keystroke::parse("b").unwrap()]
- );
+ cx.update_window(window_id, |cx| {
+ // Sanity check
+ assert_eq!(
+ cx.keystrokes_for_action(view_1.id(), &Action1)
+ .unwrap()
+ .as_slice(),
+ &[Keystroke::parse("a").unwrap()]
+ );
+ assert_eq!(
+ cx.keystrokes_for_action(view_2.id(), &Action2)
+ .unwrap()
+ .as_slice(),
+ &[Keystroke::parse("b").unwrap()]
+ );
- // The 'a' keystroke propagates up the view tree from view_2
- // to view_1. The action, Action1, is handled by view_1.
- assert_eq!(
- cx.keystrokes_for_action(window_id, view_2.id(), &Action1)
- .unwrap()
- .as_slice(),
- &[Keystroke::parse("a").unwrap()]
- );
+ // The 'a' keystroke propagates up the view tree from view_2
+ // to view_1. The action, Action1, is handled by view_1.
+ assert_eq!(
+ cx.keystrokes_for_action(view_2.id(), &Action1)
+ .unwrap()
+ .as_slice(),
+ &[Keystroke::parse("a").unwrap()]
+ );
- // Actions that are handled below the current view don't have bindings
- assert_eq!(
- cx.keystrokes_for_action(window_id, view_1.id(), &Action2),
- None
- );
+ // Actions that are handled below the current view don't have bindings
+ assert_eq!(cx.keystrokes_for_action(view_1.id(), &Action2), None);
- // Actions that are handled in other branches of the tree should not have a binding
- assert_eq!(
- cx.keystrokes_for_action(window_id, view_2.id(), &GlobalAction),
- None
- );
+ // Actions that are handled in other branches of the tree should not have a binding
+ assert_eq!(cx.keystrokes_for_action(view_2.id(), &GlobalAction), None);
+
+ // Check that global actions do not have a binding, even if a binding does exist in another view
+ assert_eq!(
+ &available_actions(view_1.id(), cx),
+ &[
+ ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
+ ("test::GlobalAction", vec![])
+ ],
+ );
+
+ // Check that view 1 actions and bindings are available even when called from view 2
+ assert_eq!(
+ &available_actions(view_2.id(), cx),
+ &[
+ ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
+ ("test::Action2", vec![Keystroke::parse("b").unwrap()]),
+ ("test::GlobalAction", vec![]),
+ ],
+ );
+ });
// Produces a list of actions and key bindings
fn available_actions(
- window_id: usize,
view_id: usize,
- cx: &mut AppContext,
+ cx: &WindowContext,
) -> Vec<(&'static str, Vec<Keystroke>)> {
- cx.available_actions(window_id, view_id)
+ cx.available_actions(view_id)
.map(|(action_name, _, bindings)| {
(
action_name,
@@ -6251,25 +6071,6 @@ mod tests {
.sorted_by(|(name1, _), (name2, _)| name1.cmp(name2))
.collect()
}
-
- // Check that global actions do not have a binding, even if a binding does exist in another view
- assert_eq!(
- &available_actions(window_id, view_1.id(), cx),
- &[
- ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
- ("test::GlobalAction", vec![])
- ],
- );
-
- // Check that view 1 actions and bindings are available even when called from view 2
- assert_eq!(
- &available_actions(window_id, view_2.id(), cx),
- &[
- ("test::Action1", vec![Keystroke::parse("a").unwrap()]),
- ("test::Action2", vec![Keystroke::parse("b").unwrap()]),
- ("test::GlobalAction", vec![]),
- ],
- );
}
#[crate::test(self)]
@@ -78,12 +78,18 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform,
move |action| {
let mut cx = cx.borrow_mut();
if let Some(main_window_id) = cx.platform.main_window_id() {
- if let Some(view_id) = cx
- .windows
- .get(&main_window_id)
- .and_then(|w| w.focused_view_id)
- {
- cx.handle_dispatch_action_from_effect(main_window_id, Some(view_id), action);
+ let dispatched = cx
+ .update_window(main_window_id, |cx| {
+ if let Some(view_id) = cx.focused_view_id() {
+ cx.handle_dispatch_action_from_effect(Some(view_id), action);
+ true
+ } else {
+ false
+ }
+ })
+ .unwrap_or(false);
+
+ if dispatched {
return;
}
}
@@ -2,7 +2,7 @@ use crate::{
elements::AnyRootElement,
geometry::rect::RectF,
json::{self, ToJson},
- keymap_matcher::{Keystroke, MatchResult},
+ keymap_matcher::{Binding, Keystroke, MatchResult},
platform::{
self, Appearance, CursorStyle, Event, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent,
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
@@ -13,10 +13,10 @@ use crate::{
},
text_layout::TextLayoutCache,
util::post_inc,
- AnyView, AnyViewHandle, AnyWeakViewHandle, AppContext, Drawable, Entity, ModelContext,
- ModelHandle, MouseRegion, MouseRegionId, ParentId, ReadView, SceneBuilder, UpdateModel,
- UpdateView, UpgradeViewHandle, View, ViewContext, ViewHandle, WeakViewHandle,
- WindowInvalidation,
+ Action, AnyView, AnyViewHandle, AnyWeakViewHandle, AppContext, Drawable, Effect, Entity,
+ ModelContext, ModelHandle, MouseRegion, MouseRegionId, ParentId, ReadModel, ReadView,
+ SceneBuilder, UpdateModel, UpdateView, UpgradeViewHandle, View, ViewContext, ViewHandle,
+ WeakViewHandle, WindowInvalidation,
};
use anyhow::{anyhow, bail, Result};
use collections::{HashMap, HashSet};
@@ -28,7 +28,10 @@ use sqlez::{
bindable::{Bind, Column, StaticColumnCount},
statement::Statement,
};
-use std::ops::{Deref, DerefMut, Range};
+use std::{
+ any::TypeId,
+ ops::{Deref, DerefMut, Range},
+};
use util::ResultExt;
use uuid::Uuid;
@@ -128,6 +131,12 @@ impl DerefMut for WindowContext<'_, '_> {
}
}
+impl ReadModel for WindowContext<'_, '_> {
+ fn read_model<T: Entity>(&self, handle: &ModelHandle<T>) -> &T {
+ self.app_context.read_model(handle)
+ }
+}
+
impl UpdateModel for WindowContext<'_, '_> {
fn update_model<T: Entity, R>(
&mut self,
@@ -230,11 +239,99 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
Some(result)
}
+ /// Return keystrokes that would dispatch the given action on the given view.
+ pub(crate) fn keystrokes_for_action(
+ &mut self,
+ view_id: usize,
+ action: &dyn Action,
+ ) -> Option<SmallVec<[Keystroke; 2]>> {
+ let window_id = self.window_id;
+ let mut contexts = Vec::new();
+ let mut handler_depth = None;
+ for (i, view_id) in self.ancestors(view_id).enumerate() {
+ if let Some(view) = self.views.get(&(window_id, view_id)) {
+ if let Some(actions) = self.actions.get(&view.as_any().type_id()) {
+ if actions.contains_key(&action.as_any().type_id()) {
+ handler_depth = Some(i);
+ }
+ }
+ contexts.push(view.keymap_context(self));
+ }
+ }
+
+ if self.global_actions.contains_key(&action.as_any().type_id()) {
+ handler_depth = Some(contexts.len())
+ }
+
+ self.keystroke_matcher
+ .bindings_for_action_type(action.as_any().type_id())
+ .find_map(|b| {
+ handler_depth
+ .map(|highest_handler| {
+ if (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..])) {
+ Some(b.keystrokes().into())
+ } else {
+ None
+ }
+ })
+ .flatten()
+ })
+ }
+
+ pub fn available_actions(
+ &self,
+ view_id: usize,
+ ) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
+ let window_id = self.window_id;
+ let mut contexts = Vec::new();
+ let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
+ for (depth, view_id) in self.ancestors(view_id).enumerate() {
+ if let Some(view) = self.views.get(&(window_id, view_id)) {
+ contexts.push(view.keymap_context(self));
+ let view_type = view.as_any().type_id();
+ if let Some(actions) = self.actions.get(&view_type) {
+ handler_depths_by_action_type.extend(
+ actions
+ .keys()
+ .copied()
+ .map(|action_type| (action_type, depth)),
+ );
+ }
+ }
+ }
+
+ handler_depths_by_action_type.extend(
+ self.global_actions
+ .keys()
+ .copied()
+ .map(|action_type| (action_type, contexts.len())),
+ );
+
+ self.action_deserializers
+ .iter()
+ .filter_map(move |(name, (type_id, deserialize))| {
+ if let Some(action_depth) = handler_depths_by_action_type.get(type_id).copied() {
+ Some((
+ *name,
+ deserialize("{}").ok()?,
+ self.keystroke_matcher
+ .bindings_for_action_type(*type_id)
+ .filter(|b| {
+ (0..=action_depth).any(|depth| b.match_context(&contexts[depth..]))
+ })
+ .collect(),
+ ))
+ } else {
+ None
+ }
+ })
+ }
+
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
let window_id = self.window_id;
if let Some(focused_view_id) = self.focused_view_id() {
let dispatch_path = self
- .ancestors(window_id, focused_view_id)
+ .ancestors(focused_view_id)
.filter_map(|view_id| {
self.views
.get(&(window_id, view_id))
@@ -252,11 +349,8 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
MatchResult::Pending => true,
MatchResult::Matches(matches) => {
for (view_id, action) in matches {
- if self.handle_dispatch_action_from_effect(
- window_id,
- Some(*view_id),
- action.as_ref(),
- ) {
+ if self.handle_dispatch_action_from_effect(Some(*view_id), action.as_ref())
+ {
self.keystroke_matcher.clear_pending();
handled_by = Some(action.boxed_clone());
break;
@@ -289,11 +383,11 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
// -> These are usually small: [Mouse Down] or [Mouse up, Click] or [Mouse Moved, Mouse Dragged?]
// -> Also updates mouse-related state
match &event {
- Event::KeyDown(e) => return self.dispatch_key_down(window_id, e),
+ Event::KeyDown(e) => return self.dispatch_key_down(e),
- Event::KeyUp(e) => return self.dispatch_key_up(window_id, e),
+ Event::KeyUp(e) => return self.dispatch_key_up(e),
- Event::ModifiersChanged(e) => return self.dispatch_modifiers_changed(window_id, e),
+ Event::ModifiersChanged(e) => return self.dispatch_modifiers_changed(e),
Event::MouseDown(e) => {
// Click events are weird because they can be fired after a drag event.
@@ -615,12 +709,10 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
any_event_handled
}
- pub fn dispatch_key_down(&mut self, window_id: usize, event: &KeyDownEvent) -> bool {
+ pub fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
+ let window_id = self.window_id;
if let Some(focused_view_id) = self.window.focused_view_id {
- for view_id in self
- .ancestors(window_id, focused_view_id)
- .collect::<Vec<_>>()
- {
+ for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
let handled = view.key_down(event, self, view_id);
self.views.insert((window_id, view_id), view);
@@ -636,12 +728,10 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
false
}
- pub fn dispatch_key_up(&mut self, window_id: usize, event: &KeyUpEvent) -> bool {
+ pub fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
+ let window_id = self.window_id;
if let Some(focused_view_id) = self.window.focused_view_id {
- for view_id in self
- .ancestors(window_id, focused_view_id)
- .collect::<Vec<_>>()
- {
+ for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
let handled = view.key_up(event, self, view_id);
self.views.insert((window_id, view_id), view);
@@ -657,16 +747,10 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
false
}
- pub fn dispatch_modifiers_changed(
- &mut self,
- window_id: usize,
- event: &ModifiersChangedEvent,
- ) -> bool {
+ pub fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
+ let window_id = self.window_id;
if let Some(focused_view_id) = self.window.focused_view_id {
- for view_id in self
- .ancestors(window_id, focused_view_id)
- .collect::<Vec<_>>()
- {
+ for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
if let Some(mut view) = self.views.remove(&(window_id, view_id)) {
let handled = view.modifiers_changed(event, self, view_id);
self.views.insert((window_id, view_id), view);
@@ -803,10 +887,117 @@ impl<'a: 'b, 'b> WindowContext<'a, 'b> {
self.window.is_fullscreen
}
+ pub(crate) fn handle_dispatch_action_from_effect(
+ &mut self,
+ view_id: Option<usize>,
+ action: &dyn Action,
+ ) -> bool {
+ if let Some(view_id) = view_id {
+ self.halt_action_dispatch = false;
+ self.visit_dispatch_path(view_id, |view_id, capture_phase, cx| {
+ cx.update_any_view(view_id, |view, cx| {
+ let type_id = view.as_any().type_id();
+ if let Some((name, mut handlers)) = cx
+ .actions_mut(capture_phase)
+ .get_mut(&type_id)
+ .and_then(|h| h.remove_entry(&action.id()))
+ {
+ for handler in handlers.iter_mut().rev() {
+ cx.halt_action_dispatch = true;
+ handler(view, action, cx, view_id);
+ if cx.halt_action_dispatch {
+ break;
+ }
+ }
+ cx.actions_mut(capture_phase)
+ .get_mut(&type_id)
+ .unwrap()
+ .insert(name, handlers);
+ }
+ });
+
+ !cx.halt_action_dispatch
+ });
+ }
+
+ if !self.halt_action_dispatch {
+ self.halt_action_dispatch = self.dispatch_global_action_any(action);
+ }
+
+ self.pending_effects
+ .push_back(Effect::ActionDispatchNotification {
+ action_id: action.id(),
+ });
+ self.halt_action_dispatch
+ }
+
+ /// Returns an iterator over all of the view ids from the passed view up to the root of the window
+ /// Includes the passed view itself
+ pub(crate) fn ancestors(&self, mut view_id: usize) -> impl Iterator<Item = usize> + '_ {
+ std::iter::once(view_id)
+ .into_iter()
+ .chain(std::iter::from_fn(move || {
+ if let Some(ParentId::View(parent_id)) =
+ self.parents.get(&(self.window_id, view_id))
+ {
+ view_id = *parent_id;
+ Some(view_id)
+ } else {
+ None
+ }
+ }))
+ }
+
+ /// Returns the id of the parent of the given view, or none if the given
+ /// view is the root.
+ pub(crate) fn parent(&self, view_id: usize) -> Option<usize> {
+ if let Some(ParentId::View(view_id)) = self.parents.get(&(self.window_id, view_id)) {
+ Some(*view_id)
+ } else {
+ None
+ }
+ }
+
+ // Traverses the parent tree. Walks down the tree toward the passed
+ // view calling visit with true. Then walks back up the tree calling visit with false.
+ // If `visit` returns false this function will immediately return.
+ fn visit_dispatch_path(
+ &mut self,
+ view_id: usize,
+ mut visit: impl FnMut(usize, bool, &mut WindowContext) -> bool,
+ ) {
+ // List of view ids from the leaf to the root of the window
+ let path = self.ancestors(view_id).collect::<Vec<_>>();
+
+ // Walk down from the root to the leaf calling visit with capture_phase = true
+ for view_id in path.iter().rev() {
+ if !visit(*view_id, true, self) {
+ return;
+ }
+ }
+
+ // Walk up from the leaf to the root calling visit with capture_phase = false
+ for view_id in path.iter() {
+ if !visit(*view_id, false, self) {
+ return;
+ }
+ }
+ }
+
pub fn focused_view_id(&self) -> Option<usize> {
self.window.focused_view_id
}
+ pub fn is_child_focused(&self, view: &AnyViewHandle) -> bool {
+ if let Some(focused_view_id) = self.focused_view_id() {
+ self.ancestors(focused_view_id)
+ .skip(1) // Skip self id
+ .any(|parent| parent == view.view_id)
+ } else {
+ false
+ }
+ }
+
pub fn window_bounds(&self) -> WindowBounds {
self.window.platform_window.bounds()
}
@@ -45,7 +45,7 @@ impl<V: View> Drawable<V> for KeystrokeLabel {
cx: &mut ViewContext<V>,
) -> (Vector2F, Element<V>) {
let mut element = if let Some(keystrokes) =
- cx.keystrokes_for_action(self.window_id, self.view_id, self.action.as_ref())
+ cx.keystrokes_for_action(self.view_id, self.action.as_ref())
{
Flex::row()
.with_children(keystrokes.iter().map(|keystroke| {
@@ -80,9 +80,25 @@ mod tests {
watch_files, watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap,
};
use fs::FakeFs;
- use gpui::{actions, Action};
+ use gpui::{actions, elements::*, Action, Entity, View, ViewContext, WindowContext};
use theme::ThemeRegistry;
+ struct TestView;
+
+ impl Entity for TestView {
+ type Event = ();
+ }
+
+ impl View for TestView {
+ fn ui_name() -> &'static str {
+ "TestView"
+ }
+
+ fn render(&mut self, _: &mut ViewContext<Self>) -> Element<Self> {
+ Empty::new().boxed()
+ }
+ }
+
#[gpui::test]
async fn test_base_keymap(cx: &mut gpui::TestAppContext) {
let executor = cx.background();
@@ -148,8 +164,10 @@ mod tests {
cx.foreground().run_until_parked();
+ let (window_id, _view) = cx.add_window(|_| TestView);
+
// Test loading the keymap base at all
- cx.update(|cx| {
+ cx.read_window(window_id, |cx| {
assert_key_bindings_for(
cx,
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
@@ -177,7 +195,7 @@ mod tests {
cx.foreground().run_until_parked();
- cx.update(|cx| {
+ cx.read_window(window_id, |cx| {
assert_key_bindings_for(
cx,
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
@@ -201,7 +219,7 @@ mod tests {
cx.foreground().run_until_parked();
- cx.update(|cx| {
+ cx.read_window(window_id, |cx| {
assert_key_bindings_for(
cx,
vec![("backspace", &B), ("[", &ActivatePrevItem)],
@@ -211,14 +229,14 @@ mod tests {
}
fn assert_key_bindings_for<'a>(
- cx: &mut AppContext,
+ cx: &WindowContext,
actions: Vec<(&'static str, &'a dyn Action)>,
line: u32,
) {
for (key, action) in actions {
// assert that...
assert!(
- cx.available_actions(0, 0).any(|(_, bound_action, b)| {
+ cx.available_actions(0).any(|(_, bound_action, b)| {
// action names match...
bound_action.name() == action.name()
&& bound_action.namespace() == action.namespace()
@@ -1,7 +1,7 @@
use crate::StatusItemView;
use gpui::{
elements::*, impl_actions, platform::CursorStyle, platform::MouseButton, AnyViewHandle,
- AppContext, Entity, Subscription, View, ViewContext, ViewHandle,
+ AppContext, Entity, Subscription, View, ViewContext, ViewHandle, WindowContext,
};
use serde::Deserialize;
use settings::Settings;
@@ -21,8 +21,8 @@ pub trait SidebarItem: View {
pub trait SidebarItemHandle {
fn id(&self) -> usize;
- fn should_show_badge(&self, cx: &AppContext) -> bool;
- fn is_focused(&self, cx: &AppContext) -> bool;
+ fn should_show_badge(&self, cx: &WindowContext) -> bool;
+ fn is_focused(&self, cx: &WindowContext) -> bool;
fn as_any(&self) -> &AnyViewHandle;
}
@@ -34,11 +34,11 @@ where
self.id()
}
- fn should_show_badge(&self, cx: &AppContext) -> bool {
+ fn should_show_badge(&self, cx: &WindowContext) -> bool {
self.read(cx).should_show_badge(cx)
}
- fn is_focused(&self, cx: &AppContext) -> bool {
+ fn is_focused(&self, cx: &WindowContext) -> bool {
ViewHandle::is_focused(self, cx) || self.read(cx).contains_focused_view(cx)
}
@@ -747,12 +747,9 @@ mod tests {
})
.await;
assert_eq!(cx.window_ids().len(), 2);
- let workspace_1 = cx
- .read_window(window_id, |cx| cx.root_view().clone())
- .unwrap()
- .downcast::<Workspace>()
- .unwrap();
- workspace_1.read_with(cx, |workspace, cx| {
+ cx.read_window(window_id, |cx| {
+ let workspace = cx.root_view().clone().downcast::<Workspace>().unwrap();
+ let workspace = workspace.read(cx);
assert_eq!(
workspace
.worktrees(cx)