tooltip: Add action, key_binding method to Tooltip. (#787)

This commit is contained in:
Jason Lee 2025-04-14 17:38:21 +08:00 committed by GitHub
parent 8ef4e2eb17
commit 9a6a0f4421
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 63 additions and 21 deletions

View file

@ -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<Self>,
) -> 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()

View file

@ -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<bool>,
size: Size,
compact: bool,
tooltip: Option<SharedString>,
key_binding: Option<Kbd>,
tooltip: Option<(
SharedString,
Option<(Rc<Box<dyn Action>>, Option<SharedString>)>,
)>,
on_click: Option<Box<dyn Fn(&ClickEvent, &mut Window, &mut App) + 'static>>,
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<SharedString>) -> Self {
self.tooltip = Some(tooltip.into());
self.tooltip = Some((tooltip.into(), None));
self
}
@ -270,10 +273,14 @@ impl Button {
tooltip: impl Into<SharedString>,
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)
})
})

View file

@ -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<Kbd>,
action: Option<(Box<dyn Action>, Option<SharedString>)>,
}
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<impl Into<Kbd>>) -> Self {
self.key_binding = kbd.map(Into::into);
pub fn key_binding(mut self, key_binding: Option<Kbd>) -> 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<Self>) -> 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()