diff --git a/studio-ui/src/lib/forms/text-field.element.scss b/studio-ui/src/lib/forms/text-field.element.scss index 0236a5a..cf29771 100644 --- a/studio-ui/src/lib/forms/text-field.element.scss +++ b/studio-ui/src/lib/forms/text-field.element.scss @@ -24,6 +24,10 @@ } } +*, *::before, *::after { + box-sizing: border-box; +} + [part^="icon-"] { color: #FFF6; } diff --git a/studio-ui/src/lib/forms/text-field.element.ts b/studio-ui/src/lib/forms/text-field.element.ts index 5df4053..e241c32 100644 --- a/studio-ui/src/lib/forms/text-field.element.ts +++ b/studio-ui/src/lib/forms/text-field.element.ts @@ -46,6 +46,7 @@ export class TextFieldElement extends FormFieldBaseElement { ` : nothing} ): void { - if (changes.has("value")) - console.log("New value:", this.value); + // FIXME: `Name` component needs special handling due to: + // 1. Its `hash` field, which doesn't get updated by just directly + // mutating the `name` field + // 2. Its use in our hierarchy. We'll need to add some logic to check + // for changed names on existing entities and notify the frontend so + // the hierarchy can reflect the new value. + + // if (changes.has("value")) { + // studio.updateComponentField({ + // component: "bevy_core::name::Name", + // reflectPath: "name", + // value: this.value, + // }); + // } super.updated(changes); } diff --git a/studio-web/src/lib/inspector/transform-inspector.element.scss b/studio-web/src/lib/inspector/transform-inspector.element.scss index c9afa4b..99a3a01 100644 --- a/studio-web/src/lib/inspector/transform-inspector.element.scss +++ b/studio-web/src/lib/inspector/transform-inspector.element.scss @@ -17,7 +17,17 @@ h4 { margin: 0 0 0.25rem; } -fieldset { +.fieldset { + position: relative; + padding-block-start: math.div(14, 16) + 0.333rem !important; + + legend { + position: absolute; + inset-block-start: 0; + } +} + +fieldset, .fieldset { display: flex; width: 100%; padding: 0; @@ -33,14 +43,10 @@ fieldset { input { appearance: textfield; - width: math.div(1, 3) * 100%; - padding: 0.5rem; border-radius: 0.25rem; background: #0004; - border: 1px solid #FFF2 { - left: 4px solid #FFF2; - } + border: 1px solid #FFF2; color: #FFFC; transition: { property: background-color, border-color, color; @@ -60,6 +66,29 @@ fieldset { color: #FFFF; } + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; + margin: 0; + } + } + + sts-text-field::part(input) { + flex: unset; + width: 100%; + + &::-webkit-outer-spin-button, + &::-webkit-inner-spin-button { + appearance: none; + margin: 0; + } + } + + input, sts-text-field { + padding: 0.5rem; + width: math.div(1, 3) * 100%; + border-left-width: 4px; + &:nth-of-type(1) { border-left-color: rgb(220, 53, 69); &:focus { @@ -78,11 +107,5 @@ fieldset { border-left-color: rgb(47, 131, 255); } } - - &::-webkit-outer-spin-button, - &::-webkit-inner-spin-button { - appearance: none; - margin: 0; - } } } diff --git a/studio-web/src/lib/inspector/transform-inspector.element.ts b/studio-web/src/lib/inspector/transform-inspector.element.ts index 006c441..5d66c53 100644 --- a/studio-web/src/lib/inspector/transform-inspector.element.ts +++ b/studio-web/src/lib/inspector/transform-inspector.element.ts @@ -1,3 +1,4 @@ +import * as studio from "@storyteller/studio" import { LitElement, html, unsafeCSS } from "lit"; import { customElement, property } from "lit/decorators.js"; import { type Quat, type Vec3 } from "./inspector.types"; @@ -17,27 +18,76 @@ export class TransformInspectorElement extends LitElement { @property({ attribute: false }) scale: Vec3 = { x: 1, y: 1, z: 1 }; + updateField(reflectPath: `${"translation"|"rotation"|"scale"}.${"x"|"y"|"z"}`) { + return ({ detail }: CustomEvent) => { + const value = parseFloat(detail); + if (Number.isFinite(value) && !Number.isNaN(value)) { + studio.updateComponentField({ + component: "bevy_transform::components::transform::Transform", + reflectPath, + value, + }); + } + } + } + protected override render = () => html` -

Transform

-
+
Translation - - - -
-
+ + + + +
Rotation - - - + + + + -
-
+ +
Scale - - - -
+ + + + `; } diff --git a/studio-web/src/lib/scene-data-provider.element.ts b/studio-web/src/lib/scene-data-provider.element.ts index ac1e1d8..1b5e034 100644 --- a/studio-web/src/lib/scene-data-provider.element.ts +++ b/studio-web/src/lib/scene-data-provider.element.ts @@ -6,7 +6,7 @@ import { type EntityMultiSpawnEvent, type EntitySelectEvent, type EntitySpawnEvent, - type InspectorChanged, + type InspectorChangedEvent, } from "@storyteller/studio"; import { TREE_SELECTION_PROVIDER, TreeSelectionProvider } from "@storyteller/studio-ui/tree"; import { match } from "@storyteller/utility"; @@ -109,7 +109,7 @@ export class SceneDataProviderElement } @on("window:inspector-changed") - onInspectorChanged({ detail: json }: InspectorChanged): void { + onInspectorChanged({ detail: json }: InspectorChangedEvent): void { this._components = json ? JSON.parse(json) : []; } diff --git a/studio/src/ui/inspector.rs b/studio/src/ui/inspector.rs index e9fa975..471c473 100644 --- a/studio/src/ui/inspector.rs +++ b/studio/src/ui/inspector.rs @@ -8,7 +8,7 @@ use bevy::{ window::PrimaryWindow, }; use serde::{Deserialize, Serialize}; -use serde_json::*; +use serde_json::{self, json, Map as JsonMap, Value as JsonValue}; use space_editor::{ space_editor_ui::{ editor_tab::{EditorTab, EditorTabName}, @@ -189,7 +189,7 @@ impl EditorTab for TestInspectorTab { 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), + value: JsonValue::from(0.0), }); } } @@ -316,7 +316,7 @@ fn update_field_by_ui(world: &mut World) { #[derive(Resource, Serialize, Deserialize, Default, Clone)] pub struct EntityDescription { - pub components: HashMap, + pub components: HashMap, } fn update_inspect_by_select_change( @@ -524,11 +524,11 @@ fn update_description( .components .iter() .map(|(name, v)| { - let mut value = Value::Object(Map::new()); + let mut value = JsonValue::Object(JsonMap::new()); value .as_object_mut() .unwrap() - .insert("name".to_string(), Value::String(name.to_string())); + .insert("name".to_string(), JsonValue::String(name.to_string())); value .as_object_mut() .unwrap() @@ -546,24 +546,33 @@ fn update_description( } #[derive(Deserialize, Serialize)] +#[serde(rename_all = "camelCase")] pub struct UpdateField { pub component: String, pub reflect_path: String, - pub value: serde_json::Value, + pub value: JsonValue, } #[wasm_bindgen(typescript_custom_section)] const EVENT_DECLARATION: &str = r#" -export interface InspectorChanged extends CustomEvent { +export interface InspectorChangedEvent extends CustomEvent { detail: string | null; } declare global { -export interface HTMLElementEventMap { - "inspector-changed": InspectorChanged; + export interface HTMLElementEventMap { + "inspector-changed": InspectorChangedEvent; + } } + +export interface UpdateField { + component: string; + reflectPath: string; + value: T; } + +export function updateComponentField(params: UpdateField): void; "#; pub(super) fn notify_changed(q_window: &Window, msg: String) { @@ -575,11 +584,9 @@ pub(super) fn notify_changed(q_window: &Window, msg: String) { 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; - }; +#[wasm_bindgen(js_name = updateComponentField, skip_typescript)] +pub fn update_component_field(msg: JsValue) -> Result<(), JsValue> { + let msg = serde_wasm_bindgen::from_value(msg)?; CHANGE_FROM_UI.write_message(msg); + Ok(()) }