From bd227859ebf6f13855c7efdba27b2eef40df76af Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sun, 19 Jul 2020 20:52:15 -0700 Subject: [PATCH] ui: only allow one thing to be hovered at a time --- crates/bevy_ui/src/focus.rs | 110 +++++++++++++++++++++--------------- 1 file changed, 66 insertions(+), 44 deletions(-) diff --git a/crates/bevy_ui/src/focus.rs b/crates/bevy_ui/src/focus.rs index 8b5c2e7ba4..3e4f0fdc6d 100644 --- a/crates/bevy_ui/src/focus.rs +++ b/crates/bevy_ui/src/focus.rs @@ -47,6 +47,7 @@ impl Default for FocusPolicy { pub struct State { cursor_moved_event_reader: EventReader, cursor_position: Vec2, + hovered_entity: Option, } pub fn ui_focus_system( @@ -55,6 +56,7 @@ pub fn ui_focus_system( mouse_button_input: Res>, cursor_moved_events: Res>, mut node_query: Query<( + Entity, &Node, &Transform, Option<&mut Click>, @@ -67,7 +69,7 @@ pub fn ui_focus_system( } if mouse_button_input.just_released(MouseButton::Left) { - for (_node, _transform, click, _hover, _focus_policy) in &mut node_query.iter() { + for (_entity, _node, _transform, click, _hover, _focus_policy) in &mut node_query.iter() { if let Some(mut click) = click { if *click == Click::Pressed { *click = Click::Released; @@ -78,55 +80,75 @@ pub fn ui_focus_system( let mouse_clicked = mouse_button_input.just_pressed(MouseButton::Left); let window = windows.get_primary().unwrap(); + let mut hovered_entity = None; - let mut query_iter = node_query.iter(); - let mut moused_over_z_sorted_nodes = query_iter - .iter() - .filter_map(|(node, transform, click, hover, focus_policy)| { - let position = transform.value.w_axis(); - // TODO: ui transform is currently in world space, so we need to move it to ui space. we should make these transforms ui space - let ui_position = position.truncate().truncate() - + Vec2::new(window.width as f32 / 2.0, window.height as f32 / 2.0); - let extents = node.size / 2.0; - let min = ui_position - extents; - let max = ui_position + extents; - // if the current cursor position is within the bounds of the node, consider it for clicking - if (min.x()..max.x()).contains(&state.cursor_position.x()) - && (min.y()..max.y()).contains(&state.cursor_position.y()) - { - Some((focus_policy, click, hover, FloatOrd(position.z()))) - } else { - if let Some(mut hover) = hover { + { + let mut query_iter = node_query.iter(); + let mut moused_over_z_sorted_nodes = query_iter + .iter() + .filter_map(|(entity, node, transform, click, hover, focus_policy)| { + let position = transform.value.w_axis(); + // TODO: ui transform is currently in world space, so we need to move it to ui space. we should make these transforms ui space + let ui_position = position.truncate().truncate() + + Vec2::new(window.width as f32 / 2.0, window.height as f32 / 2.0); + let extents = node.size / 2.0; + let min = ui_position - extents; + let max = ui_position + extents; + // if the current cursor position is within the bounds of the node, consider it for clicking + if (min.x()..max.x()).contains(&state.cursor_position.x()) + && (min.y()..max.y()).contains(&state.cursor_position.y()) + { + Some((entity, focus_policy, click, hover, FloatOrd(position.z()))) + } else { + if let Some(mut hover) = hover { + if *hover == Hover::Hovered { + *hover = Hover::NotHovered; + } + } + None + } + }) + .collect::>(); + + moused_over_z_sorted_nodes.sort_by_key(|(_, _, _, _, z)| -*z); + for (entity, focus_policy, click, hover, _) in moused_over_z_sorted_nodes { + if mouse_clicked { + // only consider nodes with ClickState "clickable" + if let Some(mut click) = click { + if *click == Click::Released { + *click = Click::Pressed; + } + } + } + // only consider nodes with Hover "hoverable" + if let Some(mut hover) = hover { + if *hover == Hover::NotHovered { + *hover = Hover::Hovered; + } + + hovered_entity = Some(entity); + } + match focus_policy.cloned().unwrap_or(FocusPolicy::Block) { + FocusPolicy::Block => { + break; + } + FocusPolicy::Pass => { /* allow the next node to be hovered/clicked */ } + } + } + } + + // if there is a new hovered entity, but an entity is currently hovered, unhover the old entity + if let Some(new_hovered_entity) = hovered_entity { + if let Some(old_hovered_entity) = state.hovered_entity { + if new_hovered_entity != old_hovered_entity { + if let Ok(mut hover) = node_query.get_mut(old_hovered_entity) { if *hover == Hover::Hovered { *hover = Hover::NotHovered; } } - None - } - }) - .collect::>(); - - moused_over_z_sorted_nodes.sort_by_key(|(_, _, _, z)| -*z); - for (focus_policy, click, hover, _) in moused_over_z_sorted_nodes { - if mouse_clicked { - // only consider nodes with ClickState "clickable" - if let Some(mut click) = click { - if *click == Click::Released { - *click = Click::Pressed; - } + state.hovered_entity = None; } } - // only consider nodes with Hover "hoverable" - if let Some(mut hover) = hover { - if *hover == Hover::NotHovered { - *hover = Hover::Hovered; - } - } - match focus_policy.cloned().unwrap_or(FocusPolicy::Block) { - FocusPolicy::Block => { - break; - } - FocusPolicy::Pass => { /* allow the next node to be hovered/clicked */ } - } + state.hovered_entity = hovered_entity; } }