diff --git a/src/data/actions.ts b/src/core/actions.ts similarity index 95% rename from src/data/actions.ts rename to src/core/actions.ts index f2a58b8..dceee31 100644 --- a/src/data/actions.ts +++ b/src/core/actions.ts @@ -9,7 +9,7 @@ export interface Action { state?: () => any; } -class ActionsClass { +export class Actions { private readonly actions: Record = {}; private readonly actionMap: Record = {}; @@ -55,5 +55,3 @@ class ActionsClass { this._holdingToggleAction = null; } } - -export const Actions = new ActionsClass(); diff --git a/src/core/editor.ts b/src/core/editor.ts new file mode 100644 index 0000000..d0f7c81 --- /dev/null +++ b/src/core/editor.ts @@ -0,0 +1,28 @@ +import { EditorData } from 'data/editor-data'; +import { InspectorData } from 'data/inspector-data'; +import { PhaserMeta } from 'data/phaser-meta'; +import { Actions } from './actions'; +import { History } from './history'; +import { Preferences } from './preferences'; + +class EditorClass { + public data: EditorData + public inspectorData: InspectorData; + public meta: PhaserMeta; + + public actions: Actions; + public history: History; + public prefs: Preferences; + + public init() { + this.data = new EditorData(); + this.inspectorData = new InspectorData(); + this.meta = new PhaserMeta(); + + this.actions = new Actions(); + this.history = new History(this.data); + this.prefs = new Preferences(); + } +} + +export const Editor = new EditorClass(); diff --git a/src/data/history.ts b/src/core/history.ts similarity index 76% rename from src/data/history.ts rename to src/core/history.ts index f9ec60b..334bbbe 100644 --- a/src/data/history.ts +++ b/src/core/history.ts @@ -1,4 +1,4 @@ -import { Data, DataOrigin } from './data'; +import { DataOrigin, EditorData } from '../data/editor-data'; export interface HistoryEntry { obj: PIXI.DisplayObject; @@ -7,15 +7,17 @@ export interface HistoryEntry { const HISTORY_LIMIT = 100; -class HistoryClass { +export class History { private readonly entries: HistoryEntry[] = []; private holdingEntry: HistoryEntry; // TODO change this name please public readonly onHistoryWalk = new Phaser.Signal(); - public holdEntry(entry: HistoryEntry) { - this.holdingEntry = entry; + constructor(private readonly data: EditorData) { } + + public prepare(obj: PIXI.DisplayObject, properties: { [id: string]: any }) { + const entry = this.holdingEntry = { obj, properties }; Object.keys(entry.properties).forEach(k => entry.properties[k] = JSON.stringify(entry.properties[k])); return this; @@ -38,7 +40,7 @@ class HistoryClass { } private apply(entry: HistoryEntry) { - Data.selectObject(entry.obj, DataOrigin.HISTORY); + this.data.selectObject(entry.obj, DataOrigin.HISTORY); const obj = entry.obj; // TODO make this recursive (if necessary) @@ -52,12 +54,10 @@ class HistoryClass { }); } else obj[pk] = prop; - Data.propertyChanged(pk, obj[pk], DataOrigin.HISTORY); + this.data.propertyChanged(pk, obj[pk], DataOrigin.HISTORY); }); obj.updateTransform(); this.onHistoryWalk.dispatch(entry); } } - -export const History = new HistoryClass(); diff --git a/src/data/preferences.ts b/src/core/preferences.ts similarity index 80% rename from src/data/preferences.ts rename to src/core/preferences.ts index 24bb104..dfa1e22 100644 --- a/src/data/preferences.ts +++ b/src/core/preferences.ts @@ -1,6 +1,6 @@ -export type PreferenceKey = keyof PreferencesClass; +export type PreferenceKey = keyof Preferences; -class PreferencesClass { +export class Preferences { private _snap = false; public get snap() { return this._snap; } @@ -25,5 +25,3 @@ class PreferencesClass { this.onPreferenceChanged.dispatch(field, value); } } - -export const Preferences = new PreferencesClass(); \ No newline at end of file diff --git a/src/data/data.ts b/src/data/editor-data.ts similarity index 96% rename from src/data/data.ts rename to src/data/editor-data.ts index 3dd5b9a..3aeeab3 100644 --- a/src/data/data.ts +++ b/src/data/editor-data.ts @@ -4,7 +4,7 @@ export enum DataOrigin { INSPECTOR = 2, }; -class DataClass { +export class EditorData { private _selectedObject: PIXI.DisplayObject; public get selectedObject() { return this._selectedObject; } @@ -49,5 +49,3 @@ class DataClass { }); } } - -export const Data = new DataClass(); \ No newline at end of file diff --git a/src/data/inspector-data.ts b/src/data/inspector-data.ts new file mode 100644 index 0000000..c4c14ae --- /dev/null +++ b/src/data/inspector-data.ts @@ -0,0 +1,49 @@ +export type InspectableTypes = 'string' | 'number' | 'boolean' | 'point' | 'default'; + +export interface InspectorPropertyModel { + name: string; + typeHint: InspectableTypes; + data?: any; + label?: string; +} + +export class InspectorData { + private readonly editors: Partial> = {}; + + public addEditors(editors: Partial>) { + Object.keys(editors).forEach(e => this.editors[e] = editors[e]); + } + + public readonly inspectableProperties: InspectorPropertyModel[] = []; + + public addInspectableProperties(properties: InspectorPropertyModel[]) { + const iproperties = this.inspectableProperties; + properties.forEach(prop => { + const i = iproperties.findIndex(e => e.name === prop.name); + if (i < 0) iproperties.push(prop); + else iproperties.splice(i, 1, prop); + }); + } + + public findEditorFor(value: any, data: InspectorPropertyModel) { + // TODO what abount null / undefined values? + + const editors = this.editors; + + // TODO this should never fail + // first, it tries to find based on the type hint + if (data.typeHint in editors) return editors[data.typeHint]; + + + // next, it tries to find by its type + const type = typeof value; + if (type !== 'object' && type in editors) + return editors[type]; + + // next, since it's an object, it tries to find by its contructor name + const ctor = value.contructor?.name; + if (ctor && ctor in editors) return editors[ctor]; + + return editors.default; + } +} diff --git a/src/data/phaser-data.ts b/src/data/phaser-data.ts deleted file mode 100644 index 6f16c44..0000000 --- a/src/data/phaser-data.ts +++ /dev/null @@ -1,44 +0,0 @@ -export interface PhaserObjectType { - name: string; - icon: string; -} - -class PhaserDataClass { - private readonly types: { [id: number]: PhaserObjectType } = { - [Phaser.BITMAPDATA]: { name: 'Phaser.BitmapData', icon: '' }, - [Phaser.BITMAPTEXT]: { name: 'Phaser.BitmapText', icon: 'fa-font' }, - [Phaser.BUTTON]: { name: 'Phaser.Button', icon: '' }, - [Phaser.CANVAS_FILTER]: { name: 'Phaser.CanvasFilter', icon: '' }, - [Phaser.CIRCLE]: { name: 'Phaser.Circle', icon: '' }, - [Phaser.ELLIPSE]: { name: 'Phaser.Ellipse', icon: '' }, - [Phaser.EMITTER]: { name: 'Phaser.Emitter', icon: '' }, - [Phaser.GRAPHICS]: { name: 'Phaser.Graphics', icon: 'fa-square' }, - [Phaser.GROUP]: { name: 'Phaser.Group', icon: 'fa-dice-d6' }, - [Phaser.IMAGE]: { name: 'Phaser.Image', icon: 'fa-image' }, - [Phaser.LINE]: { name: 'Phaser.Line', icon: '' }, - [Phaser.MATRIX]: { name: 'Phaser.Matrix', icon: '' }, - [Phaser.POINT]: { name: 'Phaser.Point', icon: '' }, - [Phaser.POINTER]: { name: 'Phaser.Pointer', icon: '' }, - [Phaser.POLYGON]: { name: 'Phaser.Polygon', icon: '' }, - [Phaser.RECTANGLE]: { name: 'Phaser.Rectangle', icon: '' }, - [Phaser.ROUNDEDRECTANGLE]: { name: 'Phaser.RoundedRectangle', icon: '' }, - [Phaser.RENDERTEXTURE]: { name: 'Phaser.RenderTexture', icon: '' }, - [Phaser.RETROFONT]: { name: 'Phaser.RetroFont', icon: 'fa-font' }, - [Phaser.SPRITE]: { name: 'Phaser.Sprite', icon: 'fa-image' }, - [Phaser.SPRITEBATCH]: { name: 'Phaser.SpriteBatch', icon: 'fa-images' }, - [Phaser.TEXT]: { name: 'Phaser.Text', icon: 'fa-font' }, - [Phaser.TILEMAP]: { name: 'Phaser.TileMap', icon: '' }, - [Phaser.TILEMAPLAYER]: { name: 'Phaser.TileMapPlayer', icon: '' }, - [Phaser.TILESPRITE]: { name: 'Phaser.TileSprite', icon: '' }, - [Phaser.WEBGL_FILTER]: { name: 'Phaser.WebGLFilter', icon: '' }, - [Phaser.ROPE]: { name: 'Phaser.Rope', icon: '' }, - [Phaser.CREATURE]: { name: 'Phaser.Creature', icon: '' }, - [Phaser.VIDEO]: { name: 'Phaser.Video', icon: '' }, - }; - - private readonly defaultType: PhaserObjectType = { name: 'default', icon: 'help_outline' }; - - public getType(t: number) { return t in this.types ? this.types[t] : this.defaultType; } -} - -export const PhaserData = new PhaserDataClass(); \ No newline at end of file diff --git a/src/data/phaser-meta.ts b/src/data/phaser-meta.ts new file mode 100644 index 0000000..1db5437 --- /dev/null +++ b/src/data/phaser-meta.ts @@ -0,0 +1,34 @@ +export interface PhaserObjectType { + name: string; + icon: string; +} + +export class PhaserMeta { + private readonly sceneObjects: Record = { + [Phaser.BITMAPTEXT]: { name: 'Phaser.BitmapText', icon: 'fa-font' }, + [Phaser.BUTTON]: { name: 'Phaser.Button', icon: '' }, + [Phaser.GRAPHICS]: { name: 'Phaser.Graphics', icon: 'fa-square' }, + [Phaser.GROUP]: { name: 'Phaser.Group', icon: 'fa-dice-d6' }, + [Phaser.IMAGE]: { name: 'Phaser.Image', icon: 'fa-image' }, + [Phaser.RETROFONT]: { name: 'Phaser.RetroFont', icon: 'fa-font' }, + [Phaser.SPRITE]: { name: 'Phaser.Sprite', icon: 'fa-image' }, + [Phaser.TEXT]: { name: 'Phaser.Text', icon: 'fa-font' }, + // [Phaser.EMITTER]: { name: 'Phaser.Emitter', icon: '' }, + // [Phaser.SPRITEBATCH]: { name: 'Phaser.SpriteBatch', icon: 'fa-images' }, + // [Phaser.TILEMAP]: { name: 'Phaser.TileMap', icon: '' }, + // [Phaser.TILEMAPLAYER]: { name: 'Phaser.TileMapPlayer', icon: '' }, + // [Phaser.TILESPRITE]: { name: 'Phaser.TileSprite', icon: '' }, + // [Phaser.WEBGL_FILTER]: { name: 'Phaser.WebGLFilter', icon: '' }, + // [Phaser.ROPE]: { name: 'Phaser.Rope', icon: '' }, + // [Phaser.CREATURE]: { name: 'Phaser.Creature', icon: '' }, + // [Phaser.VIDEO]: { name: 'Phaser.Video', icon: '' }, + }; + + public addSceneObjects(objects: Record) { + Object.keys(objects).forEach(k => this.sceneObjects[k] = objects[k]); + } + + private readonly defaultType: PhaserObjectType = { name: 'default', icon: 'fa-question' }; + + public getType(t: number) { return t in this.sceneObjects ? this.sceneObjects[t] : this.defaultType; } +} diff --git a/src/editor/_colors.scss b/src/editor-view/_colors.scss similarity index 100% rename from src/editor/_colors.scss rename to src/editor-view/_colors.scss diff --git a/src/editor/actions/actions-toolbar.scss b/src/editor-view/actions/actions-toolbar.scss similarity index 100% rename from src/editor/actions/actions-toolbar.scss rename to src/editor-view/actions/actions-toolbar.scss diff --git a/src/editor/actions/actions-toolbar.ts b/src/editor-view/actions/actions-toolbar.ts similarity index 79% rename from src/editor/actions/actions-toolbar.ts rename to src/editor-view/actions/actions-toolbar.ts index 2816869..b4d82c2 100644 --- a/src/editor/actions/actions-toolbar.ts +++ b/src/editor-view/actions/actions-toolbar.ts @@ -1,5 +1,5 @@ -import { Actions } from 'data/actions'; -import { Widget } from 'editor/widget/widget'; +import { Widget } from 'editor-view/widget/widget'; +import { Editor } from 'core/editor'; import './actions-toolbar.scss'; import { ActionButton } from './button/action-button'; @@ -14,7 +14,7 @@ export class ActionsToolbar extends Widget { private createButton(actionId: string) { const btn = document.createElement(ActionButton.tagName) as ActionButton; - btn.setAction(Actions.getAction(actionId)); + btn.setAction(Editor.actions.getAction(actionId)); this.appendChild(btn); } } diff --git a/src/editor/actions/button/action-button.scss b/src/editor-view/actions/button/action-button.scss similarity index 100% rename from src/editor/actions/button/action-button.scss rename to src/editor-view/actions/button/action-button.scss diff --git a/src/editor/actions/button/action-button.ts b/src/editor-view/actions/button/action-button.ts similarity index 100% rename from src/editor/actions/button/action-button.ts rename to src/editor-view/actions/button/action-button.ts diff --git a/src/editor/editor.scss b/src/editor-view/editor-view.scss similarity index 94% rename from src/editor/editor.scss rename to src/editor-view/editor-view.scss index ccafcef..fec2db4 100644 --- a/src/editor/editor.scss +++ b/src/editor-view/editor-view.scss @@ -1,4 +1,4 @@ -phred-editor { +phred-editor-view { flex: 1; display: flex; align-items: stretch; diff --git a/src/editor/editor.ts b/src/editor-view/editor-view.ts similarity index 87% rename from src/editor/editor.ts rename to src/editor-view/editor-view.ts index 07d5fc4..b7a675c 100644 --- a/src/editor/editor.ts +++ b/src/editor-view/editor-view.ts @@ -1,13 +1,14 @@ -import { Data, DataOrigin } from 'data/data'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; import { ActionsToolbar } from './actions/actions-toolbar'; -import './editor.scss'; +import './editor-view.scss'; import { ObjectTreeInspector } from './object-tree/inspector/object-tree-inspector'; import { Panel } from './panel/panel'; import { PropertiesInspector } from './properties/inspector/properties-inspector'; import { Widget } from './widget/widget'; -export class Editor extends Widget { - public static readonly tagName: string = 'phred-editor'; +export class EditorView extends Widget { + public static readonly tagName: string = 'phred-editor-view'; private _game: Phaser.Game; private actions: ActionsToolbar; @@ -17,7 +18,7 @@ export class Editor extends Widget { public connectedCallback() { super.connectedCallback(); - Data.onSelectedObjectChanged.add(this.selectObject, this); + Editor.data.onSelectedObjectChanged.add(this.selectObject, this); const script = document.createElement('script'); script.src = 'https://kit.fontawesome.com/7ba4e59e46.js'; @@ -72,4 +73,4 @@ export class Editor extends Widget { } } -customElements.define(Editor.tagName, Editor); +customElements.define(EditorView.tagName, EditorView); diff --git a/src/editor/inspector/inspector.scss b/src/editor-view/inspector/inspector.scss similarity index 100% rename from src/editor/inspector/inspector.scss rename to src/editor-view/inspector/inspector.scss diff --git a/src/editor/inspector/inspector.ts b/src/editor-view/inspector/inspector.ts similarity index 93% rename from src/editor/inspector/inspector.ts rename to src/editor-view/inspector/inspector.ts index 0c5a13e..b70a6e9 100644 --- a/src/editor/inspector/inspector.ts +++ b/src/editor-view/inspector/inspector.ts @@ -1,4 +1,4 @@ -import { Widget } from 'editor/widget/widget'; +import { Widget } from 'editor-view/widget/widget'; import './inspector.scss'; export abstract class Inspector extends Widget { diff --git a/src/editor/object-tree/inspector/object-tree-inspector.scss b/src/editor-view/object-tree/inspector/object-tree-inspector.scss similarity index 100% rename from src/editor/object-tree/inspector/object-tree-inspector.scss rename to src/editor-view/object-tree/inspector/object-tree-inspector.scss diff --git a/src/editor/object-tree/inspector/object-tree-inspector.ts b/src/editor-view/object-tree/inspector/object-tree-inspector.ts similarity index 92% rename from src/editor/object-tree/inspector/object-tree-inspector.ts rename to src/editor-view/object-tree/inspector/object-tree-inspector.ts index d4ad43b..585a62f 100644 --- a/src/editor/object-tree/inspector/object-tree-inspector.ts +++ b/src/editor-view/object-tree/inspector/object-tree-inspector.ts @@ -1,5 +1,6 @@ -import { Data, DataOrigin } from 'data/data'; -import { Inspector } from 'editor/inspector/inspector'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; +import { Inspector } from 'editor-view/inspector/inspector'; import { ObjectMapItemModel, ObjectTreeModel } from '../model/object-tree-model'; import { TreeNode } from '../tree-node/tree-node'; import './object-tree-inspector.scss'; @@ -11,7 +12,7 @@ export class ObjectTreeInspector extends Inspector { public connectedCallback() { super.connectedCallback(); this.title = 'Objects'; - Data.onPropertyChanged.add(this.onPropertyChanged, this); + Editor.data.onPropertyChanged.add(this.onPropertyChanged, this); } private onPropertyChanged(_: DataOrigin, property: string, value: any) { @@ -56,7 +57,7 @@ export class ObjectTreeInspector extends Inspector { if (node?.model === this._lastSelectedModel) return; if (this._lastSelectedModel) this._lastSelectedModel.node.clearSelection(); this._lastSelectedModel = node.model; - Data.selectObject(node.model.obj, DataOrigin.INSPECTOR); + Editor.data.selectObject(node.model.obj, DataOrigin.INSPECTOR); } private onNodeCollapseStateChanged(node: TreeNode, collapsed: boolean, all: boolean) { diff --git a/src/editor/object-tree/model/object-tree-model.ts b/src/editor-view/object-tree/model/object-tree-model.ts similarity index 89% rename from src/editor/object-tree/model/object-tree-model.ts rename to src/editor-view/object-tree/model/object-tree-model.ts index 8f77fb8..15b9520 100644 --- a/src/editor/object-tree/model/object-tree-model.ts +++ b/src/editor-view/object-tree/model/object-tree-model.ts @@ -1,4 +1,5 @@ -import { PhaserData, PhaserObjectType } from 'data/phaser-data'; +import { Editor } from 'core/editor'; +import { PhaserObjectType } from 'data/phaser-meta'; import { IdUtil } from 'util/id.util'; import { TreeNode } from '../tree-node/tree-node'; @@ -23,7 +24,7 @@ export class ObjectTreeModel { } private createNode(child: PIXI.DisplayObject, map: Record, parent: ObjectMapItemModel, level: number) { - const type = PhaserData.getType(child.type); + const type = Editor.meta.getType(child.type); child.__instanceId = IdUtil.genIntId(); child.__type = type.name; const isLeaf = !(child.children && child.children.length > 0); diff --git a/src/editor/object-tree/tree-node/tree-node.scss b/src/editor-view/object-tree/tree-node/tree-node.scss similarity index 100% rename from src/editor/object-tree/tree-node/tree-node.scss rename to src/editor-view/object-tree/tree-node/tree-node.scss diff --git a/src/editor/object-tree/tree-node/tree-node.ts b/src/editor-view/object-tree/tree-node/tree-node.ts similarity index 97% rename from src/editor/object-tree/tree-node/tree-node.ts rename to src/editor-view/object-tree/tree-node/tree-node.ts index 4a71916..a451adb 100644 --- a/src/editor/object-tree/tree-node/tree-node.ts +++ b/src/editor-view/object-tree/tree-node/tree-node.ts @@ -1,4 +1,4 @@ -import { PhaserObjectType } from 'data/phaser-data'; +import { PhaserObjectType } from 'data/phaser-meta'; import { ObjectMapItemModel } from '../model/object-tree-model'; import './tree-node.scss'; diff --git a/src/editor/panel/panel.scss b/src/editor-view/panel/panel.scss similarity index 100% rename from src/editor/panel/panel.scss rename to src/editor-view/panel/panel.scss diff --git a/src/editor/panel/panel.ts b/src/editor-view/panel/panel.ts similarity index 81% rename from src/editor/panel/panel.ts rename to src/editor-view/panel/panel.ts index f3138f4..8f0c091 100644 --- a/src/editor/panel/panel.ts +++ b/src/editor-view/panel/panel.ts @@ -1,5 +1,5 @@ -import { Inspector } from 'editor/inspector/inspector'; -import { Widget } from 'editor/widget/widget'; +import { Inspector } from 'editor-view/inspector/inspector'; +import { Widget } from 'editor-view/widget/widget'; import './panel.scss'; export class Panel extends Widget { diff --git a/src/editor/properties/editors/boolean/boolean-property-editor.ts b/src/editor-view/properties/editors/boolean/boolean-property-editor.ts similarity index 89% rename from src/editor/properties/editors/boolean/boolean-property-editor.ts rename to src/editor-view/properties/editors/boolean/boolean-property-editor.ts index 44c0761..8541a07 100644 --- a/src/editor/properties/editors/boolean/boolean-property-editor.ts +++ b/src/editor-view/properties/editors/boolean/boolean-property-editor.ts @@ -1,4 +1,4 @@ -import { PropertyInspectionData } from 'editor/properties-editors'; +import { InspectorPropertyModel } from 'data/inspector-data'; import { PropertyEditor } from '../property-editor'; export class BooleanPropertyEditor extends PropertyEditor { @@ -6,7 +6,7 @@ export class BooleanPropertyEditor extends PropertyEditor { private input: HTMLInputElement; - protected createInnerContent(fieldId: string, _value: boolean, prop: PropertyInspectionData) { + protected createInnerContent(fieldId: string, _value: boolean, prop: InspectorPropertyModel) { const input = this.input = document.createElement('input'); input.id = fieldId; input.setAttribute('type', 'checkbox'); diff --git a/src/editor/properties/editors/number/number-property-editor.ts b/src/editor-view/properties/editors/number/number-property-editor.ts similarity index 90% rename from src/editor/properties/editors/number/number-property-editor.ts rename to src/editor-view/properties/editors/number/number-property-editor.ts index 59c65e0..8200057 100644 --- a/src/editor/properties/editors/number/number-property-editor.ts +++ b/src/editor-view/properties/editors/number/number-property-editor.ts @@ -1,4 +1,4 @@ -import { PropertyInspectionData } from 'editor/properties-editors'; +import { InspectorPropertyModel } from 'data/inspector-data'; import { PropertyEditor } from '../property-editor'; export class NumberPropertyEditor extends PropertyEditor { @@ -6,7 +6,7 @@ export class NumberPropertyEditor extends PropertyEditor { private input: HTMLInputElement; - protected createInnerContent(fieldId: string, _value: number, prop: PropertyInspectionData) { + protected createInnerContent(fieldId: string, _value: number, prop: InspectorPropertyModel) { const input = this.input = document.createElement('input'); input.id = fieldId; input.setAttribute('type', 'number'); diff --git a/src/editor/properties/editors/point/point-property-editor.scss b/src/editor-view/properties/editors/point/point-property-editor.scss similarity index 100% rename from src/editor/properties/editors/point/point-property-editor.scss rename to src/editor-view/properties/editors/point/point-property-editor.scss diff --git a/src/editor/properties/editors/point/point-property-editor.ts b/src/editor-view/properties/editors/point/point-property-editor.ts similarity index 95% rename from src/editor/properties/editors/point/point-property-editor.ts rename to src/editor-view/properties/editors/point/point-property-editor.ts index f61f50c..6d4877d 100644 --- a/src/editor/properties/editors/point/point-property-editor.ts +++ b/src/editor-view/properties/editors/point/point-property-editor.ts @@ -1,9 +1,8 @@ -import { PropertyInspectionData } from 'editor/properties-editors'; +import { InspectorPropertyModel } from 'data/inspector-data'; import { NumberPropertyEditor } from '../number/number-property-editor'; import { PropertyEditor } from '../property-editor'; import './point-property-editor.scss'; - export class PointPropertyEditor extends PropertyEditor { public static readonly tagName: string = 'phed-point-property-editor'; @@ -16,7 +15,7 @@ export class PointPropertyEditor extends PropertyEditor { this.classList.add('has-children'); } - protected createInnerContent(fieldId: string, value: PIXI.Point, prop: PropertyInspectionData) { + protected createInnerContent(fieldId: string, value: PIXI.Point, prop: InspectorPropertyModel) { const parent = this.appendChild(document.createElement('div')); const xinput = this.xinput = document.createElement(NumberPropertyEditor.tagName) as NumberPropertyEditor; diff --git a/src/editor/properties/editors/property-editor.scss b/src/editor-view/properties/editors/property-editor.scss similarity index 100% rename from src/editor/properties/editors/property-editor.scss rename to src/editor-view/properties/editors/property-editor.scss diff --git a/src/editor/properties/editors/property-editor.ts b/src/editor-view/properties/editors/property-editor.ts similarity index 71% rename from src/editor/properties/editors/property-editor.ts rename to src/editor-view/properties/editors/property-editor.ts index 9b680a0..9d74882 100644 --- a/src/editor/properties/editors/property-editor.ts +++ b/src/editor-view/properties/editors/property-editor.ts @@ -1,11 +1,11 @@ -import { Data, DataOrigin } from 'data/data'; -import { History } from 'data/history'; -import { PropertyInspectionData } from 'editor/properties-editors'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; +import { InspectorPropertyModel } from 'data/inspector-data'; import { IdUtil } from 'util/id.util'; import './property-editor.scss'; export abstract class PropertyEditor extends HTMLElement { - protected prop: PropertyInspectionData; + protected prop: InspectorPropertyModel; public changedOutsideInspector = false; protected _internalValue: T; @@ -14,7 +14,7 @@ export abstract class PropertyEditor extends HTMLElement { this.classList.add('property-editor'); } - public setContent(prop: PropertyInspectionData, value: T, fieldId?: string) { + public setContent(prop: InspectorPropertyModel, value: T, fieldId?: string) { fieldId = fieldId ?? `${prop.name}-${IdUtil.genHexId()}`; this.prop = prop; @@ -28,14 +28,14 @@ export abstract class PropertyEditor extends HTMLElement { this.onchange = this.onValueChanged.bind(this); } - protected createLabel(fieldId: string, prop: PropertyInspectionData): HTMLElement { + protected createLabel(fieldId: string, prop: InspectorPropertyModel): HTMLElement { const label = document.createElement('label'); label.append(prop.label ?? prop.name); label.setAttribute('for', fieldId); return label; } - protected createContent(value: T, fieldId: string, prop: PropertyInspectionData) { + protected createContent(value: T, fieldId: string, prop: InspectorPropertyModel) { const propContent = document.createElement('div'); propContent.classList.add('property-content', prop.name); @@ -45,7 +45,7 @@ export abstract class PropertyEditor extends HTMLElement { return propContent; } - protected abstract createInnerContent(fieldId: string, value: T, prop: PropertyInspectionData): HTMLElement; + protected abstract createInnerContent(fieldId: string, value: T, prop: InspectorPropertyModel): HTMLElement; public propertyChangedOutsideInspector(value: T) { this.changedOutsideInspector = true; @@ -59,7 +59,7 @@ export abstract class PropertyEditor extends HTMLElement { } if (save) this.savePreviousValue(); this.updateInternalValue(e); - Data.propertyChanged(this.prop.name, this.getInternalValue(), DataOrigin.INSPECTOR); + Editor.data.propertyChanged(this.prop.name, this.getInternalValue(), DataOrigin.INSPECTOR); } public getInternalValue(): T { return this._internalValue; } @@ -67,11 +67,8 @@ export abstract class PropertyEditor extends HTMLElement { public abstract updateInternalValue(e: Event): void; public savePreviousValue() { - History.holdEntry({ - obj: Data.selectedObject, - properties: { - [this.prop.name]: this.getInternalValue() - } + Editor.history.prepare(Editor.data.selectedObject, { + [this.prop.name]: this.getInternalValue() }).commit(); } } diff --git a/src/editor/properties/editors/string/string-property-editor.ts b/src/editor-view/properties/editors/string/string-property-editor.ts similarity index 89% rename from src/editor/properties/editors/string/string-property-editor.ts rename to src/editor-view/properties/editors/string/string-property-editor.ts index 11d4223..aa3b8b5 100644 --- a/src/editor/properties/editors/string/string-property-editor.ts +++ b/src/editor-view/properties/editors/string/string-property-editor.ts @@ -1,11 +1,11 @@ -import { PropertyInspectionData } from 'editor/properties-editors'; +import { InspectorPropertyModel } from 'data/inspector-data'; import { PropertyEditor } from '../property-editor'; export class StringPropertyEditor extends PropertyEditor { public static readonly tagName: string = 'phed-string-property-editor'; private input: HTMLInputElement; - protected createInnerContent(fieldId: string, _value: string, prop: PropertyInspectionData) { + protected createInnerContent(fieldId: string, _value: string, prop: InspectorPropertyModel) { const input = this.input = document.createElement('input'); input.id = fieldId; input.setAttribute('type', 'text'); diff --git a/src/editor/properties/inspector/properties-inspector.scss b/src/editor-view/properties/inspector/properties-inspector.scss similarity index 100% rename from src/editor/properties/inspector/properties-inspector.scss rename to src/editor-view/properties/inspector/properties-inspector.scss diff --git a/src/editor/properties/inspector/properties-inspector.ts b/src/editor-view/properties/inspector/properties-inspector.ts similarity index 74% rename from src/editor/properties/inspector/properties-inspector.ts rename to src/editor-view/properties/inspector/properties-inspector.ts index fc2213e..4825a3b 100644 --- a/src/editor/properties/inspector/properties-inspector.ts +++ b/src/editor-view/properties/inspector/properties-inspector.ts @@ -1,6 +1,7 @@ -import { Data, DataOrigin } from 'data/data'; -import { Inspector } from 'editor/inspector/inspector'; -import { PropertiesEditors, PropertyInspectionData } from 'editor/properties-editors'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; +import { InspectorPropertyModel } from 'data/inspector-data'; +import { Inspector } from 'editor-view/inspector/inspector'; import { PropertyEditor } from '../editors/property-editor'; import './properties-inspector.scss'; @@ -11,7 +12,7 @@ export class PropertiesInspector extends Inspector { public connectedCallback() { super.connectedCallback(); this.title = 'Properties'; - Data.onPropertyChanged.add(this.onPropertyChanged, this); + Editor.data.onPropertyChanged.add(this.onPropertyChanged, this); } private onPropertyChanged(origin: DataOrigin, property: string, value: any) { @@ -19,7 +20,7 @@ export class PropertiesInspector extends Inspector { this.editors[property]?.propertyChangedOutsideInspector(value); } - private createPropertyEditor(prop: PropertyInspectionData, value: any, tagName: string) { + private createPropertyEditor(prop: InspectorPropertyModel, value: any, tagName: string) { const editor = this.content.appendChild(document.createElement(tagName)) as PropertyEditor; editor.setContent(prop, value); return editor; @@ -39,14 +40,14 @@ export class PropertiesInspector extends Inspector { this.content.style.visibility = 'visible'; - PropertiesEditors.inspectableProperties + Editor.inspectorData.inspectableProperties .forEach(prop => { if (!(prop.name in obj)) return; - const elementId = PropertiesEditors.findEditorFor(obj[prop.name], prop); + const elementId = Editor.inspectorData.findEditorFor(obj[prop.name], prop); const editor = this.createPropertyEditor(prop, obj[prop.name], elementId); this.editors[prop.name] = editor; }); } } -customElements.define(PropertiesInspector.tagName, PropertiesInspector); \ No newline at end of file +customElements.define(PropertiesInspector.tagName, PropertiesInspector); diff --git a/src/editor/widget/widget.scss b/src/editor-view/widget/widget.scss similarity index 100% rename from src/editor/widget/widget.scss rename to src/editor-view/widget/widget.scss diff --git a/src/editor/widget/widget.ts b/src/editor-view/widget/widget.ts similarity index 100% rename from src/editor/widget/widget.ts rename to src/editor-view/widget/widget.ts diff --git a/src/editor/properties-editors.ts b/src/editor/properties-editors.ts deleted file mode 100644 index e8baf59..0000000 --- a/src/editor/properties-editors.ts +++ /dev/null @@ -1,66 +0,0 @@ -import { BooleanPropertyEditor } from './properties/editors/boolean/boolean-property-editor'; -import { NumberPropertyEditor } from './properties/editors/number/number-property-editor'; -import { PointPropertyEditor } from './properties/editors/point/point-property-editor'; -import { StringPropertyEditor } from './properties/editors/string/string-property-editor'; - -export type InspectableTypes = 'string' | 'number' | 'boolean' | 'point' | 'default'; - -export interface PropertyInspectionData { - name: string; - typeHint: InspectableTypes; - data?: any; - label?: string; -} - - -// TODO this name is awful. Please find a better one -class PropertiesEditorsClass { - private readonly editors: Record = { - // basic types - string: StringPropertyEditor.tagName, - number: NumberPropertyEditor.tagName, - boolean: BooleanPropertyEditor.tagName, - - // PIXI/Phaser types - point: PointPropertyEditor.tagName, - - // default - default: StringPropertyEditor.tagName, - }; - - public readonly inspectableProperties: PropertyInspectionData[] = [ - { name: '__type', label: 'type', typeHint: 'string', data: { readonly: true } }, - { name: 'name', typeHint: 'string' }, - { name: 'position', typeHint: 'point' }, - { name: 'scale', typeHint: 'point', data: { step: 0.1 } }, - { name: 'pivot', typeHint: 'point' }, - { name: 'anchor', typeHint: 'point', data: { step: 0.1 } }, - { name: 'alpha', typeHint: 'number', data: { min: 0, max: 1, step: 0.1 } }, - { name: 'visible', typeHint: 'boolean' }, - { name: 'angle', typeHint: 'number', data: { readonly: true } }, - ]; - - public findEditorFor(value: any, data: PropertyInspectionData) { - // TODO what abount null / undefined values? - - const editors = this.editors; - - // TODO this should never fail - // first, it tries to find based on the type hint - if (data.typeHint in editors) return editors[data.typeHint]; - - - // next, it tries to find by its type - const type = typeof value; - if (type !== 'object' && type in editors) - return editors[type]; - - // next, since it's an object, it tries to find by its contructor name - const ctor = value.contructor?.name; - if (ctor && ctor in editors) return editors[ctor]; - - return editors.default; - } -} - -export const PropertiesEditors = new PropertiesEditorsClass(); \ No newline at end of file diff --git a/src/plugin.ts b/src/plugin.ts index e8b8401..18b203d 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -1,41 +1,73 @@ -import { Actions } from 'data/actions'; -import { Data } from 'data/data'; -import { History } from 'data/history'; -import { Preferences } from 'data/preferences'; +import { BooleanPropertyEditor } from 'editor-view/properties/editors/boolean/boolean-property-editor'; +import { NumberPropertyEditor } from 'editor-view/properties/editors/number/number-property-editor'; +import { PointPropertyEditor } from 'editor-view/properties/editors/point/point-property-editor'; +import { StringPropertyEditor } from 'editor-view/properties/editors/string/string-property-editor'; import Phaser from 'phaser-ce'; -import { Editor } from './editor/editor'; +import { Editor } from './core/editor'; +import { EditorView } from './editor-view/editor-view'; import './plugin.scss'; -import { SceneEditor } from './scene/scene-editor'; +import { SceneView } from './scene-view/scene-view'; export class Plugin extends Phaser.Plugin { public constructor(game: Phaser.Game, group?: Phaser.Group | Phaser.Stage) { super(game, game.plugins); group = group ?? game.world; - const scene = new SceneEditor(game, group, game.stage); + Editor.init(); + + const scene = new SceneView(game, group, game.stage); this.setupActions(scene); - - const editor = document.createElement(Editor.tagName) as Editor; - document.body.appendChild(editor); - + this.setupInspectorData(); + + const editorView = document.createElement(EditorView.tagName) as EditorView; + document.body.appendChild(editorView); + const update = this.update; this.update = () => { if (group.children.length === 0) return; this.update = update; - editor.setup(game, group); + editorView.setup(game, group); } - - Actions.setContainer('#phred-game-container'); + + Editor.actions.setContainer('#phred-game-container'); + } + + private setupInspectorData() { + Editor.inspectorData.addEditors({ + // basic types + string: StringPropertyEditor.tagName, + number: NumberPropertyEditor.tagName, + boolean: BooleanPropertyEditor.tagName, + + // PIXI/Phaser types + point: PointPropertyEditor.tagName, + + // default + default: StringPropertyEditor.tagName, + }); + + Editor.inspectorData.addInspectableProperties([ + { name: '__type', label: 'type', typeHint: 'string', data: { readonly: true } }, + { name: 'name', typeHint: 'string' }, + { name: 'position', typeHint: 'point' }, + { name: 'scale', typeHint: 'point', data: { step: 0.1 } }, + { name: 'pivot', typeHint: 'point' }, + { name: 'anchor', typeHint: 'point', data: { step: 0.1 } }, + { name: 'alpha', typeHint: 'number', data: { min: 0, max: 1, step: 0.1 } }, + { name: 'visible', typeHint: 'boolean' }, + { name: 'angle', typeHint: 'number', data: { readonly: true } }, + ]); } - private setupActions(scene: SceneEditor) { - Actions.add( + private setupActions(scene: SceneView) { + const { history, prefs } = Editor; + Editor.actions.add( { id: 'UNDO', label: 'undo', icon: 'fa-undo-alt', shortcut: 'ctrl+z', - command: History.undo.bind(History) + command: history.undo.bind(history) }, { id: 'MOVE_UP_1', @@ -82,21 +114,21 @@ export class Plugin extends Phaser.Plugin { label: 'snap', icon: 'fa-compress', toggle: true, - command: () => Preferences.snap = !Preferences.snap, - state: () => Preferences.snap, + command: () => prefs.snap = !prefs.snap, + state: () => prefs.snap, }, { id: 'TOGGLE_GIZMOS', toggle: true, hold: true, shortcut: 'shift+Shift', - command: () => Preferences.gizmos = !Preferences.gizmos, - state: () => Preferences.gizmos, + command: () => prefs.gizmos = !prefs.gizmos, + state: () => prefs.gizmos, }, ); } public postUpdate() { - Data.dispatchScheduledEvents(); + Editor.data.dispatchScheduledEvents(); } } diff --git a/src/scene/scene-colors.ts b/src/scene-view/scene-colors.ts similarity index 100% rename from src/scene/scene-colors.ts rename to src/scene-view/scene-colors.ts diff --git a/src/scene/scene-model.ts b/src/scene-view/scene-model.ts similarity index 98% rename from src/scene/scene-model.ts rename to src/scene-view/scene-model.ts index 5095cda..e491ead 100644 --- a/src/scene/scene-model.ts +++ b/src/scene-view/scene-model.ts @@ -1,4 +1,4 @@ -export class SceneMovel { +export class SceneModel { private _lastSelectionTree: PIXI.DisplayObject[]; private _lastSelectionTreeIndex = -1; diff --git a/src/scene/scene-editor.ts b/src/scene-view/scene-view.ts similarity index 82% rename from src/scene/scene-editor.ts rename to src/scene-view/scene-view.ts index 9a74de8..a14992d 100644 --- a/src/scene/scene-editor.ts +++ b/src/scene-view/scene-view.ts @@ -1,14 +1,14 @@ -import { Data, DataOrigin } from 'data/data'; -import { History, HistoryEntry } from 'data/history'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; import { DragUtil } from '../util/drag.util'; -import { SceneMovel } from './scene-model'; +import { SceneModel } from './scene-model'; import { Selection } from './selection/selection'; -export class SceneEditor extends Phaser.Group { +export class SceneView extends Phaser.Group { private readonly touchArea: Phaser.Graphics; private readonly container: Phaser.Group | Phaser.Stage; private readonly selection: Selection; - private readonly model: SceneMovel; + private readonly model: SceneModel; /** Whether the mouse down has already selected an object */ private _hasSelected: boolean; @@ -30,7 +30,7 @@ export class SceneEditor extends Phaser.Group { game.stage.__skip = true; game.world.__skip = true; - this.model = new SceneMovel(); + this.model = new SceneModel(); this.container = container; this.touchArea = this.createTouchArea(game); @@ -39,8 +39,8 @@ export class SceneEditor extends Phaser.Group { this.selection = new Selection(game); this.addChild(this.selection); - Data.onPropertyChanged.add(this.onPropertyChanged.bind(this)); - Data.onSelectedObjectChanged.add(this.onObjectSelected.bind(this)); + Editor.data.onPropertyChanged.add(this.onPropertyChanged.bind(this)); + Editor.data.onSelectedObjectChanged.add(this.onObjectSelected.bind(this)); } private onPropertyChanged(origin: DataOrigin, property: string, value: any, obj: PIXI.DisplayObject) { @@ -77,13 +77,10 @@ export class SceneEditor extends Phaser.Group { && this.trySelectOver(pointer); this._lastPos.set(pointer.x, pointer.y); - const obj = Data.selectedObject + const obj = Editor.data.selectedObject if (!obj) return; - History.holdEntry({ - obj: Data.selectedObject, - properties: { position: obj.position.clone() }, - }); + Editor.history.prepare(Editor.data.selectedObject, { position: obj.position.clone() }); } private onInputUp(_: any, pointer: Phaser.Pointer) { @@ -91,10 +88,10 @@ export class SceneEditor extends Phaser.Group { const wasDragging = this._isDragging; this._isDragging = false; if (wasDragging) { - History.commit(); + Editor.history.commit(); return; } - History.cancel(); + Editor.history.cancel(); if (this._hasSelected) return; this.trySelectOver(pointer); this._hasSelected = false; @@ -111,7 +108,7 @@ export class SceneEditor extends Phaser.Group { private selectObject(obj: PIXI.DisplayObject, dispatch: boolean) { this.selection.select(obj); - if (dispatch) Data.selectObject(obj, DataOrigin.SCENE); + if (dispatch) Editor.data.selectObject(obj, DataOrigin.SCENE); } private getObjectsUnderPoint(x: number, y: number, children: PIXI.DisplayObject[], objects: PIXI.DisplayObject[]) { @@ -125,13 +122,13 @@ export class SceneEditor extends Phaser.Group { } public moveSelectedObject(deltaX: number, deltaY: number) { - const obj = Data.selectedObject; + const obj = Editor.data.selectedObject; if (!obj) return; - History.holdEntry({ obj, properties: { position: obj.position.clone() } }).commit(); + Editor.history.prepare(obj, { position: obj.position.clone() }).commit(); obj.position.set(obj.position.x + deltaX, obj.position.y + deltaY); obj.updateTransform(); this.selection.redraw(); - Data.propertyChanged('position', obj.position.clone(), DataOrigin.SCENE); + Editor.data.propertyChanged('position', obj.position.clone(), DataOrigin.SCENE); } public update() { diff --git a/src/scene/selection/rotation/rotation.gizmo.ts b/src/scene-view/selection/rotation/rotation.gizmo.ts similarity index 100% rename from src/scene/selection/rotation/rotation.gizmo.ts rename to src/scene-view/selection/rotation/rotation.gizmo.ts diff --git a/src/scene/selection/rotation/rotation.handler.ts b/src/scene-view/selection/rotation/rotation.handler.ts similarity index 100% rename from src/scene/selection/rotation/rotation.handler.ts rename to src/scene-view/selection/rotation/rotation.handler.ts diff --git a/src/scene/selection/scale/scale.gizmo.ts b/src/scene-view/selection/scale/scale.gizmo.ts similarity index 100% rename from src/scene/selection/scale/scale.gizmo.ts rename to src/scene-view/selection/scale/scale.gizmo.ts diff --git a/src/scene/selection/scale/scale.handler.ts b/src/scene-view/selection/scale/scale.handler.ts similarity index 84% rename from src/scene/selection/scale/scale.handler.ts rename to src/scene-view/selection/scale/scale.handler.ts index 6688825..0a8a121 100644 --- a/src/scene/selection/scale/scale.handler.ts +++ b/src/scene-view/selection/scale/scale.handler.ts @@ -1,5 +1,5 @@ -import { Data, DataOrigin } from 'data/data'; -import { History } from 'data/history'; +import { Editor } from 'core/editor'; +import { DataOrigin } from 'data/editor-data'; import { ScaleGizmo } from './scale.gizmo'; import { Scaler } from './scaler'; @@ -55,13 +55,10 @@ export class ScaleHandler extends Phaser.Group { } private startScaling(gizmos: ScaleGizmo) { - const obj = Data.selectedObject; - History.holdEntry({ - obj, - properties: { - scale: obj.scale.clone(), - position: obj.position.clone(), - }, + const obj = Editor.data.selectedObject; + Editor.history.prepare(obj, { + scale: obj.scale.clone(), + position: obj.position.clone(), }); this.scaler.startScaling(this.selectedObject, gizmos.factorH, gizmos.factorV); @@ -71,7 +68,7 @@ export class ScaleHandler extends Phaser.Group { private stopScaling() { this._scaling = false; this.scaler.stopScaling(); - History.commit(); + Editor.history.commit(); } public handle() { @@ -80,8 +77,8 @@ export class ScaleHandler extends Phaser.Group { this.scaler.scaleToPoint(pointer.x, pointer.y); const pos = this.scaler.getObjectStopPosition(); - Data.propertyChanged('scale', this.selectedObject.scale, DataOrigin.SCENE); - Data.propertyChanged('position', pos, DataOrigin.SCENE); + Editor.data.propertyChanged('scale', this.selectedObject.scale, DataOrigin.SCENE); + Editor.data.propertyChanged('position', pos, DataOrigin.SCENE); return true; } diff --git a/src/scene/selection/scale/scaler.ts b/src/scene-view/selection/scale/scaler.ts similarity index 100% rename from src/scene/selection/scale/scaler.ts rename to src/scene-view/selection/scale/scaler.ts diff --git a/src/scene/selection/selection.ts b/src/scene-view/selection/selection.ts similarity index 89% rename from src/scene/selection/selection.ts rename to src/scene-view/selection/selection.ts index 132584a..83e2ce9 100644 --- a/src/scene/selection/selection.ts +++ b/src/scene-view/selection/selection.ts @@ -1,5 +1,6 @@ -import { Data, DataOrigin } from 'data/data'; -import { PreferenceKey, Preferences } from 'data/preferences'; +import { Editor } from 'core/editor'; +import { PreferenceKey } from 'core/preferences'; +import { DataOrigin } from 'data/editor-data'; import { PointUtil } from 'util/math.util'; import { ANCHOR_COLOR, ANCHOR_STROKE, BORDER_COLOR, BORDER_STROKE, PIVOT_COLOR, PIVOT_STROKE } from '../scene-colors'; import { ScaleHandler } from './scale/scale.handler'; @@ -22,9 +23,10 @@ export class Selection extends Phaser.Group { this.scaleHandler = new ScaleHandler(game); this.addChild(this.scaleHandler); - Preferences.onPreferenceChanged.add(this.onPreferencesChanged, this); - this.alpha = Preferences.gizmos ? 1 : 0; - this.moveFn = Preferences.snap ? this.snapMove : this.freeMove; + const prefs = Editor.prefs; + prefs.onPreferenceChanged.add(this.onPreferencesChanged, this); + this.alpha = prefs.gizmos ? 1 : 0; + this.moveFn = prefs.snap ? this.snapMove : this.freeMove; this.select(null); } @@ -100,7 +102,7 @@ export class Selection extends Phaser.Group { const scale = this._selectedObject.parent?.worldScale ?? PointUtil.one; this.moveFn(pos, scale, deltaX, deltaY); this.redraw(); - Data.propertyChanged('position', pos, DataOrigin.SCENE); + Editor.data.propertyChanged('position', pos, DataOrigin.SCENE); } private moveFn: (pos: PIXI.Point, scale: PIXI.Point, deltaX: number, deltaY: number) => void; diff --git a/tsconfig.json b/tsconfig.json index bdae64b..a80bd9a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -15,8 +15,11 @@ "forceConsistentCasingInFileNames": true, "baseUrl": "src", "paths": { - "scene/*": ["scene/*"], - "editor/*": ["editor/*"], + "core/*": ["core/*"], + "data/*": ["data/*"], + "scene-view/*": ["scene-view/*"], + "editor-view/*": ["editor-view/*"], + "util/*": ["util/*"] } }, "include": ["src"],