Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_ui/src/lib.rs
9353 views
1
#![expect(missing_docs, reason = "Not all docs are written yet, see #3492.")]
2
#![cfg_attr(docsrs, feature(doc_cfg))]
3
#![doc(
4
html_logo_url = "https://bevy.org/assets/icon.png",
5
html_favicon_url = "https://bevy.org/assets/icon.png"
6
)]
7
8
//! This crate contains Bevy's UI system, which can be used to create UI for both 2D and 3D games
9
//! # Basic usage
10
//! Spawn UI elements with [`widget::Button`], [`ImageNode`](widget::ImageNode), [`Text`](prelude::Text) and [`Node`]
11
//! This UI is laid out with the Flexbox and CSS Grid layout models (see <https://cssreference.io/flexbox/>)
12
13
pub mod auto_directional_navigation;
14
pub mod interaction_states;
15
pub mod measurement;
16
pub mod update;
17
pub mod widget;
18
19
pub mod gradients;
20
#[cfg(feature = "bevy_picking")]
21
pub mod picking_backend;
22
pub mod ui_transform;
23
24
use bevy_derive::{Deref, DerefMut};
25
#[cfg(feature = "bevy_picking")]
26
use bevy_picking::PickingSystems;
27
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
28
mod accessibility;
29
// This module is not re-exported, but is instead made public.
30
// This is intended to discourage accidental use of the experimental API.
31
pub mod experimental;
32
mod focus;
33
mod geometry;
34
mod layout;
35
mod stack;
36
mod ui_node;
37
38
pub use focus::*;
39
pub use geometry::*;
40
pub use gradients::*;
41
pub use interaction_states::{Checkable, Checked, InteractionDisabled, Pressed};
42
pub use layout::*;
43
pub use measurement::*;
44
pub use ui_node::*;
45
pub use ui_transform::*;
46
47
/// The UI prelude.
48
///
49
/// This includes the most common types in this crate, re-exported for your convenience.
50
pub mod prelude {
51
#[doc(hidden)]
52
#[cfg(feature = "bevy_picking")]
53
pub use crate::picking_backend::{UiPickingCamera, UiPickingPlugin, UiPickingSettings};
54
#[doc(hidden)]
55
pub use crate::widget::{Text, TextShadow, TextUiReader, TextUiWriter};
56
#[doc(hidden)]
57
pub use {
58
crate::{
59
geometry::*,
60
gradients::*,
61
ui_node::*,
62
ui_transform::*,
63
widget::{Button, ImageNode, Label, NodeImageMode, ViewportNode},
64
Interaction, UiScale,
65
},
66
// `bevy_sprite` re-exports for texture slicing
67
bevy_sprite::{BorderRect, SliceScaleMode, SpriteImageMode, TextureSlicer},
68
bevy_text::TextBackgroundColor,
69
};
70
}
71
72
use bevy_app::{prelude::*, AnimationSystems, HierarchyPropagatePlugin, PropagateSet};
73
use bevy_camera::CameraUpdateSystems;
74
use bevy_ecs::prelude::*;
75
use bevy_input::InputSystems;
76
use bevy_transform::TransformSystems;
77
use layout::ui_surface::UiSurface;
78
use stack::ui_stack_system;
79
pub use stack::UiStack;
80
use update::{propagate_ui_target_cameras, update_clipping_system};
81
82
/// The basic plugin for Bevy UI
83
#[derive(Default)]
84
pub struct UiPlugin;
85
86
/// The label enum labeling the types of systems in the Bevy UI
87
#[derive(Debug, Hash, PartialEq, Eq, Clone, SystemSet)]
88
pub enum UiSystems {
89
/// After this label, input interactions with UI entities have been updated for this frame.
90
///
91
/// Runs in [`PreUpdate`].
92
Focus,
93
/// All UI systems in [`PostUpdate`] will run in or after this label.
94
Prepare,
95
/// Propagate UI component values needed by layout.
96
Propagate,
97
/// Update content requirements before layout.
98
Content,
99
/// After this label, the ui layout state has been updated.
100
///
101
/// Runs in [`PostUpdate`].
102
Layout,
103
/// UI systems ordered after [`UiSystems::Layout`].
104
///
105
/// Runs in [`PostUpdate`].
106
PostLayout,
107
/// After this label, the [`UiStack`] resource has been updated.
108
///
109
/// Runs in [`PostUpdate`].
110
Stack,
111
}
112
113
/// The current scale of the UI.
114
///
115
/// A multiplier to fixed-sized ui values.
116
/// **Note:** This will only affect fixed ui values like [`Val::Px`]
117
#[derive(Debug, Reflect, Resource, Deref, DerefMut)]
118
#[reflect(Resource, Debug, Default)]
119
pub struct UiScale(pub f32);
120
121
impl Default for UiScale {
122
fn default() -> Self {
123
Self(1.0)
124
}
125
}
126
127
// Marks systems that can be ambiguous with [`widget::text_system`] if the `bevy_text` feature is enabled.
128
// See https://github.com/bevyengine/bevy/pull/11391 for more details.
129
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
130
struct AmbiguousWithText;
131
132
#[derive(SystemSet, Debug, Hash, PartialEq, Eq, Clone)]
133
struct AmbiguousWithUpdateText2dLayout;
134
135
impl Plugin for UiPlugin {
136
fn build(&self, app: &mut App) {
137
app.init_resource::<UiSurface>()
138
.init_resource::<UiScale>()
139
.init_resource::<UiStack>()
140
.configure_sets(
141
PostUpdate,
142
(
143
CameraUpdateSystems,
144
UiSystems::Prepare.after(AnimationSystems),
145
UiSystems::Propagate,
146
UiSystems::Content,
147
UiSystems::Layout,
148
UiSystems::PostLayout,
149
)
150
.chain(),
151
)
152
.configure_sets(
153
PostUpdate,
154
PropagateSet::<ComputedUiTargetCamera>::default().in_set(UiSystems::Propagate),
155
)
156
.add_plugins(HierarchyPropagatePlugin::<ComputedUiTargetCamera>::new(
157
PostUpdate,
158
))
159
.configure_sets(
160
PostUpdate,
161
PropagateSet::<ComputedUiRenderTargetInfo>::default().in_set(UiSystems::Propagate),
162
)
163
.add_plugins(HierarchyPropagatePlugin::<ComputedUiRenderTargetInfo>::new(
164
PostUpdate,
165
))
166
.add_systems(
167
PreUpdate,
168
ui_focus_system.in_set(UiSystems::Focus).after(InputSystems),
169
);
170
171
#[cfg(feature = "bevy_picking")]
172
app.add_plugins(picking_backend::UiPickingPlugin)
173
.add_systems(
174
First,
175
widget::viewport_picking.in_set(PickingSystems::PostInput),
176
);
177
178
let ui_layout_system_config = ui_layout_system
179
.in_set(UiSystems::Layout)
180
.before(TransformSystems::Propagate);
181
182
let ui_layout_system_config = ui_layout_system_config
183
// Text and Text2D operate on disjoint sets of entities
184
.ambiguous_with(bevy_sprite::update_text2d_layout)
185
.ambiguous_with(bevy_text::detect_text_needs_rerender::<bevy_sprite::Text2d>);
186
187
app.add_systems(
188
PostUpdate,
189
(
190
propagate_ui_target_cameras.in_set(UiSystems::Prepare),
191
ui_layout_system_config,
192
ui_stack_system
193
.in_set(UiSystems::Stack)
194
// These systems don't care about stack index
195
.ambiguous_with(widget::measure_text_system)
196
.ambiguous_with(update_clipping_system)
197
.ambiguous_with(ui_layout_system)
198
.ambiguous_with(widget::update_viewport_render_target_size)
199
.in_set(AmbiguousWithText),
200
update_clipping_system.after(TransformSystems::Propagate),
201
// Potential conflicts: `Assets<Image>`
202
// They run independently since `widget::image_node_system` will only ever observe
203
// its own ImageNode, and `widget::text_system` & `bevy_text::update_text2d_layout`
204
// will never modify a pre-existing `Image` asset.
205
widget::update_image_content_size_system
206
.in_set(UiSystems::Content)
207
.in_set(AmbiguousWithText)
208
.in_set(AmbiguousWithUpdateText2dLayout),
209
// Potential conflicts: `Assets<Image>`
210
// `widget::text_system` and `bevy_text::update_text2d_layout` run independently
211
// since this system will only ever update viewport images.
212
widget::update_viewport_render_target_size
213
.in_set(UiSystems::PostLayout)
214
.in_set(AmbiguousWithText)
215
.in_set(AmbiguousWithUpdateText2dLayout),
216
),
217
);
218
219
build_text_interop(app);
220
}
221
}
222
223
fn build_text_interop(app: &mut App) {
224
use widget::Text;
225
226
app.add_systems(
227
PostUpdate,
228
(
229
(
230
bevy_text::detect_text_needs_rerender::<Text>,
231
widget::measure_text_system,
232
)
233
.chain()
234
.after(bevy_text::load_font_assets_into_fontdb_system)
235
.in_set(UiSystems::Content)
236
// Text and Text2d are independent.
237
.ambiguous_with(bevy_text::detect_text_needs_rerender::<bevy_sprite::Text2d>)
238
// Potential conflict: `Assets<Image>`
239
// Since both systems will only ever insert new [`Image`] assets,
240
// they will never observe each other's effects.
241
.ambiguous_with(bevy_sprite::update_text2d_layout)
242
// We assume Text is on disjoint UI entities to ImageNode and UiTextureAtlasImage
243
// FIXME: Add an archetype invariant for this https://github.com/bevyengine/bevy/issues/1481.
244
.ambiguous_with(widget::update_image_content_size_system),
245
widget::text_system
246
.in_set(UiSystems::PostLayout)
247
.after(bevy_text::load_font_assets_into_fontdb_system)
248
.after(bevy_asset::AssetEventSystems)
249
// Text2d and bevy_ui text are entirely on separate entities
250
.ambiguous_with(bevy_text::detect_text_needs_rerender::<bevy_sprite::Text2d>)
251
.ambiguous_with(bevy_sprite::update_text2d_layout)
252
.ambiguous_with(bevy_sprite::calculate_bounds_text2d),
253
),
254
);
255
256
app.add_plugins(accessibility::AccessibilityPlugin);
257
258
app.add_observer(interaction_states::on_add_disabled)
259
.add_observer(interaction_states::on_remove_disabled)
260
.add_observer(interaction_states::on_add_checkable)
261
.add_observer(interaction_states::on_remove_checkable)
262
.add_observer(interaction_states::on_add_checked)
263
.add_observer(interaction_states::on_remove_checked);
264
265
app.configure_sets(
266
PostUpdate,
267
AmbiguousWithText.ambiguous_with(widget::text_system),
268
);
269
270
app.configure_sets(
271
PostUpdate,
272
AmbiguousWithUpdateText2dLayout.ambiguous_with(bevy_sprite::update_text2d_layout),
273
);
274
}
275
276