Implement proper hover, make UI system Last
This commit is contained in:
parent
388509840e
commit
a6c6eb981d
|
@ -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);
|
||||
|
|
|
@ -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) {}
|
||||
}
|
||||
|
|
21
src/lib.rs
21
src/lib.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
30
src/tick.rs
30
src/tick.rs
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue