Add missing shortcuts in tooltips (#18282)

Fixes some missing shortcuts from Tooltips like the project search,
buffer search, quick action bar, ....


https://github.com/user-attachments/assets/d3a0160a-8d6e-4ddc-bf82-1fabeca42d59

This should hopefully help new users learn and discover some nice
keyboard shortcuts

Release Notes:

- Display keyboard shortcuts inside tooltips in the project search,
buffer search etc.
This commit is contained in:
Bennet Bo Fenner 2024-09-27 11:06:48 +02:00 committed by GitHub
parent a1d2e1106e
commit 1c5d9c221a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
9 changed files with 199 additions and 43 deletions

View file

@ -196,7 +196,7 @@
} }
}, },
{ {
"context": "BufferSearchBar && in_replace", "context": "BufferSearchBar && in_replace > Editor",
"bindings": { "bindings": {
"enter": "search::ReplaceNext", "enter": "search::ReplaceNext",
"ctrl-enter": "search::ReplaceAll" "ctrl-enter": "search::ReplaceAll"

View file

@ -232,7 +232,7 @@
} }
}, },
{ {
"context": "BufferSearchBar && in_replace", "context": "BufferSearchBar && in_replace > Editor",
"bindings": { "bindings": {
"enter": "search::ReplaceNext", "enter": "search::ReplaceNext",
"cmd-enter": "search::ReplaceAll" "cmd-enter": "search::ReplaceAll"

View file

@ -1,7 +1,7 @@
use editor::Editor; use editor::Editor;
use gpui::{ use gpui::{
Element, EventEmitter, IntoElement, ParentElement, Render, StyledText, Subscription, Element, EventEmitter, FocusableView, IntoElement, ParentElement, Render, StyledText,
ViewContext, Subscription, ViewContext,
}; };
use itertools::Itertools; use itertools::Itertools;
use std::cmp; use std::cmp;
@ -90,17 +90,30 @@ impl Render for Breadcrumbs {
ButtonLike::new("toggle outline view") ButtonLike::new("toggle outline view")
.child(breadcrumbs_stack) .child(breadcrumbs_stack)
.style(ButtonStyle::Transparent) .style(ButtonStyle::Transparent)
.on_click(move |_, cx| { .on_click({
if let Some(editor) = editor.upgrade() { let editor = editor.clone();
outline::toggle(editor, &editor::actions::ToggleOutline, cx) move |_, cx| {
if let Some(editor) = editor.upgrade() {
outline::toggle(editor, &editor::actions::ToggleOutline, cx)
}
} }
}) })
.tooltip(|cx| { .tooltip(move |cx| {
Tooltip::for_action( if let Some(editor) = editor.upgrade() {
"Show symbol outline", let focus_handle = editor.read(cx).focus_handle(cx);
&editor::actions::ToggleOutline, Tooltip::for_action_in(
cx, "Show symbol outline",
) &editor::actions::ToggleOutline,
&focus_handle,
cx,
)
} else {
Tooltip::for_action(
"Show symbol outline",
&editor::actions::ToggleOutline,
cx,
)
}
}), }),
), ),
None => element None => element

View file

@ -8,8 +8,8 @@ use editor::actions::{
use editor::{Editor, EditorSettings}; use editor::{Editor, EditorSettings};
use gpui::{ use gpui::{
Action, AnchorCorner, ClickEvent, ElementId, EventEmitter, InteractiveElement, ParentElement, Action, AnchorCorner, ClickEvent, ElementId, EventEmitter, FocusHandle, FocusableView,
Render, Styled, Subscription, View, ViewContext, WeakView, InteractiveElement, ParentElement, Render, Styled, Subscription, View, ViewContext, WeakView,
}; };
use search::{buffer_search, BufferSearchBar}; use search::{buffer_search, BufferSearchBar};
use settings::{Settings, SettingsStore}; use settings::{Settings, SettingsStore};
@ -110,12 +110,15 @@ impl Render for QuickActionBar {
) )
}; };
let focus_handle = editor.read(cx).focus_handle(cx);
let search_button = editor.is_singleton(cx).then(|| { let search_button = editor.is_singleton(cx).then(|| {
QuickActionBarButton::new( QuickActionBarButton::new(
"toggle buffer search", "toggle buffer search",
IconName::MagnifyingGlass, IconName::MagnifyingGlass,
!self.buffer_search_bar.read(cx).is_dismissed(), !self.buffer_search_bar.read(cx).is_dismissed(),
Box::new(buffer_search::Deploy::find()), Box::new(buffer_search::Deploy::find()),
focus_handle.clone(),
"Buffer Search", "Buffer Search",
{ {
let buffer_search_bar = self.buffer_search_bar.clone(); let buffer_search_bar = self.buffer_search_bar.clone();
@ -133,6 +136,7 @@ impl Render for QuickActionBar {
IconName::ZedAssistant, IconName::ZedAssistant,
false, false,
Box::new(InlineAssist::default()), Box::new(InlineAssist::default()),
focus_handle.clone(),
"Inline Assist", "Inline Assist",
{ {
let workspace = self.workspace.clone(); let workspace = self.workspace.clone();
@ -321,6 +325,7 @@ struct QuickActionBarButton {
icon: IconName, icon: IconName,
toggled: bool, toggled: bool,
action: Box<dyn Action>, action: Box<dyn Action>,
focus_handle: FocusHandle,
tooltip: SharedString, tooltip: SharedString,
on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>, on_click: Box<dyn Fn(&ClickEvent, &mut WindowContext)>,
} }
@ -331,6 +336,7 @@ impl QuickActionBarButton {
icon: IconName, icon: IconName,
toggled: bool, toggled: bool,
action: Box<dyn Action>, action: Box<dyn Action>,
focus_handle: FocusHandle,
tooltip: impl Into<SharedString>, tooltip: impl Into<SharedString>,
on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static, on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
) -> Self { ) -> Self {
@ -339,6 +345,7 @@ impl QuickActionBarButton {
icon, icon,
toggled, toggled,
action, action,
focus_handle,
tooltip: tooltip.into(), tooltip: tooltip.into(),
on_click: Box::new(on_click), on_click: Box::new(on_click),
} }
@ -355,7 +362,9 @@ impl RenderOnce for QuickActionBarButton {
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.style(ButtonStyle::Subtle) .style(ButtonStyle::Subtle)
.selected(self.toggled) .selected(self.toggled)
.tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx)) .tooltip(move |cx| {
Tooltip::for_action_in(tooltip.clone(), &*action, &self.focus_handle, cx)
})
.on_click(move |event, cx| (self.on_click)(event, cx)) .on_click(move |event, cx| (self.on_click)(event, cx))
} }
} }

View file

@ -13,9 +13,10 @@ use editor::{
}; };
use futures::channel::oneshot; use futures::channel::oneshot;
use gpui::{ use gpui::{
actions, div, impl_actions, Action, AppContext, ClickEvent, EventEmitter, FocusableView, Hsla, actions, div, impl_actions, Action, AppContext, ClickEvent, EventEmitter, FocusHandle,
InteractiveElement as _, IntoElement, KeyContext, ParentElement as _, Render, ScrollHandle, FocusableView, Hsla, InteractiveElement as _, IntoElement, KeyContext, ParentElement as _,
Styled, Subscription, Task, TextStyle, View, ViewContext, VisualContext as _, WindowContext, Render, ScrollHandle, Styled, Subscription, Task, TextStyle, View, ViewContext,
VisualContext as _, WindowContext,
}; };
use project::{ use project::{
search::SearchQuery, search::SearchQuery,
@ -142,6 +143,8 @@ impl Render for BufferSearchBar {
return div().id("search_bar"); return div().id("search_bar");
} }
let focus_handle = self.focus_handle(cx);
let narrow_mode = let narrow_mode =
self.scroll_handle.bounds().size.width / cx.rem_size() < 340. / BASE_REM_SIZE_IN_PX; self.scroll_handle.bounds().size.width / cx.rem_size() < 340. / BASE_REM_SIZE_IN_PX;
let hide_inline_icons = self.editor_needed_width let hide_inline_icons = self.editor_needed_width
@ -217,6 +220,7 @@ impl Render for BufferSearchBar {
div.children(supported_options.case.then(|| { div.children(supported_options.case.then(|| {
self.render_search_option_button( self.render_search_option_button(
SearchOptions::CASE_SENSITIVE, SearchOptions::CASE_SENSITIVE,
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_case_sensitive(&ToggleCaseSensitive, cx) this.toggle_case_sensitive(&ToggleCaseSensitive, cx)
}), }),
@ -225,6 +229,7 @@ impl Render for BufferSearchBar {
.children(supported_options.word.then(|| { .children(supported_options.word.then(|| {
self.render_search_option_button( self.render_search_option_button(
SearchOptions::WHOLE_WORD, SearchOptions::WHOLE_WORD,
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_whole_word(&ToggleWholeWord, cx) this.toggle_whole_word(&ToggleWholeWord, cx)
}), }),
@ -233,6 +238,7 @@ impl Render for BufferSearchBar {
.children(supported_options.regex.then(|| { .children(supported_options.regex.then(|| {
self.render_search_option_button( self.render_search_option_button(
SearchOptions::REGEX, SearchOptions::REGEX,
focus_handle.clone(),
cx.listener(|this, _, cx| this.toggle_regex(&ToggleRegex, cx)), cx.listener(|this, _, cx| this.toggle_regex(&ToggleRegex, cx)),
) )
})) }))
@ -250,7 +256,17 @@ impl Render for BufferSearchBar {
})) }))
.selected(self.replace_enabled) .selected(self.replace_enabled)
.size(ButtonSize::Compact) .size(ButtonSize::Compact)
.tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Toggle replace",
&ToggleReplace,
&focus_handle,
cx,
)
}
}),
) )
}) })
.when(supported_options.selection, |this| { .when(supported_options.selection, |this| {
@ -268,8 +284,16 @@ impl Render for BufferSearchBar {
})) }))
.selected(self.selection_search_enabled) .selected(self.selection_search_enabled)
.size(ButtonSize::Compact) .size(ButtonSize::Compact)
.tooltip(|cx| { .tooltip({
Tooltip::for_action("Toggle search selection", &ToggleSelection, cx) let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Toggle search selection",
&ToggleSelection,
&focus_handle,
cx,
)
}
}), }),
) )
}) })
@ -280,8 +304,16 @@ impl Render for BufferSearchBar {
IconButton::new("select-all", ui::IconName::SelectAll) IconButton::new("select-all", ui::IconName::SelectAll)
.on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone())) .on_click(|_, cx| cx.dispatch_action(SelectAllMatches.boxed_clone()))
.size(ButtonSize::Compact) .size(ButtonSize::Compact)
.tooltip(|cx| { .tooltip({
Tooltip::for_action("Select all matches", &SelectAllMatches, cx) let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Select all matches",
&SelectAllMatches,
&focus_handle,
cx,
)
}
}), }),
) )
.child(render_nav_button( .child(render_nav_button(
@ -289,12 +321,14 @@ impl Render for BufferSearchBar {
self.active_match_index.is_some(), self.active_match_index.is_some(),
"Select previous match", "Select previous match",
&SelectPrevMatch, &SelectPrevMatch,
focus_handle.clone(),
)) ))
.child(render_nav_button( .child(render_nav_button(
ui::IconName::ChevronRight, ui::IconName::ChevronRight,
self.active_match_index.is_some(), self.active_match_index.is_some(),
"Select next match", "Select next match",
&SelectNextMatch, &SelectNextMatch,
focus_handle.clone(),
)) ))
.when(!narrow_mode, |this| { .when(!narrow_mode, |this| {
this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child( this.child(h_flex().ml_2().min_w(rems_from_px(40.)).child(
@ -335,8 +369,16 @@ impl Render for BufferSearchBar {
.flex_none() .flex_none()
.child( .child(
IconButton::new("search-replace-next", ui::IconName::ReplaceNext) IconButton::new("search-replace-next", ui::IconName::ReplaceNext)
.tooltip(move |cx| { .tooltip({
Tooltip::for_action("Replace next", &ReplaceNext, cx) let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Replace next match",
&ReplaceNext,
&focus_handle,
cx,
)
}
}) })
.on_click( .on_click(
cx.listener(|this, _, cx| this.replace_next(&ReplaceNext, cx)), cx.listener(|this, _, cx| this.replace_next(&ReplaceNext, cx)),
@ -344,8 +386,16 @@ impl Render for BufferSearchBar {
) )
.child( .child(
IconButton::new("search-replace-all", ui::IconName::ReplaceAll) IconButton::new("search-replace-all", ui::IconName::ReplaceAll)
.tooltip(move |cx| { .tooltip({
Tooltip::for_action("Replace all", &ReplaceAll, cx) let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Replace all matches",
&ReplaceAll,
&focus_handle,
cx,
)
}
}) })
.on_click( .on_click(
cx.listener(|this, _, cx| this.replace_all(&ReplaceAll, cx)), cx.listener(|this, _, cx| this.replace_all(&ReplaceAll, cx)),
@ -719,10 +769,11 @@ impl BufferSearchBar {
fn render_search_option_button( fn render_search_option_button(
&self, &self,
option: SearchOptions, option: SearchOptions,
focus_handle: FocusHandle,
action: impl Fn(&ClickEvent, &mut WindowContext) + 'static, action: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
) -> impl IntoElement { ) -> impl IntoElement {
let is_active = self.search_options.contains(option); let is_active = self.search_options.contains(option);
option.as_button(is_active, action) option.as_button(is_active, focus_handle, action)
} }
pub fn focus_editor(&mut self, _: &FocusEditor, cx: &mut ViewContext<Self>) { pub fn focus_editor(&mut self, _: &FocusEditor, cx: &mut ViewContext<Self>) {
@ -1122,6 +1173,7 @@ impl BufferSearchBar {
}); });
cx.focus(handle); cx.focus(handle);
} }
fn toggle_replace(&mut self, _: &ToggleReplace, cx: &mut ViewContext<Self>) { fn toggle_replace(&mut self, _: &ToggleReplace, cx: &mut ViewContext<Self>) {
if self.active_searchable_item.is_some() { if self.active_searchable_item.is_some() {
self.replace_enabled = !self.replace_enabled; self.replace_enabled = !self.replace_enabled;
@ -1134,6 +1186,7 @@ impl BufferSearchBar {
cx.notify(); cx.notify();
} }
} }
fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) { fn replace_next(&mut self, _: &ReplaceNext, cx: &mut ViewContext<Self>) {
let mut should_propagate = true; let mut should_propagate = true;
if !self.dismissed && self.active_search.is_some() { if !self.dismissed && self.active_search.is_some() {
@ -1161,6 +1214,7 @@ impl BufferSearchBar {
cx.stop_propagation(); cx.stop_propagation();
} }
} }
pub fn replace_all(&mut self, _: &ReplaceAll, cx: &mut ViewContext<Self>) { pub fn replace_all(&mut self, _: &ReplaceAll, cx: &mut ViewContext<Self>) {
if !self.dismissed && self.active_search.is_some() { if !self.dismissed && self.active_search.is_some() {
if let Some(searchable_item) = self.active_searchable_item.as_ref() { if let Some(searchable_item) = self.active_searchable_item.as_ref() {

View file

@ -1551,6 +1551,7 @@ impl Render for ProjectSearchBar {
return div(); return div();
}; };
let search = search.read(cx); let search = search.read(cx);
let focus_handle = search.focus_handle(cx);
let query_column = h_flex() let query_column = h_flex()
.flex_1() .flex_1()
@ -1571,18 +1572,21 @@ impl Render for ProjectSearchBar {
h_flex() h_flex()
.child(SearchOptions::CASE_SENSITIVE.as_button( .child(SearchOptions::CASE_SENSITIVE.as_button(
self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx), self.is_option_enabled(SearchOptions::CASE_SENSITIVE, cx),
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx); this.toggle_search_option(SearchOptions::CASE_SENSITIVE, cx);
}), }),
)) ))
.child(SearchOptions::WHOLE_WORD.as_button( .child(SearchOptions::WHOLE_WORD.as_button(
self.is_option_enabled(SearchOptions::WHOLE_WORD, cx), self.is_option_enabled(SearchOptions::WHOLE_WORD, cx),
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_search_option(SearchOptions::WHOLE_WORD, cx); this.toggle_search_option(SearchOptions::WHOLE_WORD, cx);
}), }),
)) ))
.child(SearchOptions::REGEX.as_button( .child(SearchOptions::REGEX.as_button(
self.is_option_enabled(SearchOptions::REGEX, cx), self.is_option_enabled(SearchOptions::REGEX, cx),
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_search_option(SearchOptions::REGEX, cx); this.toggle_search_option(SearchOptions::REGEX, cx);
}), }),
@ -1603,7 +1607,17 @@ impl Render for ProjectSearchBar {
.map(|search| search.read(cx).filters_enabled) .map(|search| search.read(cx).filters_enabled)
.unwrap_or_default(), .unwrap_or_default(),
) )
.tooltip(|cx| Tooltip::for_action("Toggle filters", &ToggleFilters, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Toggle filters",
&ToggleFilters,
&focus_handle,
cx,
)
}
}),
) )
.child( .child(
IconButton::new("project-search-toggle-replace", IconName::Replace) IconButton::new("project-search-toggle-replace", IconName::Replace)
@ -1616,7 +1630,17 @@ impl Render for ProjectSearchBar {
.map(|search| search.read(cx).replace_enabled) .map(|search| search.read(cx).replace_enabled)
.unwrap_or_default(), .unwrap_or_default(),
) )
.tooltip(|cx| Tooltip::for_action("Toggle replace", &ToggleReplace, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Toggle replace",
&ToggleReplace,
&focus_handle,
cx,
)
}
}),
), ),
); );
@ -1650,8 +1674,16 @@ impl Render for ProjectSearchBar {
}) })
} }
})) }))
.tooltip(|cx| { .tooltip({
Tooltip::for_action("Go to previous match", &SelectPrevMatch, cx) let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Go to previous match",
&SelectPrevMatch,
&focus_handle,
cx,
)
}
}), }),
) )
.child( .child(
@ -1664,7 +1696,17 @@ impl Render for ProjectSearchBar {
}) })
} }
})) }))
.tooltip(|cx| Tooltip::for_action("Go to next match", &SelectNextMatch, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Go to next match",
&SelectNextMatch,
&focus_handle,
cx,
)
}
}),
) )
.child( .child(
h_flex() h_flex()
@ -1702,6 +1744,7 @@ impl Render for ProjectSearchBar {
.border_color(cx.theme().colors().border) .border_color(cx.theme().colors().border)
.rounded_lg() .rounded_lg()
.child(self.render_text_input(&search.replacement_editor, cx)); .child(self.render_text_input(&search.replacement_editor, cx));
let focus_handle = search.replacement_editor.read(cx).focus_handle(cx);
let replace_actions = h_flex().when(search.replace_enabled, |this| { let replace_actions = h_flex().when(search.replace_enabled, |this| {
this.child( this.child(
IconButton::new("project-search-replace-next", IconName::ReplaceNext) IconButton::new("project-search-replace-next", IconName::ReplaceNext)
@ -1712,7 +1755,17 @@ impl Render for ProjectSearchBar {
}) })
} }
})) }))
.tooltip(|cx| Tooltip::for_action("Replace next match", &ReplaceNext, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Replace next match",
&ReplaceNext,
&focus_handle,
cx,
)
}
}),
) )
.child( .child(
IconButton::new("project-search-replace-all", IconName::ReplaceAll) IconButton::new("project-search-replace-all", IconName::ReplaceAll)
@ -1723,7 +1776,17 @@ impl Render for ProjectSearchBar {
}) })
} }
})) }))
.tooltip(|cx| Tooltip::for_action("Replace all matches", &ReplaceAll, cx)), .tooltip({
let focus_handle = focus_handle.clone();
move |cx| {
Tooltip::for_action_in(
"Replace all matches",
&ReplaceAll,
&focus_handle,
cx,
)
}
}),
) )
}); });
h_flex() h_flex()
@ -1790,6 +1853,7 @@ impl Render for ProjectSearchBar {
search search
.search_options .search_options
.contains(SearchOptions::INCLUDE_IGNORED), .contains(SearchOptions::INCLUDE_IGNORED),
focus_handle.clone(),
cx.listener(|this, _, cx| { cx.listener(|this, _, cx| {
this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, cx); this.toggle_search_option(SearchOptions::INCLUDE_IGNORED, cx);
}), }),

View file

@ -1,7 +1,7 @@
use bitflags::bitflags; use bitflags::bitflags;
pub use buffer_search::BufferSearchBar; pub use buffer_search::BufferSearchBar;
use editor::SearchSettings; use editor::SearchSettings;
use gpui::{actions, Action, AppContext, IntoElement}; use gpui::{actions, Action, AppContext, FocusHandle, IntoElement};
use project::search::SearchQuery; use project::search::SearchQuery;
pub use project_search::ProjectSearchView; pub use project_search::ProjectSearchView;
use ui::{prelude::*, Tooltip}; use ui::{prelude::*, Tooltip};
@ -106,6 +106,7 @@ impl SearchOptions {
pub fn as_button( pub fn as_button(
&self, &self,
active: bool, active: bool,
focus_handle: FocusHandle,
action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static, action: impl Fn(&gpui::ClickEvent, &mut WindowContext) + 'static,
) -> impl IntoElement { ) -> impl IntoElement {
IconButton::new(self.label(), self.icon()) IconButton::new(self.label(), self.icon())
@ -115,7 +116,7 @@ impl SearchOptions {
.tooltip({ .tooltip({
let action = self.to_toggle_action(); let action = self.to_toggle_action();
let label = self.label(); let label = self.label();
move |cx| Tooltip::for_action(label, &*action, cx) move |cx| Tooltip::for_action_in(label, &*action, &focus_handle, cx)
}) })
} }
} }

View file

@ -1,4 +1,4 @@
use gpui::{Action, IntoElement}; use gpui::{Action, FocusHandle, IntoElement};
use ui::IconButton; use ui::IconButton;
use ui::{prelude::*, Tooltip}; use ui::{prelude::*, Tooltip};
@ -7,12 +7,13 @@ pub(super) fn render_nav_button(
active: bool, active: bool,
tooltip: &'static str, tooltip: &'static str,
action: &'static dyn Action, action: &'static dyn Action,
focus_handle: FocusHandle,
) -> impl IntoElement { ) -> impl IntoElement {
IconButton::new( IconButton::new(
SharedString::from(format!("search-nav-button-{}", action.name())), SharedString::from(format!("search-nav-button-{}", action.name())),
icon, icon,
) )
.on_click(|_, cx| cx.dispatch_action(action.boxed_clone())) .on_click(|_, cx| cx.dispatch_action(action.boxed_clone()))
.tooltip(move |cx| Tooltip::for_action(tooltip, action, cx)) .tooltip(move |cx| Tooltip::for_action_in(tooltip, action, &focus_handle, cx))
.disabled(!active) .disabled(!active)
} }

View file

@ -166,7 +166,16 @@ impl TerminalPanel {
pub fn asssistant_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) { pub fn asssistant_enabled(&mut self, enabled: bool, cx: &mut ViewContext<Self>) {
self.assistant_enabled = enabled; self.assistant_enabled = enabled;
if enabled { if enabled {
self.assistant_tab_bar_button = Some(cx.new_view(|_| InlineAssistTabBarButton).into()); let focus_handle = self
.pane
.read(cx)
.active_item()
.map(|item| item.focus_handle(cx))
.unwrap_or(self.focus_handle(cx));
self.assistant_tab_bar_button = Some(
cx.new_view(move |_| InlineAssistTabBarButton { focus_handle })
.into(),
);
} else { } else {
self.assistant_tab_bar_button = None; self.assistant_tab_bar_button = None;
} }
@ -859,16 +868,21 @@ impl Panel for TerminalPanel {
} }
} }
struct InlineAssistTabBarButton; struct InlineAssistTabBarButton {
focus_handle: FocusHandle,
}
impl Render for InlineAssistTabBarButton { impl Render for InlineAssistTabBarButton {
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement { fn render(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
let focus_handle = self.focus_handle.clone();
IconButton::new("terminal_inline_assistant", IconName::ZedAssistant) IconButton::new("terminal_inline_assistant", IconName::ZedAssistant)
.icon_size(IconSize::Small) .icon_size(IconSize::Small)
.on_click(cx.listener(|_, _, cx| { .on_click(cx.listener(|_, _, cx| {
cx.dispatch_action(InlineAssist::default().boxed_clone()); cx.dispatch_action(InlineAssist::default().boxed_clone());
})) }))
.tooltip(move |cx| Tooltip::for_action("Inline Assist", &InlineAssist::default(), cx)) .tooltip(move |cx| {
Tooltip::for_action_in("Inline Assist", &InlineAssist::default(), &focus_handle, cx)
})
} }
} }