use bevy_ecs::hierarchy::ChildOf;
use bevy_ecs::prelude::*;
use super::PointerState;
#[derive(Clone, Debug)]
pub enum Phase<T> {
Tunnel(T),
Bubble(T),
}
impl<T> Phase<T> {
#[inline]
pub fn value(&self) -> &T {
match self {
Phase::Tunnel(v) | Phase::Bubble(v) => v,
}
}
#[inline]
pub fn is_tunnel(&self) -> bool {
matches!(self, Phase::Tunnel(_))
}
#[inline]
pub fn is_bubble(&self) -> bool {
matches!(self, Phase::Bubble(_))
}
}
pub type EventHandler<T> =
fn(world: &mut World, sender: Entity, entity: Entity, ev: &Phase<T>) -> bool;
pub type PointerEventHandler = EventHandler<PointerState>;
#[derive(Component, Clone, Copy)]
#[component(storage = "SparseSet")]
pub struct OnPointerPressed(pub PointerEventHandler);
#[derive(Component, Clone, Copy)]
#[component(storage = "SparseSet")]
pub struct OnPointerReleased(pub PointerEventHandler);
#[derive(Component, Clone, Copy)]
#[component(storage = "SparseSet")]
pub struct OnPointerEntered(pub PointerEventHandler);
#[derive(Component, Clone, Copy)]
#[component(storage = "SparseSet")]
pub struct OnPointerExited(pub PointerEventHandler);
#[derive(Component, Clone, Copy)]
#[component(storage = "SparseSet")]
pub struct OnPointerMoved(pub PointerEventHandler);
pub fn build_bubble_path(world: &World, start: Entity) -> Vec<Entity> {
use crate::ecs::window::Window;
let mut path = vec![start];
if world.get::<Window>(start).is_some() {
return path;
}
let mut current = start;
while let Some(child_of) = world.get::<ChildOf>(current) {
let parent = child_of.parent();
path.push(parent);
if world.get::<Window>(parent).is_some() {
break;
}
current = parent;
}
path
}
pub fn dispatch_event_for_handler<T: Clone, H: Component + Copy>(
world: &mut World,
sender: Entity,
path: &[Entity],
event: &T,
get_handler: fn(&H) -> EventHandler<T>,
) {
for &entity in path.iter().rev() {
if world.get_entity(entity).is_err() {
return; }
if let Some(handler_comp) = world.get::<H>(entity).copied() {
let handler = get_handler(&handler_comp);
if handler(world, sender, entity, &Phase::Tunnel(event.clone())) {
return; }
}
}
for &entity in path.iter() {
if world.get_entity(entity).is_err() {
return; }
if let Some(handler_comp) = world.get::<H>(entity).copied() {
let handler = get_handler(&handler_comp);
if handler(world, sender, entity, &Phase::Bubble(event.clone())) {
return; }
}
}
}
pub fn dispatch_pointer_events(world: &mut World) {
let targets: Vec<(Entity, PointerState)> = {
let mut query = world.query::<(Entity, &PointerState)>();
query.iter(world).map(|(e, s)| (e, s.clone())).collect()
};
for (sender, state) in &targets {
let path = build_bubble_path(world, *sender);
dispatch_event_for_handler::<PointerState, OnPointerMoved>(
world,
*sender,
&path,
state,
|h| h.0,
);
if state.left_down || state.right_down || state.middle_down {
dispatch_event_for_handler::<PointerState, OnPointerPressed>(
world,
*sender,
&path,
state,
|h| h.0,
);
}
if state.released.any() {
dispatch_event_for_handler::<PointerState, OnPointerReleased>(
world,
*sender,
&path,
state,
|h| h.0,
);
}
}
for (entity, _) in &targets {
if let Some(mut pointer_state) = world.get_mut::<PointerState>(*entity) {
pointer_state.left_down = false;
pointer_state.right_down = false;
pointer_state.middle_down = false;
pointer_state.xbutton1_down = false;
pointer_state.xbutton2_down = false;
pointer_state.double_click = super::DoubleClick::None;
pointer_state.released = super::ButtonReleased::default();
}
}
}
#[cfg(test)]
mod tests;