1use alloc::collections::BTreeMap;
7use core::fmt::Debug;
8use std::collections::HashSet;
9
10use crate::{
11 backend::{self, HitData},
12 pointer::{PointerAction, PointerId, PointerInput, PointerInteraction, PointerPress},
13 PickingBehavior,
14};
15
16use bevy_derive::{Deref, DerefMut};
17use bevy_ecs::prelude::*;
18use bevy_math::FloatOrd;
19use bevy_reflect::prelude::*;
20use bevy_utils::HashMap;
21
22type DepthSortedHits = Vec<(Entity, HitData)>;
23
24type PickLayer = FloatOrd;
27
28type LayerMap = BTreeMap<PickLayer, DepthSortedHits>;
30
31type OverMap = HashMap<PointerId, LayerMap>;
34
35#[derive(Debug, Deref, DerefMut, Default, Resource)]
57pub struct HoverMap(pub HashMap<PointerId, HashMap<Entity, HitData>>);
58
59#[derive(Debug, Deref, DerefMut, Default, Resource)]
61pub struct PreviousHoverMap(pub HashMap<PointerId, HashMap<Entity, HitData>>);
62
63pub fn update_focus(
66 picking_behavior: Query<&PickingBehavior>,
68 pointers: Query<&PointerId>,
69 mut under_pointer: EventReader<backend::PointerHits>,
70 mut pointer_input: EventReader<PointerInput>,
71 mut over_map: Local<OverMap>,
73 mut hover_map: ResMut<HoverMap>,
75 mut previous_hover_map: ResMut<PreviousHoverMap>,
76) {
77 reset_maps(
78 &mut hover_map,
79 &mut previous_hover_map,
80 &mut over_map,
81 &pointers,
82 );
83 build_over_map(&mut under_pointer, &mut over_map, &mut pointer_input);
84 build_hover_map(&pointers, picking_behavior, &over_map, &mut hover_map);
85}
86
87fn reset_maps(
89 hover_map: &mut HoverMap,
90 previous_hover_map: &mut PreviousHoverMap,
91 over_map: &mut OverMap,
92 pointers: &Query<&PointerId>,
93) {
94 core::mem::swap(&mut previous_hover_map.0, &mut hover_map.0);
98
99 for entity_set in hover_map.values_mut() {
100 entity_set.clear();
101 }
102 for layer_map in over_map.values_mut() {
103 layer_map.clear();
104 }
105
106 let active_pointers: Vec<PointerId> = pointers.iter().copied().collect();
108 hover_map.retain(|pointer, _| active_pointers.contains(pointer));
109 over_map.retain(|pointer, _| active_pointers.contains(pointer));
110}
111
112fn build_over_map(
114 backend_events: &mut EventReader<backend::PointerHits>,
115 pointer_over_map: &mut Local<OverMap>,
116 pointer_input: &mut EventReader<PointerInput>,
117) {
118 let cancelled_pointers: HashSet<PointerId> = pointer_input
119 .read()
120 .filter_map(|p| {
121 if let PointerAction::Canceled = p.action {
122 Some(p.pointer_id)
123 } else {
124 None
125 }
126 })
127 .collect();
128
129 for entities_under_pointer in backend_events
130 .read()
131 .filter(|e| !cancelled_pointers.contains(&e.pointer))
132 {
133 let pointer = entities_under_pointer.pointer;
134 let layer_map = pointer_over_map
135 .entry(pointer)
136 .or_insert_with(BTreeMap::new);
137 for (entity, pick_data) in entities_under_pointer.picks.iter() {
138 let layer = entities_under_pointer.order;
139 let hits = layer_map.entry(FloatOrd(layer)).or_default();
140 hits.push((*entity, pick_data.clone()));
141 }
142 }
143
144 for layers in pointer_over_map.values_mut() {
145 for hits in layers.values_mut() {
146 hits.sort_by_key(|(_, hit)| FloatOrd(hit.depth));
147 }
148 }
149}
150
151fn build_hover_map(
155 pointers: &Query<&PointerId>,
156 picking_behavior: Query<&PickingBehavior>,
157 over_map: &Local<OverMap>,
158 hover_map: &mut HoverMap,
160) {
161 for pointer_id in pointers.iter() {
162 let pointer_entity_set = hover_map.entry(*pointer_id).or_default();
163 if let Some(layer_map) = over_map.get(pointer_id) {
164 for (entity, pick_data) in layer_map.values().rev().flatten() {
166 if let Ok(picking_behavior) = picking_behavior.get(*entity) {
167 if picking_behavior.is_hoverable {
168 pointer_entity_set.insert(*entity, pick_data.clone());
169 }
170 if picking_behavior.should_block_lower {
171 break;
172 }
173 } else {
174 pointer_entity_set.insert(*entity, pick_data.clone()); break; }
177 }
178 }
179 }
180}
181
182#[derive(Component, Copy, Clone, Default, Eq, PartialEq, Debug, Reflect)]
192#[reflect(Component, Default, PartialEq, Debug)]
193pub enum PickingInteraction {
194 Pressed = 2,
196 Hovered = 1,
198 #[default]
200 None = 0,
201}
202
203pub fn update_interactions(
205 hover_map: Res<HoverMap>,
207 previous_hover_map: Res<PreviousHoverMap>,
208 mut commands: Commands,
210 mut pointers: Query<(&PointerId, &PointerPress, &mut PointerInteraction)>,
211 mut interact: Query<&mut PickingInteraction>,
212) {
213 for (pointer, _, mut pointer_interaction) in &mut pointers {
215 pointer_interaction.sorted_entities.clear();
216 if let Some(previously_hovered_entities) = previous_hover_map.get(pointer) {
217 for entity in previously_hovered_entities.keys() {
218 if let Ok(mut interaction) = interact.get_mut(*entity) {
219 *interaction = PickingInteraction::None;
220 }
221 }
222 }
223 }
224
225 let mut new_interaction_state = HashMap::<Entity, PickingInteraction>::new();
230 for (pointer, pointer_press, mut pointer_interaction) in &mut pointers {
231 if let Some(pointers_hovered_entities) = hover_map.get(pointer) {
232 let mut sorted_entities: Vec<_> = pointers_hovered_entities.clone().drain().collect();
234 sorted_entities.sort_by_key(|(_entity, hit)| FloatOrd(hit.depth));
235 pointer_interaction.sorted_entities = sorted_entities;
236
237 for hovered_entity in pointers_hovered_entities.iter().map(|(entity, _)| entity) {
238 merge_interaction_states(pointer_press, hovered_entity, &mut new_interaction_state);
239 }
240 }
241 }
242
243 for (hovered_entity, new_interaction) in new_interaction_state.drain() {
245 if let Ok(mut interaction) = interact.get_mut(hovered_entity) {
246 *interaction = new_interaction;
247 } else if let Some(mut entity_commands) = commands.get_entity(hovered_entity) {
248 entity_commands.try_insert(new_interaction);
249 }
250 }
251}
252
253fn merge_interaction_states(
255 pointer_press: &PointerPress,
256 hovered_entity: &Entity,
257 new_interaction_state: &mut HashMap<Entity, PickingInteraction>,
258) {
259 let new_interaction = match pointer_press.is_any_pressed() {
260 true => PickingInteraction::Pressed,
261 false => PickingInteraction::Hovered,
262 };
263
264 if let Some(old_interaction) = new_interaction_state.get_mut(hovered_entity) {
265 if *old_interaction != new_interaction
267 && matches!(
268 (*old_interaction, new_interaction),
269 (PickingInteraction::Hovered, PickingInteraction::Pressed)
270 | (PickingInteraction::None, PickingInteraction::Pressed)
271 | (PickingInteraction::None, PickingInteraction::Hovered)
272 )
273 {
274 *old_interaction = new_interaction;
275 }
276 } else {
277 new_interaction_state.insert(*hovered_entity, new_interaction);
278 }
279}