diff --git a/studio-frontend/src/dioxus-machine.ts b/studio-frontend/src/dioxus-machine.ts index 911869f..291f190 100644 --- a/studio-frontend/src/dioxus-machine.ts +++ b/studio-frontend/src/dioxus-machine.ts @@ -1,4 +1,4 @@ -import { type InspectorDomChanged } from "dist/studio/studio"; +import { register_new_change, type InspectorDomChanged } from "@storyteller/studio"; console.log("Hello, World!"); @@ -16,6 +16,7 @@ function unpack_vnode( var attr = node_data["attributes"][i]; element.setAttribute(attr[0], attr[1]); } + element.setAttribute("onchange_id", node_data["onchange"]); //apply children if (node_data["children"] != undefined) { for (var j = 0; j < node_data["children"].length; j++) { @@ -24,6 +25,17 @@ function unpack_vnode( element.appendChild(child_element); } } + //add onchange hook if input + if (node_data["tag"] == "input") { + element.onchange = (event: Event) => { + const input = event.target as HTMLInputElement; + const id = input.getAttribute("onchange_id"); + if (id) { + const value = input.value; + register_new_change(id, value); + } + } + } return element; } } @@ -37,21 +49,31 @@ function apply_muts( var lit_root = document.getElementById("inspector"); var shadowroot = lit_root?.shadowRoot; var root = shadowroot?.getElementById("inspector"); - console.log("root", root); if (root) { //remove all children - while (root.firstChild) { - root.removeChild(root.firstChild); - } + } else { console.log("root not found"); return; } - var vdom = JSON.parse(event.detail); + var muts = JSON.parse(event.detail); + + if (muts.length > 0) { + console.log("muts", muts); + } - var element = unpack_vnode(vdom); - root.appendChild(element); + for (var i = 0; i < muts.length; i++) { + var mut = muts[i]; + if (mut["ReplaceAll"] != undefined) { + var vnode = mut["ReplaceAll"]; + var element = unpack_vnode(vnode); + while (root.firstChild) { + root.removeChild(root.firstChild); + } + root.appendChild(element); + } + } } else { console.log('Selection has been cleared.'); @@ -61,7 +83,6 @@ function apply_muts( document.addEventListener('inspector-dom-changed', (event: CustomEvent) => { // Cast the event to the InspectorDomChanged type to access its details in TypeScript const customEvent = event as InspectorDomChanged; - console.log(customEvent); // Now you can access the detail property, which is either a string or null apply_muts(customEvent); }); diff --git a/studio/src/ui/inspector.rs b/studio/src/ui/inspector.rs index b9b288b..1da4b6e 100644 --- a/studio/src/ui/inspector.rs +++ b/studio/src/ui/inspector.rs @@ -1,6 +1,6 @@ -use std::{sync::{Arc, Mutex, RwLock}, thread::yield_now}; +use std::{any::Any, sync::{Arc, Mutex, RwLock}, thread::yield_now}; -use bevy::{ecs::world::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}, prelude::*, reflect::TypeData, utils::{hashbrown::HashMap, tracing::field}, window::PrimaryWindow}; +use bevy::{ecs::{component::ComponentId, world::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}}, prelude::*, reflect::{ReflectFromPtr, TypeData}, utils::{hashbrown::HashMap, tracing::field}, window::PrimaryWindow}; use serde::{Deserialize, Serialize}; use space_editor::{prelude::Selected, space_editor_ui::inspector::inspect}; use wasm_bindgen::JsCast; @@ -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, VComponent, VNode}; +use super::utils::{show_reflect, ComponentProp, InspectedEntity, ReflectChange, VComponent, VNode, VirtualDom}; pub struct InspectorPlugin; @@ -58,6 +58,9 @@ impl InspectorFunctions { fn show_selected( world: &mut World ) { + if !world.is_resource_added::() { + world.init_resource::(); + } unsafe { let cell = world.as_unsafe_world_cell(); @@ -69,25 +72,87 @@ fn show_selected( let mut functions = cell.get_resource_mut::().unwrap(); + let mut virtial_dom = cell.get_resource_mut::().unwrap(); + let app_type_registry = cell.get_resource::().unwrap(); + let app_type_registry = app_type_registry.read(); + let e = cell.get_entity(selected_entity).unwrap(); - let mut cell = InspectedEntity::none(); - cell.id = Some(e.id()); - cell.components = functions.get_reflect_copy(&e); + if let Some(changes) = super::utils::wasm::ONCHAGE_BUFFER.take_message() { + for (change_id, value) in changes { + info!("change: {} : {}", change_id, value); + let change = serde_json::from_str::(&change_id).unwrap(); + 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(); + } + } + + + } + } + } + + + let mut inspected = InspectedEntity::none(); + inspected.id = Some(e.id()); + inspected.components = functions.get_reflect_copy(&e); + + let id = e.id(); + + let components = cell.components().clone(); let vnode = VNode::Component(VComponent { tag: "div".to_string(), attributes: vec![ - + ], - children: cell.components.into_iter().map(|c| show_reflect(c.as_ref())).filter_map(|c| c).collect(), + children: inspected.components.into_iter().map(|c| { + show_reflect( + c.as_ref(), + ReflectChange { + entity_id: id.index(), + generation: id.generation(), + reflect_path: "".to_string(), + compoent_id: format!("{:?}", components.get_id(c.get_represented_type_info().unwrap().type_id()).unwrap()), + } + ) + }) + .filter_map(|v| v) + .collect(), ..Default::default() }); + + let muts = virtial_dom.create_mutations(Some(vnode)); - let q_window = world.query_filtered::<&Window, With>().single(world); + let q_window = cell.world_mut().query_filtered::<&Window, With>().single(cell.world()); - let _ = notify_dom_changed(q_window, serde_json::to_string(&vnode).unwrap()); + let _ = notify_dom_changed(q_window, serde_json::to_string(&muts).unwrap()); } } @@ -129,12 +194,5 @@ declare global { wasm::dispatch_to_js(q_window, "inspector-dom-changed", &msg) } - pub static MUTATION_BUFFER: WasmMessageBuffer = WasmMessageBuffer::new(); - - #[wasm_bindgen(js_name = getMutation)] - pub fn get_mutation() -> String { - - - MUTATION_BUFFER.take_message().unwrap_or("{}".to_string()) - } + } diff --git a/studio/src/ui/utils.rs b/studio/src/ui/utils.rs index d73606d..2ea0ff3 100644 --- a/studio/src/ui/utils.rs +++ b/studio/src/ui/utils.rs @@ -1,4 +1,6 @@ +use std::any::Any; + use bevy::{prelude::*, utils::HashMap}; use ::serde::{Serialize, Deserialize}; use uuid::*; @@ -7,7 +9,45 @@ trait NodeProp : Clone + PartialEq { } -fn check_predefined(r : &dyn Reflect) -> Option { +#[derive(Serialize, Deserialize)] +pub enum Mutation { + ReplaceAll(VNode), + RemoveAll +} + +#[derive(Default, Resource)] +pub struct VirtualDom { + last_node : Option, +} + +impl VirtualDom { + pub fn create_mutations(&mut self, node : Option) -> Vec { + if self.last_node == node { + return vec![] + } else { + self.last_node = node.clone(); + return vec![Mutation::ReplaceAll(node.unwrap())] + } + } +} + +#[cfg(feature = "wasm")] +pub mod wasm { + use wasm_bindgen::prelude::*; + + use crate::wasm::{StaticBuffer, WasmMessageBuffer}; + + pub static ONCHAGE_BUFFER: WasmMessageBuffer> = WasmMessageBuffer::new(); + + #[wasm_bindgen(js_name = register_new_change)] + pub fn register_new_change(change : String, value : String) { + let mut data = ONCHAGE_BUFFER.take_message().unwrap_or_default(); + data.push((change, value)); + ONCHAGE_BUFFER.write_message(data); + } +} + +fn check_predefined(r : &dyn Reflect, onchange : ReflectChange) -> Option { if let Some(name) = r.downcast_ref::() { Some(VNode::Component(VComponent { tag: "input".to_string(), @@ -16,19 +56,20 @@ fn check_predefined(r : &dyn Reflect) -> Option { ("value".to_string(), name.to_string()), ], children: vec![], - onchange: None + onchange: Some(onchange.add_path("name").to_string()) })) } else if let Some(transform) = r.downcast_ref::() { + Some(VNode::Component(VComponent { tag: "div".to_string(), attributes: vec![], children: vec![ VNode::Text("Pos: ".to_string()), - show_reflect(&transform.translation).unwrap(), + show_reflect(&transform.translation, onchange.add_path("translation")).unwrap(), VNode::Text("Rot: ".to_string()), - show_reflect(&transform.rotation).unwrap(), + show_reflect(&transform.rotation, onchange.add_path("rotation")).unwrap(), VNode::Text("Scale: ".to_string()), - show_reflect(&transform.scale).unwrap(), + show_reflect(&transform.scale, onchange.add_path("scale")).unwrap(), ], onchange: None })) @@ -116,9 +157,9 @@ fn check_predefined(r : &dyn Reflect) -> Option { } } -pub fn show_reflect(r : &dyn Reflect) -> Option { +pub fn show_reflect(r : &dyn Reflect, reflect: ReflectChange) -> Option { - if let Some(vnode) = check_predefined(r) { + if let Some(vnode) = check_predefined(r, reflect.clone()) { return Some(vnode); } @@ -154,7 +195,7 @@ pub fn show_reflect(r : &dyn Reflect) -> Option { ("value".to_string(), s.to_string()), ], children: vec![], - onchange: None + onchange: Some(reflect.to_string()) })) } else if let Some(b) = v.downcast_ref::() { Some(VNode::Component(VComponent { @@ -164,7 +205,7 @@ pub fn show_reflect(r : &dyn Reflect) -> Option { ("value".to_string(), b.to_string()), ], children: vec![], - onchange: None + onchange: Some(reflect.to_string()) })) } else if let Some(i) = v.downcast_ref::() { Some(VNode::Component(VComponent { @@ -174,7 +215,7 @@ pub fn show_reflect(r : &dyn Reflect) -> Option { ("value".to_string(), i.to_string()), ], children: vec![], - onchange: None + onchange: Some(reflect.to_string()) })) } else if let Some(f) = v.downcast_ref::() { Some(VNode::Component(VComponent { @@ -184,7 +225,7 @@ pub fn show_reflect(r : &dyn Reflect) -> Option { ("value".to_string(), f.to_string()), ], children: vec![], - onchange: None + onchange: Some(reflect.to_string()) })) } else { Some(VNode::Text(format!("{:?}", v))) @@ -204,7 +245,7 @@ pub struct VComponent { pub tag: String, pub attributes: Vec<(String, String)>, pub children: Vec, - pub onchange: Option // event name if onchange is set + pub onchange: Option // id for onchange detection } #[derive(Clone, PartialEq, Serialize, Deserialize)] @@ -213,17 +254,6 @@ pub enum VNode { Component(VComponent), } -struct Element { - last_prop: Option

, - last_elements: Vec, - prop_event : HashMap>, - -} - -struct VirtualDom { - -} - pub struct InspectedEntity { pub id: Option, pub components: Vec>, @@ -241,6 +271,29 @@ impl Clone for ComponentProp { } } +#[derive(Serialize, Deserialize, Clone)] +pub struct ReflectChange { + pub entity_id : u32, + pub generation : u32, + pub reflect_path: String, + pub compoent_id: String +} + +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) + } + } + + pub fn to_string(&self) -> String { + serde_json::to_string(self).unwrap() + } +} + impl<'a> PartialEq for ComponentProp { fn eq(&self, other: &Self) -> bool { recursive_reflect_eq(self.component.as_ref(), other.component.as_ref())