Show a placed object's struct the way its asset does
The instance inspector drew its own, flatter version of a struct: one row per field, no bundle head. It now draws the same block as the asset side, because there is only one implementation left - both call buildStructBlock. What the instance does not get is everything that touches the shape: renaming a field, '+ Field', and the per-field '×'. The shape belongs to the asset; a placed object overrides values. The revert and override marks sit on the head rather than on a field, since an override covers the whole bundle at once. Underneath this, editing a single field of an unoverridden struct used to blank its siblings: structFieldsOf filled the untouched fields from spec.read, which is object.overrides[key] and therefore undefined until something is overridden. structOf then handed back the empty shape. PreviewedValue carries 'current' now - the resolved value the row actually shows - and the fields come from that. Without it the old path stays, which is right for the asset side, whose prop bag is dense.
This commit is contained in:
1 parent
14f542365e
commit
9f482573e5
9 files changed
+606
-207
No files matched your search
@@ -41,10 +41,18 @@ import { Button, TextField } from '../../ui';
|
||||
import { addCollider, createContainer, removeObjects } from '../actions';
|
||||
import { aspectActions, sizeCommand } from './aspectAction';
|
||||
import { defSignature } from './assetSection';
|
||||
import { createFieldRow, type FieldRow, labelledRow } from './fieldFactory';
|
||||
import { createFieldRow, type FieldRow, labelledRow, type RowActions } from './fieldFactory';
|
||||
import { addGap, addNote, addTitle, type InspectorHost } from './host';
|
||||
import type { ListSlot } from '../state';
|
||||
import { parentRow } from './parentRow';
|
||||
import { listOpener, type PreviewedValue, previewingHandlers, structFields } from './previewHandlers';
|
||||
import {
|
||||
listOpener,
|
||||
type PreviewedValue,
|
||||
previewingHandlers,
|
||||
structFields,
|
||||
structFieldsOf,
|
||||
} from './previewHandlers';
|
||||
import { buildStructBlock } from './structBlock';
|
||||
import { currentProps, currentRef, deviations, positionMoved, targetOf, viewOf } from './objectQueries';
|
||||
import { componentRow, OFFSET_AXES, offsetKey, POSITION_AXES } from './vectorRows';
|
||||
|
||||
@@ -309,85 +317,100 @@ function propertyRow(
|
||||
const overrideSpec: PreviewedValue<ObjectRef> = {
|
||||
target: () => currentRef(host.state, holderId, objectId),
|
||||
read: (ref) => ref.object.overrides[def.key],
|
||||
current: () => currentProps(host.state, holderId, objectId)?.[def.key],
|
||||
write: (ref, value) => ref.setOverride(def.key, value),
|
||||
remove: (ref) => ref.setOverride(def.key, undefined),
|
||||
commit: (value) => new SetOverrideCommand(holderId, objectId, def.key, value),
|
||||
};
|
||||
const row = createFieldRow(
|
||||
host,
|
||||
def,
|
||||
values[def.key] ?? def.default,
|
||||
{
|
||||
...previewingHandlers<ObjectRef>(host, overrideSpec),
|
||||
...listOpener(host.state, def, () => ({ kind: 'object', holderId, objectId, key: def.key })),
|
||||
...structFields<ObjectRef>(host, def, overrideSpec, (field) => ({
|
||||
kind: 'object',
|
||||
holderId,
|
||||
objectId,
|
||||
key: def.key,
|
||||
field,
|
||||
})),
|
||||
const fieldSlot = (field: string): ListSlot => ({
|
||||
kind: 'object',
|
||||
holderId,
|
||||
objectId,
|
||||
key: def.key,
|
||||
field,
|
||||
});
|
||||
const rowActions: RowActions = {
|
||||
extra: aspectActions(
|
||||
host,
|
||||
owner,
|
||||
def,
|
||||
() => currentProps(host.state, holderId, objectId),
|
||||
(values) =>
|
||||
host.state.execute(
|
||||
sizeCommand(values, (axis, value) => new SetOverrideCommand(holderId, objectId, axis, value)),
|
||||
),
|
||||
),
|
||||
reset: () => {
|
||||
const { template, asset } = bases();
|
||||
return [
|
||||
...(template
|
||||
? [
|
||||
{
|
||||
label: 'Reset to container',
|
||||
onSelect: () =>
|
||||
host.state.execute(new RevertOverrideToTemplateCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(asset
|
||||
? [
|
||||
{
|
||||
label: 'Reset to asset',
|
||||
onSelect: () => host.state.execute(new ClearOverrideCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
{
|
||||
extra: aspectActions(
|
||||
host,
|
||||
owner,
|
||||
def,
|
||||
() => currentProps(host.state, holderId, objectId),
|
||||
(values) =>
|
||||
host.state.execute(
|
||||
sizeCommand(values, (axis, value) => new SetOverrideCommand(holderId, objectId, axis, value)),
|
||||
),
|
||||
),
|
||||
reset: () => {
|
||||
const { template, asset } = bases();
|
||||
return [
|
||||
...(template
|
||||
? [
|
||||
{
|
||||
label: 'Reset to container',
|
||||
onSelect: () =>
|
||||
host.state.execute(new RevertOverrideToTemplateCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(asset
|
||||
? [
|
||||
{
|
||||
label: 'Reset to asset',
|
||||
onSelect: () => host.state.execute(new ClearOverrideCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
apply: () => {
|
||||
const { template, applyToAsset } = bases();
|
||||
return [
|
||||
...(template
|
||||
? [
|
||||
{
|
||||
label: 'Apply to container',
|
||||
onSelect: () =>
|
||||
host.state.execute(new ApplyOverrideToContainerCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(applyToAsset
|
||||
? [
|
||||
{
|
||||
label: 'Apply to asset',
|
||||
onSelect: () =>
|
||||
host.state.execute(new ApplyOverrideToAssetCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
apply: () => {
|
||||
const { template, applyToAsset } = bases();
|
||||
return [
|
||||
...(template
|
||||
? [
|
||||
{
|
||||
label: 'Apply to container',
|
||||
onSelect: () =>
|
||||
host.state.execute(new ApplyOverrideToContainerCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
...(applyToAsset
|
||||
? [
|
||||
{
|
||||
label: 'Apply to asset',
|
||||
onSelect: () =>
|
||||
host.state.execute(new ApplyOverrideToAssetCommand(holderId, objectId, def.key)),
|
||||
},
|
||||
]
|
||||
: []),
|
||||
];
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
// A struct is a block of rows, which only it can add to the body; the same one the asset shows,
|
||||
// minus the renaming and the field buttons – a placed object overrides values, not the shape.
|
||||
const row =
|
||||
def.kind === 'struct'
|
||||
? buildStructBlock(
|
||||
host,
|
||||
def,
|
||||
values[def.key] ?? def.default,
|
||||
structFieldsOf<ObjectRef>(host, def, overrideSpec, fieldSlot),
|
||||
{ actions: rowActions },
|
||||
)
|
||||
: createFieldRow(
|
||||
host,
|
||||
def,
|
||||
values[def.key] ?? def.default,
|
||||
{
|
||||
...previewingHandlers<ObjectRef>(host, overrideSpec),
|
||||
...listOpener(host.state, def, () => ({ kind: 'object', holderId, objectId, key: def.key })),
|
||||
...structFields<ObjectRef>(host, def, overrideSpec, fieldSlot),
|
||||
},
|
||||
rowActions,
|
||||
);
|
||||
host.fields.set(def.key, row);
|
||||
host.body.add(row.row);
|
||||
if (def.kind !== 'struct') host.body.add(row.row);
|
||||
return row;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user