diff --git a/studio/src/ui/inspector.rs b/studio/src/ui/inspector.rs index e1851ab..61ed1ff 100644 --- a/studio/src/ui/inspector.rs +++ b/studio/src/ui/inspector.rs @@ -1,4 +1,4 @@ -use std::marker::PhantomData; +use std::{marker::PhantomData, sync::Arc}; use bevy::{ prelude::*, @@ -8,10 +8,19 @@ use bevy::{ }; use serde::{Deserialize, Serialize}; use serde_json::*; -use space_editor::space_prefab::component::MeshPrimitivePrefab; +use space_editor::{ + space_editor_ui::{ + editor_tab::{EditorTab, EditorTabName}, + EditorUiAppExt, + }, + space_prefab::component::MeshPrimitivePrefab, +}; use wasm_bindgen::prelude::*; -use crate::{interaction::Selected, wasm}; +use crate::{ + interaction::Selected, + wasm::{self, StaticBuffer, WasmMessageBuffer}, +}; pub struct InspectorPlugin; @@ -23,6 +32,7 @@ impl Plugin for InspectorPlugin { app.configure_sets( Update, ( + InspectorSystem::WriteChanges, InspectorSystem::DetectChanges, InspectorSystem::PerComponentCollect, InspectorSystem::CollectChanges, @@ -35,12 +45,32 @@ impl Plugin for InspectorPlugin { update_description.in_set(InspectorSystem::CollectChanges), ); + app.add_systems( + Update, + update_field_by_ui.in_set(InspectorSystem::WriteChanges), + ); + + app.editor_tab_by_trait( + EditorTabName::Other("Inspector tester".to_string()), + TestInspectorTab::default(), + ); + + app.init_resource::(); + app.register_inspect::(); app.register_inspect::(); app.register_inspect::(); } } +#[derive(Resource, Default)] +struct InspectGetters { + getters: HashMap< + String, + Arc, + >, +} + trait AppInspectable { fn register_inspect(&mut self); } @@ -62,11 +92,51 @@ impl AppInspectable for App { Update, auto_serialize_system::.in_set(InspectorSystem::PerComponentCollect), ); + + { + let mut getters = self.world.get_resource_mut::().unwrap(); + getters.getters.insert( + T::get_type_registration() + .type_info() + .type_path() + .to_string(), + Arc::new(|entity, path, f| { + let mut component = entity.get_mut::().unwrap(); + let reflect_field = component.reflect_path_mut(path).unwrap(); + (f)(reflect_field); + }), + ); + } + } +} + +#[derive(Default, Resource)] +pub struct TestInspectorTab {} + +impl EditorTab for TestInspectorTab { + fn ui( + &mut self, + ui: &mut bevy_inspector_egui::egui::Ui, + _commands: &mut Commands, + _world: &mut World, + ) { + if ui.button("Test set transform x to zero").clicked() { + CHANGE_FROM_UI.write_message(UpdateField { + component: "bevy_transform::components::transform::Transform".to_string(), + reflect_path: ".translation.x".to_string(), + value: serde_json::Value::from(0.0), + }); + } + } + + fn title(&self) -> bevy_inspector_egui::egui::WidgetText { + "Inspector tester".into() } } #[derive(SystemSet, Clone, Copy, PartialEq, Eq, Hash, Debug)] pub enum InspectorSystem { + WriteChanges, DetectChanges, PerComponentCollect, CollectChanges, @@ -88,6 +158,94 @@ impl Default for UpdateInspect { } } +fn update_field_by_ui(world: &mut World) { + unsafe { + let unsafe_world = world.as_unsafe_world_cell(); + let Some(msg) = CHANGE_FROM_UI.take_message() else { + return; + }; + + let mut selected_query = unsafe_world + .world_mut() + .query_filtered::>(); + let Ok(entity) = selected_query.get_single(unsafe_world.world_mut()) else { + return; + }; + + let getters = unsafe_world.get_resource_mut::().unwrap(); + + let mut entity_mut = unsafe_world.world_mut().entity_mut(entity); + + let Some(getter) = getters.getters.get(&msg.component) else { + error!("Failed to find getter for component: {}", msg.component); + return; + }; + + getter.as_ref()(&mut entity_mut, &msg.reflect_path, &|reflect_field| { + if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + *val = msg.value.as_str().unwrap().to_string(); + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else if let Some(val) = reflect_field.downcast_mut::() { + let Ok(msg_value) = serde_json::from_value(msg.value.clone()) else { + error!("Failed to parse value: {}", msg.value); + return; + }; + *val = msg_value; + } else { + error!("Unsupported type {}!", &msg.reflect_path); + return; + } + }); + } +} + #[derive(Resource, Serialize, Deserialize, Default, Clone)] pub struct EntityDescription { pub components: HashMap, @@ -194,6 +352,13 @@ fn update_description( } } +#[derive(Deserialize, Serialize)] +pub struct UpdateField { + pub component: String, + pub reflect_path: String, + pub value: serde_json::Value, +} + #[wasm_bindgen(typescript_custom_section)] const EVENT_DECLARATION: &str = r#" @@ -214,3 +379,14 @@ pub(super) fn notify_changed(q_window: &Window, msg: String) { error!("Failed to dispatch inspector-changed: {}", e); } } + +static CHANGE_FROM_UI: WasmMessageBuffer = WasmMessageBuffer::new(); + +#[wasm_bindgen(js_name = changeValue)] +pub fn change_value(msg: JsValue) { + let Ok(msg) = serde_wasm_bindgen::from_value(msg) else { + error!("Failed to parse message"); + return; + }; + CHANGE_FROM_UI.write_message(msg); +}