From 17f963e4cc356f0175549eafe87aa116d00325d1 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sat, 23 Dec 2023 12:09:09 -0800 Subject: [PATCH] Fix event dispatch --- src/apply_mutations.rs | 32 ++++-------- src/events.rs | 116 +++++++++++++++++++++++++++++++++-------- src/tick.rs | 27 ++++------ 3 files changed, 112 insertions(+), 63 deletions(-) diff --git a/src/apply_mutations.rs b/src/apply_mutations.rs index c3bd575..161395b 100644 --- a/src/apply_mutations.rs +++ b/src/apply_mutations.rs @@ -1,4 +1,7 @@ -use crate::{events::is_supported_event, parse_attributes::set_attribute, tick::IntrinsicTextNode}; +use crate::{ + events::{insert_event_listener, remove_event_listener}, + parse_attributes::set_attribute, +}; use bevy::{ ecs::{entity::Entity, system::Command, world::World}, hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent}, @@ -75,7 +78,6 @@ pub fn apply_mutations( TextLayoutInfo::default(), TextFlags::default(), ContentSize::default(), - IntrinsicTextNode, )); element_id_to_bevy_ui_entity.insert(id, entity); bevy_ui_entity_to_element_id.insert(entity, id); @@ -184,21 +186,10 @@ pub fn apply_mutations( .insert(Text::from_section(value, TextStyle::default())); } Mutation::NewEventListener { name, id } => { - if !is_supported_event(name) { - panic!("Encountered unsupported bevy_dioxus event `{name}`."); - } - if name == "mouse_enter" || name == "mouse_exit" { - world - .entity_mut(element_id_to_bevy_ui_entity[&id]) - .insert(RelativeCursorPosition::default()); - } + insert_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id])); } Mutation::RemoveEventListener { name, id } => { - if name == "mouse_enter" || name == "mouse_exit" { - world - .entity_mut(element_id_to_bevy_ui_entity[&id]) - .remove::<RelativeCursorPosition>(); - } + remove_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id])); } Mutation::Remove { id } => { let entity = element_id_to_bevy_ui_entity[&id]; @@ -336,13 +327,10 @@ impl BevyTemplateNode { .id() } Self::IntrinsicTextNode(text) => world - .spawn(( - TextBundle { - text: text.clone(), - ..default() - }, - IntrinsicTextNode, - )) + .spawn(TextBundle { + text: text.clone(), + ..default() + }) .id(), } } diff --git a/src/events.rs b/src/events.rs index 0555ccd..c05148c 100644 --- a/src/events.rs +++ b/src/events.rs @@ -1,9 +1,13 @@ use bevy::{ ecs::{ + component::Component, entity::Entity, event::{Event, EventWriter, Events, ManualEventReader}, system::{Local, Query, Resource}, + world::World, }, + hierarchy::Parent, + prelude::EntityWorldMut, ui::RelativeCursorPosition, utils::EntityHashSet, }; @@ -12,6 +16,18 @@ use dioxus::core::ScopeState; use std::{any::Any, mem, rc::Rc}; // TODO: Other events +pub mod events { + super::impl_event! [ + (); + onclick + onclick_down + onclick_up + onmouse_over + onmouse_out + onmouse_enter + onmouse_exit + ]; +} #[derive(Resource, Default)] pub struct EventReaders { @@ -25,7 +41,7 @@ pub struct EventReaders { } impl EventReaders { - pub fn get_dioxus_events( + pub fn read_events( &mut self, click: &Events<Pointer<Click>>, click_down: &Events<Pointer<Down>>, @@ -61,31 +77,85 @@ impl EventReaders { } } -pub fn is_supported_event(event: &str) -> bool { - match event { - "click" => true, - "click_down" => true, - "click_up" => true, - "mouse_over" => true, - "mouse_out" => true, - "mouse_enter" => true, - "mouse_exit" => true, - _ => false, +pub fn insert_event_listener(name: &str, mut entity: EntityWorldMut<'_>) { + match name { + "click" => entity.insert(HasClickEventListener), + "click_down" => entity.insert(HasClickDownEventListener), + "click_up" => entity.insert(HasClickUpEventListener), + "mouse_over" => &mut entity, + "mouse_out" => &mut entity, + "mouse_enter" => entity.insert(( + HasMouseEnterEventListener, + RelativeCursorPosition::default(), + )), + "mouse_exit" => { + entity.insert((HasMouseExitEventListener, RelativeCursorPosition::default())) + } + _ => panic!("Encountered unsupported bevy_dioxus event `{name}`."), + }; +} + +pub fn remove_event_listener(name: &str, mut entity: EntityWorldMut<'_>) { + match name { + "click" => entity.remove::<HasClickEventListener>(), + "click_down" => entity.remove::<HasClickDownEventListener>(), + "click_up" => entity.remove::<HasClickUpEventListener>(), + "mouse_over" => &mut entity, + "mouse_out" => &mut entity, + "mouse_enter" => { + entity.remove::<HasMouseEnterEventListener>(); + if !entity.contains::<HasMouseExitEventListener>() { + entity.remove::<RelativeCursorPosition>(); + } + &mut entity + } + "mouse_exit" => { + entity.remove::<HasMouseExitEventListener>(); + if !entity.contains::<HasMouseEnterEventListener>() { + entity.remove::<RelativeCursorPosition>(); + } + &mut entity + } + _ => unreachable!(), + }; +} + +#[derive(Component)] +pub struct HasClickEventListener; + +#[derive(Component)] +pub struct HasClickDownEventListener; + +#[derive(Component)] +pub struct HasClickUpEventListener; + +#[derive(Component)] +pub struct HasMouseEnterEventListener; + +#[derive(Component)] +pub struct HasMouseExitEventListener; + +// ---------------------------------------------------------------------------- + +pub fn bubble_event(event_name: &str, target_entity: &mut Entity, world: &World) { + match event_name { + "click" => bubble_event_helper::<HasClickEventListener>(target_entity, world), + "click_down" => bubble_event_helper::<HasClickDownEventListener>(target_entity, world), + "click_up" => bubble_event_helper::<HasClickUpEventListener>(target_entity, world), + _ => unreachable!(), + }; +} + +fn bubble_event_helper<T: Component>(target_entity: &mut Entity, world: &World) { + while !world.entity(*target_entity).contains::<T>() { + *target_entity = match world.entity(*target_entity).get::<Parent>() { + Some(parent) => parent.get(), + None => return, + }; } } -pub mod events { - super::impl_event! [ - (); - onclick - onclick_down - onclick_up - onmouse_over - onmouse_out - onmouse_enter - onmouse_exit - ]; -} +// ---------------------------------------------------------------------------- pub fn generate_mouse_enter_leave_events( entities: Query<(Entity, &RelativeCursorPosition)>, diff --git a/src/tick.rs b/src/tick.rs index 04daf49..d971608 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -1,14 +1,15 @@ use crate::{ - apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry, - ecs_hooks::EcsContext, events::EventReaders, DioxusUiRoot, UiContext, UiRoot, + apply_mutations::apply_mutations, + deferred_system::DeferredSystemRegistry, + ecs_hooks::EcsContext, + events::{bubble_event, EventReaders}, + DioxusUiRoot, UiContext, UiRoot, }; use bevy::{ ecs::{ - component::Component, entity::Entity, world::{Mut, World}, }, - hierarchy::Parent, utils::HashMap, }; use std::{any::Any, mem, rc::Rc, sync::Arc}; @@ -17,7 +18,7 @@ pub fn tick_dioxus_ui(world: &mut World) { run_deferred_systems(world); let ui_events = world.resource_scope(|world, mut event_readers: Mut<EventReaders>| { - event_readers.get_dioxus_events( + event_readers.read_events( world.resource(), world.resource(), world.resource(), @@ -75,20 +76,13 @@ fn dispatch_ui_events( world: &World, ) { for (mut target, name, data, bubbles) in events { - let mut target_element_id = ui_root.bevy_ui_entity_to_element_id.get(&target).copied(); if *bubbles { - while target_element_id.is_none() - || world.entity(target).contains::<IntrinsicTextNode>() - { - target = world.entity(target).get::<Parent>().unwrap().get(); - target_element_id = ui_root.bevy_ui_entity_to_element_id.get(&target).copied(); - } + bubble_event(name, &mut target, world); } - - if let Some(target_element_id) = target_element_id { + if let Some(target_element_id) = ui_root.bevy_ui_entity_to_element_id.get(&target) { ui_root .virtual_dom - .handle_event(name, Rc::clone(data), target_element_id, *bubbles); + .handle_event(name, Rc::clone(data), *target_element_id, *bubbles); } } } @@ -139,6 +133,3 @@ fn render_ui(root_entity: Entity, ui_root: &mut UiRoot, world: &mut World) { world, ); } - -#[derive(Component)] -pub struct IntrinsicTextNode;