Add support for click events

This commit is contained in:
JMS55 2023-12-12 00:12:06 -08:00
parent efb9ec93e5
commit 37774fb3e3
7 changed files with 115 additions and 38 deletions

18
Cargo.lock generated
View file

@ -802,8 +802,10 @@ dependencies = [
"bevy_math", "bevy_math",
"bevy_picking_core", "bevy_picking_core",
"bevy_picking_input", "bevy_picking_input",
"bevy_picking_ui",
"bevy_reflect", "bevy_reflect",
"bevy_render", "bevy_render",
"bevy_ui",
"bevy_utils", "bevy_utils",
"bevy_window", "bevy_window",
] ]
@ -868,6 +870,22 @@ dependencies = [
"bevy_window", "bevy_window",
] ]
[[package]]
name = "bevy_picking_ui"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a020364f01021b1faf9cc4de85b592505440d5b6274fb39c9d9a916b7639872b"
dependencies = [
"bevy_app",
"bevy_ecs",
"bevy_math",
"bevy_picking_core",
"bevy_render",
"bevy_transform",
"bevy_ui",
"bevy_window",
]
[[package]] [[package]]
name = "bevy_ptr" name = "bevy_ptr"
version = "0.12.0" version = "0.12.0"

View file

@ -6,10 +6,13 @@ edition = "2021"
[dependencies] [dependencies]
bevy = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
dioxus = "0.4" dioxus = "0.4"
bevy_mod_picking = { version = "0.17", default-features = false } bevy_mod_picking = { version = "0.17", default-features = false, features = [
"backend_bevy_ui",
] }
[patch.crates-io] [patch.crates-io]
bevy_app = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_app = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_asset = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_core = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_core = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_ecs = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_ecs = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_hierarchy = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_hierarchy = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
@ -17,6 +20,8 @@ bevy_input = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_math = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_math = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_reflect = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_reflect = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_render = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_render = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_transform = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_ui = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_utils = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_utils = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }
bevy_window = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } bevy_window = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" }

View file

@ -1,13 +1,14 @@
use bevy::{ use bevy::{
app::{App, Startup}, app::{App, Startup},
core_pipeline::core_2d::Camera2dBundle, core_pipeline::{clear_color::ClearColor, core_2d::Camera2dBundle},
ecs::system::Commands, ecs::system::{Commands, ResMut},
render::color::Color,
ui::node_bundles::NodeBundle, ui::node_bundles::NodeBundle,
DefaultPlugins, DefaultPlugins,
}; };
use bevy_dioxus::{ use bevy_dioxus::{
bevy_mod_picking::DefaultPickingPlugins, dioxus::prelude::*, DioxusUiBundle, DioxusUiPlugin, bevy_mod_picking::DefaultPickingPlugins, dioxus::prelude::*, hooks::use_system, DioxusUiBundle,
DioxusUiRoot, DioxusUiPlugin, DioxusUiRoot,
}; };
fn main() { fn main() {
@ -24,5 +25,18 @@ fn main() {
} }
fn ui_root(cx: Scope) -> Element { fn ui_root(cx: Scope) -> Element {
render!("Hello") let mut count = use_state(cx, || 0);
let change_clear_color = use_system(cx, |mut clear_color: ResMut<ClearColor>| {
clear_color.0 = Color::RED;
});
render!(
div {
onclick: move |_| {
count += 1;
change_clear_color.schedule();
},
"Count: {count}"
}
)
} }

View file

@ -1,3 +1,4 @@
use crate::events::is_supported_event;
use bevy::{ use bevy::{
ecs::{entity::Entity, system::Commands}, ecs::{entity::Entity, system::Commands},
hierarchy::BuildChildren, hierarchy::BuildChildren,
@ -7,7 +8,7 @@ use bevy::{
node_bundles::{NodeBundle, TextBundle}, node_bundles::{NodeBundle, TextBundle},
*, *,
}, },
utils::{HashMap, HashSet}, utils::{EntityHashMap, HashMap},
}; };
use dioxus::core::{ElementId, Mutation, Mutations, Template, TemplateAttribute, TemplateNode}; use dioxus::core::{ElementId, Mutation, Mutations, Template, TemplateAttribute, TemplateNode};
@ -15,7 +16,7 @@ pub fn apply_mutations(
mutations: Mutations, mutations: Mutations,
hierarchy: &mut HashMap<(Entity, u8), Entity>, hierarchy: &mut HashMap<(Entity, u8), Entity>,
element_id_to_bevy_ui_entity: &mut HashMap<ElementId, Entity>, element_id_to_bevy_ui_entity: &mut HashMap<ElementId, Entity>,
event_listeners: &mut HashSet<(Event, ElementId)>, bevy_ui_entity_to_element_id: &mut EntityHashMap<Entity, ElementId>,
templates: &mut HashMap<String, BevyTemplate>, templates: &mut HashMap<String, BevyTemplate>,
root_entity: Entity, root_entity: Entity,
commands: &mut Commands, commands: &mut Commands,
@ -28,6 +29,7 @@ pub fn apply_mutations(
} }
element_id_to_bevy_ui_entity.insert(ElementId(0), root_entity); element_id_to_bevy_ui_entity.insert(ElementId(0), root_entity);
bevy_ui_entity_to_element_id.insert(root_entity, ElementId(0));
let mut stack = vec![root_entity]; let mut stack = vec![root_entity];
for edit in mutations.edits { for edit in mutations.edits {
@ -50,6 +52,7 @@ pub fn apply_mutations(
let entity = BevyTemplateNode::from_dioxus(&TemplateNode::Text { text: value }) let entity = BevyTemplateNode::from_dioxus(&TemplateNode::Text { text: value })
.spawn(commands, hierarchy); .spawn(commands, hierarchy);
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);
stack.push(entity); stack.push(entity);
} }
Mutation::HydrateText { path, value, id } => { Mutation::HydrateText { path, value, id } => {
@ -61,10 +64,12 @@ pub fn apply_mutations(
.entity(entity) .entity(entity)
.insert(Text::from_section(value, TextStyle::default())); .insert(Text::from_section(value, TextStyle::default()));
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);
} }
Mutation::LoadTemplate { name, index, id } => { Mutation::LoadTemplate { name, index, id } => {
let entity = templates[name].roots[index].spawn(commands, hierarchy); let entity = templates[name].roots[index].spawn(commands, hierarchy);
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);
stack.push(entity); stack.push(entity);
} }
Mutation::ReplaceWith { id, m } => todo!(), Mutation::ReplaceWith { id, m } => todo!(),
@ -77,13 +82,17 @@ pub fn apply_mutations(
id, id,
ns, ns,
} => todo!(), } => todo!(),
Mutation::SetText { value, id } => todo!(), Mutation::SetText { value, id } => {
Mutation::NewEventListener { name, id } => { commands
event_listeners.insert((Event::from_dioxus(name), id)); .entity(element_id_to_bevy_ui_entity[&id])
.insert(Text::from_section(value, TextStyle::default()));
} }
Mutation::RemoveEventListener { name, id } => { Mutation::NewEventListener { name, id: _ } => {
event_listeners.remove(&(Event::from_dioxus(name), id)); if !is_supported_event(name) {
panic!("Encountered unsupported bevy_dioxus event `{name}`.");
} }
}
Mutation::RemoveEventListener { .. } => {}
Mutation::Remove { id } => todo!(), Mutation::Remove { id } => todo!(),
Mutation::PushRoot { id } => stack.push(element_id_to_bevy_ui_entity[&id]), Mutation::PushRoot { id } => stack.push(element_id_to_bevy_ui_entity[&id]),
} }
@ -177,20 +186,6 @@ impl BevyTemplateNode {
} }
} }
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
pub enum Event {
Click,
}
impl Event {
pub fn from_dioxus(event: &str) -> Self {
match event {
"click" => Self::Click,
_ => panic!("Encountered unsupported bevy_dioxus event `{event}`."),
}
}
}
fn parse_style_attributes(attributes: &[TemplateAttribute]) -> Style { fn parse_style_attributes(attributes: &[TemplateAttribute]) -> Style {
let mut style = Style::default(); let mut style = Style::default();
for attribute in attributes { for attribute in attributes {

32
src/events.rs Normal file
View file

@ -0,0 +1,32 @@
use bevy::ecs::{
entity::Entity,
event::{Events, ManualEventReader},
system::Resource,
};
use bevy_mod_picking::events::{Click, Pointer};
use dioxus::html::MouseData;
use std::{any::Any, rc::Rc};
#[derive(Resource, Default)]
pub struct EventReaders {
clicks: ManualEventReader<Pointer<Click>>,
}
impl EventReaders {
pub fn get_dioxus_events(
&mut self,
clicks: &Events<Pointer<Click>>,
) -> Vec<(Entity, &'static str, Rc<dyn Any>)> {
let mut events: Vec<(Entity, &'static str, Rc<dyn Any>)> = Vec::new();
for event in self.clicks.read(clicks) {
events.push((event.target, "click", Rc::new(MouseData::default())));
}
events
}
}
pub fn is_supported_event(event: &str) -> bool {
event == "click"
}

View file

@ -1,18 +1,20 @@
mod apply_mutations; mod apply_mutations;
mod deferred_system; mod deferred_system;
mod events;
pub mod hooks; pub mod hooks;
mod tick; mod tick;
use self::{ use self::{
apply_mutations::{BevyTemplate, Event}, apply_mutations::BevyTemplate,
deferred_system::DeferredSystemRegistry, deferred_system::DeferredSystemRegistry,
events::EventReaders,
tick::{tick_dioxus_ui, VirtualDomUnsafe}, tick::{tick_dioxus_ui, VirtualDomUnsafe},
}; };
use bevy::{ use bevy::{
app::{App, Plugin, Update}, app::{App, Plugin, Update},
ecs::{bundle::Bundle, component::Component, entity::Entity}, ecs::{bundle::Bundle, component::Component, entity::Entity},
ui::node_bundles::NodeBundle, ui::node_bundles::NodeBundle,
utils::{HashMap, HashSet}, utils::{EntityHashMap, HashMap},
}; };
use dioxus::core::{Element, ElementId, Scope}; use dioxus::core::{Element, ElementId, Scope};
@ -25,6 +27,7 @@ pub struct DioxusUiPlugin;
impl Plugin for DioxusUiPlugin { impl Plugin for DioxusUiPlugin {
fn build(&self, app: &mut App) { fn build(&self, app: &mut App) {
app.init_resource::<DeferredSystemRegistry>() app.init_resource::<DeferredSystemRegistry>()
.init_resource::<EventReaders>()
.add_systems(Update, tick_dioxus_ui); .add_systems(Update, tick_dioxus_ui);
} }
} }
@ -40,7 +43,7 @@ pub struct DioxusUiRoot {
virtual_dom: VirtualDomUnsafe, virtual_dom: VirtualDomUnsafe,
hierarchy: HashMap<(Entity, u8), Entity>, hierarchy: HashMap<(Entity, u8), Entity>,
element_id_to_bevy_ui_entity: HashMap<ElementId, Entity>, element_id_to_bevy_ui_entity: HashMap<ElementId, Entity>,
event_listeners: HashSet<(Event, ElementId)>, bevy_ui_entity_to_element_id: EntityHashMap<Entity, ElementId>,
templates: HashMap<String, BevyTemplate>, templates: HashMap<String, BevyTemplate>,
needs_rebuild: bool, needs_rebuild: bool,
} }
@ -51,7 +54,7 @@ impl DioxusUiRoot {
virtual_dom: VirtualDomUnsafe::new(root_component), virtual_dom: VirtualDomUnsafe::new(root_component),
hierarchy: HashMap::new(), hierarchy: HashMap::new(),
element_id_to_bevy_ui_entity: HashMap::new(), element_id_to_bevy_ui_entity: HashMap::new(),
event_listeners: HashSet::new(), bevy_ui_entity_to_element_id: EntityHashMap::default(),
templates: HashMap::new(), templates: HashMap::new(),
needs_rebuild: true, needs_rebuild: true,
} }

View file

@ -1,5 +1,6 @@
use crate::{ use crate::{
apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry, DioxusUiRoot, apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry,
events::EventReaders, DioxusUiRoot,
}; };
use bevy::{ use bevy::{
ecs::{ ecs::{
@ -11,11 +12,17 @@ use bevy::{
utils::synccell::SyncCell, utils::synccell::SyncCell,
}; };
use dioxus::core::{Element, Scope, ScopeState, VirtualDom}; use dioxus::core::{Element, Scope, ScopeState, VirtualDom};
use std::{mem, sync::Arc}; use std::{mem, rc::Rc, sync::Arc};
pub fn tick_dioxus_ui(world: &mut World) { pub fn tick_dioxus_ui(world: &mut World) {
let world_ptr: *mut World = world; let world_ptr: *mut World = world;
let world_cell = world.as_unsafe_world_cell(); let world_cell = world.as_unsafe_world_cell();
let events = unsafe {
world_cell
.get_resource_mut::<EventReaders>()
.unwrap()
.get_dioxus_events(world_cell.get_resource().unwrap())
};
let mut command_queue = CommandQueue::default(); let mut command_queue = CommandQueue::default();
let mut commands = Commands::new_from_entities(&mut command_queue, world_cell.entities()); let mut commands = Commands::new_from_entities(&mut command_queue, world_cell.entities());
@ -29,7 +36,7 @@ pub fn tick_dioxus_ui(world: &mut World) {
virtual_dom, virtual_dom,
hierarchy, hierarchy,
element_id_to_bevy_ui_entity, element_id_to_bevy_ui_entity,
event_listeners, bevy_ui_entity_to_element_id,
templates, templates,
needs_rebuild, needs_rebuild,
} = &mut *dioxus_ui_root; } = &mut *dioxus_ui_root;
@ -39,15 +46,18 @@ pub fn tick_dioxus_ui(world: &mut World) {
.base_scope() .base_scope()
.provide_context(EcsContext { world: world_ptr }); .provide_context(EcsContext { world: world_ptr });
// TODO: Handle events from winit for (target, name, data) in &events {
// virtual_dom.handle_event(todo!(), todo!(), todo!(), todo!()); if let Some(target) = bevy_ui_entity_to_element_id.get(target) {
virtual_dom.handle_event(name, Rc::clone(data), *target, true);
}
}
if *needs_rebuild { if *needs_rebuild {
apply_mutations( apply_mutations(
virtual_dom.rebuild(), virtual_dom.rebuild(),
hierarchy, hierarchy,
element_id_to_bevy_ui_entity, element_id_to_bevy_ui_entity,
event_listeners, bevy_ui_entity_to_element_id,
templates, templates,
root_entity, root_entity,
&mut commands, &mut commands,
@ -59,7 +69,7 @@ pub fn tick_dioxus_ui(world: &mut World) {
virtual_dom.render_immediate(), virtual_dom.render_immediate(),
hierarchy, hierarchy,
element_id_to_bevy_ui_entity, element_id_to_bevy_ui_entity,
event_listeners, bevy_ui_entity_to_element_id,
templates, templates,
root_entity, root_entity,
&mut commands, &mut commands,