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_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"

View file

@ -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" }

View file

@ -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<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::{
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<ElementId, Entity>,
event_listeners: &mut HashSet<(Event, ElementId)>,
bevy_ui_entity_to_element_id: &mut EntityHashMap<Entity, ElementId>,
templates: &mut HashMap<String, BevyTemplate>,
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 {

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 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::<DeferredSystemRegistry>()
.init_resource::<EventReaders>()
.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<ElementId, Entity>,
event_listeners: HashSet<(Event, ElementId)>,
bevy_ui_entity_to_element_id: EntityHashMap<Entity, ElementId>,
templates: HashMap<String, BevyTemplate>,
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,
}

View file

@ -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::<EventReaders>()
.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,