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