From 4271eb36241b97aee562df9f1d3da44cfed5755c Mon Sep 17 00:00:00 2001 From: K Simmons Date: Thu, 4 Aug 2022 20:55:10 -0700 Subject: [PATCH 1/9] Event dispatch moved to MutableAppContext. No longer dispatches from presenter. Not currently handling key presses properly --- crates/collab/src/integration_tests.rs | 29 +- crates/command_palette/src/command_palette.rs | 10 +- crates/contacts_panel/src/contacts_panel.rs | 4 +- crates/context_menu/src/context_menu.rs | 4 +- crates/diagnostics/src/diagnostics.rs | 4 +- crates/editor/src/editor.rs | 4 +- crates/editor/src/items.rs | 4 +- crates/gpui/src/app.rs | 521 ++++++++++-------- crates/gpui/src/presenter.rs | 64 +-- crates/gpui/src/test.rs | 20 +- crates/search/src/buffer_search.rs | 8 +- crates/search/src/project_search.rs | 7 +- crates/workspace/src/pane.rs | 4 +- crates/workspace/src/workspace.rs | 45 +- 14 files changed, 392 insertions(+), 336 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 2a68c2056e..4118e5963c 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -18,6 +18,7 @@ use futures::{channel::mpsc, Future, StreamExt as _}; use gpui::{ executor::{self, Deterministic}, geometry::vector::vec2f, + test::EmptyView, ModelHandle, Task, TestAppContext, ViewHandle, }; use language::{ @@ -67,7 +68,7 @@ async fn test_share_project( cx_b2: &mut TestAppContext, ) { cx_a.foreground().forbid_parking(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); + let (_, window_b) = cx_b.add_window(|_| EmptyView); let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await; let client_a = server.create_client(cx_a, "user_a").await; let client_b = server.create_client(cx_b, "user_b").await; @@ -145,7 +146,7 @@ async fn test_share_project( .await .unwrap(); - let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx)); + let editor_b = cx_b.add_view(&window_b, |cx| Editor::for_buffer(buffer_b, None, cx)); // TODO // // Create a selection set as client B and see that selection set as client A. @@ -1736,8 +1737,8 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu .update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx)) .await .unwrap(); - let (window_b, _) = cx_b.add_window(|_| EmptyView); - let editor_b = cx_b.add_view(window_b, |cx| { + let (_, window_b) = cx_b.add_window(|_| EmptyView); + let editor_b = cx_b.add_view(&window_b, |cx| { Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx) }); @@ -5387,8 +5388,8 @@ impl TestClient { project: &ModelHandle, cx: &mut TestAppContext, ) -> ViewHandle { - let (window_id, _) = cx.add_window(|_| EmptyView); - cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx)) + let (_, root_view) = cx.add_window(|_| EmptyView); + cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx)) } async fn simulate_host( @@ -5901,19 +5902,3 @@ fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> { }) .collect() } - -struct EmptyView; - -impl gpui::Entity for EmptyView { - type Event = (); -} - -impl gpui::View for EmptyView { - fn ui_name() -> &'static str { - "empty view" - } - - fn render(&mut self, _: &mut gpui::RenderContext) -> gpui::ElementBox { - gpui::Element::boxed(gpui::elements::Empty::new()) - } -} diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 5f438057ee..5f213284e8 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -85,8 +85,8 @@ impl CommandPalette { let focused_view_id = cx.focused_view_id(window_id).unwrap_or(workspace.id()); cx.as_mut().defer(move |cx| { - let this = cx.add_view(window_id, |cx| Self::new(focused_view_id, cx)); workspace.update(cx, |workspace, cx| { + let this = cx.add_view(|cx| Self::new(focused_view_id, cx)); workspace.toggle_modal(cx, |_, cx| { cx.subscribe(&this, Self::on_event).detach(); this @@ -110,10 +110,10 @@ impl CommandPalette { } => { let window_id = *window_id; let focused_view_id = *focused_view_id; - let action = (*action).boxed_clone(); + let action = action.boxed_clone(); workspace.dismiss_modal(cx); cx.as_mut() - .defer(move |cx| cx.dispatch_action_at(window_id, focused_view_id, &*action)) + .defer(move |cx| cx.dispatch_any_action_at(window_id, focused_view_id, action)) } } } @@ -345,8 +345,8 @@ mod tests { }); let project = Project::test(app_state.fs.clone(), [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); - let editor = cx.add_view(window_id, |cx| { + let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); + let editor = cx.add_view(&workspace, |cx| { let mut editor = Editor::single_line(None, cx); editor.set_text("abc", cx); editor diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index aef9037879..4aff5b1a74 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -1248,8 +1248,8 @@ mod tests { .0 .read_with(cx, |worktree, _| worktree.id().to_proto()); - let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx)); - let panel = cx.add_view(0, |cx| { + let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); + let panel = cx.add_view(&workspace, |cx| { ContactsPanel::new( user_store.clone(), project_store.clone(), diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index d49f817de6..b17718577f 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -156,9 +156,7 @@ impl ContextMenu { fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext) { if let Some(ix) = self.selected_index { if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) { - let window_id = cx.window_id(); - let view_id = cx.view_id(); - cx.dispatch_action_at(window_id, view_id, action.as_ref()); + cx.dispatch_any_action(action.boxed_clone()); self.reset(cx); } } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 8d5746f8fb..e9692c5493 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -786,7 +786,7 @@ mod tests { .await; let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await; - let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx)); + let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); // Create some diagnostics project.update(cx, |project, cx| { @@ -873,7 +873,7 @@ mod tests { }); // Open the project diagnostics view while there are already diagnostics. - let view = cx.add_view(0, |cx| { + let view = cx.add_view(&workspace, |cx| { ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx) }); diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7c2d560a41..e4ff272440 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -7099,10 +7099,10 @@ mod tests { fn test_navigation_history(cx: &mut gpui::MutableAppContext) { cx.set_global(Settings::test(cx)); use workspace::Item; - let pane = cx.add_view(Default::default(), |cx| Pane::new(cx)); + let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(cx)); let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx); - cx.add_window(Default::default(), |cx| { + cx.add_view(&pane, |cx| { let mut editor = build_editor(buffer.clone(), cx); let handle = cx.handle(); editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle))); diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 5bb8d4d0b2..9837c5070f 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -54,8 +54,8 @@ impl FollowableItem for Editor { }) }) .unwrap_or_else(|| { - cx.add_view(pane.window_id(), |cx| { - Editor::for_buffer(buffer, Some(project), cx) + pane.update(&mut cx, |_, cx| { + cx.add_view(|cx| Editor::for_buffer(buffer, Some(project), cx)) }) }); editor.update(&mut cx, |editor, cx| { diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index cc4c1191f6..a1b782039a 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -229,18 +229,12 @@ impl App { move |action| { let mut cx = cx.borrow_mut(); if let Some(key_window_id) = cx.cx.platform.key_window_id() { - if let Some((presenter, _)) = - cx.presenters_and_platform_windows.get(&key_window_id) - { - let presenter = presenter.clone(); - let path = presenter.borrow().dispatch_path(cx.as_ref()); - cx.dispatch_action_any(key_window_id, &path, action); - } else { - cx.dispatch_global_action_any(action); + if let Some(view_id) = cx.focused_view_id(key_window_id) { + cx.handle_dispatch_action_any_effect(key_window_id, Some(view_id), action); + return; } - } else { - cx.dispatch_global_action_any(action); } + cx.dispatch_global_action_any(action); } })); @@ -462,15 +456,9 @@ impl TestAppContext { pub fn dispatch_action(&self, window_id: usize, action: A) { let mut cx = self.cx.borrow_mut(); - let dispatch_path = cx - .presenters_and_platform_windows - .get(&window_id) - .unwrap() - .0 - .borrow() - .dispatch_path(cx.as_ref()); - - cx.dispatch_action_any(window_id, &dispatch_path, &action); + if let Some(view_id) = cx.focused_view_id(window_id) { + cx.handle_dispatch_action_any_effect(window_id, Some(view_id), &action); + } } pub fn dispatch_global_action(&self, action: A) { @@ -485,9 +473,8 @@ impl TestAppContext { .unwrap() .0 .clone(); - let dispatch_path = presenter.borrow().dispatch_path(cx.as_ref()); - if cx.dispatch_keystroke(window_id, dispatch_path, &keystroke) { + if cx.dispatch_keystroke(window_id, &keystroke) { return true; } if presenter.borrow_mut().dispatch_event( @@ -533,6 +520,18 @@ impl TestAppContext { (window_id, view) } + pub fn add_view( + &mut self, + parent_handle: impl Into, + build_view: F, + ) -> ViewHandle + where + T: View, + F: FnOnce(&mut ViewContext) -> T, + { + self.cx.borrow_mut().add_view(parent_handle, build_view) + } + pub fn window_ids(&self) -> Vec { self.cx.borrow().window_ids().collect() } @@ -541,26 +540,6 @@ impl TestAppContext { self.cx.borrow().root_view(window_id) } - pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle - where - T: View, - F: FnOnce(&mut ViewContext) -> T, - { - self.cx.borrow_mut().add_view(window_id, build_view) - } - - pub fn add_option_view( - &mut self, - window_id: usize, - build_view: F, - ) -> Option> - where - T: View, - F: FnOnce(&mut ViewContext) -> Option, - { - self.cx.borrow_mut().add_option_view(window_id, build_view) - } - pub fn read T>(&self, callback: F) -> T { callback(self.cx.borrow().as_ref()) } @@ -786,14 +765,6 @@ impl AsyncAppContext { self.update(|cx| cx.add_model(build_model)) } - pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle - where - T: View, - F: FnOnce(&mut ViewContext) -> T, - { - self.update(|cx| cx.add_view(window_id, build_view)) - } - pub fn add_window( &mut self, window_options: WindowOptions, @@ -1021,6 +992,7 @@ impl MutableAppContext { cx: AppContext { models: Default::default(), views: Default::default(), + parents: Default::default(), windows: Default::default(), globals: Default::default(), element_states: Default::default(), @@ -1645,17 +1617,7 @@ impl MutableAppContext { ) -> impl Iterator, SmallVec<[&Binding; 1]>)> { let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect(); - let presenter = self - .presenters_and_platform_windows - .get(&window_id) - .unwrap() - .0 - .clone(); - let mut dispatch_path = Vec::new(); - presenter - .borrow() - .compute_dispatch_path_from(view_id, &mut dispatch_path); - for view_id in dispatch_path { + for view_id in self.parents(window_id, 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) { @@ -1684,9 +1646,8 @@ impl MutableAppContext { pub fn is_action_available(&self, action: &dyn Action) -> bool { let action_type = action.as_any().type_id(); if let Some(window_id) = self.cx.platform.key_window_id() { - if let Some((presenter, _)) = self.presenters_and_platform_windows.get(&window_id) { - let dispatch_path = presenter.borrow().dispatch_path(&self.cx); - for view_id in dispatch_path { + if let Some(focused_view_id) = self.focused_view_id(window_id) { + for view_id in self.parents(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) { @@ -1724,83 +1685,76 @@ impl MutableAppContext { None } - pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) { - let presenter = self - .presenters_and_platform_windows - .get(&window_id) - .unwrap() - .0 - .clone(); - let mut dispatch_path = Vec::new(); - presenter - .borrow() - .compute_dispatch_path_from(view_id, &mut dispatch_path); - self.dispatch_action_any(window_id, &dispatch_path, action); - } + // pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) { + // let presenter = self + // .presenters_and_platform_windows + // .get(&window_id) + // .unwrap() + // .0 + // .clone(); + // let mut dispatch_path = Vec::new(); + // presenter + // .borrow() + // .compute_dispatch_path_from(view_id, &mut dispatch_path); + // self.dispatch_action_any(window_id, &dispatch_path, action); + // } - pub fn dispatch_action( + // pub fn dispatch_action( + // &mut self, + // window_id: usize, + // dispatch_path: Vec, + // action: &A, + // ) { + // self.dispatch_action_any(window_id, &dispatch_path, action); + // } + + // 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, - dispatch_path: Vec, - action: &A, - ) { - self.dispatch_action_any(window_id, &dispatch_path, action); - } - - pub(crate) fn dispatch_action_any( - &mut self, - window_id: usize, - path: &[usize], - action: &dyn Action, + view_id: usize, + mut visit: impl FnMut(usize, bool, &mut MutableAppContext) -> bool, ) -> bool { - self.update(|this| { - this.halt_action_dispatch = false; - for (capture_phase, view_id) in path - .iter() - .map(|view_id| (true, *view_id)) - .chain(path.iter().rev().map(|view_id| (false, *view_id))) - { - if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { - let type_id = view.as_any().type_id(); + // List of view ids from the leaf to the root of the window + let mut path = vec![view_id]; + let mut current_view = view_id; + while let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, current_view)) { + current_view = *parent_id; + path.push(current_view); + } - if let Some((name, mut handlers)) = this - .actions_mut(capture_phase) - .get_mut(&type_id) - .and_then(|h| h.remove_entry(&action.id())) - { - for handler in handlers.iter_mut().rev() { - this.halt_action_dispatch = true; - handler(view.as_mut(), action, this, window_id, view_id); - if this.halt_action_dispatch { - break; - } - } - this.actions_mut(capture_phase) - .get_mut(&type_id) - .unwrap() - .insert(name, handlers); - } - - this.cx.views.insert((window_id, view_id), view); - - if this.halt_action_dispatch { - break; - } - } + // 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; } + } - if !this.halt_action_dispatch { - this.halt_action_dispatch = this.dispatch_global_action_any(action); + // 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; } + } - this.pending_effects - .push_back(Effect::ActionDispatchNotification { - action_id: action.id(), - }); - this.halt_action_dispatch + true + } + + // 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 parents(&self, window_id: usize, mut view_id: usize) -> impl Iterator + '_ { + 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 + } }) } - fn actions_mut( &mut self, capture_phase: bool, @@ -1836,34 +1790,34 @@ impl MutableAppContext { self.keystroke_matcher.clear_bindings(); } - pub fn dispatch_keystroke( - &mut self, - window_id: usize, - dispatch_path: Vec, - keystroke: &Keystroke, - ) -> bool { - let mut context_chain = Vec::new(); - for view_id in &dispatch_path { - let view = self - .cx - .views - .get(&(window_id, *view_id)) - .expect("view in responder chain does not exist"); - context_chain.push(view.keymap_context(self.as_ref())); - } - + pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool { let mut pending = false; - for (i, cx) in context_chain.iter().enumerate().rev() { - match self - .keystroke_matcher - .push_keystroke(keystroke.clone(), dispatch_path[i], cx) - { - MatchResult::None => {} - MatchResult::Pending => pending = true, - MatchResult::Action(action) => { - if self.dispatch_action_any(window_id, &dispatch_path[0..=i], action.as_ref()) { - self.keystroke_matcher.clear_pending(); - return true; + + if let Some(view_id) = self.focused_view_id(window_id) { + for view_id in self.parents(window_id, view_id).collect::>() { + let keymap_context = self + .cx + .views + .get(&(window_id, view_id)) + .expect("View passed to visit does not exist") + .keymap_context(self.as_ref()); + + match self.keystroke_matcher.push_keystroke( + keystroke.clone(), + view_id, + &keymap_context, + ) { + MatchResult::None => {} + MatchResult::Pending => pending = true, + MatchResult::Action(action) => { + if self.handle_dispatch_action_any_effect( + window_id, + Some(view_id), + action.as_ref(), + ) { + self.keystroke_matcher.clear_pending(); + return true; + } } } } @@ -1917,15 +1871,14 @@ impl MutableAppContext { { self.update(|this| { let type_id = TypeId::of::(); - let mut state = this - .cx - .globals - .remove(&type_id) - .expect("no global has been added for this type"); - let result = update(state.downcast_mut().unwrap(), this); - this.cx.globals.insert(type_id, state); - this.notify_global(type_id); - result + if let Some(mut state) = this.cx.globals.remove(&type_id) { + let result = update(state.downcast_mut().unwrap(), this); + this.cx.globals.insert(type_id, state); + this.notify_global(type_id); + result + } else { + panic!("No global added for {}", std::any::type_name::()); + } }) } @@ -1955,7 +1908,9 @@ impl MutableAppContext { { self.update(|this| { let window_id = post_inc(&mut this.next_window_id); - let root_view = this.add_view(window_id, build_root_view); + let root_view = this + .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) + .unwrap(); this.cx.windows.insert( window_id, Window { @@ -1979,7 +1934,9 @@ impl MutableAppContext { F: FnOnce(&mut ViewContext) -> T, { self.update(|this| { - let root_view = this.add_view(window_id, build_root_view); + let root_view = this + .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) + .unwrap(); let window = this.cx.windows.get_mut(&window_id).unwrap(); window.root_view = root_view.clone().into(); window.focused_view_id = Some(root_view.id()); @@ -2009,11 +1966,7 @@ impl MutableAppContext { app.update(|cx| { if let Some(presenter) = presenter.upgrade() { if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event { - if cx.dispatch_keystroke( - window_id, - presenter.borrow().dispatch_path(cx.as_ref()), - keystroke, - ) { + if cx.dispatch_keystroke(window_id, keystroke) { return true; } } @@ -2079,18 +2032,45 @@ impl MutableAppContext { ) } - pub fn add_view(&mut self, window_id: usize, build_view: F) -> ViewHandle + pub fn add_view( + &mut self, + parent_handle: impl Into, + build_view: F, + ) -> ViewHandle where T: View, F: FnOnce(&mut ViewContext) -> T, { - self.add_option_view(window_id, |cx| Some(build_view(cx))) - .unwrap() + let parent_handle = parent_handle.into(); + self.build_and_insert_view( + parent_handle.window_id, + ParentId::View(parent_handle.view_id), + |cx| Some(build_view(cx)), + ) + .unwrap() } pub fn add_option_view( + &mut self, + parent_handle: impl Into, + build_view: F, + ) -> Option> + where + T: View, + F: FnOnce(&mut ViewContext) -> Option, + { + let parent_handle = parent_handle.into(); + self.build_and_insert_view( + parent_handle.window_id, + ParentId::View(parent_handle.view_id), + build_view, + ) + } + + pub(crate) fn build_and_insert_view( &mut self, window_id: usize, + parent_id: ParentId, build_view: F, ) -> Option> where @@ -2102,6 +2082,7 @@ impl MutableAppContext { let mut cx = ViewContext::new(this, window_id, view_id); let handle = if let Some(view) = build_view(&mut cx) { this.cx.views.insert((window_id, view_id), Box::new(view)); + this.cx.parents.insert((window_id, view_id), parent_id); if let Some(window) = this.cx.windows.get_mut(&window_id) { window .invalidation @@ -2154,6 +2135,7 @@ impl MutableAppContext { None } }); + self.cx.parents.remove(&(window_id, view_id)); if let Some(view_id) = change_focus_to { self.handle_focus_effect(window_id, Some(view_id)); @@ -2316,6 +2298,17 @@ impl MutableAppContext { Effect::RefreshWindows => { refreshing = true; } + Effect::DispatchActionFrom { + window_id, + view_id, + action, + } => { + self.handle_dispatch_action_any_effect( + window_id, + Some(view_id), + action.as_ref(), + ); + } Effect::ActionDispatchNotification { action_id } => { self.handle_action_dispatch_notification_effect(action_id) } @@ -2403,6 +2396,23 @@ impl MutableAppContext { self.pending_effects.push_back(Effect::RefreshWindows); } + pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: impl Action) { + self.dispatch_any_action_at(window_id, view_id, Box::new(action)); + } + + pub fn dispatch_any_action_at( + &mut self, + window_id: usize, + view_id: usize, + action: Box, + ) { + self.pending_effects.push_back(Effect::DispatchActionFrom { + window_id, + view_id, + action, + }); + } + fn perform_window_refresh(&mut self) { let mut presenters = mem::take(&mut self.presenters_and_platform_windows); for (window_id, (presenter, window)) in &mut presenters { @@ -2569,6 +2579,55 @@ impl MutableAppContext { }) } + fn handle_dispatch_action_any_effect( + &mut self, + window_id: usize, + view_id: Option, + action: &dyn Action, + ) -> bool { + self.update(|this| { + if let Some(view_id) = view_id { + this.visit_dispatch_path(window_id, view_id, |view_id, capture_phase, this| { + if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { + let type_id = view.as_any().type_id(); + + if let Some((name, mut handlers)) = this + .actions_mut(capture_phase) + .get_mut(&type_id) + .and_then(|h| h.remove_entry(&action.id())) + { + for handler in handlers.iter_mut().rev() { + this.halt_action_dispatch = true; + handler(view.as_mut(), action, this, window_id, view_id); + if this.halt_action_dispatch { + break; + } + } + this.actions_mut(capture_phase) + .get_mut(&type_id) + .unwrap() + .insert(name, handlers); + } + + this.cx.views.insert((window_id, view_id), view); + } + + !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 + }) + } + fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) { let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock()); for (_, callback) in &mut callbacks { @@ -2750,9 +2809,15 @@ impl Deref for MutableAppContext { } } +pub enum ParentId { + View(usize), + Root, +} + pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, + parents: HashMap<(usize, usize), ParentId>, windows: HashMap, globals: HashMap>, element_states: HashMap>, @@ -2977,6 +3042,11 @@ pub enum Effect { callback: WindowFullscreenCallback, }, RefreshWindows, + DispatchActionFrom { + window_id: usize, + view_id: usize, + action: Box, + }, ActionDispatchNotification { action_id: TypeId, }, @@ -3060,6 +3130,13 @@ impl Debug for Effect { .field("view_id", view_id) .field("subscription_id", subscription_id) .finish(), + Effect::DispatchActionFrom { + window_id, view_id, .. + } => f + .debug_struct("Effect::DispatchActionFrom") + .field("window_id", window_id) + .field("view_id", view_id) + .finish(), Effect::ActionDispatchNotification { action_id, .. } => f .debug_struct("Effect::ActionDispatchNotification") .field("action_id", action_id) @@ -3640,7 +3717,11 @@ impl<'a, T: View> ViewContext<'a, T> { S: View, F: FnOnce(&mut ViewContext) -> S, { - self.app.add_view(self.window_id, build_view) + self.app + .build_and_insert_view(self.window_id, ParentId::View(self.view_id), |cx| { + Some(build_view(cx)) + }) + .unwrap() } pub fn add_option_view(&mut self, build_view: F) -> Option> @@ -3648,7 +3729,8 @@ impl<'a, T: View> ViewContext<'a, T> { S: View, F: FnOnce(&mut ViewContext) -> Option, { - self.app.add_option_view(self.window_id, build_view) + self.app + .build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view) } pub fn replace_root_view(&mut self, build_root_view: F) -> ViewHandle @@ -3658,7 +3740,9 @@ impl<'a, T: View> ViewContext<'a, T> { { let window_id = self.window_id; self.update(|this| { - let root_view = this.add_view(window_id, build_root_view); + let root_view = this + .build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx))) + .unwrap(); let window = this.cx.windows.get_mut(&window_id).unwrap(); window.root_view = root_view.clone().into(); window.focused_view_id = Some(root_view.id()); @@ -3802,6 +3886,11 @@ impl<'a, T: View> ViewContext<'a, T> { self.app.notify_view(self.window_id, self.view_id); } + pub fn dispatch_any_action(&mut self, action: Box) { + self.app + .dispatch_any_action_at(self.window_id, self.view_id, action) + } + pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext)) { let handle = self.handle(); self.app.defer(move |cx| { @@ -5797,9 +5886,9 @@ mod tests { } } - let (window_id, _) = cx.add_window(Default::default(), |cx| View::new(None, cx)); - let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx)); - let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx)); + let (_, root_view) = cx.add_window(Default::default(), |cx| View::new(None, cx)); + let handle_1 = cx.add_view(&root_view, |cx| View::new(None, cx)); + let handle_2 = cx.add_view(&root_view, |cx| View::new(Some(handle_1.clone()), cx)); assert_eq!(cx.cx.views.len(), 3); handle_1.update(cx, |view, cx| { @@ -5973,8 +6062,8 @@ mod tests { type Event = usize; } - let (window_id, handle_1) = cx.add_window(Default::default(), |_| View::default()); - let handle_2 = cx.add_view(window_id, |_| View::default()); + let (_, handle_1) = cx.add_window(Default::default(), |_| View::default()); + let handle_2 = cx.add_view(&handle_1, |_| View::default()); let handle_3 = cx.add_model(|_| Model); handle_1.update(cx, |_, cx| { @@ -6214,9 +6303,9 @@ mod tests { type Event = (); } - let (window_id, _) = cx.add_window(Default::default(), |_| View); - let observing_view = cx.add_view(window_id, |_| View); - let emitting_view = cx.add_view(window_id, |_| View); + let (_, root_view) = cx.add_window(Default::default(), |_| View); + let observing_view = cx.add_view(&root_view, |_| View); + let emitting_view = cx.add_view(&root_view, |_| View); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -6390,8 +6479,8 @@ mod tests { type Event = (); } - let (window_id, _) = cx.add_window(Default::default(), |_| View); - let observing_view = cx.add_view(window_id, |_| View); + let (_, root_view) = cx.add_window(Default::default(), |_| View); + let observing_view = cx.add_view(root_view, |_| View); let observing_model = cx.add_model(|_| Model); let observed_model = cx.add_model(|_| Model); @@ -6513,9 +6602,9 @@ mod tests { } } - let (window_id, _) = cx.add_window(Default::default(), |_| View); - let observing_view = cx.add_view(window_id, |_| View); - let observed_view = cx.add_view(window_id, |_| View); + let (_, root_view) = cx.add_window(Default::default(), |_| View); + let observing_view = cx.add_view(&root_view, |_| View); + let observed_view = cx.add_view(&root_view, |_| View); let observation_count = Rc::new(RefCell::new(0)); observing_view.update(cx, |_, cx| { @@ -6587,11 +6676,11 @@ mod tests { } let view_events: Arc>> = Default::default(); - let (window_id, view_1) = cx.add_window(Default::default(), |_| View { + let (_, view_1) = cx.add_window(Default::default(), |_| View { events: view_events.clone(), name: "view 1".to_string(), }); - let view_2 = cx.add_view(window_id, |_| View { + let view_2 = cx.add_view(&view_1, |_| View { events: view_events.clone(), name: "view 2".to_string(), }); @@ -6813,11 +6902,6 @@ mod tests { } }); - let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 }); - let view_2 = cx.add_view(window_id, |_| ViewB { id: 2 }); - let view_3 = cx.add_view(window_id, |_| ViewA { id: 3 }); - let view_4 = cx.add_view(window_id, |_| ViewB { id: 4 }); - let observed_actions = Rc::new(RefCell::new(Vec::new())); cx.observe_actions({ let observed_actions = observed_actions.clone(); @@ -6825,9 +6909,14 @@ mod tests { }) .detach(); - cx.dispatch_action( + let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 }); + let view_2 = cx.add_view(&view_1, |_| ViewB { id: 2 }); + 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_any_effect( window_id, - vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()], + Some(view_4.id()), &Action("bar".to_string()), ); @@ -6848,10 +6937,15 @@ mod tests { assert_eq!(*observed_actions.borrow(), [Action::default().id()]); // Remove view_1, which doesn't propagate the action + + let (window_id, view_2) = cx.add_window(Default::default(), |_| ViewA { id: 1 }); + let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 }); + let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 }); + actions.borrow_mut().clear(); - cx.dispatch_action( + cx.handle_dispatch_action_any_effect( window_id, - vec![view_2.id(), view_3.id(), view_4.id()], + Some(view_4.id()), &Action("bar".to_string()), ); @@ -6924,8 +7018,9 @@ mod tests { view_3.keymap_context.set.insert("c".into()); let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1); - let view_2 = cx.add_view(window_id, |_| view_2); - let view_3 = cx.add_view(window_id, |_| view_3); + let view_2 = cx.add_view(&view_1, |_| view_2); + let view_3 = cx.add_view(&view_2, |_| view_3); + cx.focus(window_id, Some(view_3.id())); // This keymap's only binding dispatches an action on view 2 because that view will have // "a" and "b" in its context, but not "c". @@ -6963,20 +7058,12 @@ mod tests { } }); - cx.dispatch_keystroke( - window_id, - vec![view_1.id(), view_2.id(), view_3.id()], - &Keystroke::parse("a").unwrap(), - ); + cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap()); assert_eq!(&*actions.borrow(), &["2 a"]); actions.borrow_mut().clear(); - cx.dispatch_keystroke( - window_id, - vec![view_1.id(), view_2.id(), view_3.id()], - &Keystroke::parse("b").unwrap(), - ); + cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap()); assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]); } @@ -7130,8 +7217,8 @@ mod tests { } } - let window_id = cx.add_window(|_| View).0; - let view = cx.add_view(window_id, |_| View); + let (_, root_view) = cx.add_window(|_| View); + let view = cx.add_view(&root_view, |_| View); let condition = view.condition(&cx, |_, _| false); cx.update(|_| drop(view)); @@ -7164,7 +7251,7 @@ mod tests { Some("render count: 0") ); - let view = cx.add_view(window_id, |cx| { + let view = cx.add_view(&root_view, |cx| { cx.refresh_windows(); View(0) }); diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index a8aefad6e9..57d3a1a3fd 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -26,7 +26,6 @@ use std::{ pub struct Presenter { window_id: usize, pub(crate) rendered_views: HashMap, - parents: HashMap, cursor_regions: Vec, mouse_regions: Vec<(MouseRegion, usize)>, font_cache: Arc, @@ -52,7 +51,6 @@ impl Presenter { Self { window_id, rendered_views: cx.render_views(window_id, titlebar_height), - parents: Default::default(), cursor_regions: Default::default(), mouse_regions: Default::default(), font_cache, @@ -67,22 +65,22 @@ impl Presenter { } } - pub fn dispatch_path(&self, app: &AppContext) -> Vec { - let mut path = Vec::new(); - if let Some(view_id) = app.focused_view_id(self.window_id) { - self.compute_dispatch_path_from(view_id, &mut path) - } - path - } + // pub fn dispatch_path(&self, app: &AppContext) -> Vec { + // let mut path = Vec::new(); + // if let Some(view_id) = app.focused_view_id(self.window_id) { + // self.compute_dispatch_path_from(view_id, &mut path) + // } + // path + // } - pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec) { - path.push(view_id); - while let Some(parent_id) = self.parents.get(&view_id).copied() { - path.push(parent_id); - view_id = parent_id; - } - path.reverse(); - } + // pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec) { + // path.push(view_id); + // while let Some(parent_id) = self.parents.get(&view_id).copied() { + // path.push(parent_id); + // view_id = parent_id; + // } + // path.reverse(); + // } pub fn invalidate( &mut self, @@ -93,7 +91,6 @@ impl Presenter { for view_id in &invalidation.removed { invalidation.updated.remove(&view_id); self.rendered_views.remove(&view_id); - self.parents.remove(&view_id); } for view_id in &invalidation.updated { self.rendered_views.insert( @@ -191,7 +188,6 @@ impl Presenter { LayoutContext { window_id: self.window_id, rendered_views: &mut self.rendered_views, - parents: &mut self.parents, font_cache: &self.font_cache, font_system: cx.platform().fonts(), text_layout_cache: &self.text_layout_cache, @@ -344,21 +340,11 @@ impl Presenter { } invalidated_views.extend(event_cx.invalidated_views); - let dispatch_directives = event_cx.dispatched_actions; for view_id in invalidated_views { cx.notify_view(self.window_id, view_id); } - let mut dispatch_path = Vec::new(); - for directive in dispatch_directives { - dispatch_path.clear(); - if let Some(view_id) = directive.dispatcher_view_id { - self.compute_dispatch_path_from(view_id, &mut dispatch_path); - } - cx.dispatch_action_any(self.window_id, &dispatch_path, directive.action.as_ref()); - } - handled } else { false @@ -372,9 +358,6 @@ impl Presenter { cx: &'a mut MutableAppContext, ) -> (bool, EventContext<'a>) { let mut hover_regions = Vec::new(); - // let mut unhovered_regions = Vec::new(); - // let mut hovered_regions = Vec::new(); - if let Event::MouseMoved( e @ MouseMovedEvent { position, @@ -446,7 +429,6 @@ impl Presenter { ) -> EventContext<'a> { EventContext { rendered_views: &mut self.rendered_views, - dispatched_actions: Default::default(), font_cache: &self.font_cache, text_layout_cache: &self.text_layout_cache, view_stack: Default::default(), @@ -473,15 +455,9 @@ impl Presenter { } } -pub struct DispatchDirective { - pub dispatcher_view_id: Option, - pub action: Box, -} - pub struct LayoutContext<'a> { window_id: usize, rendered_views: &'a mut HashMap, - parents: &'a mut HashMap, view_stack: Vec, pub font_cache: &'a Arc, pub font_system: Arc, @@ -506,9 +482,6 @@ impl<'a> LayoutContext<'a> { } fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F { - if let Some(parent_id) = self.view_stack.last() { - self.parents.insert(view_id, *parent_id); - } self.view_stack.push(view_id); let mut rendered_view = self.rendered_views.remove(&view_id).unwrap(); let size = rendered_view.layout(constraint, self); @@ -637,7 +610,6 @@ impl<'a> Deref for PaintContext<'a> { pub struct EventContext<'a> { rendered_views: &'a mut HashMap, - dispatched_actions: Vec, pub font_cache: &'a FontCache, pub text_layout_cache: &'a TextLayoutCache, pub app: &'a mut MutableAppContext, @@ -692,10 +664,8 @@ impl<'a> EventContext<'a> { } pub fn dispatch_any_action(&mut self, action: Box) { - self.dispatched_actions.push(DispatchDirective { - dispatcher_view_id: self.view_stack.last().copied(), - action, - }); + self.app + .dispatch_any_action_at(self.window_id, *self.view_stack.last().unwrap(), action) } pub fn dispatch_action(&mut self, action: A) { diff --git a/crates/gpui/src/test.rs b/crates/gpui/src/test.rs index c8a69c4e7d..4122ad09b7 100644 --- a/crates/gpui/src/test.rs +++ b/crates/gpui/src/test.rs @@ -1,6 +1,6 @@ use crate::{ - executor, platform, Entity, FontCache, Handle, LeakDetector, MutableAppContext, Platform, - Subscription, TestAppContext, + elements::Empty, executor, platform, Element, ElementBox, Entity, FontCache, Handle, + LeakDetector, MutableAppContext, Platform, RenderContext, Subscription, TestAppContext, View, }; use futures::StreamExt; use parking_lot::Mutex; @@ -162,3 +162,19 @@ where Observation { rx, _subscription } } + +pub struct EmptyView; + +impl Entity for EmptyView { + type Event = (); +} + +impl View for EmptyView { + fn ui_name() -> &'static str { + "empty view" + } + + fn render(&mut self, _: &mut RenderContext) -> ElementBox { + Element::boxed(Empty::new()) + } +} diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index 52631e71b4..ada785f854 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -600,7 +600,7 @@ impl BufferSearchBar { mod tests { use super::*; use editor::{DisplayPoint, Editor}; - use gpui::{color::Color, TestAppContext}; + use gpui::{color::Color, test::EmptyView, TestAppContext}; use language::Buffer; use std::sync::Arc; use unindent::Unindent as _; @@ -629,11 +629,13 @@ mod tests { cx, ) }); - let editor = cx.add_view(Default::default(), |cx| { + let (_, root_view) = cx.add_window(|_| EmptyView); + + let editor = cx.add_view(&root_view, |cx| { Editor::for_buffer(buffer.clone(), None, cx) }); - let search_bar = cx.add_view(Default::default(), |cx| { + let search_bar = cx.add_view(&root_view, |cx| { let mut search_bar = BufferSearchBar::new(cx); search_bar.set_active_pane_item(Some(&editor), cx); search_bar.show(false, true, cx); diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 39415b3832..6cbd4c7e2d 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -933,7 +933,8 @@ mod tests { cx.update(|cx| { let mut settings = Settings::test(cx); settings.theme = Arc::new(theme); - cx.set_global(settings) + cx.set_global(settings); + cx.set_global(ActiveSearches::default()); }); let fs = FakeFs::new(cx.background()); @@ -949,9 +950,7 @@ mod tests { .await; let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await; let search = cx.add_model(|cx| ProjectSearch::new(project, cx)); - let search_view = cx.add_view(Default::default(), |cx| { - ProjectSearchView::new(search.clone(), cx) - }); + let (_, search_view) = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx)); search_view.update(cx, |search_view, cx| { search_view diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index a05b9ac1a8..354331974f 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -386,7 +386,7 @@ impl Pane { project_entry_id: ProjectEntryId, focus_item: bool, cx: &mut ViewContext, - build_item: impl FnOnce(&mut MutableAppContext) -> Box, + build_item: impl FnOnce(&mut ViewContext) -> Box, ) -> Box { let existing_item = pane.update(cx, |pane, cx| { for (ix, item) in pane.items.iter().enumerate() { @@ -403,7 +403,7 @@ impl Pane { if let Some(existing_item) = existing_item { existing_item } else { - let item = build_item(cx); + let item = pane.update(cx, |_, cx| build_item(cx)); Self::add_item(workspace, pane, item.boxed_clone(), true, focus_item, cx); item } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index a27754aa13..d1a239cbb4 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -59,7 +59,7 @@ use waiting_room::WaitingRoom; type ProjectItemBuilders = HashMap< TypeId, - fn(usize, ModelHandle, AnyModelHandle, &mut MutableAppContext) -> Box, + fn(ModelHandle, AnyModelHandle, &mut ViewContext) -> Box, >; type FollowableItemBuilder = fn( @@ -219,9 +219,9 @@ pub fn init(app_state: Arc, cx: &mut MutableAppContext) { pub fn register_project_item(cx: &mut MutableAppContext) { cx.update_default_global(|builders: &mut ProjectItemBuilders, _| { - builders.insert(TypeId::of::(), |window_id, project, model, cx| { + builders.insert(TypeId::of::(), |project, model, cx| { let item = model.downcast::().unwrap(); - Box::new(cx.add_view(window_id, |cx| I::for_project_item(project, item, cx))) + Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx))) }); }); } @@ -1475,12 +1475,11 @@ impl Workspace { ) -> Task< Result<( ProjectEntryId, - impl 'static + FnOnce(&mut MutableAppContext) -> Box, + impl 'static + FnOnce(&mut ViewContext) -> Box, )>, > { let project = self.project().clone(); let project_item = project.update(cx, |project, cx| project.open_path(path, cx)); - let window_id = cx.window_id(); cx.as_mut().spawn(|mut cx| async move { let (project_entry_id, project_item) = project_item.await?; let build_item = cx.update(|cx| { @@ -1490,7 +1489,7 @@ impl Workspace { .cloned() })?; let build_item = - move |cx: &mut MutableAppContext| build_item(window_id, project, project_item, cx); + move |cx: &mut ViewContext| build_item(project, project_item, cx); Ok((project_entry_id, build_item)) }) } @@ -2732,7 +2731,7 @@ fn open_new(app_state: &Arc, cx: &mut MutableAppContext) { (app_state.initialize_workspace)(&mut workspace, app_state, cx); workspace }); - cx.dispatch_action(window_id, vec![workspace.id()], &NewFile); + cx.dispatch_action_at(window_id, workspace.id(), NewFile); } #[cfg(test)] @@ -2751,10 +2750,10 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); + let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); // Adding an item with no ambiguity renders the tab without detail. - let item1 = cx.add_view(window_id, |_| { + let item1 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]); item @@ -2766,7 +2765,7 @@ mod tests { // Adding an item that creates ambiguity increases the level of detail on // both tabs. - let item2 = cx.add_view(window_id, |_| { + let item2 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item @@ -2780,7 +2779,7 @@ mod tests { // Adding an item that creates ambiguity increases the level of detail only // on the ambiguous tabs. In this case, the ambiguity can't be resolved so // we stop at the highest detail available. - let item3 = cx.add_view(window_id, |_| { + let item3 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]); item @@ -2820,12 +2819,12 @@ mod tests { project.worktrees(cx).next().unwrap().read(cx).id() }); - let item1 = cx.add_view(window_id, |_| { + let item1 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.project_path = Some((worktree_id, "one.txt").into()); item }); - let item2 = cx.add_view(window_id, |_| { + let item2 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.project_path = Some((worktree_id, "two.txt").into()); item @@ -2914,19 +2913,19 @@ mod tests { let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx)); // When there are no dirty items, there's nothing to do. - let item1 = cx.add_view(window_id, |_| TestItem::new()); + let item1 = cx.add_view(&workspace, |_| TestItem::new()); workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx)); let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx)); assert_eq!(task.await.unwrap(), true); // When there are dirty untitled items, prompt to save each one. If the user // cancels any prompt, then abort. - let item2 = cx.add_view(window_id, |_| { + let item2 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item }); - let item3 = cx.add_view(window_id, |_| { + let item3 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item.project_entry_ids = vec![ProjectEntryId::from_proto(1)]; @@ -2953,27 +2952,27 @@ mod tests { let project = Project::test(fs, None, cx).await; let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); - let item1 = cx.add_view(window_id, |_| { + let item1 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item.project_entry_ids = vec![ProjectEntryId::from_proto(1)]; item }); - let item2 = cx.add_view(window_id, |_| { + let item2 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item.has_conflict = true; item.project_entry_ids = vec![ProjectEntryId::from_proto(2)]; item }); - let item3 = cx.add_view(window_id, |_| { + let item3 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item.has_conflict = true; item.project_entry_ids = vec![ProjectEntryId::from_proto(3)]; item }); - let item4 = cx.add_view(window_id, |_| { + let item4 = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.is_dirty = true; item @@ -3144,7 +3143,7 @@ mod tests { let project = Project::test(fs, [], cx).await; let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); - let item = cx.add_view(window_id, |_| { + let item = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.project_entry_ids = vec![ProjectEntryId::from_proto(1)]; item @@ -3259,9 +3258,9 @@ mod tests { let fs = FakeFs::new(cx.background()); let project = Project::test(fs, [], cx).await; - let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); + let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx)); - let item = cx.add_view(window_id, |_| { + let item = cx.add_view(&workspace, |_| { let mut item = TestItem::new(); item.project_entry_ids = vec![ProjectEntryId::from_proto(1)]; item From 6442ec59e71f44fb44920e6d1dec326338e9279b Mon Sep 17 00:00:00 2001 From: K Simmons Date: Fri, 5 Aug 2022 12:38:05 -0700 Subject: [PATCH 2/9] Switch action dispatch to use MutableAppContext parent utilities and delete parent map from presenter --- crates/gpui/src/app.rs | 101 +++++++++++++++-------------- crates/gpui/src/presenter.rs | 41 +++++++++++- crates/workspace/src/pane.rs | 1 + crates/workspace/src/sidebar.rs | 1 + crates/workspace/src/status_bar.rs | 2 + crates/workspace/src/workspace.rs | 6 ++ 6 files changed, 100 insertions(+), 52 deletions(-) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index a1b782039a..ac9ef6f829 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -230,7 +230,7 @@ impl App { let mut cx = cx.borrow_mut(); if let Some(key_window_id) = cx.cx.platform.key_window_id() { if let Some(view_id) = cx.focused_view_id(key_window_id) { - cx.handle_dispatch_action_any_effect(key_window_id, Some(view_id), action); + cx.handle_dispatch_action_from_effect(key_window_id, Some(view_id), action); return; } } @@ -457,7 +457,7 @@ impl TestAppContext { pub fn dispatch_action(&self, window_id: usize, action: A) { let mut cx = self.cx.borrow_mut(); if let Some(view_id) = cx.focused_view_id(window_id) { - cx.handle_dispatch_action_any_effect(window_id, Some(view_id), &action); + cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action); } } @@ -477,6 +477,7 @@ impl TestAppContext { if cx.dispatch_keystroke(window_id, &keystroke) { return true; } + if presenter.borrow_mut().dispatch_event( Event::KeyDown(KeyDownEvent { keystroke: keystroke.clone(), @@ -1606,6 +1607,12 @@ impl MutableAppContext { } } + pub(crate) fn name_for_view(&self, window_id: usize, view_id: usize) -> Option<&str> { + self.views + .get(&(window_id, view_id)) + .map(|view| view.ui_name()) + } + pub fn all_action_names<'a>(&'a self) -> impl Iterator + 'a { self.action_deserializers.keys().copied() } @@ -1685,29 +1692,6 @@ impl MutableAppContext { None } - // pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) { - // let presenter = self - // .presenters_and_platform_windows - // .get(&window_id) - // .unwrap() - // .0 - // .clone(); - // let mut dispatch_path = Vec::new(); - // presenter - // .borrow() - // .compute_dispatch_path_from(view_id, &mut dispatch_path); - // self.dispatch_action_any(window_id, &dispatch_path, action); - // } - - // pub fn dispatch_action( - // &mut self, - // window_id: usize, - // dispatch_path: Vec, - // action: &A, - // ) { - // self.dispatch_action_any(window_id, &dispatch_path, action); - // } - // 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. @@ -1719,12 +1703,7 @@ impl MutableAppContext { mut visit: impl FnMut(usize, bool, &mut MutableAppContext) -> bool, ) -> bool { // List of view ids from the leaf to the root of the window - let mut path = vec![view_id]; - let mut current_view = view_id; - while let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, current_view)) { - current_view = *parent_id; - path.push(current_view); - } + let path = self.parents(window_id, view_id).collect::>(); // Walk down from the root to the leaf calling visit with capture_phase = true for view_id in path.iter().rev() { @@ -1746,15 +1725,18 @@ impl MutableAppContext { // 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 parents(&self, window_id: usize, mut view_id: usize) -> impl Iterator + '_ { - 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 - } - }) + 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 + } + })) } + fn actions_mut( &mut self, capture_phase: bool, @@ -1793,8 +1775,8 @@ impl MutableAppContext { pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool { let mut pending = false; - if let Some(view_id) = self.focused_view_id(window_id) { - for view_id in self.parents(window_id, view_id).collect::>() { + if let Some(focused_view_id) = self.focused_view_id(window_id) { + for view_id in self.parents(window_id, focused_view_id).collect::>() { let keymap_context = self .cx .views @@ -1810,7 +1792,7 @@ impl MutableAppContext { MatchResult::None => {} MatchResult::Pending => pending = true, MatchResult::Action(action) => { - if self.handle_dispatch_action_any_effect( + if self.handle_dispatch_action_from_effect( window_id, Some(view_id), action.as_ref(), @@ -2303,7 +2285,7 @@ impl MutableAppContext { view_id, action, } => { - self.handle_dispatch_action_any_effect( + self.handle_dispatch_action_from_effect( window_id, Some(view_id), action.as_ref(), @@ -2579,7 +2561,7 @@ impl MutableAppContext { }) } - fn handle_dispatch_action_any_effect( + fn handle_dispatch_action_from_effect( &mut self, window_id: usize, view_id: Option, @@ -2587,6 +2569,7 @@ impl MutableAppContext { ) -> 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| { if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { let type_id = view.as_any().type_id(); @@ -2809,6 +2792,7 @@ impl Deref for MutableAppContext { } } +#[derive(Debug)] pub enum ParentId { View(usize), Root, @@ -2817,7 +2801,7 @@ pub enum ParentId { pub struct AppContext { models: HashMap>, views: HashMap<(usize, usize), Box>, - parents: HashMap<(usize, usize), ParentId>, + pub(crate) parents: HashMap<(usize, usize), ParentId>, windows: HashMap, globals: HashMap>, element_states: HashMap>, @@ -3733,6 +3717,21 @@ impl<'a, T: View> ViewContext<'a, T> { .build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view) } + pub fn reparent(&mut self, view_handle: impl Into) { + let view_handle = view_handle.into(); + if self.window_id != view_handle.window_id { + panic!("Can't reparent view to a view from a different window"); + } + self.cx + .parents + .remove(&(view_handle.window_id, view_handle.view_id)); + let new_parent_id = self.view_id; + self.cx.parents.insert( + (view_handle.window_id, view_handle.view_id), + ParentId::View(new_parent_id), + ); + } + pub fn replace_root_view(&mut self, build_root_view: F) -> ViewHandle where V: View, @@ -6914,7 +6913,7 @@ 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_any_effect( + cx.handle_dispatch_action_from_effect( window_id, Some(view_4.id()), &Action("bar".to_string()), @@ -6938,12 +6937,12 @@ mod tests { // Remove view_1, which doesn't propagate the action - let (window_id, view_2) = cx.add_window(Default::default(), |_| ViewA { id: 1 }); + let (window_id, view_2) = cx.add_window(Default::default(), |_| ViewB { id: 2 }); let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 }); let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 }); actions.borrow_mut().clear(); - cx.handle_dispatch_action_any_effect( + cx.handle_dispatch_action_from_effect( window_id, Some(view_4.id()), &Action("bar".to_string()), @@ -7019,8 +7018,10 @@ mod tests { let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1); let view_2 = cx.add_view(&view_1, |_| view_2); - let view_3 = cx.add_view(&view_2, |_| view_3); - cx.focus(window_id, Some(view_3.id())); + let view_3 = cx.add_view(&view_2, |cx| { + cx.focus_self(); + view_3 + }); // This keymap's only binding dispatches an action on view 2 because that view will have // "a" and "b" in its context, but not "c". diff --git a/crates/gpui/src/presenter.rs b/crates/gpui/src/presenter.rs index 57d3a1a3fd..9cba57810b 100644 --- a/crates/gpui/src/presenter.rs +++ b/crates/gpui/src/presenter.rs @@ -10,8 +10,8 @@ use crate::{ text_layout::TextLayoutCache, Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity, FontSystem, ModelHandle, MouseButtonEvent, MouseMovedEvent, MouseRegion, MouseRegionId, - ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle, - View, ViewHandle, WeakModelHandle, WeakViewHandle, + ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, + UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle, }; use collections::{HashMap, HashSet}; use pathfinder_geometry::vector::{vec2f, Vector2F}; @@ -482,6 +482,43 @@ impl<'a> LayoutContext<'a> { } fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F { + let print_error = |view_id| { + format!( + "{} with id {}", + self.app.name_for_view(self.window_id, view_id).unwrap(), + view_id, + ) + }; + match ( + self.view_stack.last(), + self.app.parents.get(&(self.window_id, view_id)), + ) { + (Some(layout_parent), Some(ParentId::View(app_parent))) => { + if layout_parent != app_parent { + panic!( + "View {} was laid out with parent {} when it was constructed with parent {}", + print_error(view_id), + print_error(*layout_parent), + print_error(*app_parent)) + } + } + (None, Some(ParentId::View(app_parent))) => panic!( + "View {} was laid out without a parent when it was constructed with parent {}", + print_error(view_id), + print_error(*app_parent) + ), + (Some(layout_parent), Some(ParentId::Root)) => panic!( + "View {} was laid out with parent {} when it was constructed as a window root", + print_error(view_id), + print_error(*layout_parent), + ), + (_, None) => panic!( + "View {} did not have a registered parent in the app context", + print_error(view_id), + ), + _ => {} + } + self.view_stack.push(view_id); let mut rendered_view = self.rendered_views.remove(&view_id).unwrap(); let size = rendered_view.layout(constraint, self); diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 354331974f..572e1032b3 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -441,6 +441,7 @@ impl Pane { pane.active_item_index = usize::MAX; }; + cx.reparent(&item); pane.items.insert(item_ix, item); pane.activate_item(item_ix, activate_pane, focus_item, false, cx); cx.notify(); diff --git a/crates/workspace/src/sidebar.rs b/crates/workspace/src/sidebar.rs index 542cd51cb6..b7ed8140c9 100644 --- a/crates/workspace/src/sidebar.rs +++ b/crates/workspace/src/sidebar.rs @@ -140,6 +140,7 @@ impl Sidebar { } }), ]; + cx.reparent(&view); self.items.push(Item { icon_path, tooltip, diff --git a/crates/workspace/src/status_bar.rs b/crates/workspace/src/status_bar.rs index f84940e8fb..ca44cf7c27 100644 --- a/crates/workspace/src/status_bar.rs +++ b/crates/workspace/src/status_bar.rs @@ -81,6 +81,7 @@ impl StatusBar { where T: 'static + StatusItemView, { + cx.reparent(&item); self.left_items.push(Box::new(item)); cx.notify(); } @@ -89,6 +90,7 @@ impl StatusBar { where T: 'static + StatusItemView, { + cx.reparent(&item); self.right_items.push(Box::new(item)); cx.notify(); } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index d1a239cbb4..02ad18eb3d 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -708,6 +708,12 @@ impl Into for Box { } } +impl Into for &Box { + fn into(self) -> AnyViewHandle { + self.to_any() + } +} + impl Clone for Box { fn clone(&self) -> Box { self.boxed_clone() From 690ea57211bc29584eafd86a3262c493e244a55f Mon Sep 17 00:00:00 2001 From: K Simmons Date: Sun, 7 Aug 2022 19:23:22 -0700 Subject: [PATCH 3/9] WIP compiling but failing with circular reference --- crates/chat_panel/src/chat_panel.rs | 6 +- crates/command_palette/src/command_palette.rs | 5 +- crates/contacts_panel/src/contact_finder.rs | 6 +- crates/contacts_panel/src/contacts_panel.rs | 8 +- crates/context_menu/src/context_menu.rs | 4 +- crates/diagnostics/src/diagnostics.rs | 2 +- crates/editor/src/editor.rs | 8 +- crates/file_finder/src/file_finder.rs | 6 +- crates/go_to_line/src/go_to_line.rs | 6 +- .../grammars/context-predicate/src/parser.c | 909 ++++++++++-------- crates/gpui/src/app.rs | 151 ++- crates/outline/src/outline.rs | 6 +- crates/picker/src/picker.rs | 6 +- crates/project_symbols/src/project_symbols.rs | 6 +- crates/search/src/buffer_search.rs | 6 +- crates/search/src/project_search.rs | 8 +- crates/terminal/src/connected_view.rs | 5 +- crates/terminal/src/terminal.rs | 7 + crates/theme_selector/src/theme_selector.rs | 6 +- crates/workspace/src/pane.rs | 15 +- crates/workspace/src/workspace.rs | 2 +- styles/package-lock.json | 1 + 22 files changed, 664 insertions(+), 515 deletions(-) diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs index fa913971df..7f947fef16 100644 --- a/crates/chat_panel/src/chat_panel.rs +++ b/crates/chat_panel/src/chat_panel.rs @@ -8,8 +8,8 @@ use gpui::{ elements::*, platform::CursorStyle, views::{ItemType, Select, SelectStyle}, - AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, - Task, View, ViewContext, ViewHandle, + AnyViewHandle, AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, + Subscription, Task, View, ViewContext, ViewHandle, }; use menu::Confirm; use postage::prelude::Stream; @@ -397,7 +397,7 @@ impl View for ChatPanel { .boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { if matches!( *self.rpc.status().borrow(), client::Status::Connected { .. } diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 5f213284e8..5d6f6e14a8 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -4,7 +4,8 @@ use gpui::{ actions, elements::{ChildView, Flex, Label, ParentElement}, keymap::Keystroke, - Action, Element, Entity, MouseState, MutableAppContext, View, ViewContext, ViewHandle, + Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, View, ViewContext, + ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -132,7 +133,7 @@ impl View for CommandPalette { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/contacts_panel/src/contact_finder.rs b/crates/contacts_panel/src/contact_finder.rs index 4027160950..978466dfcb 100644 --- a/crates/contacts_panel/src/contact_finder.rs +++ b/crates/contacts_panel/src/contact_finder.rs @@ -1,7 +1,7 @@ use client::{ContactRequestStatus, User, UserStore}; use gpui::{ - actions, elements::*, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext, Task, - View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext, + RenderContext, Task, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -42,7 +42,7 @@ impl View for ContactFinder { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs index 4aff5b1a74..9bc7972b29 100644 --- a/crates/contacts_panel/src/contacts_panel.rs +++ b/crates/contacts_panel/src/contacts_panel.rs @@ -13,9 +13,9 @@ use gpui::{ geometry::{rect::RectF, vector::vec2f}, impl_actions, impl_internal_actions, platform::CursorStyle, - AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton, - MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakModelHandle, - WeakViewHandle, + AnyViewHandle, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, + MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, + WeakModelHandle, WeakViewHandle, }; use join_project_notification::JoinProjectNotification; use menu::{Confirm, SelectNext, SelectPrev}; @@ -1152,7 +1152,7 @@ impl View for ContactsPanel { .boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.filter_editor); } diff --git a/crates/context_menu/src/context_menu.rs b/crates/context_menu/src/context_menu.rs index b17718577f..e301e62467 100644 --- a/crates/context_menu/src/context_menu.rs +++ b/crates/context_menu/src/context_menu.rs @@ -1,6 +1,6 @@ use gpui::{ elements::*, geometry::vector::Vector2F, impl_internal_actions, keymap, platform::CursorStyle, - Action, AppContext, Axis, Entity, MouseButton, MutableAppContext, RenderContext, + Action, AnyViewHandle, AppContext, Axis, Entity, MouseButton, MutableAppContext, RenderContext, SizeConstraint, Subscription, View, ViewContext, }; use menu::*; @@ -106,7 +106,7 @@ impl View for ContextMenu { .boxed() } - fn on_blur(&mut self, cx: &mut ViewContext) { + fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { self.reset(cx); } } diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index e9692c5493..42a3bb9f13 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -99,7 +99,7 @@ impl View for ProjectDiagnosticsEditor { } } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { if !self.path_states.is_empty() { cx.focus(&self.editor); } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index e4ff272440..6e84b275aa 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -29,8 +29,8 @@ use gpui::{ geometry::vector::{vec2f, Vector2F}, impl_actions, impl_internal_actions, platform::CursorStyle, - text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity, - ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, + text_layout, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, + Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use highlight_matching_bracket::refresh_matching_bracket_highlights; @@ -6025,7 +6025,7 @@ impl View for Editor { "Editor" } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { let focused_event = EditorFocused(cx.handle()); cx.emit_global(focused_event); if let Some(rename) = self.pending_rename.as_ref() { @@ -6046,7 +6046,7 @@ impl View for Editor { } } - fn on_blur(&mut self, cx: &mut ViewContext) { + fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { let blurred_event = EditorBlurred(cx.handle()); cx.emit_global(blurred_event); self.focused = false; diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index d69c95605d..aeec799015 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -1,7 +1,7 @@ use fuzzy::PathMatch; use gpui::{ - actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext, - RenderContext, Task, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, + MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId}; @@ -53,7 +53,7 @@ impl View for FileFinder { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/go_to_line/src/go_to_line.rs b/crates/go_to_line/src/go_to_line.rs index f2df235a7b..3ca50cee42 100644 --- a/crates/go_to_line/src/go_to_line.rs +++ b/crates/go_to_line/src/go_to_line.rs @@ -1,7 +1,7 @@ use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor}; use gpui::{ - actions, elements::*, geometry::vector::Vector2F, Axis, Entity, MutableAppContext, - RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, Axis, Entity, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use menu::{Cancel, Confirm}; use settings::Settings; @@ -183,7 +183,7 @@ impl View for GoToLine { .named("go to line") } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.line_editor); } } diff --git a/crates/gpui/grammars/context-predicate/src/parser.c b/crates/gpui/grammars/context-predicate/src/parser.c index e2af5e03ca..57a7364a43 100644 --- a/crates/gpui/grammars/context-predicate/src/parser.c +++ b/crates/gpui/grammars/context-predicate/src/parser.c @@ -35,115 +35,132 @@ enum { sym_parenthesized = 16, }; -static const char * const ts_symbol_names[] = { - [ts_builtin_sym_end] = "end", - [sym_identifier] = "identifier", - [anon_sym_BANG] = "!", - [anon_sym_AMP_AMP] = "&&", - [anon_sym_PIPE_PIPE] = "||", - [anon_sym_EQ_EQ] = "==", - [anon_sym_BANG_EQ] = "!=", - [anon_sym_LPAREN] = "(", - [anon_sym_RPAREN] = ")", - [sym_source] = "source", - [sym__expression] = "_expression", - [sym_not] = "not", - [sym_and] = "and", - [sym_or] = "or", - [sym_equal] = "equal", - [sym_not_equal] = "not_equal", - [sym_parenthesized] = "parenthesized", +static const char *const ts_symbol_names[] = { + [ts_builtin_sym_end] = "end", + [sym_identifier] = "identifier", + [anon_sym_BANG] = "!", + [anon_sym_AMP_AMP] = "&&", + [anon_sym_PIPE_PIPE] = "||", + [anon_sym_EQ_EQ] = "==", + [anon_sym_BANG_EQ] = "!=", + [anon_sym_LPAREN] = "(", + [anon_sym_RPAREN] = ")", + [sym_source] = "source", + [sym__expression] = "_expression", + [sym_not] = "not", + [sym_and] = "and", + [sym_or] = "or", + [sym_equal] = "equal", + [sym_not_equal] = "not_equal", + [sym_parenthesized] = "parenthesized", }; static const TSSymbol ts_symbol_map[] = { - [ts_builtin_sym_end] = ts_builtin_sym_end, - [sym_identifier] = sym_identifier, - [anon_sym_BANG] = anon_sym_BANG, - [anon_sym_AMP_AMP] = anon_sym_AMP_AMP, - [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE, - [anon_sym_EQ_EQ] = anon_sym_EQ_EQ, - [anon_sym_BANG_EQ] = anon_sym_BANG_EQ, - [anon_sym_LPAREN] = anon_sym_LPAREN, - [anon_sym_RPAREN] = anon_sym_RPAREN, - [sym_source] = sym_source, - [sym__expression] = sym__expression, - [sym_not] = sym_not, - [sym_and] = sym_and, - [sym_or] = sym_or, - [sym_equal] = sym_equal, - [sym_not_equal] = sym_not_equal, - [sym_parenthesized] = sym_parenthesized, + [ts_builtin_sym_end] = ts_builtin_sym_end, + [sym_identifier] = sym_identifier, + [anon_sym_BANG] = anon_sym_BANG, + [anon_sym_AMP_AMP] = anon_sym_AMP_AMP, + [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE, + [anon_sym_EQ_EQ] = anon_sym_EQ_EQ, + [anon_sym_BANG_EQ] = anon_sym_BANG_EQ, + [anon_sym_LPAREN] = anon_sym_LPAREN, + [anon_sym_RPAREN] = anon_sym_RPAREN, + [sym_source] = sym_source, + [sym__expression] = sym__expression, + [sym_not] = sym_not, + [sym_and] = sym_and, + [sym_or] = sym_or, + [sym_equal] = sym_equal, + [sym_not_equal] = sym_not_equal, + [sym_parenthesized] = sym_parenthesized, }; static const TSSymbolMetadata ts_symbol_metadata[] = { - [ts_builtin_sym_end] = { - .visible = false, - .named = true, - }, - [sym_identifier] = { - .visible = true, - .named = true, - }, - [anon_sym_BANG] = { - .visible = true, - .named = false, - }, - [anon_sym_AMP_AMP] = { - .visible = true, - .named = false, - }, - [anon_sym_PIPE_PIPE] = { - .visible = true, - .named = false, - }, - [anon_sym_EQ_EQ] = { - .visible = true, - .named = false, - }, - [anon_sym_BANG_EQ] = { - .visible = true, - .named = false, - }, - [anon_sym_LPAREN] = { - .visible = true, - .named = false, - }, - [anon_sym_RPAREN] = { - .visible = true, - .named = false, - }, - [sym_source] = { - .visible = true, - .named = true, - }, - [sym__expression] = { - .visible = false, - .named = true, - }, - [sym_not] = { - .visible = true, - .named = true, - }, - [sym_and] = { - .visible = true, - .named = true, - }, - [sym_or] = { - .visible = true, - .named = true, - }, - [sym_equal] = { - .visible = true, - .named = true, - }, - [sym_not_equal] = { - .visible = true, - .named = true, - }, - [sym_parenthesized] = { - .visible = true, - .named = true, - }, + [ts_builtin_sym_end] = + { + .visible = false, + .named = true, + }, + [sym_identifier] = + { + .visible = true, + .named = true, + }, + [anon_sym_BANG] = + { + .visible = true, + .named = false, + }, + [anon_sym_AMP_AMP] = + { + .visible = true, + .named = false, + }, + [anon_sym_PIPE_PIPE] = + { + .visible = true, + .named = false, + }, + [anon_sym_EQ_EQ] = + { + .visible = true, + .named = false, + }, + [anon_sym_BANG_EQ] = + { + .visible = true, + .named = false, + }, + [anon_sym_LPAREN] = + { + .visible = true, + .named = false, + }, + [anon_sym_RPAREN] = + { + .visible = true, + .named = false, + }, + [sym_source] = + { + .visible = true, + .named = true, + }, + [sym__expression] = + { + .visible = false, + .named = true, + }, + [sym_not] = + { + .visible = true, + .named = true, + }, + [sym_and] = + { + .visible = true, + .named = true, + }, + [sym_or] = + { + .visible = true, + .named = true, + }, + [sym_equal] = + { + .visible = true, + .named = true, + }, + [sym_not_equal] = + { + .visible = true, + .named = true, + }, + [sym_parenthesized] = + { + .visible = true, + .named = true, + }, }; enum { @@ -152,340 +169,378 @@ enum { field_right = 3, }; -static const char * const ts_field_names[] = { - [0] = NULL, - [field_expression] = "expression", - [field_left] = "left", - [field_right] = "right", +static const char *const ts_field_names[] = { + [0] = NULL, + [field_expression] = "expression", + [field_left] = "left", + [field_right] = "right", }; static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = { - [1] = {.index = 0, .length = 1}, - [2] = {.index = 1, .length = 2}, + [1] = {.index = 0, .length = 1}, + [2] = {.index = 1, .length = 2}, }; static const TSFieldMapEntry ts_field_map_entries[] = { - [0] = - {field_expression, 1}, - [1] = - {field_left, 0}, + [0] = {field_expression, 1}, + [1] = {field_left, 0}, {field_right, 2}, }; -static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = { - [0] = {0}, +static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT] + [MAX_ALIAS_SEQUENCE_LENGTH] = { + [0] = {0}, }; static const uint16_t ts_non_terminal_alias_map[] = { - 0, + 0, }; static bool ts_lex(TSLexer *lexer, TSStateId state) { START_LEXER(); eof = lexer->eof(lexer); switch (state) { - case 0: - if (eof) ADVANCE(7); - if (lookahead == '!') ADVANCE(10); - if (lookahead == '&') ADVANCE(2); - if (lookahead == '(') ADVANCE(15); - if (lookahead == ')') ADVANCE(16); - if (lookahead == '=') ADVANCE(4); - if (lookahead == '|') ADVANCE(5); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || - lookahead == ' ') SKIP(0) - if (lookahead == '-' || - ('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8); - END_STATE(); - case 1: - if (lookahead == '!') ADVANCE(9); - if (lookahead == '(') ADVANCE(15); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || - lookahead == ' ') SKIP(1) - if (lookahead == '-' || - ('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8); - END_STATE(); - case 2: - if (lookahead == '&') ADVANCE(11); - END_STATE(); - case 3: - if (lookahead == '=') ADVANCE(14); - END_STATE(); - case 4: - if (lookahead == '=') ADVANCE(13); - END_STATE(); - case 5: - if (lookahead == '|') ADVANCE(12); - END_STATE(); - case 6: - if (eof) ADVANCE(7); - if (lookahead == '!') ADVANCE(3); - if (lookahead == '&') ADVANCE(2); - if (lookahead == ')') ADVANCE(16); - if (lookahead == '=') ADVANCE(4); - if (lookahead == '|') ADVANCE(5); - if (lookahead == '\t' || - lookahead == '\n' || - lookahead == '\r' || - lookahead == ' ') SKIP(6) - END_STATE(); - case 7: - ACCEPT_TOKEN(ts_builtin_sym_end); - END_STATE(); - case 8: - ACCEPT_TOKEN(sym_identifier); - if (lookahead == '-' || - ('0' <= lookahead && lookahead <= '9') || - ('A' <= lookahead && lookahead <= 'Z') || - lookahead == '_' || - ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8); - END_STATE(); - case 9: - ACCEPT_TOKEN(anon_sym_BANG); - END_STATE(); - case 10: - ACCEPT_TOKEN(anon_sym_BANG); - if (lookahead == '=') ADVANCE(14); - END_STATE(); - case 11: - ACCEPT_TOKEN(anon_sym_AMP_AMP); - END_STATE(); - case 12: - ACCEPT_TOKEN(anon_sym_PIPE_PIPE); - END_STATE(); - case 13: - ACCEPT_TOKEN(anon_sym_EQ_EQ); - END_STATE(); - case 14: - ACCEPT_TOKEN(anon_sym_BANG_EQ); - END_STATE(); - case 15: - ACCEPT_TOKEN(anon_sym_LPAREN); - END_STATE(); - case 16: - ACCEPT_TOKEN(anon_sym_RPAREN); - END_STATE(); - default: - return false; + case 0: + if (eof) + ADVANCE(7); + if (lookahead == '!') + ADVANCE(10); + if (lookahead == '&') + ADVANCE(2); + if (lookahead == '(') + ADVANCE(15); + if (lookahead == ')') + ADVANCE(16); + if (lookahead == '=') + ADVANCE(4); + if (lookahead == '|') + ADVANCE(5); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || + lookahead == ' ') + SKIP(0) + if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) + ADVANCE(8); + END_STATE(); + case 1: + if (lookahead == '!') + ADVANCE(9); + if (lookahead == '(') + ADVANCE(15); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || + lookahead == ' ') + SKIP(1) + if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) + ADVANCE(8); + END_STATE(); + case 2: + if (lookahead == '&') + ADVANCE(11); + END_STATE(); + case 3: + if (lookahead == '=') + ADVANCE(14); + END_STATE(); + case 4: + if (lookahead == '=') + ADVANCE(13); + END_STATE(); + case 5: + if (lookahead == '|') + ADVANCE(12); + END_STATE(); + case 6: + if (eof) + ADVANCE(7); + if (lookahead == '!') + ADVANCE(3); + if (lookahead == '&') + ADVANCE(2); + if (lookahead == ')') + ADVANCE(16); + if (lookahead == '=') + ADVANCE(4); + if (lookahead == '|') + ADVANCE(5); + if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' || + lookahead == ' ') + SKIP(6) + END_STATE(); + case 7: + ACCEPT_TOKEN(ts_builtin_sym_end); + END_STATE(); + case 8: + ACCEPT_TOKEN(sym_identifier); + if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') || + ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' || + ('a' <= lookahead && lookahead <= 'z')) + ADVANCE(8); + END_STATE(); + case 9: + ACCEPT_TOKEN(anon_sym_BANG); + END_STATE(); + case 10: + ACCEPT_TOKEN(anon_sym_BANG); + if (lookahead == '=') + ADVANCE(14); + END_STATE(); + case 11: + ACCEPT_TOKEN(anon_sym_AMP_AMP); + END_STATE(); + case 12: + ACCEPT_TOKEN(anon_sym_PIPE_PIPE); + END_STATE(); + case 13: + ACCEPT_TOKEN(anon_sym_EQ_EQ); + END_STATE(); + case 14: + ACCEPT_TOKEN(anon_sym_BANG_EQ); + END_STATE(); + case 15: + ACCEPT_TOKEN(anon_sym_LPAREN); + END_STATE(); + case 16: + ACCEPT_TOKEN(anon_sym_RPAREN); + END_STATE(); + default: + return false; } } static const TSLexMode ts_lex_modes[STATE_COUNT] = { - [0] = {.lex_state = 0}, - [1] = {.lex_state = 1}, - [2] = {.lex_state = 1}, - [3] = {.lex_state = 1}, - [4] = {.lex_state = 1}, - [5] = {.lex_state = 1}, - [6] = {.lex_state = 6}, - [7] = {.lex_state = 0}, - [8] = {.lex_state = 0}, - [9] = {.lex_state = 0}, - [10] = {.lex_state = 0}, - [11] = {.lex_state = 0}, - [12] = {.lex_state = 0}, - [13] = {.lex_state = 0}, - [14] = {.lex_state = 0}, - [15] = {.lex_state = 0}, - [16] = {.lex_state = 0}, - [17] = {.lex_state = 0}, + [0] = {.lex_state = 0}, [1] = {.lex_state = 1}, [2] = {.lex_state = 1}, + [3] = {.lex_state = 1}, [4] = {.lex_state = 1}, [5] = {.lex_state = 1}, + [6] = {.lex_state = 6}, [7] = {.lex_state = 0}, [8] = {.lex_state = 0}, + [9] = {.lex_state = 0}, [10] = {.lex_state = 0}, [11] = {.lex_state = 0}, + [12] = {.lex_state = 0}, [13] = {.lex_state = 0}, [14] = {.lex_state = 0}, + [15] = {.lex_state = 0}, [16] = {.lex_state = 0}, [17] = {.lex_state = 0}, }; static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = { - [0] = { - [ts_builtin_sym_end] = ACTIONS(1), - [sym_identifier] = ACTIONS(1), - [anon_sym_BANG] = ACTIONS(1), - [anon_sym_AMP_AMP] = ACTIONS(1), - [anon_sym_PIPE_PIPE] = ACTIONS(1), - [anon_sym_EQ_EQ] = ACTIONS(1), - [anon_sym_BANG_EQ] = ACTIONS(1), - [anon_sym_LPAREN] = ACTIONS(1), - [anon_sym_RPAREN] = ACTIONS(1), - }, - [1] = { - [sym_source] = STATE(15), - [sym__expression] = STATE(13), - [sym_not] = STATE(13), - [sym_and] = STATE(13), - [sym_or] = STATE(13), - [sym_equal] = STATE(13), - [sym_not_equal] = STATE(13), - [sym_parenthesized] = STATE(13), - [sym_identifier] = ACTIONS(3), - [anon_sym_BANG] = ACTIONS(5), - [anon_sym_LPAREN] = ACTIONS(7), - }, - [2] = { - [sym__expression] = STATE(7), - [sym_not] = STATE(7), - [sym_and] = STATE(7), - [sym_or] = STATE(7), - [sym_equal] = STATE(7), - [sym_not_equal] = STATE(7), - [sym_parenthesized] = STATE(7), - [sym_identifier] = ACTIONS(3), - [anon_sym_BANG] = ACTIONS(5), - [anon_sym_LPAREN] = ACTIONS(7), - }, - [3] = { - [sym__expression] = STATE(14), - [sym_not] = STATE(14), - [sym_and] = STATE(14), - [sym_or] = STATE(14), - [sym_equal] = STATE(14), - [sym_not_equal] = STATE(14), - [sym_parenthesized] = STATE(14), - [sym_identifier] = ACTIONS(3), - [anon_sym_BANG] = ACTIONS(5), - [anon_sym_LPAREN] = ACTIONS(7), - }, - [4] = { - [sym__expression] = STATE(11), - [sym_not] = STATE(11), - [sym_and] = STATE(11), - [sym_or] = STATE(11), - [sym_equal] = STATE(11), - [sym_not_equal] = STATE(11), - [sym_parenthesized] = STATE(11), - [sym_identifier] = ACTIONS(3), - [anon_sym_BANG] = ACTIONS(5), - [anon_sym_LPAREN] = ACTIONS(7), - }, - [5] = { - [sym__expression] = STATE(12), - [sym_not] = STATE(12), - [sym_and] = STATE(12), - [sym_or] = STATE(12), - [sym_equal] = STATE(12), - [sym_not_equal] = STATE(12), - [sym_parenthesized] = STATE(12), - [sym_identifier] = ACTIONS(3), - [anon_sym_BANG] = ACTIONS(5), - [anon_sym_LPAREN] = ACTIONS(7), - }, + [0] = + { + [ts_builtin_sym_end] = ACTIONS(1), + [sym_identifier] = ACTIONS(1), + [anon_sym_BANG] = ACTIONS(1), + [anon_sym_AMP_AMP] = ACTIONS(1), + [anon_sym_PIPE_PIPE] = ACTIONS(1), + [anon_sym_EQ_EQ] = ACTIONS(1), + [anon_sym_BANG_EQ] = ACTIONS(1), + [anon_sym_LPAREN] = ACTIONS(1), + [anon_sym_RPAREN] = ACTIONS(1), + }, + [1] = + { + [sym_source] = STATE(15), + [sym__expression] = STATE(13), + [sym_not] = STATE(13), + [sym_and] = STATE(13), + [sym_or] = STATE(13), + [sym_equal] = STATE(13), + [sym_not_equal] = STATE(13), + [sym_parenthesized] = STATE(13), + [sym_identifier] = ACTIONS(3), + [anon_sym_BANG] = ACTIONS(5), + [anon_sym_LPAREN] = ACTIONS(7), + }, + [2] = + { + [sym__expression] = STATE(7), + [sym_not] = STATE(7), + [sym_and] = STATE(7), + [sym_or] = STATE(7), + [sym_equal] = STATE(7), + [sym_not_equal] = STATE(7), + [sym_parenthesized] = STATE(7), + [sym_identifier] = ACTIONS(3), + [anon_sym_BANG] = ACTIONS(5), + [anon_sym_LPAREN] = ACTIONS(7), + }, + [3] = + { + [sym__expression] = STATE(14), + [sym_not] = STATE(14), + [sym_and] = STATE(14), + [sym_or] = STATE(14), + [sym_equal] = STATE(14), + [sym_not_equal] = STATE(14), + [sym_parenthesized] = STATE(14), + [sym_identifier] = ACTIONS(3), + [anon_sym_BANG] = ACTIONS(5), + [anon_sym_LPAREN] = ACTIONS(7), + }, + [4] = + { + [sym__expression] = STATE(11), + [sym_not] = STATE(11), + [sym_and] = STATE(11), + [sym_or] = STATE(11), + [sym_equal] = STATE(11), + [sym_not_equal] = STATE(11), + [sym_parenthesized] = STATE(11), + [sym_identifier] = ACTIONS(3), + [anon_sym_BANG] = ACTIONS(5), + [anon_sym_LPAREN] = ACTIONS(7), + }, + [5] = + { + [sym__expression] = STATE(12), + [sym_not] = STATE(12), + [sym_and] = STATE(12), + [sym_or] = STATE(12), + [sym_equal] = STATE(12), + [sym_not_equal] = STATE(12), + [sym_parenthesized] = STATE(12), + [sym_identifier] = ACTIONS(3), + [anon_sym_BANG] = ACTIONS(5), + [anon_sym_LPAREN] = ACTIONS(7), + }, }; static const uint16_t ts_small_parse_table[] = { - [0] = 3, - ACTIONS(11), 1, - anon_sym_EQ_EQ, - ACTIONS(13), 1, - anon_sym_BANG_EQ, - ACTIONS(9), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [13] = 1, - ACTIONS(15), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [20] = 1, - ACTIONS(17), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [27] = 1, - ACTIONS(19), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [34] = 1, - ACTIONS(21), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [41] = 1, - ACTIONS(23), 4, - ts_builtin_sym_end, - anon_sym_AMP_AMP, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [48] = 2, - ACTIONS(27), 1, - anon_sym_AMP_AMP, - ACTIONS(25), 3, - ts_builtin_sym_end, - anon_sym_PIPE_PIPE, - anon_sym_RPAREN, - [57] = 3, - ACTIONS(27), 1, - anon_sym_AMP_AMP, - ACTIONS(29), 1, - ts_builtin_sym_end, - ACTIONS(31), 1, - anon_sym_PIPE_PIPE, - [67] = 3, - ACTIONS(27), 1, - anon_sym_AMP_AMP, - ACTIONS(31), 1, - anon_sym_PIPE_PIPE, - ACTIONS(33), 1, - anon_sym_RPAREN, - [77] = 1, - ACTIONS(35), 1, - ts_builtin_sym_end, - [81] = 1, - ACTIONS(37), 1, - sym_identifier, - [85] = 1, - ACTIONS(39), 1, - sym_identifier, + [0] = 3, + ACTIONS(11), + 1, + anon_sym_EQ_EQ, + ACTIONS(13), + 1, + anon_sym_BANG_EQ, + ACTIONS(9), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [13] = 1, + ACTIONS(15), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [20] = 1, + ACTIONS(17), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [27] = 1, + ACTIONS(19), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [34] = 1, + ACTIONS(21), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [41] = 1, + ACTIONS(23), + 4, + ts_builtin_sym_end, + anon_sym_AMP_AMP, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [48] = 2, + ACTIONS(27), + 1, + anon_sym_AMP_AMP, + ACTIONS(25), + 3, + ts_builtin_sym_end, + anon_sym_PIPE_PIPE, + anon_sym_RPAREN, + [57] = 3, + ACTIONS(27), + 1, + anon_sym_AMP_AMP, + ACTIONS(29), + 1, + ts_builtin_sym_end, + ACTIONS(31), + 1, + anon_sym_PIPE_PIPE, + [67] = 3, + ACTIONS(27), + 1, + anon_sym_AMP_AMP, + ACTIONS(31), + 1, + anon_sym_PIPE_PIPE, + ACTIONS(33), + 1, + anon_sym_RPAREN, + [77] = 1, + ACTIONS(35), + 1, + ts_builtin_sym_end, + [81] = 1, + ACTIONS(37), + 1, + sym_identifier, + [85] = 1, + ACTIONS(39), + 1, + sym_identifier, }; static const uint32_t ts_small_parse_table_map[] = { - [SMALL_STATE(6)] = 0, - [SMALL_STATE(7)] = 13, - [SMALL_STATE(8)] = 20, - [SMALL_STATE(9)] = 27, - [SMALL_STATE(10)] = 34, - [SMALL_STATE(11)] = 41, - [SMALL_STATE(12)] = 48, - [SMALL_STATE(13)] = 57, - [SMALL_STATE(14)] = 67, - [SMALL_STATE(15)] = 77, - [SMALL_STATE(16)] = 81, - [SMALL_STATE(17)] = 85, + [SMALL_STATE(6)] = 0, [SMALL_STATE(7)] = 13, [SMALL_STATE(8)] = 20, + [SMALL_STATE(9)] = 27, [SMALL_STATE(10)] = 34, [SMALL_STATE(11)] = 41, + [SMALL_STATE(12)] = 48, [SMALL_STATE(13)] = 57, [SMALL_STATE(14)] = 67, + [SMALL_STATE(15)] = 77, [SMALL_STATE(16)] = 81, [SMALL_STATE(17)] = 85, }; static const TSParseActionEntry ts_parse_actions[] = { - [0] = {.entry = {.count = 0, .reusable = false}}, - [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(), - [3] = {.entry = {.count = 1, .reusable = true}}, SHIFT(6), - [5] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2), - [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3), - [9] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__expression, 1), - [11] = {.entry = {.count = 1, .reusable = true}}, SHIFT(16), - [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(17), - [15] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_not, 2, .production_id = 1), - [17] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_equal, 3, .production_id = 2), - [19] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_not_equal, 3, .production_id = 2), - [21] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_parenthesized, 3, .production_id = 1), - [23] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_and, 3, .production_id = 2), - [25] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_or, 3, .production_id = 2), - [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(4), - [29] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source, 1), - [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(5), - [33] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10), - [35] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(), - [37] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8), - [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(9), + [0] = {.entry = {.count = 0, .reusable = false}}, + [1] = {.entry = {.count = 1, .reusable = false}}, + RECOVER(), + [3] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(6), + [5] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(2), + [7] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(3), + [9] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym__expression, 1), + [11] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(16), + [13] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(17), + [15] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_not, 2, .production_id = 1), + [17] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_equal, 3, .production_id = 2), + [19] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_not_equal, 3, .production_id = 2), + [21] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_parenthesized, 3, .production_id = 1), + [23] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_and, 3, .production_id = 2), + [25] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_or, 3, .production_id = 2), + [27] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(4), + [29] = {.entry = {.count = 1, .reusable = true}}, + REDUCE(sym_source, 1), + [31] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(5), + [33] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(10), + [35] = {.entry = {.count = 1, .reusable = true}}, + ACCEPT_INPUT(), + [37] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(8), + [39] = {.entry = {.count = 1, .reusable = true}}, + SHIFT(9), }; #ifdef __cplusplus @@ -497,30 +552,30 @@ extern "C" { extern const TSLanguage *tree_sitter_context_predicate(void) { static const TSLanguage language = { - .version = LANGUAGE_VERSION, - .symbol_count = SYMBOL_COUNT, - .alias_count = ALIAS_COUNT, - .token_count = TOKEN_COUNT, - .external_token_count = EXTERNAL_TOKEN_COUNT, - .state_count = STATE_COUNT, - .large_state_count = LARGE_STATE_COUNT, - .production_id_count = PRODUCTION_ID_COUNT, - .field_count = FIELD_COUNT, - .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, - .parse_table = &ts_parse_table[0][0], - .small_parse_table = ts_small_parse_table, - .small_parse_table_map = ts_small_parse_table_map, - .parse_actions = ts_parse_actions, - .symbol_names = ts_symbol_names, - .field_names = ts_field_names, - .field_map_slices = ts_field_map_slices, - .field_map_entries = ts_field_map_entries, - .symbol_metadata = ts_symbol_metadata, - .public_symbol_map = ts_symbol_map, - .alias_map = ts_non_terminal_alias_map, - .alias_sequences = &ts_alias_sequences[0][0], - .lex_modes = ts_lex_modes, - .lex_fn = ts_lex, + .version = LANGUAGE_VERSION, + .symbol_count = SYMBOL_COUNT, + .alias_count = ALIAS_COUNT, + .token_count = TOKEN_COUNT, + .external_token_count = EXTERNAL_TOKEN_COUNT, + .state_count = STATE_COUNT, + .large_state_count = LARGE_STATE_COUNT, + .production_id_count = PRODUCTION_ID_COUNT, + .field_count = FIELD_COUNT, + .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH, + .parse_table = &ts_parse_table[0][0], + .small_parse_table = ts_small_parse_table, + .small_parse_table_map = ts_small_parse_table_map, + .parse_actions = ts_parse_actions, + .symbol_names = ts_symbol_names, + .field_names = ts_field_names, + .field_map_slices = ts_field_map_slices, + .field_map_entries = ts_field_map_entries, + .symbol_metadata = ts_symbol_metadata, + .public_symbol_map = ts_symbol_map, + .alias_map = ts_non_terminal_alias_map, + .alias_sequences = &ts_alias_sequences[0][0], + .lex_modes = ts_lex_modes, + .lex_fn = ts_lex, }; return &language; } diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index ac9ef6f829..8246ea31ed 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -55,8 +55,8 @@ pub trait Entity: 'static { pub trait View: Entity + Sized { fn ui_name() -> &'static str; fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox; - fn on_focus(&mut self, _: &mut ViewContext) {} - fn on_blur(&mut self, _: &mut ViewContext) {} + fn on_focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext) {} + fn on_focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext) {} fn keymap_context(&self, _: &AppContext) -> keymap::Context { Self::default_keymap_context() } @@ -1903,7 +1903,7 @@ impl MutableAppContext { is_fullscreen: false, }, ); - root_view.update(this, |view, cx| view.on_focus(cx)); + root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx)); this.open_platform_window(window_id, window_options); (window_id, root_view) @@ -2500,14 +2500,16 @@ impl MutableAppContext { window.is_active = active; //Handle focus - let view_id = window.focused_view_id?; - if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { - if active { - view.on_focus(this, window_id, view_id); - } else { - view.on_blur(this, window_id, view_id); + let focused_id = window.focused_view_id?; + for view_id in this.parents(window_id, focused_id).collect::>() { + if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { + if active { + view.on_focus_in(this, window_id, view_id, focused_id); + } else { + view.on_focus_out(this, window_id, view_id, focused_id); + } + this.cx.views.insert((window_id, view_id), view); } - this.cx.views.insert((window_id, view_id), view); } let mut observations = this.window_activation_observations.clone(); @@ -2537,26 +2539,45 @@ impl MutableAppContext { blurred_id }); - if let Some(blurred_id) = blurred_id { - if let Some(mut blurred_view) = this.cx.views.remove(&(window_id, blurred_id)) { - blurred_view.on_blur(this, window_id, blurred_id); - this.cx.views.insert((window_id, blurred_id), blurred_view); + let blurred_parents = blurred_id + .map(|blurred_id| this.parents(window_id, blurred_id).collect::>()) + .unwrap_or_default(); + let focused_parents = focused_id + .map(|focused_id| this.parents(window_id, focused_id).collect::>()) + .unwrap_or_default(); - let mut subscriptions = this.focus_observations.clone(); - subscriptions - .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this)); + if let Some(blurred_id) = blurred_id { + for view_id in blurred_parents.iter().copied() { + // We've reached a common anscestor. Break. + if focused_parents.contains(&view_id) { + break; + } + + if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { + view.on_focus_out(this, window_id, view_id, blurred_id); + this.cx.views.insert((window_id, view_id), view); + } } + + let mut subscriptions = this.focus_observations.clone(); + subscriptions + .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this)); } if let Some(focused_id) = focused_id { - if let Some(mut focused_view) = this.cx.views.remove(&(window_id, focused_id)) { - focused_view.on_focus(this, window_id, focused_id); - this.cx.views.insert((window_id, focused_id), focused_view); - - let mut subscriptions = this.focus_observations.clone(); - subscriptions - .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this)); + for view_id in focused_parents { + if blurred_parents.contains(&view_id) { + break; + } + if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { + view.on_focus_in(this, window_id, view_id, focused_id); + this.cx.views.insert((window_id, focused_id), view); + } } + + let mut subscriptions = this.focus_observations.clone(); + subscriptions + .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this)); } }) } @@ -2742,7 +2763,7 @@ impl ReadView for MutableAppContext { if let Some(view) = self.cx.views.get(&(handle.window_id, handle.view_id)) { view.as_any().downcast_ref().expect("downcast is type safe") } else { - panic!("circular view reference"); + panic!("circular view reference for type {}", type_name::()); } } } @@ -3216,8 +3237,20 @@ pub trait AnyView { ) -> Option>>>; fn ui_name(&self) -> &'static str; fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox; - fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); - fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize); + fn on_focus_in( + &mut self, + cx: &mut MutableAppContext, + window_id: usize, + view_id: usize, + focused_id: usize, + ); + fn on_focus_out( + &mut self, + cx: &mut MutableAppContext, + window_id: usize, + view_id: usize, + focused_id: usize, + ); fn keymap_context(&self, cx: &AppContext) -> keymap::Context; fn debug_json(&self, cx: &AppContext) -> serde_json::Value; @@ -3242,6 +3275,14 @@ pub trait AnyView { window_id: usize, view_id: usize, ); + fn any_handle(&self, window_id: usize, view_id: usize, cx: &AppContext) -> AnyViewHandle { + AnyViewHandle::new( + window_id, + view_id, + self.as_any().type_id(), + cx.ref_counts.clone(), + ) + } } impl AnyView for T @@ -3275,14 +3316,48 @@ where View::render(self, &mut RenderContext::new(params, cx)) } - fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) { + fn on_focus_in( + &mut self, + cx: &mut MutableAppContext, + window_id: usize, + view_id: usize, + focused_id: usize, + ) { let mut cx = ViewContext::new(cx, window_id, view_id); - View::on_focus(self, &mut cx); + let focused_view_handle: AnyViewHandle = if view_id == focused_id { + cx.handle().into() + } else { + let focused_type = cx + .views + .get(&(window_id, focused_id)) + .unwrap() + .as_any() + .type_id(); + AnyViewHandle::new(window_id, focused_id, focused_type, cx.ref_counts.clone()) + }; + View::on_focus_in(self, focused_view_handle, &mut cx); } - fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) { + fn on_focus_out( + &mut self, + cx: &mut MutableAppContext, + window_id: usize, + view_id: usize, + blurred_id: usize, + ) { let mut cx = ViewContext::new(cx, window_id, view_id); - View::on_blur(self, &mut cx); + let blurred_view_handle: AnyViewHandle = if view_id == blurred_id { + cx.handle().into() + } else { + let blurred_type = cx + .views + .get(&(window_id, blurred_id)) + .unwrap() + .as_any() + .type_id(); + AnyViewHandle::new(window_id, blurred_id, blurred_type, cx.ref_counts.clone()) + }; + View::on_focus_out(self, blurred_view_handle, &mut cx); } fn keymap_context(&self, cx: &AppContext) -> keymap::Context { @@ -6665,12 +6740,16 @@ mod tests { "View" } - fn on_focus(&mut self, _: &mut ViewContext) { - self.events.lock().push(format!("{} focused", &self.name)); + fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { + if cx.handle().id() == focused.id() { + self.events.lock().push(format!("{} focused", &self.name)); + } } - fn on_blur(&mut self, _: &mut ViewContext) { - self.events.lock().push(format!("{} blurred", &self.name)); + fn on_focus_out(&mut self, blurred: AnyViewHandle, cx: &mut ViewContext) { + if cx.handle().id() == blurred.id() { + self.events.lock().push(format!("{} blurred", &self.name)); + } } } @@ -7018,7 +7097,7 @@ mod tests { let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1); let view_2 = cx.add_view(&view_1, |_| view_2); - let view_3 = cx.add_view(&view_2, |cx| { + cx.add_view(&view_2, |cx| { cx.focus_self(); view_3 }); diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 19b309116a..8a8c559cf3 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -4,8 +4,8 @@ use editor::{ }; use fuzzy::StringMatch; use gpui::{ - actions, elements::*, geometry::vector::Vector2F, AppContext, Entity, MouseState, - MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, + actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Entity, + MouseState, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, }; use language::Outline; use ordered_float::OrderedFloat; @@ -52,7 +52,7 @@ impl View for OutlineView { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index 6f247ebb6e..d3f729ba83 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -7,8 +7,8 @@ use gpui::{ geometry::vector::{vec2f, Vector2F}, keymap, platform::CursorStyle, - AppContext, Axis, Element, ElementBox, Entity, MouseButton, MouseState, MutableAppContext, - RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, + AnyViewHandle, AppContext, Axis, Element, ElementBox, Entity, MouseButton, MouseState, + MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev}; use settings::Settings; @@ -118,7 +118,7 @@ impl View for Picker { cx } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.query_editor); } } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 8f2305eaff..755bca20d6 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -3,8 +3,8 @@ use editor::{ }; use fuzzy::{StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext, - RenderContext, Task, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, + MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, }; use ordered_float::OrderedFloat; use picker::{Picker, PickerDelegate}; @@ -51,7 +51,7 @@ impl View for ProjectSymbolsView { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index ada785f854..d9a048b604 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -6,8 +6,8 @@ use crate::{ use collections::HashMap; use editor::{Anchor, Autoscroll, Editor}; use gpui::{ - actions, elements::*, impl_actions, platform::CursorStyle, Action, AppContext, Entity, - MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, + actions, elements::*, impl_actions, platform::CursorStyle, Action, AnyViewHandle, AppContext, + Entity, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use language::OffsetRangeExt; @@ -80,7 +80,7 @@ impl View for BufferSearchBar { "BufferSearchBar" } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.query_editor); } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index 6cbd4c7e2d..bcbeb0ac5f 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -6,9 +6,9 @@ use crate::{ use collections::HashMap; use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll, MAX_TAB_TITLE_LEN}; use gpui::{ - actions, elements::*, platform::CursorStyle, Action, AppContext, ElementBox, Entity, - ModelContext, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, - View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, + actions, elements::*, platform::CursorStyle, Action, AnyViewHandle, AppContext, ElementBox, + Entity, ModelContext, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, + Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle, }; use menu::Confirm; use project::{search::SearchQuery, Project}; @@ -190,7 +190,7 @@ impl View for ProjectSearchView { } } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { let handle = cx.weak_handle(); cx.update_global(|state: &mut ActiveSearches, cx| { state diff --git a/crates/terminal/src/connected_view.rs b/crates/terminal/src/connected_view.rs index 8b3099990a..ed9b994e75 100644 --- a/crates/terminal/src/connected_view.rs +++ b/crates/terminal/src/connected_view.rs @@ -6,7 +6,8 @@ use gpui::{ geometry::vector::Vector2F, impl_internal_actions, keymap::Keystroke, - AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle, + AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View, + ViewContext, ViewHandle, }; use workspace::pane; @@ -190,7 +191,7 @@ impl View for ConnectedView { .boxed() } - fn on_focus(&mut self, _cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, _cx: &mut ViewContext) { self.has_new_content = false; } diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 4e5ca37a9d..b71f6bdcb2 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -161,6 +161,13 @@ impl Dimensions for TerminalSize { fn columns(&self) -> usize { self.num_columns() } + + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { + cx.emit(Event::Activate); + cx.defer(|view, cx| { + cx.focus(view.content.handle()); + }); + } } #[derive(Error, Debug)] diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index 106e6ad429..d1e81b11a7 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -1,7 +1,7 @@ use fuzzy::{match_strings, StringMatch, StringMatchCandidate}; use gpui::{ - actions, elements::*, AppContext, Element, ElementBox, Entity, MouseState, MutableAppContext, - RenderContext, View, ViewContext, ViewHandle, + actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState, + MutableAppContext, RenderContext, View, ViewContext, ViewHandle, }; use picker::{Picker, PickerDelegate}; use settings::Settings; @@ -249,7 +249,7 @@ impl View for ThemeSelector { ChildView::new(self.picker.clone()).boxed() } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.picker); } } diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 572e1032b3..f2cfee3ec1 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -13,9 +13,9 @@ use gpui::{ }, impl_actions, impl_internal_actions, platform::{CursorStyle, NavigationDirection}, - AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, MouseButtonEvent, - MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, ViewHandle, - WeakViewHandle, + AnyViewHandle, AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, + MouseButtonEvent, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, + ViewHandle, WeakViewHandle, }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; @@ -830,6 +830,7 @@ impl Pane { pub fn focus_active_item(&mut self, cx: &mut ViewContext) { if let Some(active_item) = self.active_item() { cx.focus(active_item); + self.activate(cx); } } @@ -1210,8 +1211,12 @@ impl View for Pane { .named("pane") } - fn on_focus(&mut self, cx: &mut ViewContext) { - self.focus_active_item(cx); + fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { + if cx.handle().id() == focused.id() { + self.focus_active_item(cx); + } else { + self.activate(cx); + } } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 02ad18eb3d..c8e804f59b 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2489,7 +2489,7 @@ impl View for Workspace { .named("workspace") } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.focus(&self.active_pane); } } diff --git a/styles/package-lock.json b/styles/package-lock.json index 5499f1852c..582f1c8496 100644 --- a/styles/package-lock.json +++ b/styles/package-lock.json @@ -5,6 +5,7 @@ "requires": true, "packages": { "": { + "name": "styles", "version": "1.0.0", "license": "ISC", "dependencies": { From 049149320c5c0e6ace074450a860037544e1c2cd Mon Sep 17 00:00:00 2001 From: K Simmons Date: Mon, 8 Aug 2022 12:21:29 -0700 Subject: [PATCH 4/9] build fix --- crates/terminal/src/terminal.rs | 7 ------- crates/terminal/src/terminal_view.rs | 2 +- 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index b71f6bdcb2..4e5ca37a9d 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -161,13 +161,6 @@ impl Dimensions for TerminalSize { fn columns(&self) -> usize { self.num_columns() } - - fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.emit(Event::Activate); - cx.defer(|view, cx| { - cx.focus(view.content.handle()); - }); - } } #[derive(Error, Debug)] diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index ba6f49af02..75c9658bd6 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -152,7 +152,7 @@ impl View for TerminalView { } } - fn on_focus(&mut self, cx: &mut ViewContext) { + fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { cx.emit(Event::Activate); cx.defer(|view, cx| { cx.focus(view.content.handle()); From 6f180ed822ea5a2bb92e1ffc88ab035a7f6912c0 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 9 Aug 2022 15:09:38 -0700 Subject: [PATCH 5/9] Pane focus working. Modals seem broken now --- crates/diagnostics/src/diagnostics.rs | 4 --- crates/editor/src/editor.rs | 3 -- crates/editor/src/items.rs | 4 --- crates/editor/src/link_go_to_definition.rs | 5 ++- crates/editor/src/mouse_context_menu.rs | 3 +- crates/gpui/src/app.rs | 14 ++------ crates/search/src/project_search.rs | 31 ++---------------- crates/terminal/src/terminal.rs | 1 - crates/terminal/src/terminal_view.rs | 9 +---- crates/workspace/src/pane.rs | 34 +++++++++++-------- crates/workspace/src/workspace.rs | 38 ++++++++-------------- 11 files changed, 43 insertions(+), 103 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index 42a3bb9f13..facccadb76 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -568,10 +568,6 @@ impl workspace::Item for ProjectDiagnosticsEditor { unreachable!() } - fn should_activate_item_on_event(event: &Self::Event) -> bool { - Editor::should_activate_item_on_event(event) - } - fn should_update_tab_on_event(event: &Event) -> bool { Editor::should_update_tab_on_event(event) } diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 6e84b275aa..128dae965c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1561,7 +1561,6 @@ impl Editor { ) { if !self.focused { cx.focus_self(); - cx.emit(Event::Activate); } let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -1623,7 +1622,6 @@ impl Editor { ) { if !self.focused { cx.focus_self(); - cx.emit(Event::Activate); } let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx)); @@ -5969,7 +5967,6 @@ fn compute_scroll_position( #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub enum Event { - Activate, BufferEdited, Edited, Reparsed, diff --git a/crates/editor/src/items.rs b/crates/editor/src/items.rs index 9837c5070f..59f9a98448 100644 --- a/crates/editor/src/items.rs +++ b/crates/editor/src/items.rs @@ -469,10 +469,6 @@ impl Item for Editor { }) } - fn should_activate_item_on_event(event: &Event) -> bool { - matches!(event, Event::Activate) - } - fn should_close_item_on_event(event: &Event) -> bool { matches!(event, Event::Closed) } diff --git a/crates/editor/src/link_go_to_definition.rs b/crates/editor/src/link_go_to_definition.rs index cc4592b6d4..cbdeb30191 100644 --- a/crates/editor/src/link_go_to_definition.rs +++ b/crates/editor/src/link_go_to_definition.rs @@ -8,8 +8,8 @@ use util::TryFutureExt; use workspace::Workspace; use crate::{ - Anchor, DisplayPoint, Editor, EditorSnapshot, Event, GoToDefinition, GoToTypeDefinition, - Select, SelectPhase, + Anchor, DisplayPoint, Editor, EditorSnapshot, GoToDefinition, GoToTypeDefinition, Select, + SelectPhase, }; #[derive(Clone, PartialEq)] @@ -355,7 +355,6 @@ fn go_to_fetched_definition_of_kind( editor_handle.update(cx, |editor, cx| { if !editor.focused { cx.focus_self(); - cx.emit(Event::Activate); } }); diff --git a/crates/editor/src/mouse_context_menu.rs b/crates/editor/src/mouse_context_menu.rs index 3098e96e07..202b009e3c 100644 --- a/crates/editor/src/mouse_context_menu.rs +++ b/crates/editor/src/mouse_context_menu.rs @@ -2,7 +2,7 @@ use context_menu::ContextMenuItem; use gpui::{geometry::vector::Vector2F, impl_internal_actions, MutableAppContext, ViewContext}; use crate::{ - DisplayPoint, Editor, EditorMode, Event, FindAllReferences, GoToDefinition, GoToTypeDefinition, + DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition, Rename, SelectMode, ToggleCodeActions, }; @@ -25,7 +25,6 @@ pub fn deploy_context_menu( ) { if !editor.focused { cx.focus_self(); - cx.emit(Event::Activate); } // Don't show context menu for inline editors diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 8246ea31ed..722e8be997 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -1239,7 +1239,7 @@ impl MutableAppContext { let mut view = self .cx .views - .remove(&(params.window_id, params.view_id)) + .remove(&(window_id, view_id)) .ok_or(anyhow!("view not found"))?; let element = view.render(params, self); self.cx.views.insert((window_id, view_id), view); @@ -1781,7 +1781,7 @@ impl MutableAppContext { .cx .views .get(&(window_id, view_id)) - .expect("View passed to visit does not exist") + .unwrap() .keymap_context(self.as_ref()); match self.keystroke_matcher.push_keystroke( @@ -2548,11 +2548,6 @@ impl MutableAppContext { if let Some(blurred_id) = blurred_id { for view_id in blurred_parents.iter().copied() { - // We've reached a common anscestor. Break. - if focused_parents.contains(&view_id) { - break; - } - if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { view.on_focus_out(this, window_id, view_id, blurred_id); this.cx.views.insert((window_id, view_id), view); @@ -2566,12 +2561,9 @@ impl MutableAppContext { if let Some(focused_id) = focused_id { for view_id in focused_parents { - if blurred_parents.contains(&view_id) { - break; - } if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) { view.on_focus_in(this, window_id, view_id, focused_id); - this.cx.views.insert((window_id, focused_id), view); + this.cx.views.insert((window_id, view_id), view); } } diff --git a/crates/search/src/project_search.rs b/crates/search/src/project_search.rs index bcbeb0ac5f..475923d6ef 100644 --- a/crates/search/src/project_search.rs +++ b/crates/search/src/project_search.rs @@ -73,7 +73,6 @@ pub struct ProjectSearchView { regex: bool, query_contains_error: bool, active_match_index: Option, - results_editor_was_focused: bool, } pub struct ProjectSearchBar { @@ -197,12 +196,6 @@ impl View for ProjectSearchView { .0 .insert(self.model.read(cx).project.downgrade(), handle) }); - - if self.results_editor_was_focused && !self.model.read(cx).match_ranges.is_empty() { - self.focus_results_editor(cx); - } else { - cx.focus(&self.query_editor); - } } } @@ -330,14 +323,6 @@ impl Item for ProjectSearchView { .update(cx, |editor, cx| editor.navigate(data, cx)) } - fn should_activate_item_on_event(event: &Self::Event) -> bool { - if let ViewEvent::EditorEvent(editor_event) = event { - Editor::should_activate_item_on_event(editor_event) - } else { - false - } - } - fn should_update_tab_on_event(event: &ViewEvent) -> bool { matches!(event, ViewEvent::UpdateTab) } @@ -385,12 +370,6 @@ impl ProjectSearchView { cx.emit(ViewEvent::EditorEvent(event.clone())) }) .detach(); - cx.observe_focus(&query_editor, |this, _, focused, _| { - if focused { - this.results_editor_was_focused = false; - } - }) - .detach(); let results_editor = cx.add_view(|cx| { let mut editor = Editor::for_multibuffer(excerpts, Some(project), cx); @@ -399,12 +378,7 @@ impl ProjectSearchView { }); cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab)) .detach(); - cx.observe_focus(&results_editor, |this, _, focused, _| { - if focused { - this.results_editor_was_focused = true; - } - }) - .detach(); + cx.subscribe(&results_editor, |this, _, event, cx| { if matches!(event, editor::Event::SelectionsChanged { .. }) { this.update_match_index(cx); @@ -423,7 +397,6 @@ impl ProjectSearchView { regex, query_contains_error: false, active_match_index: None, - results_editor_was_focused: false, }; this.model_changed(false, cx); this @@ -905,6 +878,8 @@ impl ToolbarItemView for ProjectSearchBar { self.subscription = None; self.active_project_search = None; if let Some(search) = active_pane_item.and_then(|i| i.downcast::()) { + let query_editor = search.read(cx).query_editor.clone(); + cx.reparent(query_editor); self.subscription = Some(cx.observe(&search, |_, _, cx| cx.notify())); self.active_project_search = Some(search); ToolbarItemLocation::PrimaryLeft { diff --git a/crates/terminal/src/terminal.rs b/crates/terminal/src/terminal.rs index 4e5ca37a9d..b54685c458 100644 --- a/crates/terminal/src/terminal.rs +++ b/crates/terminal/src/terminal.rs @@ -60,7 +60,6 @@ const DEBUG_LINE_HEIGHT: f32 = 5.; pub enum Event { TitleChanged, CloseTerminal, - Activate, Bell, Wakeup, } diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index 75c9658bd6..a3492c2a94 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -153,10 +153,7 @@ impl View for TerminalView { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.emit(Event::Activate); - cx.defer(|view, cx| { - cx.focus(view.content.handle()); - }); + cx.focus(self.content.handle()); } fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context { @@ -314,10 +311,6 @@ impl Item for TerminalView { fn should_close_item_on_event(event: &Self::Event) -> bool { matches!(event, &Event::CloseTerminal) } - - fn should_activate_item_on_event(event: &Self::Event) -> bool { - matches!(event, &Event::Activate) - } } ///Get's the working directory for the given workspace, respecting the user's settings. diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index f2cfee3ec1..4a96699dee 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -13,9 +13,9 @@ use gpui::{ }, impl_actions, impl_internal_actions, platform::{CursorStyle, NavigationDirection}, - AnyViewHandle, AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, - MouseButtonEvent, MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, - ViewHandle, WeakViewHandle, + AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext, + ModelHandle, MouseButton, MouseButtonEvent, MutableAppContext, PromptLevel, Quad, + RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle, }; use project::{Project, ProjectEntryId, ProjectPath}; use serde::Deserialize; @@ -132,7 +132,7 @@ pub fn init(cx: &mut MutableAppContext) { } pub enum Event { - Activate, + Focused, ActivateItem { local: bool }, Remove, RemoveItem, @@ -144,6 +144,7 @@ pub struct Pane { items: Vec>, is_active: bool, active_item_index: usize, + last_focused_view: Option, autoscroll: bool, nav_history: Rc>, toolbar: ViewHandle, @@ -193,6 +194,7 @@ impl Pane { items: Vec::new(), is_active: true, active_item_index: 0, + last_focused_view: None, autoscroll: false, nav_history: Rc::new(RefCell::new(NavHistory { mode: NavigationMode::Normal, @@ -219,10 +221,6 @@ impl Pane { } } - pub fn activate(&self, cx: &mut ViewContext) { - cx.emit(Event::Activate); - } - pub fn go_back( workspace: &mut Workspace, pane: Option>, @@ -287,7 +285,7 @@ impl Pane { mode: NavigationMode, cx: &mut ViewContext, ) -> Task<()> { - workspace.activate_pane(pane.clone(), cx); + cx.focus(pane.clone()); let to_load = pane.update(cx, |pane, cx| { loop { @@ -523,7 +521,7 @@ impl Pane { self.focus_active_item(cx); } if activate_pane { - self.activate(cx); + cx.emit(Event::Focused); } self.autoscroll = true; cx.notify(); @@ -830,7 +828,6 @@ impl Pane { pub fn focus_active_item(&mut self, cx: &mut ViewContext) { if let Some(active_item) = self.active_item() { cx.focus(active_item); - self.activate(cx); } } @@ -1212,11 +1209,20 @@ impl View for Pane { } fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext) { - if cx.handle().id() == focused.id() { - self.focus_active_item(cx); + if cx.is_self_focused() { + if let Some(last_focused_view) = self + .last_focused_view + .as_ref() + .and_then(|handle| handle.upgrade(cx)) + { + cx.focus(last_focused_view); + } else { + self.focus_active_item(cx); + } } else { - self.activate(cx); + self.last_focused_view = Some(focused.downgrade()); } + cx.emit(Event::Focused); } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c8e804f59b..c4b625c725 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -297,9 +297,6 @@ pub trait Item: View { project: ModelHandle, cx: &mut ViewContext, ) -> Task>; - fn should_activate_item_on_event(_: &Self::Event) -> bool { - false - } fn should_close_item_on_event(_: &Self::Event) -> bool { false } @@ -577,15 +574,6 @@ impl ItemHandle for ViewHandle { return; } - if T::should_activate_item_on_event(event) { - pane.update(cx, |pane, cx| { - if let Some(ix) = pane.index_for_item(&item) { - pane.activate_item(ix, true, true, false, cx); - pane.activate(cx); - } - }); - } - if T::should_update_tab_on_event(event) { pane.update(cx, |_, cx| { cx.emit(pane::Event::ChangeItemTitle); @@ -1438,7 +1426,7 @@ impl Workspace { }) .detach(); self.panes.push(pane.clone()); - self.activate_pane(pane.clone(), cx); + cx.focus(pane.clone()); cx.emit(Event::PaneAdded(pane.clone())); pane } @@ -1533,7 +1521,6 @@ impl Workspace { } }); if let Some((pane, ix)) = result { - self.activate_pane(pane.clone(), cx); pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, false, cx)); true } else { @@ -1544,7 +1531,7 @@ impl Workspace { fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext) { let panes = self.center.panes(); if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) { - self.activate_pane(pane, cx); + cx.focus(pane); } else { self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx); } @@ -1560,7 +1547,7 @@ impl Workspace { let next_ix = (ix + 1) % panes.len(); panes[next_ix].clone() }; - self.activate_pane(next_pane, cx); + cx.focus(next_pane); } pub fn activate_previous_pane(&mut self, cx: &mut ViewContext) { @@ -1573,10 +1560,10 @@ impl Workspace { let prev_ix = if ix == 0 { panes.len() - 1 } else { ix - 1 }; panes[prev_ix].clone() }; - self.activate_pane(prev_pane, cx); + cx.focus(prev_pane); } - fn activate_pane(&mut self, pane: ViewHandle, cx: &mut ViewContext) { + fn handle_pane_focused(&mut self, pane: ViewHandle, cx: &mut ViewContext) { if self.active_pane != pane { self.active_pane .update(cx, |pane, cx| pane.set_active(false, cx)); @@ -1587,7 +1574,6 @@ impl Workspace { status_bar.set_active_pane(&self.active_pane, cx); }); self.active_item_path_changed(cx); - cx.focus(&self.active_pane); cx.notify(); } @@ -1614,8 +1600,8 @@ impl Workspace { pane::Event::Remove => { self.remove_pane(pane, cx); } - pane::Event::Activate => { - self.activate_pane(pane, cx); + pane::Event::Focused => { + self.handle_pane_focused(pane, cx); } pane::Event::ActivateItem { local } => { if *local { @@ -1648,7 +1634,6 @@ impl Workspace { ) -> Option> { pane.read(cx).active_item().map(|item| { let new_pane = self.add_pane(cx); - self.activate_pane(new_pane.clone(), cx); if let Some(clone) = item.clone_on_split(cx.as_mut()) { Pane::add_item(self, new_pane.clone(), clone, true, true, cx); } @@ -1661,7 +1646,7 @@ impl Workspace { fn remove_pane(&mut self, pane: ViewHandle, cx: &mut ViewContext) { if self.center.remove(&pane).unwrap() { self.panes.retain(|p| p != &pane); - self.activate_pane(self.panes.last().unwrap().clone(), cx); + cx.focus(self.panes.last().unwrap().clone()); self.unfollow(&pane, cx); self.last_leaders_by_pane.remove(&pane.downgrade()); cx.notify(); @@ -2490,7 +2475,10 @@ impl View for Workspace { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.active_pane); + if cx.is_self_focused() { + println!("Active Pane Focused"); + cx.focus(&self.active_pane); + } } } @@ -3110,7 +3098,7 @@ mod tests { // once for project entry 0, and once for project entry 2. After those two // prompts, the task should complete. let close = workspace.update(cx, |workspace, cx| { - workspace.activate_pane(left_pane.clone(), cx); + cx.focus(left_pane.clone()); Pane::close_items(workspace, left_pane.clone(), cx, |_| true) }); From f5a6a112c8318f8c766d2a211d5954ae86787411 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 9 Aug 2022 15:51:17 -0700 Subject: [PATCH 6/9] Address picker issues --- crates/command_palette/src/command_palette.rs | 4 +++- crates/file_finder/src/file_finder.rs | 4 +++- crates/outline/src/outline.rs | 4 +++- crates/picker/src/picker.rs | 4 +++- crates/project_symbols/src/project_symbols.rs | 4 +++- crates/search/src/buffer_search.rs | 4 +++- crates/terminal/src/terminal_view.rs | 4 +++- crates/theme_selector/src/theme_selector.rs | 4 +++- crates/workspace/src/workspace.rs | 1 - 9 files changed, 24 insertions(+), 9 deletions(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 5d6f6e14a8..6bef826e5b 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -134,7 +134,9 @@ impl View for CommandPalette { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.picker); + if cx.is_self_focused() { + cx.focus(&self.picker); + } } } diff --git a/crates/file_finder/src/file_finder.rs b/crates/file_finder/src/file_finder.rs index aeec799015..dbdc3ec329 100644 --- a/crates/file_finder/src/file_finder.rs +++ b/crates/file_finder/src/file_finder.rs @@ -54,7 +54,9 @@ impl View for FileFinder { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.picker); + if cx.is_self_focused() { + cx.focus(&self.picker); + } } } diff --git a/crates/outline/src/outline.rs b/crates/outline/src/outline.rs index 8a8c559cf3..414a48ed10 100644 --- a/crates/outline/src/outline.rs +++ b/crates/outline/src/outline.rs @@ -53,7 +53,9 @@ impl View for OutlineView { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.picker); + if cx.is_self_focused() { + cx.focus(&self.picker); + } } } diff --git a/crates/picker/src/picker.rs b/crates/picker/src/picker.rs index d3f729ba83..19a01fcbad 100644 --- a/crates/picker/src/picker.rs +++ b/crates/picker/src/picker.rs @@ -119,7 +119,9 @@ impl View for Picker { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.query_editor); + if cx.is_self_focused() { + cx.focus(&self.query_editor); + } } } diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 755bca20d6..c310cfb043 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -52,7 +52,9 @@ impl View for ProjectSymbolsView { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.picker); + if cx.is_self_focused() { + cx.focus(&self.picker); + } } } diff --git a/crates/search/src/buffer_search.rs b/crates/search/src/buffer_search.rs index d9a048b604..bc6f37202d 100644 --- a/crates/search/src/buffer_search.rs +++ b/crates/search/src/buffer_search.rs @@ -81,7 +81,9 @@ impl View for BufferSearchBar { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.query_editor); + if cx.is_self_focused() { + cx.focus(&self.query_editor); + } } fn render(&mut self, cx: &mut RenderContext) -> ElementBox { diff --git a/crates/terminal/src/terminal_view.rs b/crates/terminal/src/terminal_view.rs index a3492c2a94..1b4544a3dc 100644 --- a/crates/terminal/src/terminal_view.rs +++ b/crates/terminal/src/terminal_view.rs @@ -153,7 +153,9 @@ impl View for TerminalView { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(self.content.handle()); + if cx.is_self_focused() { + cx.focus(self.content.handle()); + } } fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context { diff --git a/crates/theme_selector/src/theme_selector.rs b/crates/theme_selector/src/theme_selector.rs index d1e81b11a7..dcda882168 100644 --- a/crates/theme_selector/src/theme_selector.rs +++ b/crates/theme_selector/src/theme_selector.rs @@ -250,6 +250,8 @@ impl View for ThemeSelector { } fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { - cx.focus(&self.picker); + if cx.is_self_focused() { + cx.focus(&self.picker); + } } } diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index c4b625c725..7043c3a683 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -2476,7 +2476,6 @@ impl View for Workspace { fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext) { if cx.is_self_focused() { - println!("Active Pane Focused"); cx.focus(&self.active_pane); } } From 3bdf05c0cdc389796fa4d22c1b05fc6c0f51f2dc Mon Sep 17 00:00:00 2001 From: K Simmons Date: Tue, 9 Aug 2022 16:11:01 -0700 Subject: [PATCH 7/9] fix up command palette --- crates/command_palette/src/command_palette.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs index 6bef826e5b..46327cf1bc 100644 --- a/crates/command_palette/src/command_palette.rs +++ b/crates/command_palette/src/command_palette.rs @@ -86,8 +86,8 @@ impl CommandPalette { let focused_view_id = cx.focused_view_id(window_id).unwrap_or(workspace.id()); cx.as_mut().defer(move |cx| { + let this = cx.add_view(workspace.clone(), |cx| Self::new(focused_view_id, cx)); workspace.update(cx, |workspace, cx| { - let this = cx.add_view(|cx| Self::new(focused_view_id, cx)); workspace.toggle_modal(cx, |_, cx| { cx.subscribe(&this, Self::on_event).detach(); this From d68f227ec4f2fd8ee87428cc6b085babbf16b7a5 Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 10 Aug 2022 16:26:53 -0700 Subject: [PATCH 8/9] Fix failing tests --- crates/collab/src/integration_tests.rs | 32 ++++++++++++++++++++++---- crates/gpui/src/app.rs | 3 ++- crates/workspace/src/workspace.rs | 13 +++++++++-- 3 files changed, 40 insertions(+), 8 deletions(-) diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs index 4118e5963c..02b8d2f81e 100644 --- a/crates/collab/src/integration_tests.rs +++ b/crates/collab/src/integration_tests.rs @@ -4246,7 +4246,10 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T // Clients A and B follow each other in split panes workspace_a.update(cx_a, |workspace, cx| { workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); - assert_ne!(*workspace.active_pane(), pane_a1); + let pane_a1 = pane_a1.clone(); + cx.defer(move |workspace, _| { + assert_ne!(*workspace.active_pane(), pane_a1); + }); }); workspace_a .update(cx_a, |workspace, cx| { @@ -4259,7 +4262,10 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T .unwrap(); workspace_b.update(cx_b, |workspace, cx| { workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx); - assert_ne!(*workspace.active_pane(), pane_b1); + let pane_b1 = pane_b1.clone(); + cx.defer(move |workspace, _| { + assert_ne!(*workspace.active_pane(), pane_b1); + }); }); workspace_b .update(cx_b, |workspace, cx| { @@ -4271,17 +4277,26 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T .await .unwrap(); + workspace_a.update(cx_a, |workspace, cx| { + workspace.activate_next_pane(cx); + }); + // Wait for focus effects to be fully flushed + workspace_a.update(cx_a, |workspace, _| { + assert_eq!(*workspace.active_pane(), pane_a1); + }); + workspace_a .update(cx_a, |workspace, cx| { - workspace.activate_next_pane(cx); - assert_eq!(*workspace.active_pane(), pane_a1); workspace.open_path((worktree_id, "3.txt"), true, cx) }) .await .unwrap(); + workspace_b.update(cx_b, |workspace, cx| { + workspace.activate_next_pane(cx); + }); + workspace_b .update(cx_b, |workspace, cx| { - workspace.activate_next_pane(cx); assert_eq!(*workspace.active_pane(), pane_b1); workspace.open_path((worktree_id, "4.txt"), true, cx) }) @@ -4311,17 +4326,24 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T Some((worktree_id, "3.txt").into()) ); workspace.activate_next_pane(cx); + }); + + workspace_a.update(cx_a, |workspace, cx| { assert_eq!( workspace.active_item(cx).unwrap().project_path(cx), Some((worktree_id, "4.txt").into()) ); }); + workspace_b.update(cx_b, |workspace, cx| { assert_eq!( workspace.active_item(cx).unwrap().project_path(cx), Some((worktree_id, "4.txt").into()) ); workspace.activate_next_pane(cx); + }); + + workspace_b.update(cx_b, |workspace, cx| { assert_eq!( workspace.active_item(cx).unwrap().project_path(cx), Some((worktree_id, "3.txt").into()) diff --git a/crates/gpui/src/app.rs b/crates/gpui/src/app.rs index 722e8be997..c028c5de73 100644 --- a/crates/gpui/src/app.rs +++ b/crates/gpui/src/app.rs @@ -7089,7 +7089,7 @@ mod tests { let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1); let view_2 = cx.add_view(&view_1, |_| view_2); - cx.add_view(&view_2, |cx| { + let _view_3 = cx.add_view(&view_2, |cx| { cx.focus_self(); view_3 }); @@ -7135,6 +7135,7 @@ mod tests { assert_eq!(&*actions.borrow(), &["2 a"]); actions.borrow_mut().clear(); + cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap()); assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]); diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 7043c3a683..5f188ef910 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,3 +1,7 @@ +/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in +/// which the workspace uses to change the activated pane. +/// This may cause issues when you're trying to write tests that use workspace focus to add items at +/// specific locations. pub mod pane; pub mod pane_group; pub mod sidebar; @@ -3088,16 +3092,21 @@ mod tests { workspace .split_pane(left_pane.clone(), SplitDirection::Right, cx) .unwrap(); - workspace.add_item(Box::new(cx.add_view(|_| item_3_4.clone())), cx); left_pane }); + //Need to cause an effect flush in order to respect new focus + workspace.update(cx, |workspace, cx| { + workspace.add_item(Box::new(cx.add_view(|_| item_3_4.clone())), cx); + cx.focus(left_pane.clone()); + }); + // When closing all of the items in the left pane, we should be prompted twice: // once for project entry 0, and once for project entry 2. After those two // prompts, the task should complete. + let close = workspace.update(cx, |workspace, cx| { - cx.focus(left_pane.clone()); Pane::close_items(workspace, left_pane.clone(), cx, |_| true) }); From 4d38dcfc54ecc634ecec2ee5623b0dd66f871f3e Mon Sep 17 00:00:00 2001 From: K Simmons Date: Wed, 10 Aug 2022 16:27:45 -0700 Subject: [PATCH 9/9] Fix workspace --- crates/workspace/src/workspace.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/workspace/src/workspace.rs b/crates/workspace/src/workspace.rs index 5f188ef910..0d4ced4293 100644 --- a/crates/workspace/src/workspace.rs +++ b/crates/workspace/src/workspace.rs @@ -1,5 +1,6 @@ /// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in /// which the workspace uses to change the activated pane. +/// /// This may cause issues when you're trying to write tests that use workspace focus to add items at /// specific locations. pub mod pane;