diff --git a/studio-frontend/src/dioxus-machine.ts b/studio-frontend/src/dioxus-machine.ts index f45f065..51d571e 100644 --- a/studio-frontend/src/dioxus-machine.ts +++ b/studio-frontend/src/dioxus-machine.ts @@ -16,8 +16,9 @@ function unpack_vnode( var attr = node_data["attributes"][i]; element.setAttribute(attr[0], attr[1]); } - element.setAttribute("onchange_id", node_data["onchange"]); - console.log("onchange", node_data["onchange"]); + if (node_data["id"] != undefined) { + element.setAttribute("id", node_data["id"]); + }; //apply children if (node_data["children"] != undefined) { for (var j = 0; j < node_data["children"].length; j++) { @@ -27,10 +28,10 @@ function unpack_vnode( } } //add onchange hook if input - if (node_data["tag"] == "input") { + if (node_data["onchange"] == true) { element.onchange = (event: Event) => { var input = event.target as HTMLInputElement; - const id = input.getAttribute("onchange_id"); + const id = input.getAttribute("id"); if (id != null) { console.log("onchange", id, input.value); const value = input.value; diff --git a/studio/src/ui/inspector.rs b/studio/src/ui/inspector.rs index 8a3fba8..5bdc8b0 100644 --- a/studio/src/ui/inspector.rs +++ b/studio/src/ui/inspector.rs @@ -8,7 +8,7 @@ use web_sys::HtmlElement; use crate::{ui::inspector::wasm::notify_dom_changed, wasm::{StaticBuffer, WasmMessageBuffer}}; -use super::utils::{show_reflect, ComponentProp, InspectedEntity, ReflectChange, VComponent, VNode, VirtualDom}; +use super::utils::{show_reflect, ComponentProp, Element, InspectedEntity, ReflectChange, VComponent, VNode, VirtualDom}; pub struct InspectorPlugin; @@ -79,41 +79,9 @@ fn show_selected( let e = cell.get_entity(selected_entity).unwrap(); if let Some(changes) = super::utils::wasm::ONCHAGE_BUFFER.take_message() { - for (change_id, value) in changes { - info!("change: {} : {}", change_id, value); - - if let Ok(change) = serde_json::from_str::(&change_id) { - let c_id = ComponentId::new( - change.compoent_id.split('(').nth(1).unwrap().split(')').next().unwrap().parse().unwrap() - ); - - if let Some(mut c) = e.get_mut_by_id(c_id) { - - let mut refl = app_type_registry.get_type_data::(cell.components().get_info_unchecked(c_id).type_id().unwrap()) - .unwrap() - .as_reflect_mut(c.as_mut()); - - if let Ok(refl_value) = refl.reflect_path_mut(change.reflect_path.as_str()) { - if let Some(v) = refl_value.downcast_mut::() { - *v = value; - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } else if let Some(v) = refl_value.downcast_mut::() { - *v = value.parse().unwrap(); - } - } - } - } + for (id, value) in changes { + info!("change: {} : {}", id, value); + virtial_dom.on_change_events.execute(cell.world_mut(), id, value); } } @@ -125,7 +93,7 @@ fn show_selected( let id = e.id(); let components = cell.components().clone(); - let vnode = VNode::Component(VComponent { + let element = Element::Component(super::utils::Component { tag: "div".to_string(), attributes: vec![ @@ -134,8 +102,7 @@ fn show_selected( show_reflect( c.as_ref(), ReflectChange { - entity_id: id.index(), - generation: id.generation(), + entity_id: id, reflect_path: "".to_string(), compoent_id: format!("{:?}", components.get_id(c.get_represented_type_info().unwrap().type_id()).unwrap()), } @@ -146,7 +113,7 @@ fn show_selected( ..Default::default() }); - let muts = virtial_dom.create_mutations(Some(vnode)); + let muts = virtial_dom.render(Some(element)); let q_window = cell.world_mut().query_filtered::<&Window, With>().single(cell.world()); diff --git a/studio/src/ui/utils.rs b/studio/src/ui/utils.rs index 980dba7..2ba1d27 100644 --- a/studio/src/ui/utils.rs +++ b/studio/src/ui/utils.rs @@ -1,7 +1,7 @@ use std::any::Any; -use bevy::{prelude::*, utils::HashMap}; +use bevy::{ecs::component::ComponentId, prelude::*, reflect::ReflectFromPtr, utils::HashMap}; use ::serde::{Serialize, Deserialize}; use uuid::*; @@ -15,12 +15,86 @@ pub enum Mutation { RemoveAll } +#[derive(Default)] +pub struct Component { + pub tag : String, + pub attributes : Vec<(String, String)>, + pub children : Vec, + pub onchange: Option> +} + +pub enum Element { + Text(String), + Component(Component) +} + +pub trait WebEventExecutor { + fn execute(&mut self, world: &mut World, data: String); +} + +#[derive(Default)] +pub struct WebEventStorage { + pub events : HashMap>, +} + +impl WebEventStorage { + pub fn add_event(&mut self, id : String, event : Box) { + self.events.insert(id, event); + } + + pub fn execute(&mut self, world: &mut World, id : String, data : String) { + if let Some(event) = self.events.get_mut(&id) { + event.execute(world, data); + } else { + println!("No event found for id: {}", id); + } + } +} + #[derive(Default, Resource)] pub struct VirtualDom { last_node : Option, + pub on_change_events: WebEventStorage, } impl VirtualDom { + + pub fn render(&mut self, element: Option) -> Vec { + let mut id_counter = 0; + + if let Some(element) = element { + let node = self.recursive_unpack_element(&mut id_counter, element); + return self.create_mutations(Some(node)); + } else { + return self.create_mutations(None); + } + } + + fn recursive_unpack_element(&mut self, counter : &mut usize, element : Element) -> VNode { + match element { + Element::Text(text) => VNode::Text(text), + Element::Component(component) => { + + let id = format!("node_{}", *counter); + *counter += 1; + + let mut onchange_enabled = false; + if let Some(onchange) = component.onchange { + self.on_change_events.add_event(id.clone(), onchange); + onchange_enabled = true; + } + + VNode::Component(VComponent { + tag: component.tag, + attributes: component.attributes, + id: Some(id), + onchange: onchange_enabled, + children: component.children.into_iter().map(|e| self.recursive_unpack_element(counter, e)).collect() + }) + }, + } + } + pub fn create_mutations(&mut self, node : Option) -> Vec { if self.last_node == node { return vec![] @@ -40,47 +114,100 @@ pub mod wasm { pub static ONCHAGE_BUFFER: WasmMessageBuffer> = WasmMessageBuffer::new(); #[wasm_bindgen(js_name = register_new_change)] - pub fn register_new_change(change : String, value : String) { + pub fn register_new_change(id : String, value : String) { let mut data = ONCHAGE_BUFFER.take_message().unwrap_or_default(); - data.push((change, value)); + data.push((id, value)); ONCHAGE_BUFFER.write_message(data); } } -fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option { +struct ReflectValueExecutor { + change : ReflectChange +} + +impl From for ReflectValueExecutor { + fn from(change : ReflectChange) -> Self { + ReflectValueExecutor { change } + } +} + +impl WebEventExecutor for ReflectValueExecutor { + fn execute(&mut self, world: &mut World, value: String) { + unsafe { + let world = world.as_unsafe_world_cell(); + let app_type_registry: &AppTypeRegistry = world.get_resource::().unwrap(); + let type_registry = app_type_registry.read(); + + + let mut e = world.get_entity(self.change.entity_id).unwrap(); + let c_id = ComponentId::new( + self.change.compoent_id.split('(').nth(1).unwrap().split(')').next().unwrap().parse().unwrap() + ); + + if let Some(mut c) = e.get_mut_by_id(c_id) { + + let mut refl = type_registry.get_type_data::(world.components().get_info_unchecked(c_id).type_id().unwrap()) + .unwrap() + .as_reflect_mut(c.as_mut()); + + if let Ok(refl_value) = refl.reflect_path_mut(self.change.reflect_path.as_str()) { + if let Some(v) = refl_value.downcast_mut::() { + *v = value; + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } else if let Some(v) = refl_value.downcast_mut::() { + *v = value.parse().unwrap(); + } + } + } + } + } +} + +fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option { if let Some(name) = r.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "text".to_string()), ("value".to_string(), name.to_string()), ], children: vec![], - onchange: Some(onchange.add_path("name").to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(onchange.add_path("name")))) })) } else if let Some(transform) = r.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "div".to_string(), attributes: vec![], children: vec![ - VNode::Text("Pos: ".to_string()), + Element::Text("Pos: ".to_string()), show_reflect(&transform.translation, onchange.add_path("translation")).unwrap(), - VNode::Text("Rot: ".to_string()), + Element::Text("Rot: ".to_string()), show_reflect(&transform.rotation, onchange.add_path("rotation")).unwrap(), - VNode::Text("Scale: ".to_string()), + Element::Text("Scale: ".to_string()), show_reflect(&transform.scale, onchange.add_path("scale")).unwrap(), ], onchange: None })) } else if let Some(quat) = r.downcast_ref::() { let euler = quat.to_euler(EulerRot::XYZ); - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "div".to_string(), attributes: vec![], children: vec![ - VNode::Text("X: ".to_string()), - VNode::Component(VComponent { + Element::Text("X: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), @@ -89,8 +216,8 @@ fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option children: vec![], onchange: None }), - VNode::Text("Y: ".to_string()), - VNode::Component(VComponent { + Element::Text("Y: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), @@ -99,8 +226,8 @@ fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option children: vec![], onchange: None }), - VNode::Text("Z: ".to_string()), - VNode::Component(VComponent { + Element::Text("Z: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), @@ -114,39 +241,39 @@ fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option })) } else if let Some(vec) = r.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "div".to_string(), attributes: vec![], children: vec![ - VNode::Text("X: ".to_string()), - VNode::Component(VComponent { + Element::Text("X: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), ("value".to_string(), vec.x.to_string()), ], children: vec![], - onchange: Some(onchange.add_path("x").to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(onchange.add_path("x")))) }), - VNode::Text("Y: ".to_string()), - VNode::Component(VComponent { + Element::Text("Y: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), ("value".to_string(), vec.y.to_string()), ], children: vec![], - onchange: Some(onchange.add_path("y").to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(onchange.add_path("y")))) }), - VNode::Text("Z: ".to_string()), - VNode::Component(VComponent { + Element::Text("Z: ".to_string()), + Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), ("value".to_string(), vec.z.to_string()), ], children: vec![], - onchange: Some(onchange.add_path("z").to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(onchange.add_path("z")))) }) ], onchange: None @@ -157,7 +284,7 @@ fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option } } -pub fn show_reflect(r : &dyn Reflect, reflect: ReflectChange) -> Option { +pub fn show_reflect(r : &dyn Reflect, reflect: ReflectChange) -> Option { if let Some(vnode) = check_predefined(r, reflect.clone()) { return Some(vnode); @@ -188,47 +315,47 @@ pub fn show_reflect(r : &dyn Reflect, reflect: ReflectChange) -> Option { }, bevy::reflect::ReflectRef::Value(v) => { if let Some(s) = v.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "text".to_string()), ("value".to_string(), s.to_string()), ], children: vec![], - onchange: Some(reflect.to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(reflect))) })) } else if let Some(b) = v.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "checkbox".to_string()), ("value".to_string(), b.to_string()), ], children: vec![], - onchange: Some(reflect.to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(reflect))) })) } else if let Some(i) = v.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), ("value".to_string(), i.to_string()), ], children: vec![], - onchange: Some(reflect.to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(reflect))) })) } else if let Some(f) = v.downcast_ref::() { - Some(VNode::Component(VComponent { + Some(Element::Component(Component { tag: "input".to_string(), attributes: vec![ ("type".to_string(), "number".to_string()), ("value".to_string(), f.to_string()), ], children: vec![], - onchange: Some(reflect.to_string()) + onchange: Some(Box::new(ReflectValueExecutor::from(reflect))) })) } else { - Some(VNode::Text(format!("{:?}", v))) + Some(Element::Text(format!("{:?}", v))) } }, } @@ -245,7 +372,8 @@ pub struct VComponent { pub tag: String, pub attributes: Vec<(String, String)>, pub children: Vec, - pub onchange: Option // id for onchange detection + pub id: Option, + pub onchange: bool, } #[derive(Clone, PartialEq, Serialize, Deserialize)] @@ -273,8 +401,7 @@ impl Clone for ComponentProp { #[derive(Serialize, Deserialize, Clone)] pub struct ReflectChange { - pub entity_id : u32, - pub generation : u32, + pub entity_id : Entity, pub reflect_path: String, pub compoent_id: String } @@ -283,7 +410,6 @@ impl ReflectChange { pub fn add_path(&self, path : &str) -> ReflectChange { ReflectChange { entity_id : self.entity_id, - generation : self.generation, compoent_id : self.compoent_id.clone(), reflect_path : format!("{}.{}", self.reflect_path, path) }