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])
|
.entity_mut(element_id_to_bevy_ui_entity[&id])
|
||||||
.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) {
|
if !is_supported_event(name) {
|
||||||
panic!("Encountered unsupported bevy_dioxus 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 } => {
|
Mutation::Remove { id } => {
|
||||||
let entity = element_id_to_bevy_ui_entity[&id];
|
let entity = element_id_to_bevy_ui_entity[&id];
|
||||||
DespawnRecursive { entity }.apply(world);
|
DespawnRecursive { entity }.apply(world);
|
||||||
|
|
|
||||||
|
|
@ -1,37 +1,51 @@
|
||||||
use bevy::ecs::{
|
use bevy::{
|
||||||
|
ecs::{
|
||||||
entity::Entity,
|
entity::Entity,
|
||||||
event::{Events, ManualEventReader},
|
event::{Event, EventWriter, Events, ManualEventReader},
|
||||||
system::Resource,
|
system::{Local, Query, Resource},
|
||||||
|
},
|
||||||
|
ui::RelativeCursorPosition,
|
||||||
|
utils::EntityHashSet,
|
||||||
};
|
};
|
||||||
use bevy_mod_picking::events::{Click, Out, Over, Pointer};
|
use bevy_mod_picking::events::{Click, Out, Over, Pointer};
|
||||||
use dioxus::core::ScopeState;
|
use dioxus::core::ScopeState;
|
||||||
use std::{any::Any, rc::Rc};
|
use std::{any::Any, mem, rc::Rc};
|
||||||
|
|
||||||
// TODO: Other events
|
// TODO: Other events
|
||||||
|
|
||||||
#[derive(Resource, Default)]
|
#[derive(Resource, Default)]
|
||||||
pub struct EventReaders {
|
pub struct EventReaders {
|
||||||
click: ManualEventReader<Pointer<Click>>,
|
click: ManualEventReader<Pointer<Click>>,
|
||||||
mouse_enter: ManualEventReader<Pointer<Over>>,
|
mouse_over: ManualEventReader<Pointer<Over>>,
|
||||||
mouse_exit: ManualEventReader<Pointer<Out>>,
|
mouse_out: ManualEventReader<Pointer<Out>>,
|
||||||
|
mouse_enter: ManualEventReader<MouseEnter>,
|
||||||
|
mouse_exit: ManualEventReader<MouseExit>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl EventReaders {
|
impl EventReaders {
|
||||||
pub fn get_dioxus_events(
|
pub fn get_dioxus_events(
|
||||||
&mut self,
|
&mut self,
|
||||||
click: &Events<Pointer<Click>>,
|
click: &Events<Pointer<Click>>,
|
||||||
mouse_enter: &Events<Pointer<Over>>,
|
mouse_over: &Events<Pointer<Over>>,
|
||||||
mouse_exit: &Events<Pointer<Out>>,
|
mouse_out: &Events<Pointer<Out>>,
|
||||||
) -> Vec<(Entity, &'static str, Rc<dyn Any>)> {
|
mouse_enter: &Events<MouseEnter>,
|
||||||
let mut events: Vec<(Entity, &'static str, Rc<dyn Any>)> = Vec::new();
|
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) {
|
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) {
|
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) {
|
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
|
events
|
||||||
}
|
}
|
||||||
|
|
@ -40,6 +54,8 @@ impl EventReaders {
|
||||||
pub fn is_supported_event(event: &str) -> bool {
|
pub fn is_supported_event(event: &str) -> bool {
|
||||||
match event {
|
match event {
|
||||||
"click" => true,
|
"click" => true,
|
||||||
|
"mouse_over" => true,
|
||||||
|
"mouse_out" => true,
|
||||||
"mouse_enter" => true,
|
"mouse_enter" => true,
|
||||||
"mouse_exit" => true,
|
"mouse_exit" => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
|
|
@ -50,11 +66,57 @@ pub mod events {
|
||||||
super::impl_event! [
|
super::impl_event! [
|
||||||
();
|
();
|
||||||
onclick
|
onclick
|
||||||
|
onmouse_over
|
||||||
|
onmouse_out
|
||||||
onmouse_enter
|
onmouse_enter
|
||||||
onmouse_exit
|
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 {
|
pub trait EventReturn<P>: Sized {
|
||||||
fn spawn(self, _cx: &ScopeState) {}
|
fn spawn(self, _cx: &ScopeState) {}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
21
src/lib.rs
21
src/lib.rs
|
|
@ -11,14 +11,17 @@ mod parse_attributes;
|
||||||
mod tick;
|
mod tick;
|
||||||
|
|
||||||
use self::{
|
use self::{
|
||||||
apply_mutations::BevyTemplate, deferred_system::DeferredSystemRegistry, events::EventReaders,
|
apply_mutations::BevyTemplate,
|
||||||
hooks::EcsSubscriptions, tick::tick_dioxus_ui,
|
deferred_system::DeferredSystemRegistry,
|
||||||
|
events::{generate_mouse_enter_leave_events, EventReaders, MouseEnter, MouseExit},
|
||||||
|
hooks::EcsSubscriptions,
|
||||||
|
tick::tick_dioxus_ui,
|
||||||
};
|
};
|
||||||
use bevy::{
|
use bevy::{
|
||||||
app::{App, Plugin, Update},
|
app::{App, Last, Plugin, PreUpdate},
|
||||||
ecs::{bundle::Bundle, component::Component, entity::Entity},
|
ecs::{bundle::Bundle, component::Component, entity::Entity, schedule::IntoSystemConfigs},
|
||||||
prelude::Deref,
|
prelude::Deref,
|
||||||
ui::node_bundles::NodeBundle,
|
ui::{node_bundles::NodeBundle, ui_focus_system},
|
||||||
utils::{EntityHashMap, HashMap},
|
utils::{EntityHashMap, HashMap},
|
||||||
};
|
};
|
||||||
use dioxus::core::{Element, ElementId, Scope, VirtualDom};
|
use dioxus::core::{Element, ElementId, Scope, VirtualDom};
|
||||||
|
|
@ -43,7 +46,13 @@ impl Plugin for DioxusUiPlugin {
|
||||||
app.init_non_send_resource::<UiContext>()
|
app.init_non_send_resource::<UiContext>()
|
||||||
.init_resource::<DeferredSystemRegistry>()
|
.init_resource::<DeferredSystemRegistry>()
|
||||||
.init_resource::<EventReaders>()
|
.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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
22
src/tick.rs
22
src/tick.rs
|
|
@ -17,7 +17,13 @@ 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(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
|
let root_entities: HashMap<Entity, DioxusUiRoot> = world
|
||||||
|
|
@ -62,20 +68,26 @@ fn run_deferred_systems(world: &mut World) {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn dispatch_ui_events(
|
fn dispatch_ui_events(
|
||||||
events: &Vec<(Entity, &str, Rc<dyn Any>)>,
|
events: &Vec<(Entity, &str, Rc<dyn Any>, bool)>,
|
||||||
ui_root: &mut UiRoot,
|
ui_root: &mut UiRoot,
|
||||||
world: &World,
|
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();
|
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>() {
|
if *bubbles {
|
||||||
|
while target_element_id.is_none()
|
||||||
|
|| world.entity(target).contains::<IntrinsicTextNode>()
|
||||||
|
{
|
||||||
target = world.entity(target).get::<Parent>().unwrap().get();
|
target = world.entity(target).get::<Parent>().unwrap().get();
|
||||||
target_element_id = ui_root.bevy_ui_entity_to_element_id.get(&target).copied();
|
target_element_id = ui_root.bevy_ui_entity_to_element_id.get(&target).copied();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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.unwrap(), true);
|
.handle_event(name, Rc::clone(data), target_element_id, *bubbles);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue