From 9a6a0f4421abd8f730e6391556e7ae2ae703b8a9 Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Mon, 14 Apr 2025 17:38:21 +0800 Subject: [PATCH] tooltip: Add `action`, `key_binding` method to Tooltip. (#787) --- crates/story/src/tooltip_story.rs | 19 ++++++++++++------ crates/ui/src/button/button.rs | 32 ++++++++++++++++++++---------- crates/ui/src/tooltip.rs | 33 ++++++++++++++++++++++++++----- 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/crates/story/src/tooltip_story.rs b/crates/story/src/tooltip_story.rs index 5837d93e..33bd65ae 100644 --- a/crates/story/src/tooltip_story.rs +++ b/crates/story/src/tooltip_story.rs @@ -1,6 +1,6 @@ use gpui::{ actions, div, App, AppContext, Context, Entity, Focusable, InteractiveElement, KeyBinding, - ParentElement, Render, StatefulInteractiveElement, Styled, Window, + Keystroke, ParentElement, Render, StatefulInteractiveElement, Styled, Window, }; use gpui_component::{ @@ -10,7 +10,7 @@ use gpui_component::{ h_flex, label::Label, tooltip::Tooltip, - v_flex, ActiveTheme, IconName, + v_flex, ActiveTheme, IconName, Kbd, }; actions!(tooltip, [Info]); @@ -56,7 +56,7 @@ impl Focusable for TooltipStory { impl Render for TooltipStory { fn render( &mut self, - window: &mut gpui::Window, + _: &mut gpui::Window, _cx: &mut gpui::Context, ) -> impl gpui::IntoElement { v_flex() @@ -75,7 +75,6 @@ impl Render for TooltipStory { "This is a tooltip with Action for display keybinding.", &Info, Some("Tooltip"), - window, )), ) .child( @@ -83,13 +82,21 @@ impl Render for TooltipStory { .justify_center() .child(Label::new("Hover me")) .id("tooltip-2") - .tooltip(|window, cx| Tooltip::new("This is a Label").build(window, cx)), + .tooltip(|window, cx| { + Tooltip::new("This is a Label") + .action(&Info, Some("Tooltip")) + .build(window, cx) + }), ) .child( div() .child(Checkbox::new("check").label("Remember me").checked(true)) .id("tooltip-3") - .tooltip(|window, cx| Tooltip::new("Checked!").build(window, cx)), + .tooltip(|window, cx| { + Tooltip::new("Checked!") + .key_binding(Some(Kbd::new(Keystroke::parse("cmd-shift-u").unwrap()))) + .build(window, cx) + }), ) .child( div() diff --git a/crates/ui/src/button/button.rs b/crates/ui/src/button/button.rs index 311a63f8..b62dca50 100644 --- a/crates/ui/src/button/button.rs +++ b/crates/ui/src/button/button.rs @@ -1,6 +1,8 @@ +use std::rc::Rc; + use crate::{ h_flex, indicator::Indicator, tooltip::Tooltip, ActiveTheme, Colorize as _, Disableable, Icon, - Kbd, Selectable, Sizable, Size, StyleSized, + Selectable, Sizable, Size, StyleSized, }; use gpui::{ div, prelude::FluentBuilder as _, relative, Action, AnyElement, App, ClickEvent, Corners, Div, @@ -183,8 +185,10 @@ pub struct Button { border_edges: Edges, size: Size, compact: bool, - tooltip: Option, - key_binding: Option, + tooltip: Option<( + SharedString, + Option<(Rc>, Option)>, + )>, on_click: Option>, pub(crate) stop_propagation: bool, loading: bool, @@ -212,7 +216,6 @@ impl Button { border_edges: Edges::all(true), size: Size::Medium, tooltip: None, - key_binding: None, on_click: None, stop_propagation: true, loading: false, @@ -261,7 +264,7 @@ impl Button { /// Set the tooltip of the button. pub fn tooltip(mut self, tooltip: impl Into) -> Self { - self.tooltip = Some(tooltip.into()); + self.tooltip = Some((tooltip.into(), None)); self } @@ -270,10 +273,14 @@ impl Button { tooltip: impl Into, action: &dyn Action, context: Option<&str>, - window: &Window, ) -> Self { - self.tooltip = Some(tooltip.into()); - self.key_binding = Kbd::binding_for_action(action, context, window); + self.tooltip = Some(( + tooltip.into(), + Some(( + Rc::new(action.boxed_clone()), + context.map(|c| c.to_string().into()), + )), + )); self } @@ -497,10 +504,15 @@ impl RenderOnce for Button { .children(self.children) }) .when(self.loading, |this| this.bg(normal_style.bg.opacity(0.8))) - .when_some(self.tooltip, |this, tooltip| { + .when_some(self.tooltip, |this, (tooltip, action)| { this.tooltip(move |window, cx| { Tooltip::new(tooltip.clone()) - .key_binding(self.key_binding.clone()) + .when_some(action.clone(), |this, (action, context)| { + this.action( + action.boxed_clone().as_ref(), + context.as_ref().map(|c| c.as_ref()), + ) + }) .build(window, cx) }) }) diff --git a/crates/ui/src/tooltip.rs b/crates/ui/src/tooltip.rs index 486db96c..c0e0f55e 100644 --- a/crates/ui/src/tooltip.rs +++ b/crates/ui/src/tooltip.rs @@ -1,6 +1,6 @@ use gpui::{ - div, prelude::FluentBuilder, px, AnyElement, AnyView, App, AppContext, Context, IntoElement, - ParentElement, Render, Styled, Window, + div, prelude::FluentBuilder, px, Action, AnyElement, AnyView, App, AppContext, Context, + IntoElement, ParentElement, Render, SharedString, Styled, Window, }; use crate::{h_flex, text::Text, ActiveTheme, Kbd}; @@ -13,6 +13,7 @@ enum TooltipContext { pub struct Tooltip { content: TooltipContext, key_binding: Option, + action: Option<(Box, Option)>, } impl Tooltip { @@ -21,6 +22,7 @@ impl Tooltip { Self { content: TooltipContext::Text(text.into()), key_binding: None, + action: None, } } @@ -32,15 +34,22 @@ impl Tooltip { { Self { key_binding: None, + action: None, content: TooltipContext::Element(Box::new(move |window, cx| { builder(window, cx).into_any_element() })), } } + /// Set Action to display key binding information for the tooltip if it exists. + pub fn action(mut self, action: &dyn Action, context: Option<&str>) -> Self { + self.action = Some((action.boxed_clone(), context.map(SharedString::new))); + self + } + /// Set KeyBinding information for the tooltip. - pub(crate) fn key_binding(mut self, kbd: Option>) -> Self { - self.key_binding = kbd.map(Into::into); + pub fn key_binding(mut self, key_binding: Option) -> Self { + self.key_binding = key_binding; self } @@ -54,6 +63,20 @@ impl FluentBuilder for Tooltip {} impl Render for Tooltip { fn render(&mut self, window: &mut Window, cx: &mut Context) -> impl IntoElement { + let key_binding = if let Some(key_binding) = &self.key_binding { + Some(key_binding.clone()) + } else { + if let Some((action, context)) = &self.action { + Kbd::binding_for_action( + action.as_ref(), + context.as_ref().map(|s| s.as_ref()), + window, + ) + } else { + None + } + }; + div().child( // Wrap in a child, to ensure the left margin is applied to the tooltip h_flex() @@ -77,7 +100,7 @@ impl Render for Tooltip { TooltipContext::Element(ref builder) => this.child(builder(window, cx)), })) }) - .when_some(self.key_binding.clone(), |this, kbd| { + .when_some(key_binding, |this, kbd| { this.child( div() .text_xs()