From 37774fb3e3f445cc5d869eb476ae975cb8a65b29 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 12 Dec 2023 00:12:06 -0800 Subject: [PATCH] Add support for click events --- Cargo.lock | 18 ++++++++++++++++++ Cargo.toml | 7 ++++++- examples/basic.rs | 24 +++++++++++++++++++----- src/apply_mutations.rs | 37 ++++++++++++++++--------------------- src/events.rs | 32 ++++++++++++++++++++++++++++++++ src/lib.rs | 11 +++++++---- src/tick.rs | 24 +++++++++++++++++------- 7 files changed, 115 insertions(+), 38 deletions(-) create mode 100644 src/events.rs diff --git a/Cargo.lock b/Cargo.lock index e8dcef2..9f802e0 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -802,8 +802,10 @@ dependencies = [ "bevy_math", "bevy_picking_core", "bevy_picking_input", + "bevy_picking_ui", "bevy_reflect", "bevy_render", + "bevy_ui", "bevy_utils", "bevy_window", ] @@ -868,6 +870,22 @@ dependencies = [ "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]] name = "bevy_ptr" version = "0.12.0" diff --git a/Cargo.toml b/Cargo.toml index a06c087..1d2f8ea 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,10 +6,13 @@ edition = "2021" [dependencies] bevy = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } 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] 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_ecs = { 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_reflect = { 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_window = { git = "https://github.com/JMS55/bevy", branch = "query_new_12" } diff --git a/examples/basic.rs b/examples/basic.rs index a0e2734..35c8337 100644 --- a/examples/basic.rs +++ b/examples/basic.rs @@ -1,13 +1,14 @@ use bevy::{ app::{App, Startup}, - core_pipeline::core_2d::Camera2dBundle, - ecs::system::Commands, + core_pipeline::{clear_color::ClearColor, core_2d::Camera2dBundle}, + ecs::system::{Commands, ResMut}, + render::color::Color, ui::node_bundles::NodeBundle, DefaultPlugins, }; use bevy_dioxus::{ - bevy_mod_picking::DefaultPickingPlugins, dioxus::prelude::*, DioxusUiBundle, DioxusUiPlugin, - DioxusUiRoot, + bevy_mod_picking::DefaultPickingPlugins, dioxus::prelude::*, hooks::use_system, DioxusUiBundle, + DioxusUiPlugin, DioxusUiRoot, }; fn main() { @@ -24,5 +25,18 @@ fn main() { } 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| { + clear_color.0 = Color::RED; + }); + + render!( + div { + onclick: move |_| { + count += 1; + change_clear_color.schedule(); + }, + "Count: {count}" + } + ) } diff --git a/src/apply_mutations.rs b/src/apply_mutations.rs index 3961e3d..872edf4 100644 --- a/src/apply_mutations.rs +++ b/src/apply_mutations.rs @@ -1,3 +1,4 @@ +use crate::events::is_supported_event; use bevy::{ ecs::{entity::Entity, system::Commands}, hierarchy::BuildChildren, @@ -7,7 +8,7 @@ use bevy::{ node_bundles::{NodeBundle, TextBundle}, *, }, - utils::{HashMap, HashSet}, + utils::{EntityHashMap, HashMap}, }; use dioxus::core::{ElementId, Mutation, Mutations, Template, TemplateAttribute, TemplateNode}; @@ -15,7 +16,7 @@ pub fn apply_mutations( mutations: Mutations, hierarchy: &mut HashMap<(Entity, u8), Entity>, element_id_to_bevy_ui_entity: &mut HashMap, - event_listeners: &mut HashSet<(Event, ElementId)>, + bevy_ui_entity_to_element_id: &mut EntityHashMap, templates: &mut HashMap, root_entity: Entity, commands: &mut Commands, @@ -28,6 +29,7 @@ pub fn apply_mutations( } 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]; for edit in mutations.edits { @@ -50,6 +52,7 @@ pub fn apply_mutations( let entity = BevyTemplateNode::from_dioxus(&TemplateNode::Text { text: value }) .spawn(commands, hierarchy); element_id_to_bevy_ui_entity.insert(id, entity); + bevy_ui_entity_to_element_id.insert(entity, id); stack.push(entity); } Mutation::HydrateText { path, value, id } => { @@ -61,10 +64,12 @@ pub fn apply_mutations( .entity(entity) .insert(Text::from_section(value, TextStyle::default())); element_id_to_bevy_ui_entity.insert(id, entity); + bevy_ui_entity_to_element_id.insert(entity, id); } Mutation::LoadTemplate { name, index, id } => { let entity = templates[name].roots[index].spawn(commands, hierarchy); element_id_to_bevy_ui_entity.insert(id, entity); + bevy_ui_entity_to_element_id.insert(entity, id); stack.push(entity); } Mutation::ReplaceWith { id, m } => todo!(), @@ -77,13 +82,17 @@ pub fn apply_mutations( id, ns, } => todo!(), - Mutation::SetText { value, id } => todo!(), - Mutation::NewEventListener { name, id } => { - event_listeners.insert((Event::from_dioxus(name), id)); + Mutation::SetText { value, id } => { + commands + .entity(element_id_to_bevy_ui_entity[&id]) + .insert(Text::from_section(value, TextStyle::default())); } - Mutation::RemoveEventListener { name, id } => { - event_listeners.remove(&(Event::from_dioxus(name), id)); + Mutation::NewEventListener { name, id: _ } => { + if !is_supported_event(name) { + panic!("Encountered unsupported bevy_dioxus event `{name}`."); + } } + Mutation::RemoveEventListener { .. } => {} Mutation::Remove { id } => todo!(), 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 { let mut style = Style::default(); for attribute in attributes { diff --git a/src/events.rs b/src/events.rs new file mode 100644 index 0000000..c8b7b2e --- /dev/null +++ b/src/events.rs @@ -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>, +} + +impl EventReaders { + pub fn get_dioxus_events( + &mut self, + clicks: &Events>, + ) -> Vec<(Entity, &'static str, Rc)> { + let mut events: Vec<(Entity, &'static str, Rc)> = 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" +} diff --git a/src/lib.rs b/src/lib.rs index 9eab704..caa075c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,18 +1,20 @@ mod apply_mutations; mod deferred_system; +mod events; pub mod hooks; mod tick; use self::{ - apply_mutations::{BevyTemplate, Event}, + apply_mutations::BevyTemplate, deferred_system::DeferredSystemRegistry, + events::EventReaders, tick::{tick_dioxus_ui, VirtualDomUnsafe}, }; use bevy::{ app::{App, Plugin, Update}, ecs::{bundle::Bundle, component::Component, entity::Entity}, ui::node_bundles::NodeBundle, - utils::{HashMap, HashSet}, + utils::{EntityHashMap, HashMap}, }; use dioxus::core::{Element, ElementId, Scope}; @@ -25,6 +27,7 @@ pub struct DioxusUiPlugin; impl Plugin for DioxusUiPlugin { fn build(&self, app: &mut App) { app.init_resource::() + .init_resource::() .add_systems(Update, tick_dioxus_ui); } } @@ -40,7 +43,7 @@ pub struct DioxusUiRoot { virtual_dom: VirtualDomUnsafe, hierarchy: HashMap<(Entity, u8), Entity>, element_id_to_bevy_ui_entity: HashMap, - event_listeners: HashSet<(Event, ElementId)>, + bevy_ui_entity_to_element_id: EntityHashMap, templates: HashMap, needs_rebuild: bool, } @@ -51,7 +54,7 @@ impl DioxusUiRoot { virtual_dom: VirtualDomUnsafe::new(root_component), hierarchy: 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(), needs_rebuild: true, } diff --git a/src/tick.rs b/src/tick.rs index 44a965a..ed8a581 100644 --- a/src/tick.rs +++ b/src/tick.rs @@ -1,5 +1,6 @@ use crate::{ - apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry, DioxusUiRoot, + apply_mutations::apply_mutations, deferred_system::DeferredSystemRegistry, + events::EventReaders, DioxusUiRoot, }; use bevy::{ ecs::{ @@ -11,11 +12,17 @@ use bevy::{ utils::synccell::SyncCell, }; 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) { let world_ptr: *mut World = world; let world_cell = world.as_unsafe_world_cell(); + let events = unsafe { + world_cell + .get_resource_mut::() + .unwrap() + .get_dioxus_events(world_cell.get_resource().unwrap()) + }; let mut command_queue = CommandQueue::default(); 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, hierarchy, element_id_to_bevy_ui_entity, - event_listeners, + bevy_ui_entity_to_element_id, templates, needs_rebuild, } = &mut *dioxus_ui_root; @@ -39,15 +46,18 @@ pub fn tick_dioxus_ui(world: &mut World) { .base_scope() .provide_context(EcsContext { world: world_ptr }); - // TODO: Handle events from winit - // virtual_dom.handle_event(todo!(), todo!(), todo!(), todo!()); + for (target, name, data) in &events { + 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 { apply_mutations( virtual_dom.rebuild(), hierarchy, element_id_to_bevy_ui_entity, - event_listeners, + bevy_ui_entity_to_element_id, templates, root_entity, &mut commands, @@ -59,7 +69,7 @@ pub fn tick_dioxus_ui(world: &mut World) { virtual_dom.render_immediate(), hierarchy, element_id_to_bevy_ui_entity, - event_listeners, + bevy_ui_entity_to_element_id, templates, root_entity, &mut commands,