@@ -56,8 +56,17 @@ pub struct State {
56
56
entities_to_reset : SmallVec < [ Entity ; 1 ] > ,
57
57
}
58
58
59
- /// The system that sets Interaction for all UI elements based on the mouse and touch cursor
60
- /// activity
59
+ pub type NodeQuery < ' a > = (
60
+ Entity ,
61
+ & ' a Node ,
62
+ & ' a GlobalTransform ,
63
+ & ' a mut Interaction ,
64
+ Option < & ' a FocusPolicy > ,
65
+ Option < & ' a CalculatedClip > ,
66
+ ) ;
67
+
68
+ /// The system that sets Interaction for all UI elements based on the mouse cursor activity
69
+ #[ allow( clippy:: type_complexity) ]
61
70
pub fn ui_focus_system (
62
71
state : Local < State > ,
63
72
windows : Res < Windows > ,
@@ -96,40 +105,29 @@ fn focus_ui<Cursor: CursorResource>(
96
105
Option < & CalculatedClip > ,
97
106
) > ,
98
107
) {
108
+ reset_interactions (
109
+ & mut node_query,
110
+ & mouse_button_input,
111
+ & touches_input,
112
+ & windows. get_cursor_position ( ) ,
113
+ & mut state,
114
+ ) ;
115
+
99
116
let cursor_position = match windows. get_cursor_position ( ) {
100
- None => {
101
- set_all_interactions_to_none ( node_query) ;
102
- return ;
103
- }
117
+ None => return ,
104
118
Some ( cursor_position) => cursor_position,
105
119
} ;
106
120
107
- // reset entities that were both clicked and released in the last frame
108
- for entity in state. entities_to_reset . drain ( ..) {
109
- if let Ok ( mut interaction) = node_query. get_component_mut :: < Interaction > ( entity) {
110
- * interaction = Interaction :: None ;
111
- }
112
- }
113
-
114
121
let mouse_released =
115
122
mouse_button_input. just_released ( MouseButton :: Left ) || touches_input. just_released ( 0 ) ;
116
- if mouse_released {
117
- for ( _entity, _node, _global_transform, mut interaction, _focus_policy, _clip) in
118
- node_query. iter_mut ( )
119
- {
120
- if * interaction == Interaction :: Clicked {
121
- * interaction = Interaction :: None ;
122
- }
123
- }
124
- }
125
123
126
124
let mouse_clicked =
127
125
mouse_button_input. just_pressed ( MouseButton :: Left ) || touches_input. just_released ( 0 ) ;
128
126
129
127
let mut moused_over_z_sorted_nodes = node_query
130
128
. iter_mut ( )
131
129
. filter_map (
132
- |( entity, node, global_transform, mut interaction, focus_policy, clip) | {
130
+ |( entity, node, global_transform, interaction, focus_policy, clip) | {
133
131
let position = global_transform. translation ;
134
132
let ui_position = position. truncate ( ) ;
135
133
let extents = node. size / 2.0 ;
@@ -147,9 +145,6 @@ fn focus_ui<Cursor: CursorResource>(
147
145
if contains_cursor {
148
146
Some ( ( entity, focus_policy, interaction, FloatOrd ( position. z ) ) )
149
147
} else {
150
- if * interaction == Interaction :: Hovered {
151
- * interaction = Interaction :: None ;
152
- }
153
148
None
154
149
}
155
150
} ,
@@ -158,19 +153,14 @@ fn focus_ui<Cursor: CursorResource>(
158
153
159
154
moused_over_z_sorted_nodes. sort_by_key ( |( _, _, _, z) | -* z) ;
160
155
161
- let mut moused_over_z_sorted_nodes = moused_over_z_sorted_nodes. into_iter ( ) ;
162
156
// set Clicked or Hovered on top nodes
163
- for ( entity, focus_policy, mut interaction, _) in moused_over_z_sorted_nodes. by_ref ( ) {
157
+ for ( entity, focus_policy, mut interaction, _) in moused_over_z_sorted_nodes {
164
158
if mouse_clicked {
165
- // only consider nodes with Interaction "clickable"
166
- if * interaction != Interaction :: Clicked {
167
- * interaction = Interaction :: Clicked ;
168
- // if the mouse was simultaneously released, reset this Interaction in the next
169
- // frame
170
- if mouse_released {
171
- state. entities_to_reset . push ( entity) ;
172
- }
159
+ // if the mouse was simultaneously released, reset this Interaction in the next frame
160
+ if * interaction != Interaction :: Clicked && mouse_released {
161
+ state. entities_to_reset . push ( entity) ;
173
162
}
163
+ * interaction = Interaction :: Clicked ;
174
164
} else if * interaction == Interaction :: None {
175
165
* interaction = Interaction :: Hovered ;
176
166
}
@@ -182,29 +172,41 @@ fn focus_ui<Cursor: CursorResource>(
182
172
FocusPolicy :: Pass => { /* allow the next node to be hovered/clicked */ }
183
173
}
184
174
}
185
- // reset lower nodes to None
186
- for ( _entity, _focus_policy, mut interaction, _) in moused_over_z_sorted_nodes {
187
- if * interaction != Interaction :: None {
188
- * interaction = Interaction :: None ;
189
- }
190
- }
191
175
}
192
176
193
- fn set_all_interactions_to_none (
194
- mut node_query : Query < (
177
+ fn reset_interactions (
178
+ node_query : & mut Query < (
195
179
Entity ,
196
180
& Node ,
197
181
& GlobalTransform ,
198
182
& mut Interaction ,
199
183
Option < & FocusPolicy > ,
200
184
Option < & CalculatedClip > ,
201
185
) > ,
186
+ mouse_button_input : & Input < MouseButton > ,
187
+ touches_input : & Touches ,
188
+ cursor_position : & Option < Vec2 > ,
189
+ state : & mut State ,
202
190
) {
191
+ let mouse_release =
192
+ mouse_button_input. just_released ( MouseButton :: Left ) || touches_input. just_released ( 0 ) ;
193
+ let input_should_leave_button_clicked = cursor_position. is_some ( ) && !mouse_release;
194
+
203
195
for ( _entity, _node, _global_transform, mut interaction, _focus_policy, _clip) in
204
196
node_query. iter_mut ( )
205
197
{
198
+ if input_should_leave_button_clicked && * interaction == Interaction :: Clicked {
199
+ continue ;
200
+ }
206
201
* interaction = Interaction :: None ;
207
202
}
203
+
204
+ // reset entities that were both clicked and released in the last frame
205
+ for entity in state. entities_to_reset . drain ( ..) {
206
+ if let Ok ( mut interaction) = node_query. get_component_mut :: < Interaction > ( entity) {
207
+ * interaction = Interaction :: None ;
208
+ }
209
+ }
208
210
}
209
211
210
212
trait CursorResource : Resource {
0 commit comments