From 6f7f0f30e280b0d896668b27b8be62b4c212f9c7 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Mon, 10 Feb 2025 02:16:12 +0200 Subject: [PATCH] Fix hover tooltips appearing after related element is pressed (#24540) Closes https://github.com/zed-industries/zed/issues/23894 Reworks all trigger declarations from `.trigger(element.tooltip(tooltip))` into `.trigger_with_tooltip(element, tooltip)` , with new API disallowing simultaneous trigger and tooltip display. All existing `.trigger(` calls were replaced, except 2 not applicable (in dock.rs and pane.rs), 15 left as ones without tooltips, and 2 unchanged places in `inline_completion_button.rs`, where https://github.com/zed-industries/zed/blob/0f7bb2e9fd6dc1fe3f0127de19df372f75ad0c4f/crates/inline_completion_button/src/inline_completion_button.rs#L311-L319 `with_animation` does not allow us to simply use the same approach. Release Notes: - Fixed hover tooltips appearing after related element is pressed --------- Co-authored-by: Danilo Leal --- crates/assistant/src/assistant_panel.rs | 6 +-- crates/assistant/src/inline_assistant.rs | 32 +++++++-------- .../src/terminal_inline_assistant.rs | 32 +++++++-------- .../src/assistant_model_selector.rs | 18 ++++----- crates/assistant2/src/assistant_panel.rs | 12 +++--- crates/assistant2/src/context_strip.rs | 28 ++++++------- .../src/context_editor.rs | 12 +++--- .../src/slash_command_picker.rs | 25 +++++++++--- crates/editor/src/hunk_diff.rs | 15 +------ crates/git_ui/src/git_panel.rs | 1 + crates/git_ui/src/repository_selector.rs | 27 +++++++++---- .../src/inline_completion_button.rs | 39 +++++-------------- .../src/language_model_selector.rs | 29 ++++++++++---- crates/repl/src/components/kernel_options.rs | 25 +++++++++--- crates/terminal_view/src/terminal_panel.rs | 13 +++---- crates/title_bar/src/application_menu.rs | 8 ++-- crates/title_bar/src/title_bar.rs | 13 +++---- crates/ui/src/components/popover_menu.rs | 26 ++++++++++++- crates/workspace/src/pane.rs | 13 +++---- crates/zed/src/zed/quick_action_bar.rs | 16 +++----- .../zed/src/zed/quick_action_bar/repl_menu.rs | 8 ++-- 21 files changed, 218 insertions(+), 180 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index b3b11fa9c7..d5e1643589 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -250,10 +250,10 @@ impl AssistantPanel { ) .child( PopoverMenu::new("assistant-panel-popover-menu") - .trigger( + .trigger_with_tooltip( IconButton::new("menu", IconName::EllipsisVertical) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("Toggle Assistant Menu")), + .icon_size(IconSize::Small), + Tooltip::text("Toggle Assistant Menu"), ) .menu(move |window, cx| { let zoom_label = if _pane.read(cx).is_zoomed() { diff --git a/crates/assistant/src/inline_assistant.rs b/crates/assistant/src/inline_assistant.rs index 53f142c029..286440f989 100644 --- a/crates/assistant/src/inline_assistant.rs +++ b/crates/assistant/src/inline_assistant.rs @@ -1595,22 +1595,22 @@ impl Render for PromptEditor { IconButton::new("context", IconName::SettingsAlt) .shape(IconButtonShape::Square) .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .tooltip(move |window, cx| { - Tooltip::with_meta( - format!( - "Using {}", - LanguageModelRegistry::read_global(cx) - .active_model() - .map(|model| model.name().0) - .unwrap_or_else(|| "No model selected".into()), - ), - None, - "Change Model", - window, - cx, - ) - }), + .icon_color(Color::Muted), + move |window, cx| { + Tooltip::with_meta( + format!( + "Using {}", + LanguageModelRegistry::read_global(cx) + .active_model() + .map(|model| model.name().0) + .unwrap_or_else(|| "No model selected".into()), + ), + None, + "Change Model", + window, + cx, + ) + }, )) .map(|el| { let CodegenStatus::Error(error) = self.codegen.read(cx).status(cx) else { diff --git a/crates/assistant/src/terminal_inline_assistant.rs b/crates/assistant/src/terminal_inline_assistant.rs index 4547ea8e67..a7f1d19667 100644 --- a/crates/assistant/src/terminal_inline_assistant.rs +++ b/crates/assistant/src/terminal_inline_assistant.rs @@ -646,22 +646,22 @@ impl Render for PromptEditor { IconButton::new("context", IconName::SettingsAlt) .shape(IconButtonShape::Square) .icon_size(IconSize::Small) - .icon_color(Color::Muted) - .tooltip(move |window, cx| { - Tooltip::with_meta( - format!( - "Using {}", - LanguageModelRegistry::read_global(cx) - .active_model() - .map(|model| model.name().0) - .unwrap_or_else(|| "No model selected".into()), - ), - None, - "Change Model", - window, - cx, - ) - }), + .icon_color(Color::Muted), + move |window, cx| { + Tooltip::with_meta( + format!( + "Using {}", + LanguageModelRegistry::read_global(cx) + .active_model() + .map(|model| model.name().0) + .unwrap_or_else(|| "No model selected".into()), + ), + None, + "Change Model", + window, + cx, + ) + }, )) .children( if let CodegenStatus::Error(error) = &self.codegen.read(cx).status { diff --git a/crates/assistant2/src/assistant_model_selector.rs b/crates/assistant2/src/assistant_model_selector.rs index cca0454bf1..0308757e59 100644 --- a/crates/assistant2/src/assistant_model_selector.rs +++ b/crates/assistant2/src/assistant_model_selector.rs @@ -74,16 +74,16 @@ impl Render for AssistantModelSelector { .color(Color::Muted) .size(IconSize::XSmall), ), + ), + move |window, cx| { + Tooltip::for_action_in( + "Change Model", + &ToggleModelSelector, + &focus_handle, + window, + cx, ) - .tooltip(move |window, cx| { - Tooltip::for_action_in( - "Change Model", - &ToggleModelSelector, - &focus_handle, - window, - cx, - ) - }), + }, ) .with_handle(self.menu_handle.clone()) } diff --git a/crates/assistant2/src/assistant_panel.rs b/crates/assistant2/src/assistant_panel.rs index 45f3d15a81..bfdeeb545b 100644 --- a/crates/assistant2/src/assistant_panel.rs +++ b/crates/assistant2/src/assistant_panel.rs @@ -660,11 +660,11 @@ impl AssistantPanel { .gap(DynamicSpacing::Base02.rems(cx)) .child( PopoverMenu::new("assistant-toolbar-new-popover-menu") - .trigger( + .trigger_with_tooltip( IconButton::new("new", IconName::Plus) .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle) - .tooltip(Tooltip::text("New…")), + .style(ButtonStyle::Subtle), + Tooltip::text("New…"), ) .anchor(Corner::TopRight) .with_handle(self.new_item_context_menu_handle.clone()) @@ -677,11 +677,11 @@ impl AssistantPanel { ) .child( PopoverMenu::new("assistant-toolbar-history-popover-menu") - .trigger( + .trigger_with_tooltip( IconButton::new("open-history", IconName::HistoryRerun) .icon_size(IconSize::Small) - .style(ButtonStyle::Subtle) - .tooltip(Tooltip::text("History…")), + .style(ButtonStyle::Subtle), + Tooltip::text("History…"), ) .anchor(Corner::TopRight) .with_handle(self.open_history_context_menu_handle.clone()) diff --git a/crates/assistant2/src/context_strip.rs b/crates/assistant2/src/context_strip.rs index d7b1503713..317eaad8a1 100644 --- a/crates/assistant2/src/context_strip.rs +++ b/crates/assistant2/src/context_strip.rs @@ -411,22 +411,22 @@ impl Render for ContextStrip { Some(context_picker.clone()) }) - .trigger( + .trigger_with_tooltip( IconButton::new("add-context", IconName::Plus) .icon_size(IconSize::Small) - .style(ui::ButtonStyle::Filled) - .tooltip({ - let focus_handle = focus_handle.clone(); - move |window, cx| { - Tooltip::for_action_in( - "Add Context", - &ToggleContextPicker, - &focus_handle, - window, - cx, - ) - } - }), + .style(ui::ButtonStyle::Filled), + { + let focus_handle = focus_handle.clone(); + move |window, cx| { + Tooltip::for_action_in( + "Add Context", + &ToggleContextPicker, + &focus_handle, + window, + cx, + ) + } + }, ) .attach(gpui::Corner::TopLeft) .anchor(gpui::Corner::BottomLeft) diff --git a/crates/assistant_context_editor/src/context_editor.rs b/crates/assistant_context_editor/src/context_editor.rs index 290cff13fa..fcfce741c1 100644 --- a/crates/assistant_context_editor/src/context_editor.rs +++ b/crates/assistant_context_editor/src/context_editor.rs @@ -2359,8 +2359,8 @@ impl ContextEditor { .icon(IconName::Plus) .icon_size(IconSize::Small) .icon_color(Color::Muted) - .icon_position(IconPosition::Start) - .tooltip(Tooltip::text("Type / to insert via keyboard")), + .icon_position(IconPosition::Start), + Tooltip::text("Type / to insert via keyboard"), ) } @@ -3323,10 +3323,10 @@ impl Render for ContextEditorToolbarItem { .color(Color::Muted) .size(IconSize::XSmall), ), - ) - .tooltip(move |window, cx| { - Tooltip::for_action("Change Model", &ToggleModelSelector, window, cx) - }), + ), + move |window, cx| { + Tooltip::for_action("Change Model", &ToggleModelSelector, window, cx) + }, ) .with_handle(self.language_model_selector_menu_handle.clone()), ) diff --git a/crates/assistant_context_editor/src/slash_command_picker.rs b/crates/assistant_context_editor/src/slash_command_picker.rs index 373e5f09dd..3bdc316030 100644 --- a/crates/assistant_context_editor/src/slash_command_picker.rs +++ b/crates/assistant_context_editor/src/slash_command_picker.rs @@ -1,17 +1,22 @@ use std::sync::Arc; use assistant_slash_command::SlashCommandWorkingSet; -use gpui::{AnyElement, DismissEvent, SharedString, Task, WeakEntity}; +use gpui::{AnyElement, AnyView, DismissEvent, SharedString, Task, WeakEntity}; use picker::{Picker, PickerDelegate, PickerEditorPosition}; use ui::{prelude::*, ListItem, ListItemSpacing, PopoverMenu, PopoverTrigger, Tooltip}; use crate::context_editor::ContextEditor; #[derive(IntoElement)] -pub(super) struct SlashCommandSelector { +pub(super) struct SlashCommandSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ working_set: Arc, active_context_editor: WeakEntity, trigger: T, + tooltip: TT, } #[derive(Clone)] @@ -48,16 +53,22 @@ pub(crate) struct SlashCommandDelegate { selected_index: usize, } -impl SlashCommandSelector { +impl SlashCommandSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ pub(crate) fn new( working_set: Arc, active_context_editor: WeakEntity, trigger: T, + tooltip: TT, ) -> Self { SlashCommandSelector { working_set, active_context_editor, trigger, + tooltip, } } } @@ -241,7 +252,11 @@ impl PickerDelegate for SlashCommandDelegate { } } -impl RenderOnce for SlashCommandSelector { +impl RenderOnce for SlashCommandSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let all_models = self .working_set @@ -322,7 +337,7 @@ impl RenderOnce for SlashCommandSelector { .ok(); PopoverMenu::new("model-switcher") .menu(move |_window, _cx| Some(picker_view.clone())) - .trigger(self.trigger) + .trigger_with_tooltip(self.trigger, self.tooltip) .attach(gpui::Corner::TopLeft) .anchor(gpui::Corner::BottomLeft) .offset(gpui::Point { diff --git a/crates/editor/src/hunk_diff.rs b/crates/editor/src/hunk_diff.rs index 8bed3e2ccb..03dac81d65 100644 --- a/crates/editor/src/hunk_diff.rs +++ b/crates/editor/src/hunk_diff.rs @@ -763,7 +763,7 @@ impl Editor { this.child({ let focus = editor.focus_handle(cx); PopoverMenu::new("hunk-controls-dropdown") - .trigger( + .trigger_with_tooltip( IconButton::new( "toggle_editor_selections_icon", IconName::EllipsisVertical, @@ -774,19 +774,8 @@ impl Editor { .toggle_state( hunk_controls_menu_handle .is_deployed(), - ) - .when( - !hunk_controls_menu_handle - .is_deployed(), - |this| { - this.tooltip(|_, cx| { - Tooltip::simple( - "Hunk Controls", - cx, - ) - }) - }, ), + Tooltip::simple("Hunk Controls", cx), ) .anchor(Corner::TopRight) .with_handle(hunk_controls_menu_handle) diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index d7aa40f8d9..c92eb56e52 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1161,6 +1161,7 @@ impl GitPanel { ButtonLike::new("active-repository") .style(ButtonStyle::Subtle) .child(Label::new(repository_display_name).size(LabelSize::Small)), + Tooltip::text("Select a repository"), ) } diff --git a/crates/git_ui/src/repository_selector.rs b/crates/git_ui/src/repository_selector.rs index ff8cfa406e..e5d9c1839a 100644 --- a/crates/git_ui/src/repository_selector.rs +++ b/crates/git_ui/src/repository_selector.rs @@ -1,6 +1,6 @@ use gpui::{ - AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, - Task, WeakEntity, + AnyElement, AnyView, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, + Subscription, Task, WeakEntity, }; use picker::{Picker, PickerDelegate}; use project::{ @@ -79,20 +79,27 @@ impl Render for RepositorySelector { } #[derive(IntoElement)] -pub struct RepositorySelectorPopoverMenu +pub struct RepositorySelectorPopoverMenu where - T: PopoverTrigger, + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, { repository_selector: Entity, trigger: T, + tooltip: TT, handle: Option>, } -impl RepositorySelectorPopoverMenu { - pub fn new(repository_selector: Entity, trigger: T) -> Self { +impl RepositorySelectorPopoverMenu +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ + pub fn new(repository_selector: Entity, trigger: T, tooltip: TT) -> Self { Self { repository_selector, trigger, + tooltip, handle: None, } } @@ -103,13 +110,17 @@ impl RepositorySelectorPopoverMenu { } } -impl RenderOnce for RepositorySelectorPopoverMenu { +impl RenderOnce for RepositorySelectorPopoverMenu +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { let repository_selector = self.repository_selector.clone(); PopoverMenu::new("repository-switcher") .menu(move |_window, _cx| Some(repository_selector.clone())) - .trigger(self.trigger) + .trigger_with_tooltip(self.trigger, self.tooltip) .attach(gpui::Corner::BottomLeft) .when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle)) } diff --git a/crates/inline_completion_button/src/inline_completion_button.rs b/crates/inline_completion_button/src/inline_completion_button.rs index ce1b7bcd83..c292b75f12 100644 --- a/crates/inline_completion_button/src/inline_completion_button.rs +++ b/crates/inline_completion_button/src/inline_completion_button.rs @@ -142,9 +142,12 @@ impl Render for InlineCompletionButton { }) }) .anchor(Corner::BottomRight) - .trigger(IconButton::new("copilot-icon", icon).tooltip(|window, cx| { - Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx) - })) + .trigger_with_tooltip( + IconButton::new("copilot-icon", icon), + |window, cx| { + Tooltip::for_action("GitHub Copilot", &ToggleMenu, window, cx) + }, + ) .with_handle(self.popover_menu_handle.clone()), ) } @@ -211,7 +214,8 @@ impl Render for InlineCompletionButton { _ => None, }) .anchor(Corner::BottomRight) - .trigger(IconButton::new("supermaven-icon", icon).tooltip( + .trigger_with_tooltip( + IconButton::new("supermaven-icon", icon), move |window, cx| { if has_menu { Tooltip::for_action( @@ -224,7 +228,7 @@ impl Render for InlineCompletionButton { Tooltip::text(tooltip_text.clone())(window, cx) } }, - )) + ) .with_handle(self.popover_menu_handle.clone()), ); } @@ -287,31 +291,6 @@ impl Render for InlineCompletionButton { .when(enabled && !show_editor_predictions, |this| { this.indicator(Indicator::dot().color(Color::Muted)) .indicator_border_color(Some(cx.theme().colors().status_bar_background)) - }) - .when(!self.popover_menu_handle.is_deployed(), |element| { - element.tooltip(move |window, cx| { - if enabled { - if show_editor_predictions { - Tooltip::for_action("Edit Prediction", &ToggleMenu, window, cx) - } else { - Tooltip::with_meta( - "Edit Prediction", - Some(&ToggleMenu), - "Hidden For This File", - window, - cx, - ) - } - } else { - Tooltip::with_meta( - "Edit Prediction", - Some(&ToggleMenu), - "Disabled For This File", - window, - cx, - ) - } - }) }); let this = cx.entity().clone(); diff --git a/crates/language_model_selector/src/language_model_selector.rs b/crates/language_model_selector/src/language_model_selector.rs index 10e8b57d68..78e5ed29b4 100644 --- a/crates/language_model_selector/src/language_model_selector.rs +++ b/crates/language_model_selector/src/language_model_selector.rs @@ -2,7 +2,7 @@ use std::sync::Arc; use feature_flags::ZedPro; use gpui::{ - Action, AnyElement, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, + Action, AnyElement, AnyView, App, DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Subscription, Task, WeakEntity, }; use language_model::{LanguageModel, LanguageModelAvailability, LanguageModelRegistry}; @@ -115,20 +115,31 @@ impl Render for LanguageModelSelector { } #[derive(IntoElement)] -pub struct LanguageModelSelectorPopoverMenu +pub struct LanguageModelSelectorPopoverMenu where - T: PopoverTrigger, + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, { language_model_selector: Entity, trigger: T, + tooltip: TT, handle: Option>, } -impl LanguageModelSelectorPopoverMenu { - pub fn new(language_model_selector: Entity, trigger: T) -> Self { +impl LanguageModelSelectorPopoverMenu +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ + pub fn new( + language_model_selector: Entity, + trigger: T, + tooltip: TT, + ) -> Self { Self { language_model_selector, trigger, + tooltip, handle: None, } } @@ -139,13 +150,17 @@ impl LanguageModelSelectorPopoverMenu { } } -impl RenderOnce for LanguageModelSelectorPopoverMenu { +impl RenderOnce for LanguageModelSelectorPopoverMenu +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ fn render(self, _window: &mut Window, _cx: &mut App) -> impl IntoElement { let language_model_selector = self.language_model_selector.clone(); PopoverMenu::new("model-switcher") .menu(move |_window, _cx| Some(language_model_selector.clone())) - .trigger(self.trigger) + .trigger_with_tooltip(self.trigger, self.tooltip) .anchor(gpui::Corner::BottomRight) .when_some(self.handle.clone(), |menu, handle| menu.with_handle(handle)) .offset(gpui::Point { diff --git a/crates/repl/src/components/kernel_options.rs b/crates/repl/src/components/kernel_options.rs index 4c00977cf6..57ee4cdcef 100644 --- a/crates/repl/src/components/kernel_options.rs +++ b/crates/repl/src/components/kernel_options.rs @@ -2,6 +2,7 @@ use crate::kernels::KernelSpecification; use crate::repl_store::ReplStore; use crate::KERNEL_DOCS_URL; +use gpui::AnyView; use gpui::DismissEvent; use gpui::FontWeight; @@ -19,10 +20,15 @@ use ui::{prelude::*, ListItem, PopoverMenu, PopoverMenuHandle, PopoverTrigger}; type OnSelect = Box; #[derive(IntoElement)] -pub struct KernelSelector { +pub struct KernelSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ handle: Option>>, on_select: OnSelect, trigger: T, + tooltip: TT, info_text: Option, worktree_id: WorktreeId, } @@ -44,12 +50,17 @@ fn truncate_path(path: &SharedString, max_length: usize) -> SharedString { } } -impl KernelSelector { - pub fn new(on_select: OnSelect, worktree_id: WorktreeId, trigger: T) -> Self { +impl KernelSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ + pub fn new(on_select: OnSelect, worktree_id: WorktreeId, trigger: T, tooltip: TT) -> Self { KernelSelector { on_select, handle: None, trigger, + tooltip, info_text: None, worktree_id, } @@ -235,7 +246,11 @@ impl PickerDelegate for KernelPickerDelegate { } } -impl RenderOnce for KernelSelector { +impl RenderOnce for KernelSelector +where + T: PopoverTrigger + ButtonCommon, + TT: Fn(&mut Window, &mut App) -> AnyView + 'static, +{ fn render(self, window: &mut Window, cx: &mut App) -> impl IntoElement { let store = ReplStore::global(cx).read(cx); @@ -262,7 +277,7 @@ impl RenderOnce for KernelSelector { PopoverMenu::new("kernel-switcher") .menu(move |_window, _cx| Some(picker_view.clone())) - .trigger(self.trigger) + .trigger_with_tooltip(self.trigger, self.tooltip) .attach(gpui::Corner::BottomLeft) .when_some(self.handle, |menu, handle| menu.with_handle(handle)) } diff --git a/crates/terminal_view/src/terminal_panel.rs b/crates/terminal_view/src/terminal_panel.rs index 14a3e111b3..af19555fe9 100644 --- a/crates/terminal_view/src/terminal_panel.rs +++ b/crates/terminal_view/src/terminal_panel.rs @@ -139,10 +139,9 @@ impl TerminalPanel { .gap(DynamicSpacing::Base02.rems(cx)) .child( PopoverMenu::new("terminal-tab-bar-popover-menu") - .trigger( - IconButton::new("plus", IconName::Plus) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("New…")), + .trigger_with_tooltip( + IconButton::new("plus", IconName::Plus).icon_size(IconSize::Small), + Tooltip::text("New…"), ) .anchor(Corner::TopRight) .with_handle(pane.new_item_context_menu_handle.clone()) @@ -169,10 +168,10 @@ impl TerminalPanel { .children(assistant_tab_bar_button.clone()) .child( PopoverMenu::new("terminal-pane-tab-bar-split") - .trigger( + .trigger_with_tooltip( IconButton::new("terminal-pane-split", IconName::Split) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("Split Pane")), + .icon_size(IconSize::Small), + Tooltip::text("Split Pane"), ) .anchor(Corner::TopRight) .with_handle(pane.split_item_context_menu_handle.clone()) diff --git a/crates/title_bar/src/application_menu.rs b/crates/title_bar/src/application_menu.rs index 955550596d..dec281b472 100644 --- a/crates/title_bar/src/application_menu.rs +++ b/crates/title_bar/src/application_menu.rs @@ -133,16 +133,14 @@ impl ApplicationMenu { .menu(move |window, cx| { Self::build_menu_from_items(entry.clone(), window, cx).into() }) - .trigger( + .trigger_with_tooltip( IconButton::new( SharedString::from(format!("{}-menu-trigger", menu_name)), ui::IconName::Menu, ) .style(ButtonStyle::Subtle) - .icon_size(IconSize::Small) - .when(!handle.is_deployed(), |this| { - this.tooltip(Tooltip::text("Open Application Menu")) - }), + .icon_size(IconSize::Small), + Tooltip::text("Open Application Menu"), ) .with_handle(handle), ) diff --git a/crates/title_bar/src/title_bar.rs b/crates/title_bar/src/title_bar.rs index b396e77014..9f430585c4 100644 --- a/crates/title_bar/src/title_bar.rs +++ b/crates/title_bar/src/title_bar.rs @@ -690,7 +690,7 @@ impl TitleBar { }) .into() }) - .trigger( + .trigger_with_tooltip( ButtonLike::new("user-menu") .child( h_flex() @@ -706,8 +706,8 @@ impl TitleBar { .color(Color::Muted), ), ) - .style(ButtonStyle::Subtle) - .tooltip(Tooltip::text("Toggle User Menu")), + .style(ButtonStyle::Subtle), + Tooltip::text("Toggle User Menu"), ) .anchor(gpui::Corner::TopRight) } else { @@ -736,10 +736,9 @@ impl TitleBar { }) .into() }) - .trigger( - IconButton::new("user-menu", IconName::ChevronDown) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("Toggle User Menu")), + .trigger_with_tooltip( + IconButton::new("user-menu", IconName::ChevronDown).icon_size(IconSize::Small), + Tooltip::text("Toggle User Menu"), ) } } diff --git a/crates/ui/src/components/popover_menu.rs b/crates/ui/src/components/popover_menu.rs index af801ec97c..095b05793a 100644 --- a/crates/ui/src/components/popover_menu.rs +++ b/crates/ui/src/components/popover_menu.rs @@ -3,8 +3,8 @@ use std::{cell::RefCell, rc::Rc}; use gpui::{ - anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnyElement, App, Bounds, - Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, Focusable as _, + anchored, deferred, div, point, prelude::FluentBuilder, px, size, AnyElement, AnyView, App, + Bounds, Corner, DismissEvent, DispatchPhase, Element, ElementId, Entity, Focusable as _, GlobalElementId, HitboxId, InteractiveElement, IntoElement, LayoutId, Length, ManagedView, MouseDownEvent, ParentElement, Pixels, Point, Style, Window, }; @@ -178,6 +178,28 @@ impl PopoverMenu { self } + pub fn trigger_with_tooltip( + mut self, + t: T, + tooltip_builder: impl Fn(&mut Window, &mut App) -> AnyView + 'static, + ) -> Self { + let on_open = self.on_open.clone(); + self.child_builder = Some(Box::new(move |menu, builder| { + let open = menu.borrow().is_some(); + t.toggle_state(open) + .when_some(builder, |el, builder| { + el.on_click(move |_, window, cx| { + show_menu(&builder, &menu, on_open.clone(), window, cx) + }) + .when(!open, |t| { + t.tooltip(move |window, cx| tooltip_builder(window, cx)) + }) + }) + .into_any_element() + })); + self + } + /// anchor defines which corner of the menu to anchor to the attachment point /// (by default the cursor position, but see attach) pub fn anchor(mut self, anchor: Corner) -> Self { diff --git a/crates/workspace/src/pane.rs b/crates/workspace/src/pane.rs index 7f85961120..7e628accdd 100644 --- a/crates/workspace/src/pane.rs +++ b/crates/workspace/src/pane.rs @@ -441,10 +441,9 @@ impl Pane { .gap(DynamicSpacing::Base04.rems(cx)) .child( PopoverMenu::new("pane-tab-bar-popover-menu") - .trigger( - IconButton::new("plus", IconName::Plus) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("New...")), + .trigger_with_tooltip( + IconButton::new("plus", IconName::Plus).icon_size(IconSize::Small), + Tooltip::text("New..."), ) .anchor(Corner::TopRight) .with_handle(pane.new_item_context_menu_handle.clone()) @@ -474,10 +473,10 @@ impl Pane { ) .child( PopoverMenu::new("pane-tab-bar-split") - .trigger( + .trigger_with_tooltip( IconButton::new("split", IconName::Split) - .icon_size(IconSize::Small) - .tooltip(Tooltip::text("Split Pane")), + .icon_size(IconSize::Small), + Tooltip::text("Split Pane"), ) .anchor(Corner::TopRight) .with_handle(pane.split_item_context_menu_handle.clone()) diff --git a/crates/zed/src/zed/quick_action_bar.rs b/crates/zed/src/zed/quick_action_bar.rs index bc523be4fd..5f2b98d444 100644 --- a/crates/zed/src/zed/quick_action_bar.rs +++ b/crates/zed/src/zed/quick_action_bar.rs @@ -168,15 +168,13 @@ impl Render for QuickActionBar { let focus = editor.focus_handle(cx); PopoverMenu::new("editor-selections-dropdown") - .trigger( + .trigger_with_tooltip( IconButton::new("toggle_editor_selections_icon", IconName::CursorIBeam) .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .style(ButtonStyle::Subtle) - .toggle_state(self.toggle_selections_handle.is_deployed()) - .when(!self.toggle_selections_handle.is_deployed(), |this| { - this.tooltip(Tooltip::text("Selection Controls")) - }), + .toggle_state(self.toggle_selections_handle.is_deployed()), + Tooltip::text("Selection Controls"), ) .with_handle(self.toggle_selections_handle.clone()) .anchor(Corner::TopRight) @@ -219,15 +217,13 @@ impl Render for QuickActionBar { let vim_mode_enabled = VimModeSetting::get_global(cx).0; PopoverMenu::new("editor-settings") - .trigger( + .trigger_with_tooltip( IconButton::new("toggle_editor_settings_icon", IconName::Sliders) .shape(IconButtonShape::Square) .icon_size(IconSize::Small) .style(ButtonStyle::Subtle) - .toggle_state(self.toggle_settings_handle.is_deployed()) - .when(!self.toggle_settings_handle.is_deployed(), |this| { - this.tooltip(Tooltip::text("Editor Controls")) - }), + .toggle_state(self.toggle_settings_handle.is_deployed()), + Tooltip::text("Editor Controls"), ) .anchor(Corner::TopRight) .with_handle(self.toggle_settings_handle.clone()) diff --git a/crates/zed/src/zed/quick_action_bar/repl_menu.rs b/crates/zed/src/zed/quick_action_bar/repl_menu.rs index 6e7f57ad94..51ed0af6b8 100644 --- a/crates/zed/src/zed/quick_action_bar/repl_menu.rs +++ b/crates/zed/src/zed/quick_action_bar/repl_menu.rs @@ -209,16 +209,16 @@ impl QuickActionBar { }) .into() }) - .trigger( + .trigger_with_tooltip( ButtonLike::new_rounded_right(element_id("dropdown")) .child( Icon::new(IconName::ChevronDownSmall) .size(IconSize::XSmall) .color(Color::Muted), ) - .tooltip(Tooltip::text("REPL Menu")) .width(rems(1.).into()) .disabled(menu_state.popover_disabled), + Tooltip::text("REPL Menu"), ); let button = ButtonLike::new_rounded_left("toggle_repl_icon") @@ -343,8 +343,8 @@ impl QuickActionBar { .color(Color::Muted) .size(IconSize::XSmall), ), - ) - .tooltip(Tooltip::text("Select Kernel")), + ), + Tooltip::text("Select Kernel"), ) .with_handle(menu_handle.clone()) .into_any_element()