mirror of
https://github.com/storytold/storyteller-bevy.git
synced 2026-10-18 07:35:19 +00:00
Basic blocking
This commit is contained in:
@@ -41,10 +41,20 @@ function unpack_vnode(
|
||||
if (id != null) {
|
||||
console.log("onchange", id, input.value);
|
||||
const value = input.value;
|
||||
register_new_change(id, value);
|
||||
register_new_change(id, "onchange", value);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (node_data["onclick"] == true) {
|
||||
element.onclick = (event: Event) => {
|
||||
var input = event.target as HTMLInputElement;
|
||||
const id = input.getAttribute("id");
|
||||
if (id != null) {
|
||||
console.log("onclick", id);
|
||||
register_new_change(id, "onclick", "");
|
||||
}
|
||||
}
|
||||
}
|
||||
return element;
|
||||
}
|
||||
}
|
||||
@@ -225,6 +235,20 @@ function append_style(parent : HTMLElement) {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.blocking-button {
|
||||
background-color: #3333336e; /* Темный фон */
|
||||
color: #fff; /* Светлый текст */
|
||||
border: none; /* Убрать стандартную рамку */
|
||||
border-radius: 7px; /* Закругленные границы */
|
||||
padding: 5px; /* Внутренний отступ */
|
||||
outline: none; /* Убрать контур при фокусе */
|
||||
}
|
||||
|
||||
.blocking-button:hover {
|
||||
border: 2px solid #555; /* Стиль границы при фокусе */
|
||||
background-color: #222; /* Немного темнее фон при фокусе */
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
|
||||
|
||||
+39
-13
@@ -1,8 +1,9 @@
|
||||
use bevy::{ecs::world::unsafe_world_cell::UnsafeEntityCell, prelude::*, window::PrimaryWindow};
|
||||
use space_editor::space_prefab::component::MeshPrimitivePrefab;
|
||||
|
||||
use crate::{ui::inspector::wasm::notify_dom_changed, wasm::StaticBuffer};
|
||||
|
||||
use super::utils::{show_reflect, Element, InspectedEntity, ReflectChange, VirtualDom};
|
||||
use super::utils::{blocking_component, show_reflect, Element, InspectedEntity, ReflectChange, VirtualDom};
|
||||
|
||||
pub struct InspectorPlugin;
|
||||
|
||||
@@ -18,6 +19,10 @@ impl Plugin for InspectorPlugin {
|
||||
app.world
|
||||
.resource_mut::<InspectorFunctions>()
|
||||
.add_simple::<Transform>();
|
||||
|
||||
app.world
|
||||
.resource_mut::<InspectorFunctions>()
|
||||
.add_simple::<MeshPrimitivePrefab>();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,8 +74,28 @@ fn show_selected(world: &mut World) {
|
||||
let cell = world.as_unsafe_world_cell();
|
||||
let mut virtial_dom = cell.get_resource_mut::<VirtualDom>().unwrap();
|
||||
|
||||
//Process events from real DOM
|
||||
if let Some(changes) = super::utils::wasm::ONCHAGE_BUFFER.take_message() {
|
||||
for (id, kind, value) in changes {
|
||||
info!("change: {} {} {}", id, kind, value);
|
||||
if kind == "onchange" {
|
||||
info!("change: {} : {}", id, value);
|
||||
virtial_dom
|
||||
.on_change_events
|
||||
.execute(cell.world_mut(), id, value);
|
||||
} else if kind == "onclick" {
|
||||
info!("click: {} : {}", id, value);
|
||||
virtial_dom
|
||||
.on_click_events
|
||||
.execute(cell.world_mut(), id, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let blocking_element = blocking_component();
|
||||
|
||||
let Some(selection) = cell.get_resource::<crate::interaction::Selection>() else {
|
||||
let muts = virtial_dom.render(None);
|
||||
let muts = virtial_dom.render(blocking_element);
|
||||
let q_window = cell
|
||||
.world_mut()
|
||||
.query_filtered::<&Window, With<PrimaryWindow>>()
|
||||
@@ -87,16 +112,7 @@ fn show_selected(world: &mut World) {
|
||||
let _app_type_registry = cell.get_resource::<AppTypeRegistry>().unwrap();
|
||||
let e = cell.get_entity(selected_entity).unwrap();
|
||||
|
||||
if let Some(changes) = super::utils::wasm::ONCHAGE_BUFFER.take_message() {
|
||||
for (id, kind, value) in changes {
|
||||
if kind == "onchange" {
|
||||
info!("change: {} : {}", id, value);
|
||||
virtial_dom
|
||||
.on_change_events
|
||||
.execute(cell.world_mut(), id, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let mut inspected = InspectedEntity::none();
|
||||
inspected.id = Some(e.id());
|
||||
@@ -137,7 +153,17 @@ fn show_selected(world: &mut World) {
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
let muts = virtial_dom.render(Some(element));
|
||||
let super_root = Element::Component(super::utils::Component {
|
||||
tag: "div".to_string(),
|
||||
attributes: vec![
|
||||
("style".to_string(), "height: 100%; display: flex; flex-direction: column".to_string()),
|
||||
],
|
||||
children: vec![blocking_element.unwrap(), element],
|
||||
..default()
|
||||
});
|
||||
|
||||
|
||||
let muts = virtial_dom.render(Some(super_root));
|
||||
|
||||
let q_window = cell
|
||||
.world_mut()
|
||||
|
||||
+153
-2
@@ -1,5 +1,8 @@
|
||||
use ::serde::{Deserialize, Serialize};
|
||||
use bevy::{ecs::component::ComponentId, prelude::*, reflect::ReflectFromPtr, utils::HashMap};
|
||||
use bevy::{ecs::component::ComponentId, prelude::*, reflect::{DynamicEnum, DynamicStruct, DynamicVariant, ReflectFromPtr, TypeInfo, VariantInfo}, utils::HashMap};
|
||||
use space_editor::{prelude::PrefabMarker, space_prefab::component::*};
|
||||
|
||||
use crate::scene::SceneElement;
|
||||
|
||||
trait NodeProp: Clone + PartialEq {}
|
||||
|
||||
@@ -83,11 +86,18 @@ impl VirtualDom {
|
||||
onchange_enabled = true;
|
||||
}
|
||||
|
||||
let mut onclick_enabled = false;
|
||||
if let Some(onclick) = component.onclick {
|
||||
self.on_click_events.add_event(id.clone(), onclick);
|
||||
onclick_enabled = true;
|
||||
}
|
||||
|
||||
VNode::Component(VComponent {
|
||||
tag: component.tag,
|
||||
attributes: component.attributes.into_iter().collect(),
|
||||
id: Some(id),
|
||||
onchange: onchange_enabled,
|
||||
onclick: onclick_enabled,
|
||||
children: component
|
||||
.children
|
||||
.into_iter()
|
||||
@@ -116,6 +126,40 @@ impl VirtualDom {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
pub fn blocking_component() -> Option<Element> {
|
||||
Some(Element::Component(Component {
|
||||
tag: "div".to_string(),
|
||||
attributes: vec![
|
||||
("style".to_string(), "padding: 10px;".to_string()),
|
||||
("class".to_string(), "inspector".to_string()),
|
||||
],
|
||||
children: vec![
|
||||
Element::Component(Component {
|
||||
tag: "button".to_string(),
|
||||
attributes: vec![
|
||||
("class".to_string(), "blocking-button".to_string()),
|
||||
],
|
||||
onclick: Some(Box::new(WorldCommandExecutor {
|
||||
func: Box::new(|world| {
|
||||
world.spawn((
|
||||
MeshPrimitivePrefab::Cube(1.0),
|
||||
SpatialBundle::default(),
|
||||
SceneElement,
|
||||
PrefabMarker
|
||||
));
|
||||
})
|
||||
})),
|
||||
children: vec![
|
||||
Element::Text("Add blocking mesh".to_string())
|
||||
],
|
||||
..default()
|
||||
})
|
||||
],
|
||||
..default()
|
||||
}))
|
||||
}
|
||||
|
||||
#[cfg(feature = "wasm")]
|
||||
pub mod wasm {
|
||||
use wasm_bindgen::prelude::*;
|
||||
@@ -214,6 +258,16 @@ impl WebEventExecutor for ReflectValueExecutor {
|
||||
}
|
||||
}
|
||||
|
||||
struct WorldCommandExecutor {
|
||||
func: Box<dyn Fn(&mut World) + Send + Sync>,
|
||||
}
|
||||
|
||||
impl WebEventExecutor for WorldCommandExecutor {
|
||||
fn execute(&mut self, world: &mut World, _value: String) {
|
||||
(self.func)(world);
|
||||
}
|
||||
}
|
||||
|
||||
struct EntityChangeExecutor {
|
||||
entity: Entity,
|
||||
func: Box<dyn Fn(&mut World, Entity, String) + Send + Sync>,
|
||||
@@ -450,7 +504,103 @@ pub fn show_reflect(r: &dyn Reflect, reflect: ReflectChange) -> Option<Element>
|
||||
bevy::reflect::ReflectRef::List(_) => None,
|
||||
bevy::reflect::ReflectRef::Array(_) => None,
|
||||
bevy::reflect::ReflectRef::Map(_) => None,
|
||||
bevy::reflect::ReflectRef::Enum(_) => None,
|
||||
bevy::reflect::ReflectRef::Enum(v) => {
|
||||
let Some(type_info) = v.get_represented_type_info() else {
|
||||
error!("invalid reflect impl: type info mismatch");
|
||||
return None;
|
||||
};
|
||||
let type_info = match type_info {
|
||||
TypeInfo::Enum(info) => info,
|
||||
_ => unreachable!("invalid reflect impl: type info mismatch"),
|
||||
};
|
||||
|
||||
|
||||
Some(Element::Component(Component {
|
||||
tag: "div".to_string(),
|
||||
attributes: vec![
|
||||
("style".to_string(), "display: flex; flex-direction: column".to_string()),
|
||||
],
|
||||
children: vec![
|
||||
Element::Component(Component {
|
||||
tag: "select".to_string(),
|
||||
attributes: vec![],
|
||||
children: type_info.iter().enumerate().map(|(i, variant)| {
|
||||
let mut attributes = vec![
|
||||
("value".to_string(), variant.name().to_string()),
|
||||
];
|
||||
|
||||
let is_active_variant = i == v.variant_index();
|
||||
if is_active_variant {
|
||||
attributes.push(("selected".to_string(), "".to_string()));
|
||||
}
|
||||
|
||||
Element::Component(Component {
|
||||
tag: "option".to_string(),
|
||||
attributes,
|
||||
children: vec![Element::Text(variant.name().to_string())],
|
||||
..default()
|
||||
})
|
||||
}).collect(),
|
||||
onchange: Some(
|
||||
EntityChangeExecutor::new_boxed(
|
||||
reflect.entity_id,
|
||||
|world, entity, value : String| {
|
||||
info!("MeshPrimitivePrefab change: {:?} {}", entity, value);
|
||||
|
||||
let Some(mut e) = world.get_entity_mut(entity) else {
|
||||
return;
|
||||
};
|
||||
|
||||
let Some(mut variant) = e.get_mut::<MeshPrimitivePrefab>() else {
|
||||
return;
|
||||
};
|
||||
|
||||
// pub enum MeshPrimitivePrefab {
|
||||
// Cube(f32),
|
||||
// Box(BoxPrefab),
|
||||
// Sphere(SpherePrefab),
|
||||
// Quad(QuadPrefab),
|
||||
// Capsule(CapsulePrefab),
|
||||
// Circle(CirclePrefab),
|
||||
// Cylinder(CylinderPrefab),
|
||||
// Icosphere(IcospherePrefab),
|
||||
// Plane(PlanePrefab),
|
||||
// RegularPolygon(RegularPolygonPrefab),
|
||||
// Torus(TorusPrefab),
|
||||
// }
|
||||
|
||||
if value == "Cube" {
|
||||
*variant = MeshPrimitivePrefab::Cube(1.0);
|
||||
} else if value == "Box" {
|
||||
*variant = MeshPrimitivePrefab::Box(BoxPrefab::default());
|
||||
} else if value == "Sphere" {
|
||||
*variant = MeshPrimitivePrefab::Sphere(SpherePrefab::default());
|
||||
} else if value == "Quad" {
|
||||
*variant = MeshPrimitivePrefab::Quad(QuadPrefab::default());
|
||||
} else if value == "Capsule" {
|
||||
*variant = MeshPrimitivePrefab::Capsule(CapsulePrefab::default());
|
||||
} else if value == "Circle" {
|
||||
*variant = MeshPrimitivePrefab::Circle(CirclePrefab::default());
|
||||
} else if value == "Cylinder" {
|
||||
*variant = MeshPrimitivePrefab::Cylinder(CylinderPrefab::default());
|
||||
} else if value == "Icosphere" {
|
||||
*variant = MeshPrimitivePrefab::Icosphere(IcospherePrefab::default());
|
||||
} else if value == "Plane" {
|
||||
*variant = MeshPrimitivePrefab::Plane(PlanePrefab::default());
|
||||
} else if value == "RegularPolygon" {
|
||||
*variant = MeshPrimitivePrefab::RegularPolygon(RegularPolygonPrefab::default());
|
||||
} else if value == "Torus" {
|
||||
*variant = MeshPrimitivePrefab::Torus(TorusPrefab::default());
|
||||
}
|
||||
}
|
||||
)
|
||||
),
|
||||
..default()
|
||||
})
|
||||
],
|
||||
..default()
|
||||
}))
|
||||
},
|
||||
bevy::reflect::ReflectRef::Value(v) => {
|
||||
if let Some(s) = v.downcast_ref::<String>() {
|
||||
Some(Element::Component(Component {
|
||||
@@ -512,6 +662,7 @@ pub struct VComponent {
|
||||
pub children: Vec<VNode>,
|
||||
pub id: Option<String>,
|
||||
pub onchange: bool,
|
||||
pub onclick: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, PartialEq, Serialize, Deserialize)]
|
||||
|
||||
Reference in New Issue
Block a user