@@ -83,14 +83,15 @@ pub trait View: Entity + Sized {
false
}
- fn keymap_context(&self, _: &AppContext) -> keymap_matcher::KeymapContext {
- Self::default_keymap_context()
+ fn update_keymap_context(&self, keymap: &mut keymap_matcher::KeymapContext, _: &AppContext) {
+ Self::reset_to_default_keymap_context(keymap);
}
- fn default_keymap_context() -> keymap_matcher::KeymapContext {
- let mut cx = keymap_matcher::KeymapContext::default();
- cx.add_identifier(Self::ui_name());
- cx
+
+ fn reset_to_default_keymap_context(keymap: &mut keymap_matcher::KeymapContext) {
+ keymap.clear();
+ keymap.add_identifier(Self::ui_name());
}
+
fn debug_json(&self, _: &AppContext) -> serde_json::Value {
serde_json::Value::Null
}
@@ -440,6 +441,7 @@ type WindowShouldCloseSubscriptionCallback = Box<dyn FnMut(&mut AppContext) -> b
pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
+ views_metadata: HashMap<(usize, usize), ViewMetadata>,
pub(crate) parents: HashMap<(usize, usize), ParentId>,
windows: HashMap<usize, Window>,
globals: HashMap<TypeId, Box<dyn Any>>,
@@ -502,6 +504,7 @@ impl AppContext {
Self {
models: Default::default(),
views: Default::default(),
+ views_metadata: Default::default(),
parents: Default::default(),
windows: Default::default(),
globals: Default::default(),
@@ -727,9 +730,9 @@ impl AppContext {
}
pub fn view_type_id(&self, window_id: usize, view_id: usize) -> Option<TypeId> {
- self.views
+ self.views_metadata
.get(&(window_id, view_id))
- .map(|view| view.as_any().type_id())
+ .map(|metadata| metadata.type_id)
}
pub fn active_labeled_tasks<'a>(
@@ -1045,9 +1048,10 @@ impl AppContext {
.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 let Some(view_metadata) =
+ cx.views_metadata.get(&(window_id, view_id))
+ {
+ if let Some(actions) = cx.actions.get(&view_metadata.type_id) {
if actions.contains_key(&action_type) {
return true;
}
@@ -1448,6 +1452,7 @@ impl AppContext {
for (window_id, view_id) in dropped_views {
self.subscriptions.remove(view_id);
self.observations.remove(view_id);
+ self.views_metadata.remove(&(window_id, view_id));
let mut view = self.views.remove(&(window_id, view_id)).unwrap();
view.release(self);
let change_focus_to = self.windows.get_mut(&window_id).and_then(|window| {
@@ -1779,9 +1784,11 @@ impl AppContext {
observed_window_id: usize,
observed_view_id: usize,
) {
- if self
+ let view_key = (observed_window_id, observed_view_id);
+ if let Some((view, mut view_metadata)) = self
.views
- .contains_key(&(observed_window_id, observed_view_id))
+ .remove(&view_key)
+ .zip(self.views_metadata.remove(&view_key))
{
if let Some(window) = self.windows.get_mut(&observed_window_id) {
window
@@ -1791,6 +1798,10 @@ impl AppContext {
.insert(observed_view_id);
}
+ view.update_keymap_context(&mut view_metadata.keymap_context, self);
+ self.views.insert(view_key, view);
+ self.views_metadata.insert(view_key, view_metadata);
+
let mut observations = self.observations.clone();
observations.emit(observed_view_id, |callback| callback(self));
}
@@ -2037,6 +2048,11 @@ pub enum ParentId {
Root,
}
+struct ViewMetadata {
+ type_id: TypeId,
+ keymap_context: KeymapContext,
+}
+
#[derive(Default, Clone)]
pub struct WindowInvalidation {
pub updated: HashSet<usize>,
@@ -2365,7 +2381,7 @@ pub trait AnyView {
cx: &mut WindowContext,
view_id: usize,
) -> bool;
- fn keymap_context(&self, cx: &AppContext) -> KeymapContext;
+ fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext);
fn debug_json(&self, cx: &WindowContext) -> serde_json::Value;
fn text_for_range(&self, range: Range<usize>, cx: &WindowContext) -> Option<String>;
@@ -2437,11 +2453,10 @@ where
cx.handle().into_any()
} else {
let focused_type = cx
- .views
+ .views_metadata
.get(&(cx.window_id, focused_id))
.unwrap()
- .as_any()
- .type_id();
+ .type_id;
AnyViewHandle::new(
cx.window_id,
focused_id,
@@ -2458,11 +2473,10 @@ where
cx.handle().into_any()
} else {
let blurred_type = cx
- .views
+ .views_metadata
.get(&(cx.window_id, blurred_id))
.unwrap()
- .as_any()
- .type_id();
+ .type_id;
AnyViewHandle::new(
cx.window_id,
blurred_id,
@@ -2493,8 +2507,8 @@ where
View::modifiers_changed(self, event, &mut cx)
}
- fn keymap_context(&self, cx: &AppContext) -> KeymapContext {
- View::keymap_context(self, cx)
+ fn update_keymap_context(&self, keymap: &mut KeymapContext, cx: &AppContext) {
+ View::update_keymap_context(self, keymap, cx)
}
fn debug_json(&self, cx: &WindowContext) -> serde_json::Value {
@@ -5559,8 +5573,8 @@ mod tests {
"View"
}
- fn keymap_context(&self, _: &AppContext) -> KeymapContext {
- self.keymap_context.clone()
+ fn update_keymap_context(&self, keymap: &mut KeymapContext, _: &AppContext) {
+ *keymap = self.keymap_context.clone();
}
}
@@ -5664,7 +5678,7 @@ mod tests {
}
#[crate::test(self)]
- fn test_keystrokes_for_action(cx: &mut AppContext) {
+ fn test_keystrokes_for_action(cx: &mut TestAppContext) {
actions!(test, [Action1, Action2, GlobalAction]);
struct View1 {}
@@ -5694,70 +5708,76 @@ mod tests {
}
}
- let (window_id, view_1) = cx.add_window(Default::default(), |_| View1 {});
+ let (_, view_1) = cx.add_window(|_| View1 {});
let view_2 = cx.add_view(&view_1, |cx| {
cx.focus_self();
View2 {}
});
- cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
- cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
- cx.add_global_action(|_: &GlobalAction, _| {});
-
- cx.add_bindings(vec![
- Binding::new("a", Action1, Some("View1")),
- Binding::new("b", Action2, Some("View1 > View2")),
- Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
- ]);
+ cx.update(|cx| {
+ cx.add_action(|_: &mut View1, _: &Action1, _cx| {});
+ cx.add_action(|_: &mut View2, _: &Action2, _cx| {});
+ cx.add_global_action(|_: &GlobalAction, _| {});
+
+ cx.add_bindings(vec![
+ Binding::new("a", Action1, Some("View1")),
+ Binding::new("b", Action2, Some("View1 > View2")),
+ Binding::new("c", GlobalAction, Some("View3")), // View 3 does not exist
+ ]);
+ });
- 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()]
- );
+ // Here we update the views to ensure that, even if they are on the stack,
+ // we can still retrieve keystrokes correctly.
+ view_1.update(cx, |_, cx| {
+ view_2.update(cx, |_, 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(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(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(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 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![]),
- ],
- );
+ // 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
@@ -2,7 +2,7 @@ use crate::{
elements::AnyRootElement,
geometry::rect::RectF,
json::ToJson,
- keymap_matcher::{Binding, Keystroke, MatchResult},
+ keymap_matcher::{Binding, KeymapContext, Keystroke, MatchResult},
platform::{
self, Appearance, CursorStyle, Event, KeyDownEvent, KeyUpEvent, ModifiersChangedEvent,
MouseButton, MouseMovedEvent, PromptLevel, WindowBounds,
@@ -34,7 +34,7 @@ use std::{
use util::ResultExt;
use uuid::Uuid;
-use super::Reference;
+use super::{Reference, ViewMetadata};
pub struct Window {
pub(crate) root_view: Option<AnyViewHandle>,
@@ -369,13 +369,13 @@ impl<'a> WindowContext<'a> {
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 let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
+ if let Some(actions) = self.actions.get(&view_metadata.type_id) {
if actions.contains_key(&action.as_any().type_id()) {
handler_depth = Some(i);
}
}
- contexts.push(view.keymap_context(self));
+ contexts.push(view_metadata.keymap_context.clone());
}
}
@@ -406,10 +406,9 @@ impl<'a> WindowContext<'a> {
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) {
+ if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
+ contexts.push(view_metadata.keymap_context.clone());
+ if let Some(actions) = self.actions.get(&view_metadata.type_id) {
handler_depths_by_action_type.extend(
actions
.keys()
@@ -458,9 +457,9 @@ impl<'a> WindowContext<'a> {
let dispatch_path = self
.ancestors(focused_view_id)
.filter_map(|view_id| {
- self.views
+ self.views_metadata
.get(&(window_id, view_id))
- .map(|view| (view_id, view.keymap_context(self)))
+ .map(|view| (view_id, view.keymap_context.clone()))
})
.collect();
@@ -1177,6 +1176,15 @@ impl<'a> WindowContext<'a> {
self.parents.insert((window_id, view_id), parent_id);
let mut cx = ViewContext::mutable(self, view_id);
let handle = if let Some(view) = build_view(&mut cx) {
+ let mut keymap_context = KeymapContext::default();
+ view.update_keymap_context(&mut keymap_context, cx.app_context());
+ self.views_metadata.insert(
+ (window_id, view_id),
+ ViewMetadata {
+ type_id: TypeId::of::<T>(),
+ keymap_context,
+ },
+ );
self.views.insert((window_id, view_id), Box::new(view));
self.window
.invalidation