Files
feuerwerk/src/editor/inspector/previewHandlers.ts
T

59 lines
2.5 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Command } from '../../core/commands/Command';
import type { PropertyValue } from '../../core/model/Properties';
import type { FieldHandlers } from './fieldFactory';
import type { InspectorHost } from './host';
/** Where an inspector row finds the value it edits, and what committing it means. */
export interface PreviewedValue<Target, Model = PropertyValue> {
/** The model object holding the value, or undefined when the selection is gone – then the row does nothing. */
target: () => Target | undefined;
/** The stored value; undefined means the key is not set on the target at all. */
read: (target: Target) => Model | undefined;
write: (target: Target, value: Model) => void;
/** Unsets the key again; needed wherever `read` can return undefined. */
remove?: (target: Target) => void;
/** The widget value as the model stores it (default: unchanged). Called on the target as it is at that moment. */
toModel?: (input: PropertyValue, target: Target) => Model;
commit: (value: Model) => Command;
}
/**
* Row handlers that preview into the model while editing and take that preview back before
* committing.
*
* Scrubbing has to be visible at once, so the value goes straight into the model and is only
* redrawn (`preview`), without an undo entry. A command records the value it finds at execute
* time, so committing right away would make the last previewed frame the undo target: the
* value from before the preview is written back first – including "the key did not exist",
* via `remove` – and only then is the command executed.
*/
export function previewingHandlers<Target, Model = PropertyValue>(host: InspectorHost, spec: PreviewedValue<Target, Model>): FieldHandlers {
const toModel = spec.toModel ?? ((input: PropertyValue) => input as unknown as Model);
let previewed = false;
let before: Model | undefined;
return {
onInput: (input) => {
const target = spec.target();
if (!target) return;
if (!previewed) {
previewed = true;
before = spec.read(target);
}
spec.write(target, toModel(input, target));
host.state.preview();
},
onChange: (input) => {
const target = spec.target();
if (!target) return;
if (previewed) {
if (before === undefined) spec.remove?.(target);
else spec.write(target, before);
}
previewed = false;
before = undefined;
// Built after the restore: `toModel` may fill the rest of a compound value from the target.
host.state.execute(spec.commit(toModel(input, target)));
},
};
}