Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_feathers/src/controls/button.rs
9441 views
1
use bevy_app::{Plugin, PreUpdate};
2
use bevy_ecs::{
3
bundle::Bundle,
4
component::Component,
5
entity::Entity,
6
hierarchy::{ChildOf, Children},
7
lifecycle::RemovedComponents,
8
query::{Added, Changed, Has, Or},
9
reflect::ReflectComponent,
10
schedule::IntoScheduleConfigs,
11
spawn::{SpawnRelated, SpawnableList},
12
system::{Commands, Query},
13
};
14
use bevy_input_focus::tab_navigation::TabIndex;
15
use bevy_picking::{hover::Hovered, PickingSystems};
16
use bevy_reflect::{prelude::ReflectDefault, Reflect};
17
use bevy_text::FontSize;
18
use bevy_ui::{AlignItems, InteractionDisabled, JustifyContent, Node, Pressed, UiRect, Val};
19
use bevy_ui_widgets::Button;
20
21
use crate::{
22
constants::{fonts, size},
23
cursor::EntityCursor,
24
font_styles::InheritableFont,
25
handle_or_path::HandleOrPath,
26
rounded_corners::RoundedCorners,
27
theme::{ThemeBackgroundColor, ThemeFontColor},
28
tokens,
29
};
30
31
/// Color variants for buttons. This also functions as a component used by the dynamic styling
32
/// system to identify which entities are buttons.
33
#[derive(Component, Default, Clone, Reflect, Debug, PartialEq, Eq)]
34
#[reflect(Component, Clone, Default)]
35
pub enum ButtonVariant {
36
/// The standard button appearance
37
#[default]
38
Normal,
39
/// A button with a more prominent color, this is used for "call to action" buttons,
40
/// default buttons for dialog boxes, and so on.
41
Primary,
42
}
43
44
/// Parameters for the button template, passed to [`button`] function.
45
#[derive(Default)]
46
pub struct ButtonProps {
47
/// Color variant for the button.
48
pub variant: ButtonVariant,
49
/// Rounded corners options
50
pub corners: RoundedCorners,
51
}
52
53
/// Template function to spawn a button.
54
///
55
/// # Arguments
56
/// * `props` - construction properties for the button.
57
/// * `overrides` - a bundle of components that are merged in with the normal button components.
58
/// * `children` - a [`SpawnableList`] of child elements, such as a label or icon for the button.
59
///
60
/// # Emitted events
61
/// * [`bevy_ui_widgets::Activate`] when any of the following happens:
62
/// * the pointer is released while hovering over the button.
63
/// * the ENTER or SPACE key is pressed while the button has keyboard focus.
64
///
65
/// These events can be disabled by adding an [`bevy_ui::InteractionDisabled`] component to the entity
66
pub fn button<C: SpawnableList<ChildOf> + Send + Sync + 'static, B: Bundle>(
67
props: ButtonProps,
68
overrides: B,
69
children: C,
70
) -> impl Bundle {
71
(
72
Node {
73
height: size::ROW_HEIGHT,
74
justify_content: JustifyContent::Center,
75
align_items: AlignItems::Center,
76
padding: UiRect::axes(Val::Px(8.0), Val::Px(0.)),
77
flex_grow: 1.0,
78
border_radius: props.corners.to_border_radius(4.0),
79
..Default::default()
80
},
81
Button,
82
props.variant,
83
Hovered::default(),
84
EntityCursor::System(bevy_window::SystemCursorIcon::Pointer),
85
TabIndex(0),
86
ThemeBackgroundColor(tokens::BUTTON_BG),
87
ThemeFontColor(tokens::BUTTON_TEXT),
88
InheritableFont {
89
font: HandleOrPath::Path(fonts::REGULAR.to_owned()),
90
font_size: FontSize::Px(14.0),
91
},
92
overrides,
93
Children::spawn(children),
94
)
95
}
96
97
fn update_button_styles(
98
q_buttons: Query<
99
(
100
Entity,
101
&ButtonVariant,
102
Has<InteractionDisabled>,
103
Has<Pressed>,
104
&Hovered,
105
&ThemeBackgroundColor,
106
&ThemeFontColor,
107
),
108
Or<(
109
Changed<Hovered>,
110
Changed<ButtonVariant>,
111
Added<Pressed>,
112
Added<InteractionDisabled>,
113
)>,
114
>,
115
mut commands: Commands,
116
) {
117
for (button_ent, variant, disabled, pressed, hovered, bg_color, font_color) in q_buttons.iter()
118
{
119
set_button_styles(
120
button_ent,
121
variant,
122
disabled,
123
pressed,
124
hovered.0,
125
bg_color,
126
font_color,
127
&mut commands,
128
);
129
}
130
}
131
132
fn update_button_styles_remove(
133
q_buttons: Query<(
134
Entity,
135
&ButtonVariant,
136
Has<InteractionDisabled>,
137
Has<Pressed>,
138
&Hovered,
139
&ThemeBackgroundColor,
140
&ThemeFontColor,
141
)>,
142
mut removed_disabled: RemovedComponents<InteractionDisabled>,
143
mut removed_pressed: RemovedComponents<Pressed>,
144
mut commands: Commands,
145
) {
146
removed_disabled
147
.read()
148
.chain(removed_pressed.read())
149
.for_each(|ent| {
150
if let Ok((button_ent, variant, disabled, pressed, hovered, bg_color, font_color)) =
151
q_buttons.get(ent)
152
{
153
set_button_styles(
154
button_ent,
155
variant,
156
disabled,
157
pressed,
158
hovered.0,
159
bg_color,
160
font_color,
161
&mut commands,
162
);
163
}
164
});
165
}
166
167
fn set_button_styles(
168
button_ent: Entity,
169
variant: &ButtonVariant,
170
disabled: bool,
171
pressed: bool,
172
hovered: bool,
173
bg_color: &ThemeBackgroundColor,
174
font_color: &ThemeFontColor,
175
commands: &mut Commands,
176
) {
177
let bg_token = match (variant, disabled, pressed, hovered) {
178
(ButtonVariant::Normal, true, _, _) => tokens::BUTTON_BG_DISABLED,
179
(ButtonVariant::Normal, false, true, _) => tokens::BUTTON_BG_PRESSED,
180
(ButtonVariant::Normal, false, false, true) => tokens::BUTTON_BG_HOVER,
181
(ButtonVariant::Normal, false, false, false) => tokens::BUTTON_BG,
182
(ButtonVariant::Primary, true, _, _) => tokens::BUTTON_PRIMARY_BG_DISABLED,
183
(ButtonVariant::Primary, false, true, _) => tokens::BUTTON_PRIMARY_BG_PRESSED,
184
(ButtonVariant::Primary, false, false, true) => tokens::BUTTON_PRIMARY_BG_HOVER,
185
(ButtonVariant::Primary, false, false, false) => tokens::BUTTON_PRIMARY_BG,
186
};
187
188
let font_color_token = match (variant, disabled) {
189
(ButtonVariant::Normal, true) => tokens::BUTTON_TEXT_DISABLED,
190
(ButtonVariant::Normal, false) => tokens::BUTTON_TEXT,
191
(ButtonVariant::Primary, true) => tokens::BUTTON_PRIMARY_TEXT_DISABLED,
192
(ButtonVariant::Primary, false) => tokens::BUTTON_PRIMARY_TEXT,
193
};
194
195
let cursor_shape = match disabled {
196
true => bevy_window::SystemCursorIcon::NotAllowed,
197
false => bevy_window::SystemCursorIcon::Pointer,
198
};
199
200
// Change background color
201
if bg_color.0 != bg_token {
202
commands
203
.entity(button_ent)
204
.insert(ThemeBackgroundColor(bg_token));
205
}
206
207
// Change font color
208
if font_color.0 != font_color_token {
209
commands
210
.entity(button_ent)
211
.insert(ThemeFontColor(font_color_token));
212
}
213
214
// Change cursor shape
215
commands
216
.entity(button_ent)
217
.insert(EntityCursor::System(cursor_shape));
218
}
219
220
/// Plugin which registers the systems for updating the button styles.
221
pub struct ButtonPlugin;
222
223
impl Plugin for ButtonPlugin {
224
fn build(&self, app: &mut bevy_app::App) {
225
app.add_systems(
226
PreUpdate,
227
(update_button_styles, update_button_styles_remove).in_set(PickingSystems::Last),
228
);
229
}
230
}
231
232