mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-28 08:58:07 +00:00
dioxus start working
This commit is contained in:
@@ -231,16 +231,30 @@ export class AppElement extends LitElement {
|
|||||||
@space-change=${this.onTransformSpaceChange}
|
@space-change=${this.onTransformSpaceChange}
|
||||||
></sts-transform-toolbar>
|
></sts-transform-toolbar>
|
||||||
|
|
||||||
<sts-inspector></sts-inspector>
|
<style>
|
||||||
|
.vertical-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
position: absolute;
|
||||||
|
top: 16px;
|
||||||
|
right: 16px;
|
||||||
|
|
||||||
<div class="hierarchy v-start h-end">
|
gap: 16px;
|
||||||
${this.sceneObjects.map(obj => html`
|
width: 290px;
|
||||||
<sts-tree class="scene-object"
|
}
|
||||||
id=${obj.id}
|
</style>
|
||||||
.name=${obj.name}
|
|
||||||
.treeChildren=${obj.children}
|
<div class="vertical-list">
|
||||||
></sts-tree>
|
<div class="hierarchy">
|
||||||
`)}
|
${this.sceneObjects.map(obj => html`
|
||||||
|
<sts-tree class="scene-object"
|
||||||
|
id=${obj.id}
|
||||||
|
.name=${obj.name}
|
||||||
|
.treeChildren=${obj.children}
|
||||||
|
></sts-tree>
|
||||||
|
`)}
|
||||||
|
</div>
|
||||||
|
<sts-inspector id="inspector"></sts-inspector>
|
||||||
</div>
|
</div>
|
||||||
` : nothing}
|
` : nothing}
|
||||||
|
|
||||||
|
|||||||
@@ -2,20 +2,250 @@ import { on } from "@storyteller/framework";
|
|||||||
import { type InspectorDomChanged } from "dist/studio/studio";
|
import { type InspectorDomChanged } from "dist/studio/studio";
|
||||||
import { LitElement, html, unsafeCSS, nothing, HTMLTemplateResult } from "lit";
|
import { LitElement, html, unsafeCSS, nothing, HTMLTemplateResult } from "lit";
|
||||||
import { customElement, state } from "lit/decorators.js";
|
import { customElement, state } from "lit/decorators.js";
|
||||||
|
import { property } from "lodash";
|
||||||
|
|
||||||
|
//vdom example
|
||||||
|
/// {
|
||||||
|
///"subtree":0,
|
||||||
|
/// "dirty_scopes":[],
|
||||||
|
/// "templates":[{"name":"studio\\src\\ui\\inspector.rs:46:15:160899","roots":[{"type":"Element","tag":"div","namespace":null,"attrs":[],"children":[{"type":"Text","text":"Inspector"},{"type":"Element","tag":"div","namespace":null,"attrs":[],"children":[{"type":"Text","text":"hello world"}]}]}],"node_paths":[],"attr_paths":[]}],
|
||||||
|
/// "edits":[{"type":"LoadTemplate","name":"studio\\src\\ui\\inspector.rs:46:15:160899","index":0,"id":1},{"type":"AppendChildren","id":0,"m":1}]}
|
||||||
|
|
||||||
|
|
||||||
@customElement("sts-inspector")
|
@customElement("sts-inspector")
|
||||||
export class AppElement extends LitElement {
|
export class AppElement extends LitElement {
|
||||||
@state() vdom?: string | null;
|
@state() vdom?: string | null;
|
||||||
|
@state() generation = 0;
|
||||||
|
@state() vdom_elements?: VdomElement[] | null;
|
||||||
|
|
||||||
@on("window:inspector-dom-changed")
|
// @on("window:inspector-dom-changed")
|
||||||
onInspectorDomChanged({ detail: state}: InspectorDomChanged): void {
|
// onInspectorDomChanged({ detail: state}: InspectorDomChanged): void {
|
||||||
console.log("inspector dom changed:", state);
|
// console.log("inspector dom changed:", state);
|
||||||
}
|
// //change vdom and activate render
|
||||||
|
// this.vdom = null;
|
||||||
|
// this.vdom = state;
|
||||||
|
// this.generation += 1;
|
||||||
|
|
||||||
protected override render = () => html`
|
// // Activate the render function
|
||||||
<div id="some_id">
|
// this.requestUpdate();
|
||||||
|
// }
|
||||||
|
|
||||||
</div>
|
protected override render() {
|
||||||
`;
|
|
||||||
|
|
||||||
|
this.shadowRoot
|
||||||
|
|
||||||
|
return html`
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.inspector {
|
||||||
|
overflow: auto;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
width: 290px;
|
||||||
|
max-height: calc(100vh - 32px);
|
||||||
|
padding: 10px 15px;
|
||||||
|
|
||||||
|
color: #FFFC;
|
||||||
|
background: #0006;
|
||||||
|
backdrop-filter: blur(16px);
|
||||||
|
border: 8px solid transparent;
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
|
||||||
|
pointer-events: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
|
||||||
|
&-thumb {
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #FFF2;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: #FFF3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id="inspector" class="inspector"></div>
|
||||||
|
`
|
||||||
|
|
||||||
|
//convert string vdom to json object
|
||||||
|
// if (this.vdom) {
|
||||||
|
// var vdom = JSON.parse(this.vdom);
|
||||||
|
|
||||||
|
// var templates = vdom["templates"];
|
||||||
|
// var edits = vdom["edits"];
|
||||||
|
|
||||||
|
// console.log("edits", edits);
|
||||||
|
// console.log("templates", templates);
|
||||||
|
|
||||||
|
// var element_stack = [];
|
||||||
|
|
||||||
|
// var element_tree = [];
|
||||||
|
|
||||||
|
// //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);
|
||||||
|
// }
|
||||||
|
// } 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
|
||||||
|
// }
|
||||||
|
// } 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];
|
||||||
|
// }
|
||||||
|
|
||||||
|
// //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"]});
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// console.log("element tree", element_tree);
|
||||||
|
|
||||||
|
// //convert any to VdomElement in element_tree
|
||||||
|
// var vdom_elements = element_tree.map((element) => {
|
||||||
|
// return element as VdomElement;
|
||||||
|
// });
|
||||||
|
|
||||||
|
// this.vdom_elements = vdom_elements;
|
||||||
|
|
||||||
|
// return html`
|
||||||
|
|
||||||
|
// <style>
|
||||||
|
// .inspector {
|
||||||
|
// overflow: auto;
|
||||||
|
|
||||||
|
// box-sizing: border-box;
|
||||||
|
// width: 290px;
|
||||||
|
// max-height: calc(100vh - 32px);
|
||||||
|
// padding: 10px 15px;
|
||||||
|
|
||||||
|
// color: #FFFC;
|
||||||
|
// background: #0006;
|
||||||
|
// backdrop-filter: blur(16px);
|
||||||
|
// border: 8px solid transparent;
|
||||||
|
// border-radius: 0.5rem;
|
||||||
|
|
||||||
|
// pointer-events: auto;
|
||||||
|
|
||||||
|
// &::-webkit-scrollbar {
|
||||||
|
// width: 8px;
|
||||||
|
// height: 8px;
|
||||||
|
|
||||||
|
// &-thumb {
|
||||||
|
// border-radius: 2px;
|
||||||
|
// background: #FFF2;
|
||||||
|
|
||||||
|
// &:hover {
|
||||||
|
// background: #FFF3;
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// </style>
|
||||||
|
|
||||||
|
// <div id="inspector" class="inspector">
|
||||||
|
// ${this.vdom_elements.map((element, index) => html`<sts-inspector-element generation=${this.generation} .vdom=${element} key=${index}></sts-inspector-element>`)}
|
||||||
|
// </div>`
|
||||||
|
|
||||||
|
|
||||||
|
// } else {
|
||||||
|
// //For each vdom element render InspectorElement
|
||||||
|
// return html`
|
||||||
|
|
||||||
|
// `
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface VdomElement {
|
||||||
|
id?: number;
|
||||||
|
type?: string;
|
||||||
|
text?: string;
|
||||||
|
tag?: string;
|
||||||
|
namespace?: string;
|
||||||
|
attrs?: any[];
|
||||||
|
children?: VdomElement[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Element for recursive render vdom tree
|
||||||
|
@customElement("sts-inspector-element")
|
||||||
|
export class InspectorElement extends LitElement {
|
||||||
|
@property({ type: Object, attribute: true })
|
||||||
|
vdom: VdomElement;
|
||||||
|
|
||||||
|
@property({ type: Number, attribute: true })
|
||||||
|
generation: number;
|
||||||
|
|
||||||
|
protected override render() {
|
||||||
|
var tag = this.vdom["tag"];
|
||||||
|
var type = this.vdom.type;
|
||||||
|
|
||||||
|
if (type == "Text") {
|
||||||
|
return html`<div>${this.vdom["text"]}</div>`
|
||||||
|
} else if (type == "Dynamic") {
|
||||||
|
return html`
|
||||||
|
<div id=${this.vdom["id"]}>
|
||||||
|
${this.vdom["children"].map((element) => html`<sts-inspector-element .vdom=${element} .generation=${this.generation}></sts-inspector-element>`)}
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tag == "div") {
|
||||||
|
return html`
|
||||||
|
<style>
|
||||||
|
.tabber {
|
||||||
|
padding: 0 0 0 10px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<div id=${this.vdom["id"]} class="tabber">
|
||||||
|
${this.vdom["children"].map((element) => html`<sts-inspector-element .vdom=${element} .generation=${this.generation}></sts-inspector-element>`)}
|
||||||
|
</div>`
|
||||||
|
} else {
|
||||||
|
return html`<div>${this.vdom["text"]}</div>`
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,226 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function laod_template(edit : any, stack : any[]) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
function apply_muts(
|
||||||
|
event : InspectorDomChanged
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (event.detail !== null) {
|
||||||
|
|
||||||
|
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 templates = vdom["templates"];
|
||||||
|
var edits = vdom["edits"];
|
||||||
|
|
||||||
|
console.log("edits", edits);
|
||||||
|
console.log("templates", templates);
|
||||||
|
|
||||||
|
var element_stack = [];
|
||||||
|
var nodes : any = {}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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];
|
||||||
|
for (var i = 0; i < many; i++) {
|
||||||
|
var current_element = element_stack.pop();
|
||||||
|
target_node.appendChild(current_element);
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
|
}
|
||||||
|
var target_node = load_child(element_stack, child_path);
|
||||||
|
target_node.replaceWith(...els);
|
||||||
|
console.log("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('Selection has been cleared.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
//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;
|
||||||
|
}
|
||||||
@@ -1 +1,2 @@
|
|||||||
import "./app/app.element";
|
import "./app/app.element";
|
||||||
|
import "./dioxus-machine";
|
||||||
|
|||||||
@@ -41,6 +41,9 @@ space_editor = {git = "https://github.com/rewin123/space_editor", rev = "ce83f86
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
dioxus = "0.4.3"
|
dioxus = "0.4.3"
|
||||||
futures-util = "0.3.30"
|
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"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
wasm = [
|
wasm = [
|
||||||
@@ -51,6 +54,8 @@ wasm = [
|
|||||||
"dep:serde-wasm-bindgen",
|
"dep:serde-wasm-bindgen",
|
||||||
"dep:web-sys",
|
"dep:web-sys",
|
||||||
"bevy_rapier3d/wasm-bindgen",
|
"bevy_rapier3d/wasm-bindgen",
|
||||||
|
"dep:sledgehammer_bindgen",
|
||||||
|
"dep:sledgehammer_utils",
|
||||||
]
|
]
|
||||||
desktop = []
|
desktop = []
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
console.log("Hello, World!");
|
||||||
|
|
||||||
|
var root;
|
||||||
|
var node_stack = [];
|
||||||
|
|
||||||
|
export function start() {
|
||||||
|
root = document.getElementById("inspector");
|
||||||
|
console.log("root", root);
|
||||||
|
}
|
||||||
@@ -16,6 +16,9 @@ export default async function main() {
|
|||||||
let count = 0;
|
let count = 0;
|
||||||
const start = performance.now();
|
const start = performance.now();
|
||||||
|
|
||||||
|
//copy inspector.ts to dist
|
||||||
|
fs.copyFileSync(path.join(__dirname, "../inspector.ts"), path.join(__dirname, "../../dist/studio/inspector.ts"));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
/** @type string[] */
|
/** @type string[] */
|
||||||
let ignore = [];
|
let ignore = [];
|
||||||
|
|||||||
+155
-155
@@ -1,193 +1,192 @@
|
|||||||
use std::sync::{Arc, Mutex, RwLock};
|
use std::sync::{Arc, Mutex, RwLock};
|
||||||
|
|
||||||
use bevy::{prelude::*, utils::hashbrown::HashMap, window::PrimaryWindow};
|
use bevy::{ecs::world::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}, prelude::*, reflect::TypeData, utils::hashbrown::HashMap, window::PrimaryWindow};
|
||||||
use dioxus::{core::{ElementId, Mutations}, prelude::*};
|
use dioxus::{prelude::*, rsx::*};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use space_editor::{prelude::Selected, space_editor_ui::inspector::inspect};
|
||||||
use wasm_bindgen::JsCast;
|
use wasm_bindgen::JsCast;
|
||||||
use web_sys::HtmlElement;
|
use web_sys::HtmlElement;
|
||||||
|
|
||||||
use crate::{ui::inspector::wasm::notify_dom_changed, wasm::{StaticBuffer, WasmMessageBuffer}};
|
use crate::{ui::inspector::wasm::notify_dom_changed, wasm::{StaticBuffer, WasmMessageBuffer}};
|
||||||
|
|
||||||
|
use super::utils::{ComponentProp, InspectedEntity};
|
||||||
|
|
||||||
pub struct InspectorPlugin;
|
pub struct InspectorPlugin;
|
||||||
|
|
||||||
impl Plugin for InspectorPlugin {
|
impl Plugin for InspectorPlugin {
|
||||||
fn build(&self, app: &mut App) {
|
fn build(&self, app: &mut App) {
|
||||||
app.add_systems(Startup, init);
|
|
||||||
|
|
||||||
app.add_systems(Update, apply_muts);
|
app.init_resource::<InspectorFunctions>();
|
||||||
|
|
||||||
|
app.add_systems(Update, show_selected);
|
||||||
|
|
||||||
|
app.world.resource_mut::<InspectorFunctions>().add_simple::<Transform>();
|
||||||
|
app.world.resource_mut::<InspectorFunctions>().add_simple::<Name>();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct InspectorDom {
|
#[derive(Resource, Default)]
|
||||||
dom: VirtualDom,
|
struct InspectorFunctions {
|
||||||
|
get_reflects: Vec<Box<dyn FnMut(&UnsafeEntityCell<'_>) -> Option<Box<dyn Reflect>> + Send + Sync>>
|
||||||
}
|
}
|
||||||
|
|
||||||
fn init(
|
impl InspectorFunctions {
|
||||||
world: &mut World,
|
fn add_getter<F: FnMut(&UnsafeEntityCell<'_>) -> Option<Box<dyn Reflect>> + Send + Sync + 'static>(&mut self, f: F) {
|
||||||
) {
|
self.get_reflects.push(Box::new(f));
|
||||||
let mut dom = VirtualDom::new(inspector);
|
|
||||||
|
|
||||||
world.insert_non_send_resource(InspectorDom { dom });
|
|
||||||
|
|
||||||
// let muts = dom.rebuild();
|
|
||||||
|
|
||||||
// info!("{:?}", muts);
|
|
||||||
|
|
||||||
// let json = serde_json::to_string(&muts).unwrap();
|
|
||||||
|
|
||||||
// q_mutation_queue.edits.push(json.clone());
|
|
||||||
|
|
||||||
// info!("{:?}", json);
|
|
||||||
|
|
||||||
// #[cfg(feature = "wasm")]
|
|
||||||
// {
|
|
||||||
// wasm::MUTATION_BUFFER.write_message(json);
|
|
||||||
// notify_dom_changed(q_window);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn recursive_find(
|
|
||||||
id : &str,
|
|
||||||
element: &web_sys::Node,
|
|
||||||
) -> Option<web_sys::Element> {
|
|
||||||
if let Some(el) = element.as_any().downcast_ref::<web_sys::Element>() {
|
|
||||||
if el.id() == *id {
|
|
||||||
return Some(el.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
let children = element.child_nodes();
|
|
||||||
|
|
||||||
for i in 0..children.length() {
|
|
||||||
if let Some(child) = children.get(i) {
|
|
||||||
if let Some(element) = recursive_find(id, &child) {
|
|
||||||
return Some(element);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
fn add_simple<T>(&mut self) where T: bevy::prelude::Component + Reflect + FromReflect + 'static {
|
||||||
}
|
|
||||||
|
|
||||||
fn apply_muts(
|
unsafe {
|
||||||
mut dom : NonSendMut<InspectorDom>,
|
self.add_getter(
|
||||||
) {
|
|cell| {
|
||||||
if let Some(window) = web_sys::window() {
|
if let Some(val) = cell.get::<T>() {
|
||||||
window.capture_events();
|
Some(Box::new(<T as FromReflect>::from_reflect(val).unwrap()) as Box<dyn Reflect>)
|
||||||
window.release_events();
|
} else {
|
||||||
info!("apply_muts 1");
|
None
|
||||||
if let Some(document) = window.document() {
|
|
||||||
|
|
||||||
info!("apply_muts 2");
|
|
||||||
|
|
||||||
if let Some(mut page_root) = document.get_elements_by_name("sts-inspector").item(0) {
|
|
||||||
let mut page_root = page_root.dyn_into::<web_sys::Element>().unwrap();
|
|
||||||
info!("apply_muts 3");
|
|
||||||
|
|
||||||
let dom = &mut dom.dom;
|
|
||||||
let muts = dom.render_immediate();
|
|
||||||
|
|
||||||
let mut element_stack = vec![];
|
|
||||||
|
|
||||||
for m in muts.edits.iter() {
|
|
||||||
match m {
|
|
||||||
dioxus::core::Mutation::AppendChildren { id, m } => {
|
|
||||||
for stack_idx in 0..*m {
|
|
||||||
let (id, template) = element_stack.pop().unwrap();
|
|
||||||
recursive_add_dom(
|
|
||||||
&id,
|
|
||||||
&template,
|
|
||||||
&mut page_root,
|
|
||||||
&document,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dioxus::core::Mutation::AssignId { path, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::CreatePlaceholder { id } => todo!(),
|
|
||||||
dioxus::core::Mutation::CreateTextNode { value, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::HydrateText { path, value, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::LoadTemplate { name, index, id } => {
|
|
||||||
let mut template = None;
|
|
||||||
for t in muts.templates.iter() {
|
|
||||||
if t.name == *name {
|
|
||||||
template = Some(t);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(template) = template {
|
|
||||||
element_stack.push((id.clone(), template.roots[*index].clone()));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dioxus::core::Mutation::ReplaceWith { id, m } => todo!(),
|
|
||||||
dioxus::core::Mutation::ReplacePlaceholder { path, m } => todo!(),
|
|
||||||
dioxus::core::Mutation::InsertAfter { id, m } => todo!(),
|
|
||||||
dioxus::core::Mutation::InsertBefore { id, m } => todo!(),
|
|
||||||
dioxus::core::Mutation::SetAttribute { name, value, id, ns } => todo!(),
|
|
||||||
dioxus::core::Mutation::SetText { value, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::NewEventListener { name, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::RemoveEventListener { name, id } => todo!(),
|
|
||||||
dioxus::core::Mutation::Remove { id } => todo!(),
|
|
||||||
dioxus::core::Mutation::PushRoot { id } => todo!(),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_reflect_copy(&mut self, cell: &UnsafeEntityCell<'_>) -> Vec<Box<dyn Reflect>> {
|
||||||
|
self.get_reflects.iter_mut().map(|f| (*f)(cell)).flatten().collect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn web_inspector(cx: Scope<InspectedEntity>) -> dioxus::core::Element {
|
||||||
|
|
||||||
|
let e = cx.props;
|
||||||
|
|
||||||
|
let components = e.components.iter().map(|c: &Box<dyn Reflect>| ComponentProp { component: c.clone_value() }).collect::<Vec<_>>();
|
||||||
|
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 recursive_add_dom(
|
fn web_reflect(cx: Scope<ComponentProp>) -> dioxus::core::Element {
|
||||||
element_id: &ElementId,
|
let res = match cx.props.component.reflect_ref() {
|
||||||
template: &TemplateNode,
|
|
||||||
parent: &mut web_sys::Element,
|
bevy::reflect::ReflectRef::Struct(v) => {
|
||||||
document: &web_sys::Document,
|
let field_count = v.field_len();
|
||||||
) {
|
render! {
|
||||||
match *template {
|
div {
|
||||||
TemplateNode::Element { tag, namespace, attrs, children } => {
|
v.type_name().split("::").last().unwrap(),
|
||||||
let mut element = document.create_element(tag).unwrap();
|
(0..field_count).map(|i| {
|
||||||
element.set_attribute("id", format!("element_{}", element_id.0).as_str()).unwrap();
|
let field = v.field_at(i).unwrap();
|
||||||
|
render! {
|
||||||
for attr in attrs {
|
web_reflect { component: field.clone_value() }
|
||||||
match *attr {
|
|
||||||
TemplateAttribute::Static { name, value, namespace } => {
|
}
|
||||||
element.set_attribute(name, value).unwrap();
|
})
|
||||||
},
|
|
||||||
TemplateAttribute::Dynamic { id } => {
|
|
||||||
// TODO
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::TupleStruct(_) => {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
parent.append_child(&element).unwrap();
|
}
|
||||||
|
|
||||||
for child in children {
|
|
||||||
recursive_add_dom(element_id, child, &mut element, document);
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
TemplateNode::Text { text } => {
|
bevy::reflect::ReflectRef::Tuple(_) => {
|
||||||
let text_node = document.create_element("p").unwrap();
|
render! {
|
||||||
text_node.set_text_content(Some(text));
|
div {
|
||||||
parent.append_child(&text_node).unwrap();
|
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TemplateNode::Dynamic { id } => {
|
bevy::reflect::ReflectRef::List(_) => {
|
||||||
todo!()
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
TemplateNode::DynamicText { id } => {
|
bevy::reflect::ReflectRef::Array(_) => {
|
||||||
todo!()
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
bevy::reflect::ReflectRef::Map(_) => {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Enum(_) => {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Value(_) => {
|
||||||
|
render! {
|
||||||
|
div {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fn show_selected(
|
||||||
|
world: &mut World
|
||||||
|
) {
|
||||||
|
unsafe {
|
||||||
|
let cell = world.as_unsafe_world_cell();
|
||||||
|
|
||||||
|
let Ok(selected_entity) = cell.world_mut().query_filtered::<Entity, With<Selected>>().get_single(cell.world()) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut functions = cell.get_resource_mut::<InspectorFunctions>().unwrap();
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
|
||||||
|
let mut dom = VirtualDom::new_with_props(web_inspector, cell);
|
||||||
|
|
||||||
|
let muts = dom.rebuild();
|
||||||
|
|
||||||
|
let q_window = world.query_filtered::<&Window, With<PrimaryWindow>>().single(world);
|
||||||
|
|
||||||
|
let _ = notify_dom_changed(q_window, serde_json::to_string(&muts).unwrap());
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inspector(
|
|
||||||
cx: Scope
|
|
||||||
) -> Element {
|
|
||||||
|
|
||||||
cx.render(rsx!{
|
|
||||||
div {
|
|
||||||
"Inspector",
|
|
||||||
div {
|
|
||||||
"hello world"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#[cfg(feature = "wasm")]
|
#[cfg(feature = "wasm")]
|
||||||
@@ -218,10 +217,11 @@ declare global {
|
|||||||
"#;
|
"#;
|
||||||
|
|
||||||
pub(super) fn notify_dom_changed(
|
pub(super) fn notify_dom_changed(
|
||||||
q_window: Query<&Window, With<PrimaryWindow>>,
|
q_window: &Window,
|
||||||
|
msg : String,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
|
|
||||||
wasm::dispatch_to_js(q_window.single(), "inspector-dom-changed", &Option::<Entity>::None)
|
wasm::dispatch_to_js(q_window, "inspector-dom-changed", &msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub static MUTATION_BUFFER: WasmMessageBuffer<String> = WasmMessageBuffer::new();
|
pub static MUTATION_BUFFER: WasmMessageBuffer<String> = WasmMessageBuffer::new();
|
||||||
@@ -232,4 +232,4 @@ declare global {
|
|||||||
|
|
||||||
MUTATION_BUFFER.take_message().unwrap_or("{}".to_string())
|
MUTATION_BUFFER.take_message().unwrap_or("{}".to_string())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use self::fps::FpsCounterPlugin;
|
|||||||
|
|
||||||
mod fps;
|
mod fps;
|
||||||
mod inspector;
|
mod inspector;
|
||||||
|
mod utils;
|
||||||
|
|
||||||
pub struct UiLayerPlugin;
|
pub struct UiLayerPlugin;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,193 @@
|
|||||||
|
|
||||||
|
use bevy::prelude::*;
|
||||||
|
use dioxus::prelude::*;
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
pub struct InspectedEntity {
|
||||||
|
pub id: Option<Entity>,
|
||||||
|
pub components: Vec<Box<dyn Reflect>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Props)]
|
||||||
|
pub struct ComponentProp {
|
||||||
|
pub component: Box<dyn Reflect>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for ComponentProp {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
component: self.component.clone_value()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> PartialEq for ComponentProp {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
recursive_reflect_eq(self.component.as_ref(), other.component.as_ref())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn recursive_reflect_eq(a : &dyn Reflect, b : &dyn Reflect) -> bool {
|
||||||
|
let ref_a = a.reflect_ref();
|
||||||
|
let ref_b = b.reflect_ref();
|
||||||
|
|
||||||
|
match ref_a {
|
||||||
|
bevy::reflect::ReflectRef::Struct(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Struct(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.field_len() != b.field_len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.field_len() {
|
||||||
|
if !recursive_reflect_eq(a.field_at(i).unwrap(), b.field_at(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::TupleStruct(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::TupleStruct(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.field_len() != b.field_len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.field_len() {
|
||||||
|
if !recursive_reflect_eq(a.field(i).unwrap(), b.field(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Tuple(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Tuple(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.field_len() != b.field_len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.field_len() {
|
||||||
|
if !recursive_reflect_eq(a.field(i).unwrap(), b.field(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::List(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::List(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.len() != b.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.len() {
|
||||||
|
if !recursive_reflect_eq(a.get(i).unwrap(), b.get(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Array(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Array(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.len() != b.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.len() {
|
||||||
|
if !recursive_reflect_eq(a.get(i).unwrap(), b.get(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Map(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Map(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.len() != b.len() {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
for i in 0..a.len() {
|
||||||
|
let (a_key, a_value) = a.get_at(i).unwrap();
|
||||||
|
let Some(b_value) = b.get(a_key) else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if !recursive_reflect_eq(a_value, b_value) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Enum(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Enum(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if a.variant_name() != b.variant_name() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if a.field_len() != b.field_len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..a.field_len() {
|
||||||
|
if !recursive_reflect_eq(a.field_at(i).unwrap(), b.field_at(i).unwrap()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
bevy::reflect::ReflectRef::Value(a) => {
|
||||||
|
let bevy::reflect::ReflectRef::Value(b) = b.reflect_ref() else {
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(res) = a.reflect_partial_eq(b) {
|
||||||
|
return res;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for InspectedEntity {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
if self.id != other.id {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if self.components.len() != other.components.len() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for i in 0..self.components.len() {
|
||||||
|
|
||||||
|
if !recursive_reflect_eq(self.components[i].as_ref(), other.components[i].as_ref()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl InspectedEntity {
|
||||||
|
pub fn none() -> Self {
|
||||||
|
Self {
|
||||||
|
id: None,
|
||||||
|
components: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user