Skip to main content

hover_color/
hover_color.rs

1use gpui::{
2    App, Application, Bounds, Context, Window, WindowBounds, WindowOptions, div, linear_color_stop,
3    linear_gradient, prelude::*, px, rgb, size,
4};
5use gpui_animation::{
6    animation::TransitionExt,
7    transition::{self},
8};
9
10struct Hover {
11    hovered: bool,
12}
13
14impl Render for Hover {
15    fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
16        let linear = std::sync::Arc::new(transition::general::Linear);
17        let gradient1 = linear_gradient(
18            30.,
19            linear_color_stop(rgb(0xfccf31), 0.),
20            linear_color_stop(rgb(0xf55555), 1.),
21        );
22        let gradient2 = linear_gradient(
23            230.,
24            linear_color_stop(rgb(0xeead92), 0.),
25            linear_color_stop(rgb(0x6018dc), 1.),
26        );
27
28        div()
29            .id("Hoverable2")
30            .flex()
31            .flex_col()
32            .gap_3()
33            .size(px(500.0))
34            .justify_center()
35            .items_center()
36            .child(
37                div()
38                    .id("Hoverable")
39                    .child("Hover over rectangle")
40                    .with_transition("Hoverable")
41                    .bg(gpui::white())
42                    .text_color(gpui::red())
43                    .flex()
44                    .text_xl()
45                    .h_10()
46                    .justify_center()
47                    .items_center()
48                    .transition_on_hover(
49                        std::time::Duration::from_millis(250),
50                        linear.clone(),
51                        |hovered, state| {
52                            if *hovered {
53                                state
54                                    .text_bg(gpui::blue())
55                                    .text_color(gpui::yellow())
56                                    .text_lg()
57                            } else {
58                                state
59                                    .text_bg(gpui::white())
60                                    .text_color(gpui::black())
61                                    .text_xl()
62                            }
63                        },
64                    ),
65            )
66            .child(
67                div().flex().gap_2().child(
68                    div()
69                        .id("Hoverable1")
70                        .size_16()
71                        .with_transition("Hoverable1")
72                        .transition_on_hover(
73                            std::time::Duration::from_millis(250),
74                            linear.clone(),
75                            |hovered, state| {
76                                if *hovered {
77                                    state.bg(gpui::yellow()).opacity(0.)
78                                } else {
79                                    state.bg(gpui::red()).opacity(1.)
80                                }
81                            },
82                        )
83                        .opacity(1.)
84                        .bg(gpui::red()),
85                ),
86            )
87            .with_transition("Hoverable2")
88            .on_hover(cx.listener(|this, hovered, _, cx| {
89                this.hovered = *hovered;
90
91                // Changes made via .when(), .when_else(), etc., do not automatically trigger the animation cycle.
92                // Unlike event-based listeners that hold and manage the App context, these declarative methods do not pass the context to the animation controller.
93                // You must manually invoke a refresh or re-render to start the transition.
94                cx.notify();
95            }))
96            .transition_when(
97                self.hovered,
98                std::time::Duration::from_millis(500),
99                transition::general::EaseInExpo,
100                move |this| this.bg(gradient2),
101            )
102            .transition_on_click(
103                std::time::Duration::from_millis(500),
104                transition::general::EaseInExpo,
105                |_, state| state.bg(gpui::blue()),
106            )
107            .transition_on_hover(
108                std::time::Duration::from_millis(500),
109                transition::general::EaseInExpo,
110                move |hovered, state| {
111                    if *hovered {
112                        state.bg(gradient2)
113                    } else {
114                        state.origin()
115                    }
116                },
117            )
118            .bg(gradient1)
119    }
120}
121
122fn main() {
123    Application::new().run(|cx: &mut App| {
124        let bounds = Bounds::centered(None, size(px(500.), px(500.0)), cx);
125        cx.open_window(
126            WindowOptions {
127                window_bounds: Some(WindowBounds::Windowed(bounds)),
128                ..Default::default()
129            },
130            |_, cx| cx.new(|_| Hover { hovered: false }),
131        )
132        .unwrap();
133    });
134}