Add buffer_search_deployed key context (#41193)

Josh Piasecki created

Release Notes:

- Pane key context now includes 'buffer_search_deployed' identifier

The goal of this PR is to add a new identifier in the key context that
will let the user target when the BufferSearchBar is deployed even if
they are not focused on it.

requested in #36930

Same rational as #40454 this will allow users to make more flexible
keybindings, by including some additional information higher up the key
context tree.

i thought adding this context to `Pane` seemed more appropriate than
`Editor` since `Terminal` also has a `BufferSearchBar`; however, I ran
into some import issues between BufferSearchBar, Search, Pane, and
Workspace which made it difficult to implement adding this context
directly inside `Pane`'s render function.

instead i added a new method called `contributes_context` to
`ToolbarItem` which will allow any toolbar item to add additional
context to the `Pane` level, which feels like it might come in handy.

here are some screen shots of the context being displayed in the Editor
and the Terminal

<img width="1653" height="1051" alt="Screenshot 2025-10-25 at 14 34 03"
src="https://github.com/user-attachments/assets/21c5b07a-8d36-4e0b-ad09-378b12d2ea38"
/>

<img width="1444" height="1167" alt="Screenshot 2025-10-25 at 12 32 21"
src="https://github.com/user-attachments/assets/86afe72f-b238-43cd-8230-9cb59fb93b2c"
/>

Change summary

crates/search/src/buffer_search.rs |  6 ++++++
crates/workspace/src/pane.rs       |  4 ++++
crates/workspace/src/toolbar.rs    | 19 +++++++++++++++++--
3 files changed, 27 insertions(+), 2 deletions(-)

Detailed changes

crates/search/src/buffer_search.rs 🔗

@@ -468,6 +468,12 @@ impl Focusable for BufferSearchBar {
 }
 
 impl ToolbarItemView for BufferSearchBar {
+    fn contribute_context(&self, context: &mut KeyContext, _cx: &App) {
+        if !self.dismissed {
+            context.add("buffer_search_deployed");
+        }
+    }
+
     fn set_active_pane_item(
         &mut self,
         item: Option<&dyn ItemHandle>,

crates/workspace/src/pane.rs 🔗

@@ -3702,6 +3702,10 @@ impl Render for Pane {
             key_context.add("EmptyPane");
         }
 
+        self.toolbar
+            .read(cx)
+            .contribute_context(&mut key_context, cx);
+
         let should_display_tab_bar = self.should_display_tab_bar.clone();
         let display_tab_bar = should_display_tab_bar(window, cx);
         let Some(project) = self.project.upgrade() else {

crates/workspace/src/toolbar.rs 🔗

@@ -1,7 +1,7 @@
 use crate::ItemHandle;
 use gpui::{
-    AnyView, App, Context, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled,
-    Window,
+    AnyView, App, Context, Entity, EntityId, EventEmitter, KeyContext, ParentElement as _, Render,
+    Styled, Window,
 };
 use ui::prelude::*;
 use ui::{h_flex, v_flex};
@@ -25,6 +25,8 @@ pub trait ToolbarItemView: Render + EventEmitter<ToolbarItemEvent> {
         _cx: &mut Context<Self>,
     ) {
     }
+
+    fn contribute_context(&self, _context: &mut KeyContext, _cx: &App) {}
 }
 
 trait ToolbarItemViewHandle: Send {
@@ -37,6 +39,7 @@ trait ToolbarItemViewHandle: Send {
         cx: &mut App,
     ) -> ToolbarItemLocation;
     fn focus_changed(&mut self, pane_focused: bool, window: &mut Window, cx: &mut App);
+    fn contribute_context(&self, context: &mut KeyContext, cx: &App);
 }
 
 #[derive(Copy, Clone, Debug, PartialEq)]
@@ -236,6 +239,14 @@ impl Toolbar {
     pub fn hidden(&self) -> bool {
         self.hidden
     }
+
+    pub fn contribute_context(&self, context: &mut KeyContext, cx: &App) {
+        for (item, location) in &self.items {
+            if *location != ToolbarItemLocation::Hidden {
+                item.contribute_context(context, cx);
+            }
+        }
+    }
 }
 
 impl<T: ToolbarItemView> ToolbarItemViewHandle for Entity<T> {
@@ -264,4 +275,8 @@ impl<T: ToolbarItemView> ToolbarItemViewHandle for Entity<T> {
             cx.notify();
         });
     }
+
+    fn contribute_context(&self, context: &mut KeyContext, cx: &App) {
+        self.read(cx).contribute_context(context, cx)
+    }
 }