1
- mod convert;
2
- pub mod debug;
1
+ use thiserror:: Error ;
3
2
4
- use crate :: { ContentSize , DefaultUiCamera , Node , Outline , Style , TargetCamera , UiScale } ;
5
3
use bevy_ecs:: {
6
4
change_detection:: { DetectChanges , DetectChangesMut } ,
7
- entity:: { Entity , EntityHashMap } ,
5
+ entity:: Entity ,
8
6
event:: EventReader ,
9
7
query:: { With , Without } ,
10
8
removal_detection:: RemovedComponents ,
11
- system:: { Query , Res , ResMut , Resource , SystemParam } ,
9
+ system:: { Query , Res , ResMut , SystemParam } ,
12
10
world:: Ref ,
13
11
} ;
14
12
use bevy_hierarchy:: { Children , Parent } ;
15
13
use bevy_math:: { UVec2 , Vec2 } ;
16
14
use bevy_render:: camera:: { Camera , NormalizedRenderTarget } ;
17
15
use bevy_transform:: components:: Transform ;
18
16
use bevy_utils:: tracing:: warn;
19
- use bevy_utils:: { default , HashMap , HashSet } ;
17
+ use bevy_utils:: { HashMap , HashSet } ;
20
18
use bevy_window:: { PrimaryWindow , Window , WindowScaleFactorChanged } ;
21
- use std:: fmt;
22
- use taffy:: { tree:: LayoutTree , Taffy } ;
23
- use thiserror:: Error ;
19
+ use ui_surface:: UiSurface ;
20
+
21
+ use crate :: { ContentSize , DefaultUiCamera , Node , Outline , Style , TargetCamera , UiScale } ;
22
+
23
+ mod convert;
24
+ pub mod debug;
25
+ pub ( crate ) mod ui_surface;
24
26
25
27
pub struct LayoutContext {
26
28
pub scale_factor : f32 ,
@@ -41,218 +43,6 @@ impl LayoutContext {
41
43
}
42
44
}
43
45
44
- #[ derive( Debug , Clone , PartialEq , Eq ) ]
45
- struct RootNodePair {
46
- // The implicit "viewport" node created by Bevy
47
- implicit_viewport_node : taffy:: node:: Node ,
48
- // The root (parentless) node specified by the user
49
- user_root_node : taffy:: node:: Node ,
50
- }
51
-
52
- #[ derive( Resource ) ]
53
- pub struct UiSurface {
54
- entity_to_taffy : EntityHashMap < taffy:: node:: Node > ,
55
- camera_entity_to_taffy : EntityHashMap < EntityHashMap < taffy:: node:: Node > > ,
56
- camera_roots : EntityHashMap < Vec < RootNodePair > > ,
57
- taffy : Taffy ,
58
- }
59
-
60
- fn _assert_send_sync_ui_surface_impl_safe ( ) {
61
- fn _assert_send_sync < T : Send + Sync > ( ) { }
62
- _assert_send_sync :: < EntityHashMap < taffy:: node:: Node > > ( ) ;
63
- _assert_send_sync :: < Taffy > ( ) ;
64
- _assert_send_sync :: < UiSurface > ( ) ;
65
- }
66
-
67
- impl fmt:: Debug for UiSurface {
68
- fn fmt ( & self , f : & mut fmt:: Formatter ) -> fmt:: Result {
69
- f. debug_struct ( "UiSurface" )
70
- . field ( "entity_to_taffy" , & self . entity_to_taffy )
71
- . field ( "camera_roots" , & self . camera_roots )
72
- . finish ( )
73
- }
74
- }
75
-
76
- impl Default for UiSurface {
77
- fn default ( ) -> Self {
78
- let mut taffy = Taffy :: new ( ) ;
79
- taffy. disable_rounding ( ) ;
80
- Self {
81
- entity_to_taffy : Default :: default ( ) ,
82
- camera_entity_to_taffy : Default :: default ( ) ,
83
- camera_roots : Default :: default ( ) ,
84
- taffy,
85
- }
86
- }
87
- }
88
-
89
- impl UiSurface {
90
- /// Retrieves the Taffy node associated with the given UI node entity and updates its style.
91
- /// If no associated Taffy node exists a new Taffy node is inserted into the Taffy layout.
92
- pub fn upsert_node ( & mut self , entity : Entity , style : & Style , context : & LayoutContext ) {
93
- let mut added = false ;
94
- let taffy = & mut self . taffy ;
95
- let taffy_node = self . entity_to_taffy . entry ( entity) . or_insert_with ( || {
96
- added = true ;
97
- taffy. new_leaf ( convert:: from_style ( context, style) ) . unwrap ( )
98
- } ) ;
99
-
100
- if !added {
101
- self . taffy
102
- . set_style ( * taffy_node, convert:: from_style ( context, style) )
103
- . unwrap ( ) ;
104
- }
105
- }
106
-
107
- /// Update the `MeasureFunc` of the taffy node corresponding to the given [`Entity`] if the node exists.
108
- pub fn try_update_measure (
109
- & mut self ,
110
- entity : Entity ,
111
- measure_func : taffy:: node:: MeasureFunc ,
112
- ) -> Option < ( ) > {
113
- let taffy_node = self . entity_to_taffy . get ( & entity) ?;
114
-
115
- self . taffy . set_measure ( * taffy_node, Some ( measure_func) ) . ok ( )
116
- }
117
-
118
- /// Update the children of the taffy node corresponding to the given [`Entity`].
119
- pub fn update_children ( & mut self , entity : Entity , children : & Children ) {
120
- let mut taffy_children = Vec :: with_capacity ( children. len ( ) ) ;
121
- for child in children {
122
- if let Some ( taffy_node) = self . entity_to_taffy . get ( child) {
123
- taffy_children. push ( * taffy_node) ;
124
- } else {
125
- warn ! (
126
- "Unstyled child in a UI entity hierarchy. You are using an entity \
127
- without UI components as a child of an entity with UI components, results may be unexpected."
128
- ) ;
129
- }
130
- }
131
-
132
- let taffy_node = self . entity_to_taffy . get ( & entity) . unwrap ( ) ;
133
- self . taffy
134
- . set_children ( * taffy_node, & taffy_children)
135
- . unwrap ( ) ;
136
- }
137
-
138
- /// Removes children from the entity's taffy node if it exists. Does nothing otherwise.
139
- pub fn try_remove_children ( & mut self , entity : Entity ) {
140
- if let Some ( taffy_node) = self . entity_to_taffy . get ( & entity) {
141
- self . taffy . set_children ( * taffy_node, & [ ] ) . unwrap ( ) ;
142
- }
143
- }
144
-
145
- /// Removes the measure from the entity's taffy node if it exists. Does nothing otherwise.
146
- pub fn try_remove_measure ( & mut self , entity : Entity ) {
147
- if let Some ( taffy_node) = self . entity_to_taffy . get ( & entity) {
148
- self . taffy . set_measure ( * taffy_node, None ) . unwrap ( ) ;
149
- }
150
- }
151
-
152
- /// Set the ui node entities without a [`Parent`] as children to the root node in the taffy layout.
153
- pub fn set_camera_children (
154
- & mut self ,
155
- camera_id : Entity ,
156
- children : impl Iterator < Item = Entity > ,
157
- ) {
158
- let viewport_style = taffy:: style:: Style {
159
- display : taffy:: style:: Display :: Grid ,
160
- // Note: Taffy percentages are floats ranging from 0.0 to 1.0.
161
- // So this is setting width:100% and height:100%
162
- size : taffy:: geometry:: Size {
163
- width : taffy:: style:: Dimension :: Percent ( 1.0 ) ,
164
- height : taffy:: style:: Dimension :: Percent ( 1.0 ) ,
165
- } ,
166
- align_items : Some ( taffy:: style:: AlignItems :: Start ) ,
167
- justify_items : Some ( taffy:: style:: JustifyItems :: Start ) ,
168
- ..default ( )
169
- } ;
170
-
171
- let camera_root_node_map = self . camera_entity_to_taffy . entry ( camera_id) . or_default ( ) ;
172
- let existing_roots = self . camera_roots . entry ( camera_id) . or_default ( ) ;
173
- let mut new_roots = Vec :: new ( ) ;
174
- for entity in children {
175
- let node = * self . entity_to_taffy . get ( & entity) . unwrap ( ) ;
176
- let root_node = existing_roots
177
- . iter ( )
178
- . find ( |n| n. user_root_node == node)
179
- . cloned ( )
180
- . unwrap_or_else ( || {
181
- if let Some ( previous_parent) = self . taffy . parent ( node) {
182
- // remove the root node from the previous implicit node's children
183
- self . taffy . remove_child ( previous_parent, node) . unwrap ( ) ;
184
- }
185
-
186
- let viewport_node = * camera_root_node_map
187
- . entry ( entity)
188
- . or_insert_with ( || self . taffy . new_leaf ( viewport_style. clone ( ) ) . unwrap ( ) ) ;
189
- self . taffy . add_child ( viewport_node, node) . unwrap ( ) ;
190
-
191
- RootNodePair {
192
- implicit_viewport_node : viewport_node,
193
- user_root_node : node,
194
- }
195
- } ) ;
196
- new_roots. push ( root_node) ;
197
- }
198
-
199
- self . camera_roots . insert ( camera_id, new_roots) ;
200
- }
201
-
202
- /// Compute the layout for each window entity's corresponding root node in the layout.
203
- pub fn compute_camera_layout ( & mut self , camera : Entity , render_target_resolution : UVec2 ) {
204
- let Some ( camera_root_nodes) = self . camera_roots . get ( & camera) else {
205
- return ;
206
- } ;
207
-
208
- let available_space = taffy:: geometry:: Size {
209
- width : taffy:: style:: AvailableSpace :: Definite ( render_target_resolution. x as f32 ) ,
210
- height : taffy:: style:: AvailableSpace :: Definite ( render_target_resolution. y as f32 ) ,
211
- } ;
212
- for root_nodes in camera_root_nodes {
213
- self . taffy
214
- . compute_layout ( root_nodes. implicit_viewport_node , available_space)
215
- . unwrap ( ) ;
216
- }
217
- }
218
-
219
- /// Removes each camera entity from the internal map and then removes their associated node from taffy
220
- pub fn remove_camera_entities ( & mut self , entities : impl IntoIterator < Item = Entity > ) {
221
- for entity in entities {
222
- if let Some ( camera_root_node_map) = self . camera_entity_to_taffy . remove ( & entity) {
223
- for ( _, node) in camera_root_node_map. iter ( ) {
224
- self . taffy . remove ( * node) . unwrap ( ) ;
225
- }
226
- }
227
- }
228
- }
229
-
230
- /// Removes each entity from the internal map and then removes their associated node from taffy
231
- pub fn remove_entities ( & mut self , entities : impl IntoIterator < Item = Entity > ) {
232
- for entity in entities {
233
- if let Some ( node) = self . entity_to_taffy . remove ( & entity) {
234
- self . taffy . remove ( node) . unwrap ( ) ;
235
- }
236
- }
237
- }
238
-
239
- /// Get the layout geometry for the taffy node corresponding to the ui node [`Entity`].
240
- /// Does not compute the layout geometry, `compute_window_layouts` should be run before using this function.
241
- pub fn get_layout ( & self , entity : Entity ) -> Result < & taffy:: layout:: Layout , LayoutError > {
242
- if let Some ( taffy_node) = self . entity_to_taffy . get ( & entity) {
243
- self . taffy
244
- . layout ( * taffy_node)
245
- . map_err ( LayoutError :: TaffyError )
246
- } else {
247
- warn ! (
248
- "Styled child in a non-UI entity hierarchy. You are using an entity \
249
- with UI components as a child of an entity without UI components, results may be unexpected."
250
- ) ;
251
- Err ( LayoutError :: InvalidHierarchy )
252
- }
253
- }
254
- }
255
-
256
46
#[ derive( Debug , Error ) ]
257
47
pub enum LayoutError {
258
48
#[ error( "Invalid hierarchy" ) ]
@@ -533,12 +323,8 @@ fn round_layout_coords(value: Vec2) -> Vec2 {
533
323
534
324
#[ cfg( test) ]
535
325
mod tests {
536
- use crate :: layout:: round_layout_coords;
537
- use crate :: prelude:: * ;
538
- use crate :: ui_layout_system;
539
- use crate :: update:: update_target_camera_system;
540
- use crate :: ContentSize ;
541
- use crate :: UiSurface ;
326
+ use taffy:: tree:: LayoutTree ;
327
+
542
328
use bevy_asset:: AssetEvent ;
543
329
use bevy_asset:: Assets ;
544
330
use bevy_core_pipeline:: core_2d:: Camera2dBundle ;
@@ -566,7 +352,13 @@ mod tests {
566
352
use bevy_window:: WindowResized ;
567
353
use bevy_window:: WindowResolution ;
568
354
use bevy_window:: WindowScaleFactorChanged ;
569
- use taffy:: tree:: LayoutTree ;
355
+
356
+ use crate :: layout:: round_layout_coords;
357
+ use crate :: layout:: ui_surface:: UiSurface ;
358
+ use crate :: prelude:: * ;
359
+ use crate :: ui_layout_system;
360
+ use crate :: update:: update_target_camera_system;
361
+ use crate :: ContentSize ;
570
362
571
363
#[ test]
572
364
fn round_layout_coords_must_round_ties_up ( ) {
0 commit comments