mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-17 00:13:17 +00:00
dioxus start working
This commit is contained in:
+155
-155
@@ -1,193 +1,192 @@
|
||||
use std::sync::{Arc, Mutex, RwLock};
|
||||
|
||||
use bevy::{prelude::*, utils::hashbrown::HashMap, window::PrimaryWindow};
|
||||
use dioxus::{core::{ElementId, Mutations}, prelude::*};
|
||||
use bevy::{ecs::world::unsafe_world_cell::{UnsafeEntityCell, UnsafeWorldCell}, prelude::*, reflect::TypeData, utils::hashbrown::HashMap, window::PrimaryWindow};
|
||||
use dioxus::{prelude::*, rsx::*};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use space_editor::{prelude::Selected, space_editor_ui::inspector::inspect};
|
||||
use wasm_bindgen::JsCast;
|
||||
use web_sys::HtmlElement;
|
||||
|
||||
use crate::{ui::inspector::wasm::notify_dom_changed, wasm::{StaticBuffer, WasmMessageBuffer}};
|
||||
|
||||
use super::utils::{ComponentProp, InspectedEntity};
|
||||
|
||||
pub struct InspectorPlugin;
|
||||
|
||||
impl Plugin for InspectorPlugin {
|
||||
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 {
|
||||
dom: VirtualDom,
|
||||
#[derive(Resource, Default)]
|
||||
struct InspectorFunctions {
|
||||
get_reflects: Vec<Box<dyn FnMut(&UnsafeEntityCell<'_>) -> Option<Box<dyn Reflect>> + Send + Sync>>
|
||||
}
|
||||
|
||||
fn init(
|
||||
world: &mut World,
|
||||
) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
impl InspectorFunctions {
|
||||
fn add_getter<F: FnMut(&UnsafeEntityCell<'_>) -> Option<Box<dyn Reflect>> + Send + Sync + 'static>(&mut self, f: F) {
|
||||
self.get_reflects.push(Box::new(f));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
fn add_simple<T>(&mut self) where T: bevy::prelude::Component + Reflect + FromReflect + 'static {
|
||||
|
||||
fn apply_muts(
|
||||
mut dom : NonSendMut<InspectorDom>,
|
||||
) {
|
||||
if let Some(window) = web_sys::window() {
|
||||
window.capture_events();
|
||||
window.release_events();
|
||||
info!("apply_muts 1");
|
||||
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!(),
|
||||
unsafe {
|
||||
self.add_getter(
|
||||
|cell| {
|
||||
if let Some(val) = cell.get::<T>() {
|
||||
Some(Box::new(<T as FromReflect>::from_reflect(val).unwrap()) as Box<dyn Reflect>)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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(
|
||||
element_id: &ElementId,
|
||||
template: &TemplateNode,
|
||||
parent: &mut web_sys::Element,
|
||||
document: &web_sys::Document,
|
||||
) {
|
||||
match *template {
|
||||
TemplateNode::Element { tag, namespace, attrs, children } => {
|
||||
let mut element = document.create_element(tag).unwrap();
|
||||
element.set_attribute("id", format!("element_{}", element_id.0).as_str()).unwrap();
|
||||
|
||||
for attr in attrs {
|
||||
match *attr {
|
||||
TemplateAttribute::Static { name, value, namespace } => {
|
||||
element.set_attribute(name, value).unwrap();
|
||||
},
|
||||
TemplateAttribute::Dynamic { id } => {
|
||||
// TODO
|
||||
},
|
||||
fn web_reflect(cx: Scope<ComponentProp>) -> dioxus::core::Element {
|
||||
let res = match cx.props.component.reflect_ref() {
|
||||
|
||||
bevy::reflect::ReflectRef::Struct(v) => {
|
||||
let field_count = v.field_len();
|
||||
render! {
|
||||
div {
|
||||
v.type_name().split("::").last().unwrap(),
|
||||
(0..field_count).map(|i| {
|
||||
let field = v.field_at(i).unwrap();
|
||||
render! {
|
||||
web_reflect { component: field.clone_value() }
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
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 } => {
|
||||
let text_node = document.create_element("p").unwrap();
|
||||
text_node.set_text_content(Some(text));
|
||||
parent.append_child(&text_node).unwrap();
|
||||
bevy::reflect::ReflectRef::Tuple(_) => {
|
||||
render! {
|
||||
div {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
TemplateNode::Dynamic { id } => {
|
||||
todo!()
|
||||
bevy::reflect::ReflectRef::List(_) => {
|
||||
render! {
|
||||
div {
|
||||
|
||||
}
|
||||
}
|
||||
},
|
||||
TemplateNode::DynamicText { id } => {
|
||||
todo!()
|
||||
bevy::reflect::ReflectRef::Array(_) => {
|
||||
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")]
|
||||
@@ -218,10 +217,11 @@ declare global {
|
||||
"#;
|
||||
|
||||
pub(super) fn notify_dom_changed(
|
||||
q_window: Query<&Window, With<PrimaryWindow>>,
|
||||
q_window: &Window,
|
||||
msg : 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();
|
||||
@@ -232,4 +232,4 @@ declare global {
|
||||
|
||||
MUTATION_BUFFER.take_message().unwrap_or("{}".to_string())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ use self::fps::FpsCounterPlugin;
|
||||
|
||||
mod fps;
|
||||
mod inspector;
|
||||
mod utils;
|
||||
|
||||
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