button: Add Support for on_hover event (#1341)

This commit is contained in:
Floyd Wang 2025-10-09 18:43:50 +08:00 committed by GitHub
parent ca9d5b77e2
commit 7a8be9a628
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 43 additions and 15 deletions

View file

@ -1,13 +1,13 @@
use gpui::{ use gpui::{
prelude::FluentBuilder, px, Action, App, AppContext as _, ClickEvent, Context, Entity, Action, App, AppContext as _, ClickEvent, Context, Entity, Focusable, InteractiveElement,
Focusable, InteractiveElement, IntoElement, ParentElement as _, Render, Styled as _, Window, IntoElement, ParentElement as _, Render, Styled as _, Window, prelude::FluentBuilder, px,
}; };
use gpui_component::{ use gpui_component::{
ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, Theme,
button::{Button, ButtonCustomVariant, ButtonGroup, ButtonVariants as _, DropdownButton}, button::{Button, ButtonCustomVariant, ButtonGroup, ButtonVariants as _, DropdownButton},
checkbox::Checkbox, checkbox::Checkbox,
h_flex, v_flex, ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, h_flex, v_flex,
Theme,
}; };
use serde::Deserialize; use serde::Deserialize;
@ -43,8 +43,12 @@ impl ButtonStory {
}) })
} }
fn on_click(ev: &ClickEvent, _window: &mut Window, _cx: &mut App) { fn on_click(ev: &ClickEvent, _: &mut Window, _: &mut App) {
println!("Button clicked! {:?}", ev); println!("Button clicked {:?}", ev);
}
fn on_hover(hovered: &bool, _: &mut Window, _: &mut App) {
println!("Button hovered {:?}", hovered);
} }
} }
@ -159,7 +163,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-2") Button::new("button-2")
@ -168,7 +173,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-4") Button::new("button-4")
@ -178,7 +184,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-4-warning") Button::new("button-4-warning")
@ -188,7 +195,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-4-success") Button::new("button-4-success")
@ -198,7 +206,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-5-info") Button::new("button-5-info")
@ -208,7 +217,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-5-ghost") Button::new("button-5-ghost")
@ -218,7 +228,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-5-link") Button::new("button-5-link")
@ -228,7 +239,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
) )
.child( .child(
Button::new("button-5-text") Button::new("button-5-text")
@ -238,7 +250,8 @@ impl Render for ButtonStory {
.selected(selected) .selected(selected)
.loading(loading) .loading(loading)
.when(compact, |this| this.compact()) .when(compact, |this| this.compact())
.on_click(Self::on_click), .on_click(Self::on_click)
.on_hover(Self::on_hover),
), ),
) )
.child( .child(

View file

@ -201,6 +201,7 @@ pub struct Button {
Option<(Rc<Box<dyn Action>>, Option<SharedString>)>, Option<(Rc<Box<dyn Action>>, Option<SharedString>)>,
)>, )>,
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>, on_click: Option<Rc<dyn Fn(&ClickEvent, &mut Window, &mut App)>>,
on_hover: Option<Rc<dyn Fn(&bool, &mut Window, &mut App)>>,
pub(crate) stop_propagation: bool, pub(crate) stop_propagation: bool,
loading: bool, loading: bool,
loading_icon: Option<Icon>, loading_icon: Option<Icon>,
@ -236,6 +237,7 @@ impl Button {
size: Size::Medium, size: Size::Medium,
tooltip: None, tooltip: None,
on_click: None, on_click: None,
on_hover: None,
stop_propagation: true, stop_propagation: true,
loading: false, loading: false,
compact: false, compact: false,
@ -325,6 +327,12 @@ impl Button {
self self
} }
/// Add hover handler, the bool parameter indicates whether the mouse is hovering.
pub fn on_hover(mut self, handler: impl Fn(&bool, &mut Window, &mut App) + 'static) -> Self {
self.on_hover = Some(Rc::new(handler));
self
}
pub fn stop_propagation(mut self, val: bool) -> Self { pub fn stop_propagation(mut self, val: bool) -> Self {
self.stop_propagation = val; self.stop_propagation = val;
self self
@ -526,6 +534,11 @@ impl RenderOnce for Button {
(on_click)(event, window, cx); (on_click)(event, window, cx);
}) })
}) })
.when_some(self.on_hover.filter(|_| clickable), |this, on_hover| {
this.on_hover(move |hovered, window, cx| {
(on_hover)(hovered, window, cx);
})
})
.when(self.disabled, |this| { .when(self.disabled, |this| {
let disabled_style = style.disabled(self.outline, cx); let disabled_style = style.disabled(self.outline, cx);
this.bg(disabled_style.bg) this.bg(disabled_style.bg)

View file

@ -601,6 +601,7 @@ mod tests {
// Test for not on a char boundary // Test for not on a char boundary
let mut rope = Rope::from("中文"); let mut rope = Rope::from("中文");
rope.replace(0..1, "New"); rope.replace(0..1, "New");
// autocorrect-disable
assert_eq!(rope.to_string(), "New文"); assert_eq!(rope.to_string(), "New文");
let mut rope = Rope::from("中文"); let mut rope = Rope::from("中文");
rope.replace(0..2, "New"); rope.replace(0..2, "New");
@ -608,6 +609,7 @@ mod tests {
let mut rope = Rope::from("中文"); let mut rope = Rope::from("中文");
rope.replace(0..3, "New"); rope.replace(0..3, "New");
assert_eq!(rope.to_string(), "New文"); assert_eq!(rope.to_string(), "New文");
// autocorrect-enable
let mut rope = Rope::from("中文"); let mut rope = Rope::from("中文");
rope.replace(1..4, "New"); rope.replace(1..4, "New");
assert_eq!(rope.to_string(), "New"); assert_eq!(rope.to_string(), "New");