Fix event dispatch

This commit is contained in:
JMS55 2023-12-23 12:09:09 -08:00
parent 9bb8a27bb7
commit 17f963e4cc
3 changed files with 112 additions and 63 deletions

View file

@ -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::{ use bevy::{
ecs::{entity::Entity, system::Command, world::World}, ecs::{entity::Entity, system::Command, world::World},
hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent}, hierarchy::{BuildWorldChildren, Children, DespawnRecursive, Parent},
@ -75,7 +78,6 @@ pub fn apply_mutations(
TextLayoutInfo::default(), TextLayoutInfo::default(),
TextFlags::default(), TextFlags::default(),
ContentSize::default(), ContentSize::default(),
IntrinsicTextNode,
)); ));
element_id_to_bevy_ui_entity.insert(id, entity); element_id_to_bevy_ui_entity.insert(id, entity);
bevy_ui_entity_to_element_id.insert(entity, id); bevy_ui_entity_to_element_id.insert(entity, id);
@ -184,21 +186,10 @@ pub fn apply_mutations(
.insert(Text::from_section(value, TextStyle::default())); .insert(Text::from_section(value, TextStyle::default()));
} }
Mutation::NewEventListener { name, id } => { Mutation::NewEventListener { name, id } => {
if !is_supported_event(name) { insert_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id]));
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());
}
} }
Mutation::RemoveEventListener { name, id } => { Mutation::RemoveEventListener { name, id } => {
if name == "mouse_enter" || name == "mouse_exit" { remove_event_listener(name, world.entity_mut(element_id_to_bevy_ui_entity[&id]));
world
.entity_mut(element_id_to_bevy_ui_entity[&id])
.remove::<RelativeCursorPosition>();
}
} }
Mutation::Remove { id } => { Mutation::Remove { id } => {
let entity = element_id_to_bevy_ui_entity[&id]; let entity = element_id_to_bevy_ui_entity[&id];
@ -336,13 +327,10 @@ impl BevyTemplateNode {
.id() .id()
} }
Self::IntrinsicTextNode(text) => world Self::IntrinsicTextNode(text) => world
.spawn(( .spawn(TextBundle {
TextBundle {
text: text.clone(), text: text.clone(),
..default() ..default()
}, })
IntrinsicTextNode,
))
.id(), .id(),
} }
} }

View file

@ -1,9 +1,13 @@
use bevy::{ use bevy::{
ecs::{ ecs::{
component::Component,
entity::Entity, entity::Entity,
event::{Event, EventWriter, Events, ManualEventReader}, event::{Event, EventWriter, Events, ManualEventReader},
system::{Local, Query, Resource}, system::{Local, Query, Resource},
world::World,
}, },
hierarchy::Parent,
prelude::EntityWorldMut,
ui::RelativeCursorPosition, ui::RelativeCursorPosition,
utils::EntityHashSet, utils::EntityHashSet,
}; };
@ -12,6 +16,18 @@ use dioxus::core::ScopeState;
use std::{any::Any, mem, rc::Rc}; use std::{any::Any, mem, rc::Rc};
// TODO: Other events // 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)] #[derive(Resource, Default)]
pub struct EventReaders { pub struct EventReaders {
@ -25,7 +41,7 @@ pub struct EventReaders {
} }
impl EventReaders { impl EventReaders {
pub fn get_dioxus_events( pub fn read_events(
&mut self, &mut self,
click: &Events<Pointer<Click>>, click: &Events<Pointer<Click>>,
click_down: &Events<Pointer<Down>>, click_down: &Events<Pointer<Down>>,
@ -61,31 +77,85 @@ impl EventReaders {
} }
} }
pub fn is_supported_event(event: &str) -> bool { pub fn insert_event_listener(name: &str, mut entity: EntityWorldMut<'_>) {
match event { match name {
"click" => true, "click" => entity.insert(HasClickEventListener),
"click_down" => true, "click_down" => entity.insert(HasClickDownEventListener),
"click_up" => true, "click_up" => entity.insert(HasClickUpEventListener),
"mouse_over" => true, "mouse_over" => &mut entity,
"mouse_out" => true, "mouse_out" => &mut entity,
"mouse_enter" => true, "mouse_enter" => entity.insert((
"mouse_exit" => true, HasMouseEnterEventListener,
_ => false, 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( pub fn generate_mouse_enter_leave_events(
entities: Query<(Entity, &RelativeCursorPosition)>, entities: Query<(Entity, &RelativeCursorPosition)>,

View file

@ -1,14 +1,15 @@
use crate::{ use crate::{
apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry, apply_mutations::apply_mutations,
ecs_hooks::EcsContext, events::EventReaders, DioxusUiRoot, UiContext, UiRoot, deferred_system::DeferredSystemRegistry,
ecs_hooks::EcsContext,
events::{bubble_event, EventReaders},
DioxusUiRoot, UiContext, UiRoot,
}; };
use bevy::{ use bevy::{
ecs::{ ecs::{
component::Component,
entity::Entity, entity::Entity,
world::{Mut, World}, world::{Mut, World},
}, },
hierarchy::Parent,
utils::HashMap, utils::HashMap,
}; };
use std::{any::Any, mem, rc::Rc, sync::Arc}; 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); run_deferred_systems(world);
let ui_events = world.resource_scope(|world, mut event_readers: Mut<EventReaders>| { 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(), world.resource(),
world.resource(), world.resource(),
@ -75,20 +76,13 @@ fn dispatch_ui_events(
world: &World, world: &World,
) { ) {
for (mut target, name, data, bubbles) in events { 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 { if *bubbles {
while target_element_id.is_none() bubble_event(name, &mut target, world);
|| 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();
} }
} if let Some(target_element_id) = ui_root.bevy_ui_entity_to_element_id.get(&target) {
if let Some(target_element_id) = target_element_id {
ui_root ui_root
.virtual_dom .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, world,
); );
} }
#[derive(Component)]
pub struct IntrinsicTextNode;