Implement proper hover, make UI system Last

This commit is contained in:
JMS55 2023-12-22 10:50:42 -08:00
parent 388509840e
commit a6c6eb981d
4 changed files with 125 additions and 31 deletions

View file

@ -183,12 +183,23 @@ pub fn apply_mutations(
.entity_mut(element_id_to_bevy_ui_entity[&id])
.insert(Text::from_section(value, TextStyle::default()));
}
Mutation::NewEventListener { name, id: _ } => {
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());
}
}
Mutation::RemoveEventListener { name, id } => {
if name == "mouse_enter" || name == "mouse_exit" {
world
.entity_mut(element_id_to_bevy_ui_entity[&id])
.remove::<RelativeCursorPosition>();
}
}
Mutation::RemoveEventListener { .. } => {}
Mutation::Remove { id } => {
let entity = element_id_to_bevy_ui_entity[&id];
DespawnRecursive { entity }.apply(world);

View file

@ -1,37 +1,51 @@
use bevy::ecs::{
entity::Entity,
event::{Events, ManualEventReader},
system::Resource,
use bevy::{
ecs::{
entity::Entity,
event::{Event, EventWriter, Events, ManualEventReader},
system::{Local, Query, Resource},
},
ui::RelativeCursorPosition,
utils::EntityHashSet,
};
use bevy_mod_picking::events::{Click, Out, Over, Pointer};
use dioxus::core::ScopeState;
use std::{any::Any, rc::Rc};
use std::{any::Any, mem, rc::Rc};
// TODO: Other events
#[derive(Resource, Default)]
pub struct EventReaders {
click: ManualEventReader<Pointer<Click>>,
mouse_enter: ManualEventReader<Pointer<Over>>,
mouse_exit: ManualEventReader<Pointer<Out>>,
mouse_over: ManualEventReader<Pointer<Over>>,
mouse_out: ManualEventReader<Pointer<Out>>,
mouse_enter: ManualEventReader<MouseEnter>,
mouse_exit: ManualEventReader<MouseExit>,
}
impl EventReaders {
pub fn get_dioxus_events(
&mut self,
click: &Events<Pointer<Click>>,
mouse_enter: &Events<Pointer<Over>>,
mouse_exit: &Events<Pointer<Out>>,
) -> Vec<(Entity, &'static str, Rc<dyn Any>)> {
let mut events: Vec<(Entity, &'static str, Rc<dyn Any>)> = Vec::new();
mouse_over: &Events<Pointer<Over>>,
mouse_out: &Events<Pointer<Out>>,
mouse_enter: &Events<MouseEnter>,
mouse_exit: &Events<MouseExit>,
) -> Vec<(Entity, &'static str, Rc<dyn Any>, bool)> {
let mut events: Vec<(Entity, &'static str, Rc<dyn Any>, bool)> = Vec::new();
for event in self.click.read(click) {
events.push((event.target, "click", Rc::new(())));
events.push((event.target, "click", Rc::new(()), true));
}
for event in self.mouse_over.read(mouse_over) {
events.push((event.target, "mouse_over", Rc::new(()), false));
}
for event in self.mouse_out.read(mouse_out) {
events.push((event.target, "mouse_out", Rc::new(()), false));
}
for event in self.mouse_enter.read(mouse_enter) {
events.push((event.target, "mouse_enter", Rc::new(())));
events.push((event.target, "mouse_enter", Rc::new(()), false));
}
for event in self.mouse_exit.read(mouse_exit) {
events.push((event.target, "mouse_exit", Rc::new(())));
events.push((event.target, "mouse_exit", Rc::new(()), false));
}
events
}
@ -40,6 +54,8 @@ impl EventReaders {
pub fn is_supported_event(event: &str) -> bool {
match event {
"click" => true,
"mouse_over" => true,
"mouse_out" => true,
"mouse_enter" => true,
"mouse_exit" => true,
_ => false,
@ -50,11 +66,57 @@ pub mod events {
super::impl_event! [
();
onclick
onmouse_over
onmouse_out
onmouse_enter
onmouse_exit
];
}
pub fn generate_mouse_enter_leave_events(
entities: Query<(Entity, &RelativeCursorPosition)>,
mut previous_over: Local<EntityHashSet<Entity>>,
mut over: Local<EntityHashSet<Entity>>,
mut enter: EventWriter<MouseEnter>,
mut leave: EventWriter<MouseExit>,
) {
mem::swap::<EntityHashSet<Entity>>(&mut previous_over, &mut over);
over.clear();
for (entity, relative_cursor_position) in &entities {
if relative_cursor_position.mouse_over() {
over.insert(entity);
}
}
enter.send_batch(
over.iter()
.copied()
.filter(|entity| !previous_over.contains(entity))
.map(|target| MouseEnter { target }),
);
leave.send_batch(
previous_over
.iter()
.copied()
.filter(|entity| !over.contains(entity))
.map(|target| MouseExit { target }),
);
}
#[derive(Event)]
pub struct MouseEnter {
target: Entity,
}
#[derive(Event)]
pub struct MouseExit {
target: Entity,
}
// ----------------------------------------------------------------------------
pub trait EventReturn<P>: Sized {
fn spawn(self, _cx: &ScopeState) {}
}

View file

@ -11,14 +11,17 @@ mod parse_attributes;
mod tick;
use self::{
apply_mutations::BevyTemplate, deferred_system::DeferredSystemRegistry, events::EventReaders,
hooks::EcsSubscriptions, tick::tick_dioxus_ui,
apply_mutations::BevyTemplate,
deferred_system::DeferredSystemRegistry,
events::{generate_mouse_enter_leave_events, EventReaders, MouseEnter, MouseExit},
hooks::EcsSubscriptions,
tick::tick_dioxus_ui,
};
use bevy::{
app::{App, Plugin, Update},
ecs::{bundle::Bundle, component::Component, entity::Entity},
app::{App, Last, Plugin, PreUpdate},
ecs::{bundle::Bundle, component::Component, entity::Entity, schedule::IntoSystemConfigs},
prelude::Deref,
ui::node_bundles::NodeBundle,
ui::{node_bundles::NodeBundle, ui_focus_system},
utils::{EntityHashMap, HashMap},
};
use dioxus::core::{Element, ElementId, Scope, VirtualDom};
@ -43,7 +46,13 @@ impl Plugin for DioxusUiPlugin {
app.init_non_send_resource::<UiContext>()
.init_resource::<DeferredSystemRegistry>()
.init_resource::<EventReaders>()
.add_systems(Update, tick_dioxus_ui);
.add_event::<MouseEnter>()
.add_event::<MouseExit>()
.add_systems(
PreUpdate,
generate_mouse_enter_leave_events.after(ui_focus_system),
)
.add_systems(Last, tick_dioxus_ui);
}
}

View file

@ -17,7 +17,13 @@ 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(world.resource(), world.resource(), world.resource())
event_readers.get_dioxus_events(
world.resource(),
world.resource(),
world.resource(),
world.resource(),
world.resource(),
)
});
let root_entities: HashMap<Entity, DioxusUiRoot> = world
@ -62,20 +68,26 @@ fn run_deferred_systems(world: &mut World) {
}
fn dispatch_ui_events(
events: &Vec<(Entity, &str, Rc<dyn Any>)>,
events: &Vec<(Entity, &str, Rc<dyn Any>, bool)>,
ui_root: &mut UiRoot,
world: &World,
) {
for (mut target, name, data) 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();
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();
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();
}
}
ui_root
.virtual_dom
.handle_event(name, Rc::clone(data), target_element_id.unwrap(), true);
if let Some(target_element_id) = target_element_id {
ui_root
.virtual_dom
.handle_event(name, Rc::clone(data), target_element_id, *bubbles);
}
}
}