From 1597e6a164ce4459f07f80cdcff6560160e0da1e Mon Sep 17 00:00:00 2001 From: rewin Date: Sun, 11 Feb 2024 18:08:31 +0300 Subject: [PATCH] Custom unpack with custom vnode --- studio-frontend/src/dioxus-machine.ts | 224 +++---------------------- studio/Cargo.toml | 10 +- studio/src/ui/inspector.rs | 225 ++----------------------- studio/src/ui/utils.rs | 228 +++++++++++++++++++++++++- 4 files changed, 263 insertions(+), 424 deletions(-) diff --git a/studio-frontend/src/dioxus-machine.ts b/studio-frontend/src/dioxus-machine.ts index d7a18d0..911869f 100644 --- a/studio-frontend/src/dioxus-machine.ts +++ b/studio-frontend/src/dioxus-machine.ts @@ -2,22 +2,30 @@ import { type InspectorDomChanged } from "dist/studio/studio"; console.log("Hello, World!"); - -function load_child(stack : any[], child_path : number[]) { - // iterate through each number and get that child - var node : any = stack[stack.length - 1]; - for (const ptr of child_path) { - var steps = ptr; - var m = 0; - for (node = node.firstChild; m < steps; m++) { - node = node.nextSibling; +function unpack_vnode( + vnode: any +) { + if (vnode["Text"] != undefined) { + var text_element = document.createTextNode(vnode["Text"]); + return text_element; + } else if (vnode["Component"] != undefined) { + var node_data = vnode["Component"]; + var element = document.createElement(node_data["tag"]); + //apply attributes + for (var i = 0; i < node_data["attributes"].length; i++) { + var attr = node_data["attributes"][i]; + element.setAttribute(attr[0], attr[1]); } + //apply children + if (node_data["children"] != undefined) { + for (var j = 0; j < node_data["children"].length; j++) { + var child = node_data["children"][j]; + var child_element = unpack_vnode(child); + element.appendChild(child_element); + } + } + return element; } - return node; -} - -function laod_template(edit : any, stack : any[]) { - } function apply_muts( @@ -41,134 +49,10 @@ function apply_muts( } var vdom = JSON.parse(event.detail); - var templates = vdom["templates"]; - var edits = vdom["edits"]; - - console.log("edits", edits); - console.log("templates", templates); - - var element_stack = []; - var nodes : any = {} - + var element = unpack_vnode(vdom); + root.appendChild(element); - nodes[0] = document.createElement("div"); - root.appendChild(nodes[0]); - element_stack.push(root); - - //loop through edits - for (const edit of edits) { - - var type = edit["type"]; - if (type == "LoadTemplate") { - // var index = edit["index"]; - // var template_name = edit["name"]; - // var id = edit["id"]; - - // var target_root = null; - // for (const template of templates) { - // if (template["name"] == template_name) { - // target_root = template["roots"][index]; - // break; - // } - // } - // if (target_root == null) { - // console.log("template not found:", template_name); - // continue; - // } else { - // target_root["id"] = id; - // element_stack.push(target_root); - // } - - var id = edit["id"]; - var index = edit["index"]; - var template_name = edit["name"]; - - var template_node = null; - for (const template of templates) { - if (template["name"] == template_name) { - template_node = template["roots"][index]; - break; - } - } - - if (template_node == null) { - console.log("template not found:", template_name); - continue; - } else { - var node = recursive_render(null, template_node); - nodes[id] = node; - element_stack.push(node); - } - - } else if (type == "AppendChildren") { - // var id = edit["id"]; - // var count = edit["m"]; - // if (id == 0) { - // for (var i = 0; i < count; i++) { - // var current_element = element_stack.pop(); - // element_tree.push(current_element); - // } - // } else { - // //TODO - // } - - var id = edit["id"]; - var many = edit["m"]; - - var target_node = nodes[id]; - - let append_nodes = element_stack.splice(element_stack.length - many); - - for (var i = 0; i < many; i++) { - target_node.appendChild(append_nodes[i]); - } - } else if (type == "HydrateText") { - // var child_path = edit["path"]; - // var target_node = element_stack[element_stack.length - 1]; - - // for (const path of child_path) { - // target_node = target_node["children"][path]; - // } - - // if (target_node == null) { - // console.log("target_node not found:", child_path); - // continue; - // } - - // //add text node to target_node - // if (target_node["children"] == undefined) { - // target_node["children"] = []; - // } - // target_node["children"].push({type: "Text", text: edit["value"], id: edit["id"]}); - - var node : HTMLElement = load_child(element_stack, edit["path"]); - if (node) { - node.textContent = edit["value"]; - } else { - console.log("node not found:", edit["path"]); - } - } else if (type == "ReplacePlaceholder") { - var child_path = edit["path"]; - var manu = edit["m"]; - - var els = []; - for (var i = 0; i < manu; i++) { - var el = element_stack.pop(); - els.push(el); - } - els.reverse(); - var target_node = load_child(element_stack, child_path); - target_node.replaceWith(...els); - console.log("") - } else if (type == "CreateTextNode") { - var text_node : any = document.createTextNode(edit["value"]); - nodes[edit["id"]] = text_node; - element_stack.push(text_node); - } - } - - } else { console.log('Selection has been cleared.'); } @@ -177,66 +61,8 @@ 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); }); - -export interface VdomElement { - id?: number; - type?: string; - text?: string; - tag?: string; - namespace?: string; - attrs?: any[]; - children?: VdomElement[]; -} - -function recursive_render( - root: HTMLElement | null, - element: VdomElement, -) { - //create element - var tag = element["tag"]; - if (element.type == "Text") { - tag = "div"; - } else if (element.type == "Dynamic") { - tag = "div"; - } - - var new_element = document.createElement(element["tag"] as string); - - //set text - if (element.text != undefined) { - new_element.textContent = element.text; - } - - //set id - if (element.id != undefined) { - new_element.id = element.id.toString(); - } - - //set attrs - if (element.attrs != undefined) { - for (const attr of element.attrs) { - var name = attr["name"]; - var value = attr["value"]; - new_element.setAttribute(name, value); - } - } - - //parent to root - if (root != null) { - root.appendChild(new_element); - } - - //render children - if (element["children"] != undefined) { - for (const child of element["children"]) { - recursive_render(new_element, child); - } - } - - return new_element; -} \ No newline at end of file diff --git a/studio/Cargo.toml b/studio/Cargo.toml index 898752d..f4bd9f0 100644 --- a/studio/Cargo.toml +++ b/studio/Cargo.toml @@ -22,7 +22,7 @@ bitflags = "2.4.0" console_error_panic_hook = { version = "0.1.7", optional = true } js-sys = { version = "0.3.65", optional = true } parking_lot = "0.12.1" -serde = { version = "1.0.192", optional = true } +serde = { version = "1.0.192", optional = true, features = ["derive"] } serde-wasm-bindgen = { version = "0.6.1", optional = true } serde_json = { version = "1.0.108", optional = true } studio-bvh = { path = "../studio-bvh" } @@ -39,11 +39,9 @@ web-sys = { version = "0.3.65", optional = true, features = [ ] } space_editor = {git = "https://github.com/rewin123/space_editor", rev = "ce83f86dc902787ab16213f3209276a784654df6"} anyhow.workspace = true -dioxus = "0.4.3" -futures-util = "0.3.30" -sledgehammer_bindgen = { version = "0.3.1", default-features = false, optional = true } -sledgehammer_utils = { version = "0.2", optional = true } pretty-type-name = "1.0.1" +async-channel = "2.2.0" +uuid = { version = "1.3.0", features = ["v4"] } [features] wasm = [ @@ -54,8 +52,6 @@ wasm = [ "dep:serde-wasm-bindgen", "dep:web-sys", "bevy_rapier3d/wasm-bindgen", - "dep:sledgehammer_bindgen", - "dep:sledgehammer_utils", ] desktop = [] diff --git a/studio/src/ui/inspector.rs b/studio/src/ui/inspector.rs index 623a62f..b9b288b 100644 --- a/studio/src/ui/inspector.rs +++ b/studio/src/ui/inspector.rs @@ -1,7 +1,6 @@ -use std::sync::{Arc, Mutex, RwLock}; +use std::{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 dioxus::{prelude::*, rsx::*}; use serde::{Deserialize, Serialize}; use space_editor::{prelude::Selected, space_editor_ui::inspector::inspect}; use wasm_bindgen::JsCast; @@ -9,7 +8,7 @@ use web_sys::HtmlElement; use crate::{ui::inspector::wasm::notify_dom_changed, wasm::{StaticBuffer, WasmMessageBuffer}}; -use super::utils::{ComponentProp, InspectedEntity}; +use super::utils::{show_reflect, ComponentProp, InspectedEntity, VComponent, VNode}; pub struct InspectorPlugin; @@ -56,213 +55,6 @@ impl InspectorFunctions { } -fn web_inspector(cx: Scope) -> dioxus::core::Element { - - let e = cx.props; - - let components = e.components.iter().map(|c: &Box| ComponentProp { component: c.clone_value() }).collect::>(); - let components_rendered = components.iter().map(|c : &ComponentProp| { - rsx! { - web_reflect { component: c.component.clone_value() } - } - }); - - if e.id.is_none() { - render! { - div { - "No entity selected" - } - } - } else { - let id_name = format!("{:?}", e.id.unwrap()); - render! { - div { - id_name, - components_rendered - } - } - } -} - -fn web_reflect(cx: Scope) -> dioxus::core::Element { - - //registered components - if cx.props.component.represents::() { - let name = ::from_reflect(cx.props.component.as_ref()).unwrap(); - return render! { - div { - p { name.as_str() } - } - }; - }else if cx.props.component.represents::() { - let transform = ::from_reflect(cx.props.component.as_ref()).unwrap(); - let euler = transform.rotation.to_euler(EulerRot::XYZ); - return render! { - div { - "style" : "flex-direction: column; display: flex; ", - "Transform\n", - format!("pos: {:.2} {:.2} {:.2}\n", transform.translation.x, transform.translation.y, transform.translation.z), - format!("rot: {:.2} {:.2} {:.2}\n", euler.0, euler.1, euler.2), - format!("scale: {:.2} {:.2} {:.2}\n", transform.scale.x, transform.scale.y, transform.scale.z) - } - }; - } - - let res = match cx.props.component.reflect_ref() { - - bevy::reflect::ReflectRef::Struct(v) => { - let field_count = v.field_len(); - - render! { - div { - p { v.type_name().split("::").last().unwrap() }, - (0..field_count).map(|i| { - let field = v.field_at(i).unwrap(); - render! { - format!("{}: ", v.name_at(i).unwrap()), - web_reflect { component: field.clone_value() } - - } - }) - - } - } - }, - bevy::reflect::ReflectRef::TupleStruct(v) => { - let field_count = v.field_len(); - - render! { - div { - p { v.type_name().split("::").last().unwrap(), }, - v.iter_fields().map(|field| { - render! { - web_reflect { component: field.clone_value() } - } - }) - } - } - }, - bevy::reflect::ReflectRef::Tuple(_) => { - render! { - div { - - } - } - }, - bevy::reflect::ReflectRef::List(_) => { - render! { - div { - - } - } - }, - bevy::reflect::ReflectRef::Array(_) => { - render! { - div { - - } - } - }, - bevy::reflect::ReflectRef::Map(_) => { - render! { - div { - - } - } - }, - bevy::reflect::ReflectRef::Enum(_) => { - render! { - div { - - } - } - }, - bevy::reflect::ReflectRef::Value(v) => { - if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else if let Some(v) = v.downcast_ref::() { - render! { - div { - format!("{:?}", v) - } - } - } else { - render! { - div { - "Not implemented reflect type" - } - } - } - }, - }; - return res; -} - - fn show_selected( world: &mut World ) { @@ -282,15 +74,20 @@ fn show_selected( let mut cell = InspectedEntity::none(); cell.id = Some(e.id()); cell.components = functions.get_reflect_copy(&e); - - let mut dom = VirtualDom::new_with_props(web_inspector, cell); + 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(), + ..Default::default() + }); - let muts = dom.rebuild(); let q_window = world.query_filtered::<&Window, With>().single(world); - let _ = notify_dom_changed(q_window, serde_json::to_string(&muts).unwrap()); + let _ = notify_dom_changed(q_window, serde_json::to_string(&vnode).unwrap()); } } diff --git a/studio/src/ui/utils.rs b/studio/src/ui/utils.rs index eaeab26..d73606d 100644 --- a/studio/src/ui/utils.rs +++ b/studio/src/ui/utils.rs @@ -1,14 +1,234 @@ -use bevy::prelude::*; -use dioxus::prelude::*; +use bevy::{prelude::*, utils::HashMap}; +use ::serde::{Serialize, Deserialize}; +use uuid::*; + +trait NodeProp : Clone + PartialEq { + +} + +fn check_predefined(r : &dyn Reflect) -> Option { + if let Some(name) = r.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "text".to_string()), + ("value".to_string(), name.to_string()), + ], + children: vec![], + onchange: None + })) + } 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(), + VNode::Text("Rot: ".to_string()), + show_reflect(&transform.rotation).unwrap(), + VNode::Text("Scale: ".to_string()), + show_reflect(&transform.scale).unwrap(), + ], + onchange: None + })) + } else if let Some(quat) = r.downcast_ref::() { + let euler = quat.to_euler(EulerRot::XYZ); + Some(VNode::Component(VComponent { + tag: "div".to_string(), + attributes: vec![], + children: vec![ + VNode::Text("X: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), euler.0.to_string()), + ], + children: vec![], + onchange: None + }), + VNode::Text("Y: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), euler.1.to_string()), + ], + children: vec![], + onchange: None + }), + VNode::Text("Z: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), euler.2.to_string()), + ], + children: vec![], + onchange: None + }) + ], + onchange: None + })) + } + else if let Some(vec) = r.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "div".to_string(), + attributes: vec![], + children: vec![ + VNode::Text("X: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), vec.x.to_string()), + ], + children: vec![], + onchange: None + }), + VNode::Text("Y: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), vec.y.to_string()), + ], + children: vec![], + onchange: None + }), + VNode::Text("Z: ".to_string()), + VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), vec.z.to_string()), + ], + children: vec![], + onchange: None + }) + ], + onchange: None + })) + + } else { + None + } +} + +pub fn show_reflect(r : &dyn Reflect) -> Option { + + if let Some(vnode) = check_predefined(r) { + return Some(vnode); + } + + match r.reflect_ref() { + bevy::reflect::ReflectRef::Struct(v) => { + None + + }, + bevy::reflect::ReflectRef::TupleStruct(_) => { + None + }, + bevy::reflect::ReflectRef::Tuple(_) => { + None + }, + bevy::reflect::ReflectRef::List(_) => { + None + }, + bevy::reflect::ReflectRef::Array(_) => { + None + }, + bevy::reflect::ReflectRef::Map(_) => { + None + }, + bevy::reflect::ReflectRef::Enum(_) => { + None + }, + bevy::reflect::ReflectRef::Value(v) => { + if let Some(s) = v.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "text".to_string()), + ("value".to_string(), s.to_string()), + ], + children: vec![], + onchange: None + })) + } else if let Some(b) = v.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "checkbox".to_string()), + ("value".to_string(), b.to_string()), + ], + children: vec![], + onchange: None + })) + } else if let Some(i) = v.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), i.to_string()), + ], + children: vec![], + onchange: None + })) + } else if let Some(f) = v.downcast_ref::() { + Some(VNode::Component(VComponent { + tag: "input".to_string(), + attributes: vec![ + ("type".to_string(), "number".to_string()), + ("value".to_string(), f.to_string()), + ], + children: vec![], + onchange: None + })) + } else { + Some(VNode::Text(format!("{:?}", v))) + } + }, + } +} + +impl NodeProp for T {} + +pub fn show_string(s : &str) -> Option { + Some(VNode::Text(s.to_string()) +)} + +#[derive(Clone, PartialEq, Serialize, Deserialize, Default)] +pub struct VComponent { + pub tag: String, + pub attributes: Vec<(String, String)>, + pub children: Vec, + pub onchange: Option // event name if onchange is set +} + +#[derive(Clone, PartialEq, Serialize, Deserialize)] +pub enum VNode { + Text(String), + Component(VComponent), +} + +struct Element { + last_prop: Option

, + last_elements: Vec, + prop_event : HashMap>, + +} + +struct VirtualDom { + +} -#[derive(Props)] pub struct InspectedEntity { pub id: Option, pub components: Vec>, } -#[derive(Props)] pub struct ComponentProp { pub component: Box, }