Proof-of-concept inspector mutations

This commit is contained in:
Danny McGee
2024-03-21 06:15:09 -04:00
parent 0c52ea6eec
commit 46d784c695
7 changed files with 145 additions and 47 deletions
@@ -24,6 +24,10 @@
}
}
*, *::before, *::after {
box-sizing: border-box;
}
[part^="icon-"] {
color: #FFF6;
}
@@ -46,6 +46,7 @@ export class TextFieldElement extends FormFieldBaseElement {
` : nothing}
<input
class="input"
part="input"
.type=${this.fieldType}
placeholder=${ifDefined(this.placeholder)}
.value=${this.value}
@@ -1,3 +1,4 @@
import * as studio from "@storyteller/studio";
import { bind } from "@storyteller/framework";
import { LitElement, PropertyValues, type TemplateResult, html, unsafeCSS } from "lit";
import { customElement, property } from "lit/decorators.js";
@@ -13,8 +14,20 @@ export class NameInspectorElement extends LitElement {
@property() value = "";
protected override updated(changes: PropertyValues<this>): 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);
}
@@ -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;
}
}
}
@@ -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<string>) => {
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`
<!-- TODO -->
<h4>Transform</h4>
<fieldset>
<div class="fieldset" role="group">
<legend>Translation</legend>
<input type="number" value=${this.translation.x.toFixed(3)} />
<input type="number" value=${this.translation.y.toFixed(3)} />
<input type="number" value=${this.translation.z.toFixed(3)} />
</fieldset>
<fieldset>
<sts-text-field
type="number"
.value=${this.translation.x.toFixed(3)}
@value-change=${this.updateField("translation.x")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.translation.y.toFixed(3)}
@value-change=${this.updateField("translation.y")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.translation.z.toFixed(3)}
@value-change=${this.updateField("translation.z")}
></sts-text-field>
</div>
<div class="fieldset" role="group">
<legend>Rotation</legend>
<input type="number" value=${this.rotation.x.toFixed(3)} />
<input type="number" value=${this.rotation.y.toFixed(3)} />
<input type="number" value=${this.rotation.z.toFixed(3)} />
<!-- TODO: This is not how quaternions work lol -->
<sts-text-field
type="number"
.value=${this.rotation.x.toFixed(3)}
@value-change=${this.updateField("rotation.x")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.rotation.y.toFixed(3)}
@value-change=${this.updateField("rotation.y")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.rotation.z.toFixed(3)}
@value-change=${this.updateField("rotation.z")}
></sts-text-field>
<!-- <input type="number" value=${this.rotation.w.toFixed(3)} /> -->
</fieldset>
<fieldset>
</div>
<div class="fieldset" role="group">
<legend>Scale</legend>
<input type="number" value=${this.scale.x.toFixed(3)} />
<input type="number" value=${this.scale.y.toFixed(3)} />
<input type="number" value=${this.scale.z.toFixed(3)} />
</fieldset>
<sts-text-field
type="number"
.value=${this.scale.x.toFixed(3)}
@value-change=${this.updateField("scale.x")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.scale.y.toFixed(3)}
@value-change=${this.updateField("scale.y")}
></sts-text-field>
<sts-text-field
type="number"
.value=${this.scale.z.toFixed(3)}
@value-change=${this.updateField("scale.z")}
></sts-text-field>
</div>
`;
}
@@ -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) : [];
}
+22 -15
View File
@@ -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<String, Value>,
pub components: HashMap<String, JsonValue>,
}
fn update_inspect_by_select_change<T: Component + Reflect + GetTypeRegistration>(
@@ -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<T> {
component: string;
reflectPath: string;
value: T;
}
export function updateComponentField<T>(params: UpdateField<T>): 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<UpdateField> = 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(())
}