From 7a8be9a628880faa54c81cdf92fc172484f67d82 Mon Sep 17 00:00:00 2001 From: Floyd Wang Date: Thu, 9 Oct 2025 18:43:50 +0800 Subject: [PATCH] button: Add Support for `on_hover` event (#1341) --- crates/story/src/button_story.rs | 43 +++++++++++++++++++++----------- crates/ui/src/button/button.rs | 13 ++++++++++ crates/ui/src/input/rope_ext.rs | 2 ++ 3 files changed, 43 insertions(+), 15 deletions(-) diff --git a/crates/story/src/button_story.rs b/crates/story/src/button_story.rs index ee83947f..7b480b76 100644 --- a/crates/story/src/button_story.rs +++ b/crates/story/src/button_story.rs @@ -1,13 +1,13 @@ use gpui::{ - prelude::FluentBuilder, px, Action, App, AppContext as _, ClickEvent, Context, Entity, - Focusable, InteractiveElement, IntoElement, ParentElement as _, Render, Styled as _, Window, + Action, App, AppContext as _, ClickEvent, Context, Entity, Focusable, InteractiveElement, + IntoElement, ParentElement as _, Render, Styled as _, Window, prelude::FluentBuilder, px, }; use gpui_component::{ + ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, Theme, button::{Button, ButtonCustomVariant, ButtonGroup, ButtonVariants as _, DropdownButton}, checkbox::Checkbox, - h_flex, v_flex, ActiveTheme, Disableable as _, Icon, IconName, Selectable as _, Sizable as _, - Theme, + h_flex, v_flex, }; use serde::Deserialize; @@ -43,8 +43,12 @@ impl ButtonStory { }) } - fn on_click(ev: &ClickEvent, _window: &mut Window, _cx: &mut App) { - println!("Button clicked! {:?}", ev); + fn on_click(ev: &ClickEvent, _: &mut Window, _: &mut App) { + 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) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-2") @@ -168,7 +173,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-4") @@ -178,7 +184,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-4-warning") @@ -188,7 +195,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-4-success") @@ -198,7 +206,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-5-info") @@ -208,7 +217,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-5-ghost") @@ -218,7 +228,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-5-link") @@ -228,7 +239,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ) .child( Button::new("button-5-text") @@ -238,7 +250,8 @@ impl Render for ButtonStory { .selected(selected) .loading(loading) .when(compact, |this| this.compact()) - .on_click(Self::on_click), + .on_click(Self::on_click) + .on_hover(Self::on_hover), ), ) .child( diff --git a/crates/ui/src/button/button.rs b/crates/ui/src/button/button.rs index 04bb6aa6..7468e4d9 100644 --- a/crates/ui/src/button/button.rs +++ b/crates/ui/src/button/button.rs @@ -201,6 +201,7 @@ pub struct Button { Option<(Rc>, Option)>, )>, on_click: Option>, + on_hover: Option>, pub(crate) stop_propagation: bool, loading: bool, loading_icon: Option, @@ -236,6 +237,7 @@ impl Button { size: Size::Medium, tooltip: None, on_click: None, + on_hover: None, stop_propagation: true, loading: false, compact: false, @@ -325,6 +327,12 @@ impl Button { 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 { self.stop_propagation = val; self @@ -526,6 +534,11 @@ impl RenderOnce for Button { (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| { let disabled_style = style.disabled(self.outline, cx); this.bg(disabled_style.bg) diff --git a/crates/ui/src/input/rope_ext.rs b/crates/ui/src/input/rope_ext.rs index 8106abbf..1fa9052b 100644 --- a/crates/ui/src/input/rope_ext.rs +++ b/crates/ui/src/input/rope_ext.rs @@ -601,6 +601,7 @@ mod tests { // Test for not on a char boundary let mut rope = Rope::from("中文"); rope.replace(0..1, "New"); + // autocorrect-disable assert_eq!(rope.to_string(), "New文"); let mut rope = Rope::from("中文"); rope.replace(0..2, "New"); @@ -608,6 +609,7 @@ mod tests { let mut rope = Rope::from("中文"); rope.replace(0..3, "New"); assert_eq!(rope.to_string(), "New文"); + // autocorrect-enable let mut rope = Rope::from("中文"); rope.replace(1..4, "New"); assert_eq!(rope.to_string(), "New");