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 Pickable,
14};
15
16use bevy_derive::{Deref, DerefMut};
17use bevy_ecs::prelude::*;
18use bevy_math::FloatOrd;
19use bevy_platform::collections::HashMap;
20use bevy_reflect::prelude::*;
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 generate_hovermap(
66 pickable: Query<&Pickable>,
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, pickable, &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::Cancel = 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.entry(pointer).or_default();
135 for (entity, pick_data) in entities_under_pointer.picks.iter() {
136 let layer = entities_under_pointer.order;
137 let hits = layer_map.entry(FloatOrd(layer)).or_default();
138 hits.push((*entity, pick_data.clone()));
139 }
140 }
141
142 for layers in pointer_over_map.values_mut() {
143 for hits in layers.values_mut() {
144 hits.sort_by_key(|(_, hit)| FloatOrd(hit.depth));
145 }
146 }
147}
148
149fn build_hover_map(
153 pointers: &Query<&PointerId>,
154 pickable: Query<&Pickable>,
155 over_map: &Local<OverMap>,
156 hover_map: &mut HoverMap,
158) {
159 for pointer_id in pointers.iter() {
160 let pointer_entity_set = hover_map.entry(*pointer_id).or_default();
161 if let Some(layer_map) = over_map.get(pointer_id) {
162 for (entity, pick_data) in layer_map.values().rev().flatten() {
164 if let Ok(pickable) = pickable.get(*entity) {
165 if pickable.is_hoverable {
166 pointer_entity_set.insert(*entity, pick_data.clone());
167 }
168 if pickable.should_block_lower {
169 break;
170 }
171 } else {
172 pointer_entity_set.insert(*entity, pick_data.clone()); break; }
175 }
176 }
177 }
178}
179
180#[derive(Component, Copy, Clone, Default, Eq, PartialEq, Debug, Reflect)]
190#[reflect(Component, Default, PartialEq, Debug, Clone)]
191pub enum PickingInteraction {
192 Pressed = 2,
194 Hovered = 1,
196 #[default]
198 None = 0,
199}
200
201pub fn update_interactions(
203 hover_map: Res<HoverMap>,
205 previous_hover_map: Res<PreviousHoverMap>,
206 mut commands: Commands,
208 mut pointers: Query<(&PointerId, &PointerPress, &mut PointerInteraction)>,
209 mut interact: Query<&mut PickingInteraction>,
210) {
211 for (pointer, _, mut pointer_interaction) in &mut pointers {
213 pointer_interaction.sorted_entities.clear();
214 if let Some(previously_hovered_entities) = previous_hover_map.get(pointer) {
215 for entity in previously_hovered_entities.keys() {
216 if let Ok(mut interaction) = interact.get_mut(*entity) {
217 *interaction = PickingInteraction::None;
218 }
219 }
220 }
221 }
222
223 let mut new_interaction_state = HashMap::<Entity, PickingInteraction>::default();
228 for (pointer, pointer_press, mut pointer_interaction) in &mut pointers {
229 if let Some(pointers_hovered_entities) = hover_map.get(pointer) {
230 let mut sorted_entities: Vec<_> = pointers_hovered_entities.clone().drain().collect();
232 sorted_entities.sort_by_key(|(_, hit)| FloatOrd(hit.depth));
233 pointer_interaction.sorted_entities = sorted_entities;
234
235 for hovered_entity in pointers_hovered_entities.iter().map(|(entity, _)| entity) {
236 merge_interaction_states(pointer_press, hovered_entity, &mut new_interaction_state);
237 }
238 }
239 }
240
241 for (hovered_entity, new_interaction) in new_interaction_state.drain() {
243 if let Ok(mut interaction) = interact.get_mut(hovered_entity) {
244 *interaction = new_interaction;
245 } else if let Ok(mut entity_commands) = commands.get_entity(hovered_entity) {
246 entity_commands.try_insert(new_interaction);
247 }
248 }
249}
250
251fn merge_interaction_states(
253 pointer_press: &PointerPress,
254 hovered_entity: &Entity,
255 new_interaction_state: &mut HashMap<Entity, PickingInteraction>,
256) {
257 let new_interaction = match pointer_press.is_any_pressed() {
258 true => PickingInteraction::Pressed,
259 false => PickingInteraction::Hovered,
260 };
261
262 if let Some(old_interaction) = new_interaction_state.get_mut(hovered_entity) {
263 if *old_interaction != new_interaction
265 && matches!(
266 (*old_interaction, new_interaction),
267 (PickingInteraction::Hovered, PickingInteraction::Pressed)
268 | (PickingInteraction::None, PickingInteraction::Pressed)
269 | (PickingInteraction::None, PickingInteraction::Hovered)
270 )
271 {
272 *old_interaction = new_interaction;
273 }
274 } else {
275 new_interaction_state.insert(*hovered_entity, new_interaction);
276 }
277}