diff --git a/.vscode/settings.json b/.vscode/settings.json index ff45a1acc..02578b575 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,3 @@ { - "workbench.colorTheme": "Solarized Dark" + "workbench.colorTheme": "Nord" } \ No newline at end of file diff --git a/CHANGELOG.MD b/CHANGELOG.MD index b3b0b9f04..c04a752cd 100644 --- a/CHANGELOG.MD +++ b/CHANGELOG.MD @@ -1,5 +1,28 @@ # Change Log +## v3.62.0, Jun 29, 2023 + +* New Event user property. +* [#284](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/284) Fixes hit area rendering for containers. +* A new Add Prefab Property command that shows a dialog. +* Replaces the Object Depth commands for the Edit move commands. +* Replaces the Object List sort commands by the Editor move commands. +* Allows change prefab properties with the Edit move commands. Remove the Move options from the Prefab Properties section's menu. +* Allows copy/cut/paste of prefab properties. +* Allows copy/cut/paste keyboard keys. +* Shows Keyboard.Key and Object List objects in the Object Variable user property's dialog.. +* Adds the new KeyCode User Property. +* Fixes hit area serialization. +* Removes the User Components section and shows user components properties as individual sections: + * Renames the Scripts context menu to Scripting and include: + - Add User Component (`M` key) + - Browse User Components (`Shift+M` key) +* Removes user component nodes from the Outline view. +* Removes the Prefab Instance section, shows the prefab instance user properties as individual sections. +* The Replace Texture Frame command responds to the `F` key. +* Fixes adding a script node to all selected game objects. +[#223](https://github.com/PhaserEditor2D/PhaserEditor2D-v3/issues/222) Fixed tab-focus on the DOM elements of the user properties. + ## v3.61.0 - May 18, 2023 * Checks if a scene file was generated by a newer and incompatible version of the editor. diff --git a/design/logo-icons/gumroad-logo.png b/design/logo-icons/gumroad-logo.png new file mode 100644 index 000000000..aa4b4555c Binary files /dev/null and b/design/logo-icons/gumroad-logo.png differ diff --git a/scripts/make-events-files.js b/scripts/make-events-files.js new file mode 100644 index 000000000..6d474a256 --- /dev/null +++ b/scripts/make-events-files.js @@ -0,0 +1,39 @@ + +const fs = require("fs"); +const process = require("process") + +const phaser_path = process.env.PHASER_PATH; +const phaser_json_path = phaser_path + "/phaser3-docs/json/phaser.json"; + +const content = fs.readFileSync(phaser_json_path); + +const data = JSON.parse(content.toString()); + +const docsMap = {}; + +let i = 1; + +for (const item of data.docs) { + + if (item.kind !== "event") { + + continue; + } + + let { name, memberof } = item; + + const fullName = `${memberof}.${name}`; + + docsMap[fullName] = item.description || item.classdesc; + + console.log(`${i++} Processing event fullName`); +} + +const output = JSON.stringify(docsMap, null, 2); + +console.log("---"); +console.log("Writing to file events.json..."); + +fs.writeFileSync("../source/editor/plugins/phasereditor2d.scene/data/events-docs.json", output); + +console.log("Done."); \ No newline at end of file diff --git a/source/editor/plugins/colibri/src/ui/controls/Control.ts b/source/editor/plugins/colibri/src/ui/controls/Control.ts index 2586ba1b8..9d676fe13 100644 --- a/source/editor/plugins/colibri/src/ui/controls/Control.ts +++ b/source/editor/plugins/colibri/src/ui/controls/Control.ts @@ -212,16 +212,33 @@ namespace colibri.ui.controls { } add(control: Control): void { + control._container = this; + this._children.push(control); + this._element.appendChild(control.getElement()); + control.onControlAdded(); } + remove(control: Control) { + + control.getElement().remove(); + + this._children = this._children.filter(c => c !== control); + + control.onControlRemoved(); + } + protected onControlAdded() { // nothing } + protected onControlRemoved() { + // nothing + } + getChildren() { return this._children; } diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts b/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts index beac02617..39e915126 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/FormBuilder.ts @@ -47,7 +47,10 @@ namespace colibri.ui.controls.properties { btn.addEventListener("click", e => callback(e)); - parent.appendChild(btn); + if (parent) { + + parent.appendChild(btn); + } return btn; } diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts index 830865ff4..4bdf7b37b 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertyPage.ts @@ -1,151 +1,5 @@ namespace colibri.ui.controls.properties { - class PropertySectionPane extends Control { - - private _section: PropertySection; - private _titleArea: HTMLDivElement; - private _formArea: HTMLDivElement; - private _page: PropertyPage; - private _menuIcon: IconControl; - private _expandIconControl: IconControl; - - constructor(page: PropertyPage, section: PropertySection) { - super(); - - this._page = page; - - this._section = section; - - this.addClass("PropertySectionPane"); - } - - createSection() { - - if (!this._formArea) { - - this._titleArea = document.createElement("div"); - this._titleArea.classList.add("PropertyTitleArea"); - this._titleArea.addEventListener("click", () => this.toggleSection()); - - this._expandIconControl = new IconControl(colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_CONTROL_TREE_COLLAPSE)); - - this._expandIconControl.getCanvas().classList.add("expanded"); - - this._expandIconControl.getCanvas().addEventListener("click", e => { - - e.stopImmediatePropagation(); - - this.toggleSection() - }); - - this._titleArea.appendChild(this._expandIconControl.getCanvas()); - - const label = document.createElement("label"); - label.innerText = this._section.getTitle(); - this._titleArea.appendChild(label); - - this._menuIcon = new IconControl(ColibriPlugin.getInstance().getIcon(ICON_SMALL_MENU)); - this._menuIcon.getCanvas().classList.add("IconButton"); - this._menuIcon.getCanvas().style.visibility = this._section.hasMenu() ? "visible" : "hidden"; - this._menuIcon.getCanvas().addEventListener("click", e => { - - e.stopPropagation(); - e.stopImmediatePropagation(); - - if (this._section.hasMenu()) { - - const menu = new Menu(); - this._section.createMenu(menu); - menu.createWithEvent(e); - } - }); - this._titleArea.appendChild(this._menuIcon.getCanvas()); - - this._formArea = document.createElement("div"); - this._formArea.classList.add("PropertyFormArea"); - this._section.create(this._formArea); - - this.getElement().appendChild(this._titleArea); - this.getElement().appendChild(this._formArea); - - this.updateExpandIcon(); - - let collapsed = this.getCollapsedStateInStorage(); - - if (collapsed === undefined) { - - this.setCollapsedStateInStorage(this._section.isCollapsedByDefault()); - - collapsed = this.getCollapsedStateInStorage(); - } - - if (collapsed === "true") { - - this.toggleSection(); - } - } - } - - private getCollapsedStateInStorage() { - - return window.localStorage[this.getLocalStorageKey() + ".collapsed"]; - } - - private setCollapsedStateInStorage(collapsed: boolean) { - - return window.localStorage[this.getLocalStorageKey() + ".collapsed"] = collapsed ? "true" : "false"; - } - - private getLocalStorageKey() { - - return `colibri.ui.controls.properties.PropertySection[${this._section.getId()}]`; - } - - - isExpanded() { - return this._expandIconControl.getCanvas().classList.contains("expanded"); - } - - private toggleSection(): void { - - if (this.isExpanded()) { - - this._formArea.style.display = "none"; - this._expandIconControl.getCanvas().classList.remove("expanded"); - - } else { - - this._formArea.style.display = "grid"; - this._expandIconControl.getCanvas().classList.add("expanded"); - } - - this._page.updateExpandStatus(); - - this.getContainer().dispatchLayoutEvent(); - - this.updateExpandIcon(); - - this.setCollapsedStateInStorage(!this.isExpanded()); - } - - private updateExpandIcon() { - - const icon = this.isExpanded() ? colibri.ICON_CONTROL_SECTION_COLLAPSE : colibri.ICON_CONTROL_SECTION_EXPAND; - - const image = ColibriPlugin.getInstance().getIcon(icon); - - this._expandIconControl.setIcon(image); - } - - getSection() { - return this._section; - } - - getFormArea() { - return this._formArea; - } - } - export class PropertyPage extends Control { private _sectionProvider: PropertySectionProvider; private _sectionPanes: PropertySectionPane[]; @@ -186,6 +40,12 @@ namespace colibri.ui.controls.properties { const pane = new PropertySectionPane(this, section); + if (section.getTypeHash()) { + + this.removePanesWithSameTypeHash(section.getTypeHash()); + } + + console.log("PropertyPage: create pane for", section.getTitle(), section.getId()); this.add(pane); this._sectionPaneMap.set(section.getId(), pane); @@ -197,7 +57,9 @@ namespace colibri.ui.controls.properties { const sectionIdList = list.map(section => section.getId()); for (const pane of this._sectionPanes) { + const index = sectionIdList.indexOf(pane.getSection().getId()); + pane.getElement().style.order = index.toString(); } @@ -206,12 +68,29 @@ namespace colibri.ui.controls.properties { } else { for (const pane of this._sectionPanes) { - + pane.getElement().style.display = "none"; } } } + private removePanesWithSameTypeHash(typeHash: string) { + + for (const pane of this._sectionPanes) { + + const section = pane.getSection(); + + if (section.getTypeHash() === typeHash) { + + console.log("PropertyPage: remove dynamic pane", section.getTitle(), section.getId()); + this.remove(pane); + } + } + + this._sectionPanes = this._sectionPanes + .filter(pane => pane.getSection().getTypeHash() !== typeHash); + } + public updateWithSelection(): void { if (!this._sectionProvider) { @@ -288,6 +167,11 @@ namespace colibri.ui.controls.properties { pane.createSection(); section.updateWithSelection(); + if (section.isDynamicTitle()) { + + pane.updateTitle(); + } + } else { pane.getElement().style.display = "none"; @@ -357,6 +241,7 @@ namespace colibri.ui.controls.properties { } getSectionProvider() { + return this._sectionProvider; } } diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts index e33bc015e..f68f49bd8 100644 --- a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySection.ts @@ -11,8 +11,18 @@ namespace colibri.ui.controls.properties { private _updaters: Updater[]; private _fillSpace: boolean; private _collapsedByDefault: boolean; + private _icon: controls.IImage; + private _typeHash: string; + + constructor( + page: PropertyPage, + id: string, + title: string, + fillSpace = false, + collapsedByDefault = false, + icon?: controls.IImage, + typeHash?: string) { - constructor(page: PropertyPage, id: string, title: string, fillSpace = false, collapsedByDefault = false, tabSectionByDefault?: string) { super(); this._page = page; @@ -20,12 +30,15 @@ namespace colibri.ui.controls.properties { this._title = title; this._fillSpace = fillSpace; this._collapsedByDefault = collapsedByDefault; + this._icon = icon; + this._typeHash = typeHash; + this._updaters = []; const localTabSection = localStorage.getItem(this.localStorageKey("tabSection")); } - abstract createForm(parent: HTMLDivElement); + abstract createForm(parent: HTMLDivElement): void; abstract canEdit(obj: any, n: number): boolean; @@ -59,6 +72,7 @@ namespace colibri.ui.controls.properties { } addUpdater(updater: Updater) { + this._updaters.push(updater); } @@ -72,6 +86,7 @@ namespace colibri.ui.controls.properties { } getPage() { + return this._page; } @@ -86,13 +101,30 @@ namespace colibri.ui.controls.properties { } getId() { + return this._id; } getTitle() { + return this._title; } + isDynamicTitle() { + + return false; + } + + getIcon() { + + return this._icon; + } + + getTypeHash() { + + return this._typeHash; + } + create(parent: HTMLDivElement): void { this.createForm(parent); } diff --git a/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionPane.ts b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionPane.ts new file mode 100644 index 000000000..89c75cb44 --- /dev/null +++ b/source/editor/plugins/colibri/src/ui/controls/properties/PropertySectionPane.ts @@ -0,0 +1,173 @@ +namespace colibri.ui.controls.properties { + + export class PropertySectionPane extends Control { + + private _section: PropertySection; + private _titleArea: HTMLDivElement; + private _formArea: HTMLDivElement; + private _page: PropertyPage; + private _menuIcon: IconControl; + private _expandIconControl: IconControl; + private _titleLabel: HTMLLabelElement; + + constructor(page: PropertyPage, section: PropertySection) { + super(); + + this._page = page; + + this._section = section; + + this.addClass("PropertySectionPane"); + + const hashType = section.getTypeHash(); + + if (hashType) { + + this.getElement().setAttribute("type-hash", section.getTypeHash()); + } + } + + createSection() { + + if (!this._formArea) { + + this._titleArea = document.createElement("div"); + this._titleArea.classList.add("PropertyTitleArea"); + this._titleArea.addEventListener("click", () => this.toggleSection()); + + this._expandIconControl = new IconControl(colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_CONTROL_TREE_COLLAPSE)); + + this._expandIconControl.getCanvas().classList.add("expanded"); + + this._expandIconControl.getCanvas().addEventListener("click", e => { + + e.stopImmediatePropagation(); + + this.toggleSection() + }); + + this._titleArea.appendChild(this._expandIconControl.getCanvas()); + + const icon = this._section.getIcon(); + + if (icon) { + + const iconControl = new IconControl(icon); + iconControl.getCanvas().classList.add("PropertySectionIcon"); + this._titleArea.appendChild(iconControl.getCanvas()); + this._titleArea.classList.add("PropertyTitleAreaWithIcon"); + } + + this._titleLabel = document.createElement("label"); + this._titleLabel.classList.add("TitleLabel"); + this.updateTitle(); + this._titleArea.appendChild(this._titleLabel); + + this._menuIcon = new IconControl(ColibriPlugin.getInstance().getIcon(ICON_SMALL_MENU)); + this._menuIcon.getCanvas().classList.add("IconButton"); + this._menuIcon.getCanvas().style.visibility = this._section.hasMenu() ? "visible" : "hidden"; + this._menuIcon.getCanvas().addEventListener("click", e => { + + e.stopPropagation(); + e.stopImmediatePropagation(); + + if (this._section.hasMenu()) { + + const menu = new Menu(); + this._section.createMenu(menu); + menu.createWithEvent(e); + } + }); + this._titleArea.appendChild(this._menuIcon.getCanvas()); + + this._formArea = document.createElement("div"); + this._formArea.classList.add("PropertyFormArea"); + this._section.create(this._formArea); + + this.getElement().appendChild(this._titleArea); + this.getElement().appendChild(this._formArea); + + this.updateExpandIcon(); + + let collapsed = this.getCollapsedStateInStorage(); + + if (collapsed === undefined) { + + this.setCollapsedStateInStorage(this._section.isCollapsedByDefault()); + + collapsed = this.getCollapsedStateInStorage(); + } + + if (collapsed === "true") { + + this.toggleSection(); + } + } + } + + private getCollapsedStateInStorage() { + + return window.localStorage[this.getLocalStorageKey() + ".collapsed"]; + } + + private setCollapsedStateInStorage(collapsed: boolean) { + + return window.localStorage[this.getLocalStorageKey() + ".collapsed"] = collapsed ? "true" : "false"; + } + + private getLocalStorageKey() { + + return `colibri.ui.controls.properties.PropertySection[${this._section.getId()}]`; + } + + + isExpanded() { + + return this._expandIconControl.getCanvas().classList.contains("expanded"); + } + + private toggleSection(): void { + + if (this.isExpanded()) { + + this._formArea.style.display = "none"; + this._expandIconControl.getCanvas().classList.remove("expanded"); + + } else { + + this._formArea.style.display = "grid"; + this._expandIconControl.getCanvas().classList.add("expanded"); + } + + this._page.updateExpandStatus(); + + this.getContainer().dispatchLayoutEvent(); + + this.updateExpandIcon(); + + this.setCollapsedStateInStorage(!this.isExpanded()); + } + + updateTitle() { + + if (this._titleLabel) { + + this._titleLabel.innerHTML = this._section.getTitle(); + } + } + + private updateExpandIcon() { + + const icon = this.isExpanded() ? colibri.ICON_CONTROL_SECTION_COLLAPSE : colibri.ICON_CONTROL_SECTION_EXPAND; + + const image = ColibriPlugin.getInstance().getIcon(icon); + + this._expandIconControl.setIcon(image); + } + + getSection() { + + return this._section; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRenderer.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRenderer.ts index 8f86eb2e7..b87d664d9 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRenderer.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRenderer.ts @@ -6,6 +6,7 @@ namespace colibri.ui.controls.viewers { private _variableSize: boolean; constructor(variableSize: boolean = true) { + this._variableSize = variableSize; } diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRendererProvider.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRendererProvider.ts index dc8fb7623..96a125324 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRendererProvider.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/EmptyCellRendererProvider.ts @@ -10,6 +10,7 @@ namespace colibri.ui.controls.viewers { } constructor(getRenderer?: (element: any) => ICellRenderer) { + this._getRenderer = getRenderer ?? ((e) => new EmptyCellRenderer()); } @@ -18,6 +19,7 @@ namespace colibri.ui.controls.viewers { } preload(obj: any): Promise { + return Controls.resolveNothingLoaded(); } } diff --git a/source/editor/plugins/colibri/src/ui/controls/viewers/LabelProvider.ts b/source/editor/plugins/colibri/src/ui/controls/viewers/LabelProvider.ts index 5608a7575..d63279e21 100644 --- a/source/editor/plugins/colibri/src/ui/controls/viewers/LabelProvider.ts +++ b/source/editor/plugins/colibri/src/ui/controls/viewers/LabelProvider.ts @@ -16,6 +16,7 @@ namespace colibri.ui.controls.viewers { } if (typeof (obj) === "string") { + return obj; } diff --git a/source/editor/plugins/colibri/src/ui/ide/Part.ts b/source/editor/plugins/colibri/src/ui/ide/Part.ts index c755f98f5..8172b4c0f 100644 --- a/source/editor/plugins/colibri/src/ui/ide/Part.ts +++ b/source/editor/plugins/colibri/src/ui/ide/Part.ts @@ -90,6 +90,7 @@ namespace colibri.ui.ide { } getSelection() { + return this._selection; } diff --git a/source/editor/plugins/colibri/src/ui/ide/utils/NameMaker.ts b/source/editor/plugins/colibri/src/ui/ide/utils/NameMaker.ts index 2b4440813..747fd76af 100644 --- a/source/editor/plugins/colibri/src/ui/ide/utils/NameMaker.ts +++ b/source/editor/plugins/colibri/src/ui/ide/utils/NameMaker.ts @@ -22,16 +22,16 @@ namespace colibri.ui.ide.utils { } } - trimNumbering(name: string) { + static trimNumbering(name: string) { - return name.replace(/[0-9 _-]+$/, "") + return name.replace(/[0-9 _-]+$/, ""); } makeName(baseName: string) { if (this._nameSet.has(baseName)) { - baseName = this.trimNumbering(baseName); + baseName = NameMaker.trimNumbering(baseName); let name: string; diff --git a/source/editor/plugins/colibri/styles/controls.css b/source/editor/plugins/colibri/styles/controls.css index 212403c83..49b37d97a 100644 --- a/source/editor/plugins/colibri/styles/controls.css +++ b/source/editor/plugins/colibri/styles/controls.css @@ -328,6 +328,14 @@ padding: 5px; } + .PropertyTitleAreaWithIcon { + grid-template-columns: 20px 20px 1fr auto !important; + } + + .PropertyTitleArea .PropertySectionIcon { + margin-right: 5px; + } + .PropertyTitleArea .IconControlCanvas { align-self: center; justify-self: center; diff --git a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts index d2ba894aa..6aef27099 100644 --- a/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts +++ b/source/editor/plugins/phasereditor2d.ide/src/IDEPlugin.ts @@ -265,6 +265,7 @@ namespace phasereditor2d.ide { } isOpeningProject() { + return this._openingProject; } @@ -322,7 +323,7 @@ namespace phasereditor2d.ide { /* program entry point */ - export const VER = "3.61.0"; + export const VER = "3.62.0"; async function main() { diff --git a/source/editor/plugins/phasereditor2d.ide/src/core/PhaserDocs.ts b/source/editor/plugins/phasereditor2d.ide/src/core/PhaserDocs.ts index b729e6448..e8f16bdae 100644 --- a/source/editor/plugins/phasereditor2d.ide/src/core/PhaserDocs.ts +++ b/source/editor/plugins/phasereditor2d.ide/src/core/PhaserDocs.ts @@ -4,43 +4,64 @@ namespace phasereditor2d.ide.core { private _data: any = null; private _plugin: colibri.Plugin; - private _filePath: string; + private _files: string[]; - constructor(plugin: colibri.Plugin, filePath: string) { + constructor(plugin: colibri.Plugin, ...files: string[]) { this._plugin = plugin; - this._filePath = filePath; + this._files = files; + } + + static markdownToHtml(txt: string) { + + const converter = new showdown.Converter(); + + return converter.makeHtml(txt); } async preload() { if (!this._data) { - console.log("Loading jsdoc " + this._plugin.getId() + ": " + this._filePath); + this._data = {}; + + for (const file of this._files) { + + console.log("Loading jsdoc " + this._plugin.getId() + ": " + file); - this._data = await this._plugin.getJSON(this._filePath); + const fileData = await this._plugin.getJSON(file); - const converter = new showdown.Converter(); + const converter = new showdown.Converter(); - // tslint:disable-next-line:forin - for (const k in this._data) { + // tslint:disable-next-line:forin + for (const k in fileData) { - const help = this._data[k]; + const help = fileData[k]; - this._data[k] = converter.makeHtml(help); + this._data[k] = converter.makeHtml(help); + } } } } - getDoc(helpKey: string): string { + getDoc(helpKey: string, wrap = true): string { if (helpKey in this._data) { - return `${helpKey}

${this._data[helpKey]}
`; + if (wrap) { + + return `${helpKey}

${this._data[helpKey]}
`; + } + + return this._data[helpKey]; } return "Help not found for: " + helpKey; } + getKeys() { + + return Object.keys(this._data); + } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/data/events-docs.json b/source/editor/plugins/phasereditor2d.scene/data/events-docs.json new file mode 100644 index 000000000..0698bad4a --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/data/events-docs.json @@ -0,0 +1,250 @@ +{ + "Phaser.Animations.Events.ADD_ANIMATION": "The Add Animation Event.\n\nThis event is dispatched when a new animation is added to the global Animation Manager.\n\nThis can happen either as a result of an animation instance being added to the Animation Manager,\nor the Animation Manager creating a new animation directly.", + "Phaser.Animations.Events.ANIMATION_COMPLETE": "The Animation Complete Event.\n\nThis event is dispatched by a Sprite when an animation playing on it completes playback.\nThis happens when the animation gets to the end of its sequence, factoring in any delays\nor repeats it may have to process.\n\nAn animation that is set to loop, or repeat forever, will never fire this event, because\nit never actually completes. If you need to handle this, listen for the `ANIMATION_STOP`\nevent instead, as this is emitted when the animation is stopped directly.\n\nListen for it on the Sprite using `sprite.on('animationcomplete', listener)`\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_COMPLETE_KEY": "The Animation Complete Dynamic Key Event.\n\nThis event is dispatched by a Sprite when an animation playing on it completes playback.\nThis happens when the animation gets to the end of its sequence, factoring in any delays\nor repeats it may have to process.\n\nAn animation that is set to loop, or repeat forever, will never fire this event, because\nit never actually completes. If you need to handle this, listen for the `ANIMATION_STOP`\nevent instead, as this is emitted when the animation is stopped directly.\n\nThe difference between this and the `ANIMATION_COMPLETE` event is that this one has a\ndynamic event name that contains the name of the animation within it. For example,\nif you had an animation called `explode` you could listen for the completion of that\nspecific animation by using: `sprite.on('animationcomplete-explode', listener)`. Or, if you\nwish to use types: `sprite.on(Phaser.Animations.Events.ANIMATION_COMPLETE_KEY + 'explode', listener)`.\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_REPEAT": "The Animation Repeat Event.\n\nThis event is dispatched by a Sprite when an animation repeats playing on it.\nThis happens if the animation was created, or played, with a `repeat` value specified.\n\nAn animation will repeat when it reaches the end of its sequence.\n\nListen for it on the Sprite using `sprite.on('animationrepeat', listener)`\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_RESTART": "The Animation Restart Event.\n\nThis event is dispatched by a Sprite when an animation restarts playing on it.\nThis only happens when the `Sprite.anims.restart` method is called.\n\nListen for it on the Sprite using `sprite.on('animationrestart', listener)`\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_START": "The Animation Start Event.\n\nThis event is dispatched by a Sprite when an animation starts playing on it.\nThis happens when the animation is played, factoring in any delay that may have been specified.\nThis event happens after the delay has expired and prior to the first update event.\n\nListen for it on the Sprite using `sprite.on('animationstart', listener)`\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_STOP": "The Animation Stop Event.\n\nThis event is dispatched by a Sprite when an animation is stopped on it. An animation\nwill only be stopeed if a method such as `Sprite.stop` or `Sprite.anims.stopAfterDelay`\nis called. It can also be emitted if a new animation is started before the current one completes.\n\nListen for it on the Sprite using `sprite.on('animationstop', listener)`\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.ANIMATION_UPDATE": "The Animation Update Event.\n\nThis event is dispatched by a Sprite when an animation playing on it updates. This happens when the animation changes frame.\nAn animation will change frame based on the frame rate and other factors like `timeScale` and `delay`. It can also change\nframe when stopped or restarted.\n\nListen for it on the Sprite using `sprite.on('animationupdate', listener)`\n\nIf an animation is playing faster than the game frame-rate can handle, it's entirely possible for it to emit several\nupdate events in a single game frame, so please be aware of this in your code. The **final** event received that frame\nis the one that is rendered to the game.\n\nThe animation event flow is as follows:\n\n1. `ANIMATION_START`\n2. `ANIMATION_UPDATE` (repeated for however many frames the animation has)\n3. `ANIMATION_REPEAT` (only if the animation is set to repeat, it then emits more update events after this)\n4. `ANIMATION_COMPLETE` (only if there is a finite, or zero, repeat count)\n5. `ANIMATION_COMPLETE_KEY` (only if there is a finite, or zero, repeat count)\n\nIf the animation is stopped directly, the `ANIMATION_STOP` event is dispatched instead of `ANIMATION_COMPLETE`.\n\nIf the animation is restarted while it is already playing, `ANIMATION_RESTART` is emitted.", + "Phaser.Animations.Events.PAUSE_ALL": "The Pause All Animations Event.\n\nThis event is dispatched when the global Animation Manager is told to pause.\n\nWhen this happens all current animations will stop updating, although it doesn't necessarily mean\nthat the game has paused as well.", + "Phaser.Animations.Events.REMOVE_ANIMATION": "The Remove Animation Event.\n\nThis event is dispatched when an animation is removed from the global Animation Manager.", + "Phaser.Animations.Events.RESUME_ALL": "The Resume All Animations Event.\n\nThis event is dispatched when the global Animation Manager resumes, having been previously paused.\n\nWhen this happens all current animations will continue updating again.", + "Phaser.Cache.Events.ADD": "The Cache Add Event.\n\nThis event is dispatched by any Cache that extends the BaseCache each time a new object is added to it.", + "Phaser.Cache.Events.REMOVE": "The Cache Remove Event.\n\nThis event is dispatched by any Cache that extends the BaseCache each time an object is removed from it.", + "Phaser.Cameras.Scene2D.Events.DESTROY": "The Destroy Camera Event.\n\nThis event is dispatched by a Camera instance when it is destroyed by the Camera Manager.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('cameradestroy', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.DESTROY, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.FADE_IN_COMPLETE": "The Camera Fade In Complete Event.\n\nThis event is dispatched by a Camera instance when the Fade In Effect completes.\n\nListen to it from a Camera instance using `Camera.on('camerafadeincomplete', listener)`.", + "Phaser.Cameras.Scene2D.Events.FADE_IN_START": "The Camera Fade In Start Event.\n\nThis event is dispatched by a Camera instance when the Fade In Effect starts.\n\nListen to it from a Camera instance using `Camera.on('camerafadeinstart', listener)`.", + "Phaser.Cameras.Scene2D.Events.FADE_OUT_COMPLETE": "The Camera Fade Out Complete Event.\n\nThis event is dispatched by a Camera instance when the Fade Out Effect completes.\n\nListen to it from a Camera instance using `Camera.on('camerafadeoutcomplete', listener)`.", + "Phaser.Cameras.Scene2D.Events.FADE_OUT_START": "The Camera Fade Out Start Event.\n\nThis event is dispatched by a Camera instance when the Fade Out Effect starts.\n\nListen to it from a Camera instance using `Camera.on('camerafadeoutstart', listener)`.", + "Phaser.Cameras.Scene2D.Events.FLASH_COMPLETE": "The Camera Flash Complete Event.\n\nThis event is dispatched by a Camera instance when the Flash Effect completes.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('cameraflashcomplete', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.FLASH_COMPLETE, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.FLASH_START": "The Camera Flash Start Event.\n\nThis event is dispatched by a Camera instance when the Flash Effect starts.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('cameraflashstart', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.FLASH_START, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.FOLLOW_UPDATE": "The Camera Follower Update Event.\n\nThis event is dispatched by a Camera instance when it is following a\nGame Object and the Camera position has been updated as a result of\nthat following.\n\nListen to it from a Camera instance using: `camera.on('followupdate', listener)`.", + "Phaser.Cameras.Scene2D.Events.PAN_COMPLETE": "The Camera Pan Complete Event.\n\nThis event is dispatched by a Camera instance when the Pan Effect completes.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerapancomplete', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.PAN_COMPLETE, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.PAN_START": "The Camera Pan Start Event.\n\nThis event is dispatched by a Camera instance when the Pan Effect starts.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerapanstart', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.PAN_START, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.POST_RENDER": "The Camera Post-Render Event.\n\nThis event is dispatched by a Camera instance after is has finished rendering.\nIt is only dispatched if the Camera is rendering to a texture.\n\nListen to it from a Camera instance using: `camera.on('postrender', listener)`.", + "Phaser.Cameras.Scene2D.Events.PRE_RENDER": "The Camera Pre-Render Event.\n\nThis event is dispatched by a Camera instance when it is about to render.\nIt is only dispatched if the Camera is rendering to a texture.\n\nListen to it from a Camera instance using: `camera.on('prerender', listener)`.", + "Phaser.Cameras.Scene2D.Events.ROTATE_COMPLETE": "The Camera Rotate Complete Event.\n\nThis event is dispatched by a Camera instance when the Rotate Effect completes.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerarotatecomplete', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.ROTATE_COMPLETE, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.ROTATE_START": "The Camera Rotate Start Event.\n\nThis event is dispatched by a Camera instance when the Rotate Effect starts.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerarotatestart', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.ROTATE_START, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.SHAKE_COMPLETE": "The Camera Shake Complete Event.\n\nThis event is dispatched by a Camera instance when the Shake Effect completes.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerashakecomplete', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.SHAKE_COMPLETE, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.SHAKE_START": "The Camera Shake Start Event.\n\nThis event is dispatched by a Camera instance when the Shake Effect starts.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerashakestart', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.SHAKE_START, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.ZOOM_COMPLETE": "The Camera Zoom Complete Event.\n\nThis event is dispatched by a Camera instance when the Zoom Effect completes.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerazoomcomplete', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.ZOOM_COMPLETE, () => {});\n```", + "Phaser.Cameras.Scene2D.Events.ZOOM_START": "The Camera Zoom Start Event.\n\nThis event is dispatched by a Camera instance when the Zoom Effect starts.\n\nListen for it via either of the following:\n\n```js\nthis.cameras.main.on('camerazoomstart', () => {});\n```\n\nor use the constant, to avoid having to remember the correct event string:\n\n```js\nthis.cameras.main.on(Phaser.Cameras.Scene2D.Events.ZOOM_START, () => {});\n```", + "Phaser.Core.Events.BLUR": "The Game Blur Event.\n\nThis event is dispatched by the Game Visibility Handler when the window in which the Game instance is embedded\nenters a blurred state. The blur event is raised when the window loses focus. This can happen if a user swaps\ntab, or if they simply remove focus from the browser to another app.", + "Phaser.Core.Events.BOOT": "The Game Boot Event.\n\nThis event is dispatched when the Phaser Game instance has finished booting, but before it is ready to start running.\nThe global systems use this event to know when to set themselves up, dispatching their own `ready` events as required.", + "Phaser.Core.Events.CONTEXT_LOST": "The Game Context Lost Event.\n\nThis event is dispatched by the Game if the WebGL Renderer it is using encounters a WebGL Context Lost event from the browser.\n\nThe renderer halts all rendering and cannot resume after this happens.", + "Phaser.Core.Events.DESTROY": "The Game Destroy Event.\n\nThis event is dispatched when the game instance has been told to destroy itself.\nLots of internal systems listen to this event in order to clear themselves out.\nCustom plugins and game code should also do the same.", + "Phaser.Core.Events.FOCUS": "The Game Focus Event.\n\nThis event is dispatched by the Game Visibility Handler when the window in which the Game instance is embedded\nenters a focused state. The focus event is raised when the window re-gains focus, having previously lost it.", + "Phaser.Core.Events.HIDDEN": "The Game Hidden Event.\n\nThis event is dispatched by the Game Visibility Handler when the document in which the Game instance is embedded\nenters a hidden state. Only browsers that support the Visibility API will cause this event to be emitted.\n\nIn most modern browsers, when the document enters a hidden state, the Request Animation Frame and setTimeout, which\ncontrol the main game loop, will automatically pause. There is no way to stop this from happening. It is something\nyour game should account for in its own code, should the pause be an issue (i.e. for multiplayer games)", + "Phaser.Core.Events.PAUSE": "The Game Pause Event.\n\nThis event is dispatched when the Game loop enters a paused state, usually as a result of the Visibility Handler.", + "Phaser.Core.Events.POST_RENDER": "The Game Post-Render Event.\n\nThis event is dispatched right at the end of the render process.\n\nEvery Scene will have rendered and been drawn to the canvas by the time this event is fired.\nUse it for any last minute post-processing before the next game step begins.", + "Phaser.Core.Events.POST_STEP": "The Game Post-Step Event.\n\nThis event is dispatched after the Scene Manager has updated.\nHook into it from plugins or systems that need to do things before the render starts.", + "Phaser.Core.Events.PRE_RENDER": "The Game Pre-Render Event.\n\nThis event is dispatched immediately before any of the Scenes have started to render.\n\nThe renderer will already have been initialized this frame, clearing itself and preparing to receive the Scenes for rendering, but it won't have actually drawn anything yet.", + "Phaser.Core.Events.PRE_STEP": "The Game Pre-Step Event.\n\nThis event is dispatched before the main Game Step starts. By this point in the game cycle none of the Scene updates have yet happened.\nHook into it from plugins or systems that need to update before the Scene Manager does.", + "Phaser.Core.Events.READY": "The Game Ready Event.\n\nThis event is dispatched when the Phaser Game instance has finished booting, the Texture Manager is fully ready,\nand all local systems are now able to start.", + "Phaser.Core.Events.RESUME": "The Game Resume Event.\n\nThis event is dispatched when the game loop leaves a paused state and resumes running.", + "Phaser.Core.Events.STEP": "The Game Step Event.\n\nThis event is dispatched after the Game Pre-Step and before the Scene Manager steps.\nHook into it from plugins or systems that need to update before the Scene Manager does, but after the core Systems have.", + "Phaser.Core.Events.VISIBLE": "The Game Visible Event.\n\nThis event is dispatched by the Game Visibility Handler when the document in which the Game instance is embedded\nenters a visible state, previously having been hidden.\n\nOnly browsers that support the Visibility API will cause this event to be emitted.", + "Phaser.Data.Events.CHANGE_DATA": "The Change Data Event.\n\nThis event is dispatched by a Data Manager when an item in the data store is changed.\n\nGame Objects with data enabled have an instance of a Data Manager under the `data` property. So, to listen for\na change data event from a Game Object you would use: `sprite.on('changedata', listener)`.\n\nThis event is dispatched for all items that change in the Data Manager.\nTo listen for the change of a specific item, use the `CHANGE_DATA_KEY_EVENT` event.", + "Phaser.Data.Events.CHANGE_DATA_KEY": "The Change Data Key Event.\n\nThis event is dispatched by a Data Manager when an item in the data store is changed.\n\nGame Objects with data enabled have an instance of a Data Manager under the `data` property. So, to listen for\nthe change of a specific data item from a Game Object you would use: `sprite.on('changedata-key', listener)`,\nwhere `key` is the unique string key of the data item. For example, if you have a data item stored called `gold`\nthen you can listen for `sprite.on('changedata-gold')`.", + "Phaser.Data.Events.DESTROY": "The Data Manager Destroy Event.\n\nThe Data Manager will listen for the destroy event from its parent, and then close itself down.", + "Phaser.Data.Events.REMOVE_DATA": "The Remove Data Event.\n\nThis event is dispatched by a Data Manager when an item is removed from it.\n\nGame Objects with data enabled have an instance of a Data Manager under the `data` property. So, to listen for\nthe removal of a data item on a Game Object you would use: `sprite.on('removedata', listener)`.", + "Phaser.Data.Events.SET_DATA": "The Set Data Event.\n\nThis event is dispatched by a Data Manager when a new item is added to the data store.\n\nGame Objects with data enabled have an instance of a Data Manager under the `data` property. So, to listen for\nthe addition of a new data item on a Game Object you would use: `sprite.on('setdata', listener)`.", + "Phaser.GameObjects.Events.ADDED_TO_SCENE": "The Game Object Added to Scene Event.\n\nThis event is dispatched when a Game Object is added to a Scene.\n\nListen for it on a Game Object instance using `GameObject.on('addedtoscene', listener)`.", + "Phaser.GameObjects.Events.DESTROY": "The Game Object Destroy Event.\n\nThis event is dispatched when a Game Object instance is being destroyed.\n\nListen for it on a Game Object instance using `GameObject.on('destroy', listener)`.", + "Phaser.GameObjects.Events.REMOVED_FROM_SCENE": "The Game Object Removed from Scene Event.\n\nThis event is dispatched when a Game Object is removed from a Scene.\n\nListen for it on a Game Object instance using `GameObject.on('removedfromscene', listener)`.", + "Phaser.GameObjects.Events.VIDEO_COMPLETE": "The Video Game Object Complete Event.\n\nThis event is dispatched when a Video finishes playback by reaching the end of its duration. It\nis also dispatched if a video marker sequence is being played and reaches the end.\n\nNote that not all videos can fire this event. Live streams, for example, have no fixed duration,\nso never technically 'complete'.\n\nIf a video is stopped from playback, via the `Video.stop` method, it will emit the\n`VIDEO_STOP` event instead of this one.\n\nListen for it from a Video Game Object instance using `Video.on('complete', listener)`.", + "Phaser.GameObjects.Events.VIDEO_CREATED": "The Video Game Object Created Event.\n\nThis event is dispatched when the texture for a Video has been created. This happens\nwhen enough of the video source has been loaded that the browser is able to render a\nframe from it.\n\nListen for it from a Video Game Object instance using `Video.on('created', listener)`.", + "Phaser.GameObjects.Events.VIDEO_ERROR": "The Video Game Object Error Event.\n\nThis event is dispatched when a Video tries to play a source that does not exist, or is the wrong file type.\n\nListen for it from a Video Game Object instance using `Video.on('error', listener)`.", + "Phaser.GameObjects.Events.VIDEO_LOCKED": "The Video Game Object Locked Event.\n\nThis event is dispatched when a Video was attempted to be played, but the browser prevented it\nfrom doing so due to the Media Engagement Interaction policy.\n\nIf you get this event you will need to wait for the user to interact with the browser before\nthe video will play. This is a browser security measure to prevent autoplaying videos with\naudio. An interaction includes a mouse click, a touch, or a key press.\n\nListen for it from a Video Game Object instance using `Video.on('locked', listener)`.", + "Phaser.GameObjects.Events.VIDEO_LOOP": "The Video Game Object Loop Event.\n\nThis event is dispatched when a Video that is currently playing has looped. This only\nhappens if the `loop` parameter was specified, or the `setLoop` method was called,\nand if the video has a fixed duration. Video streams, for example, cannot loop, as\nthey have no duration.\n\nLooping is based on the result of the Video `timeupdate` event. This event is not\nframe-accurate, due to the way browsers work, so please do not rely on this loop\nevent to be time or frame precise.\n\nListen for it from a Video Game Object instance using `Video.on('loop', listener)`.", + "Phaser.GameObjects.Events.VIDEO_PLAYING": "The Video Game Object Playing Event.\n\nThe playing event is fired after playback is first started,\nand whenever it is restarted. For example it is fired when playback\nresumes after having been paused or delayed due to lack of data.\n\nListen for it from a Video Game Object instance using `Video.on('playing', listener)`.", + "Phaser.GameObjects.Events.VIDEO_PLAY": "The Video Game Object Play Event.\n\nThis event is dispatched when a Video begins playback. For videos that do not require\ninteraction unlocking, this is usually as soon as the `Video.play` method is called.\nHowever, for videos that require unlocking, it is fired once playback begins after\nthey've been unlocked.\n\nListen for it from a Video Game Object instance using `Video.on('play', listener)`.", + "Phaser.GameObjects.Events.VIDEO_SEEKED": "The Video Game Object Seeked Event.\n\nThis event is dispatched when a Video completes seeking to a new point in its timeline.\n\nListen for it from a Video Game Object instance using `Video.on('seeked', listener)`.", + "Phaser.GameObjects.Events.VIDEO_SEEKING": "The Video Game Object Seeking Event.\n\nThis event is dispatched when a Video _begins_ seeking to a new point in its timeline.\nWhen the seek is complete, it will dispatch the `VIDEO_SEEKED` event to conclude.\n\nListen for it from a Video Game Object instance using `Video.on('seeking', listener)`.", + "Phaser.GameObjects.Events.VIDEO_STALLED": "The Video Game Object Stalled Event.\n\nThis event is dispatched by a Video Game Object when the video playback stalls.\n\nThis can happen if the video is buffering.\n\nIf will fire for any of the following native DOM events:\n\n`stalled`\n`suspend`\n`waiting`\n\nListen for it from a Video Game Object instance using `Video.on('stalled', listener)`.\n\nNote that being stalled isn't always a negative thing. A video can be stalled if it\nhas downloaded enough data in to its buffer to not need to download any more until\nthe current batch of frames have rendered.", + "Phaser.GameObjects.Events.VIDEO_STOP": "The Video Game Object Stopped Event.\n\nThis event is dispatched when a Video is stopped from playback via a call to the `Video.stop` method,\neither directly via game code, or indirectly as the result of changing a video source or destroying it.\n\nListen for it from a Video Game Object instance using `Video.on('stop', listener)`.", + "Phaser.GameObjects.Events.VIDEO_TEXTURE": "The Video Game Object Texture Ready Event.\n\nThis event is dispatched by a Video Game Object when it has finished creating its texture.\n\nThis happens when the video has finished loading enough data for its first frame.\n\nIf you wish to use the Video texture elsewhere in your game, such as as a Sprite texture,\nthen you should listen for this event first, before creating the Sprites that use it.\n\nListen for it from a Video Game Object instance using `Video.on('textureready', listener)`.", + "Phaser.GameObjects.Events.VIDEO_UNLOCKED": "The Video Game Object Unlocked Event.\n\nThis event is dispatched when a Video that was prevented from playback due to the browsers\nMedia Engagement Interaction policy, is unlocked by a user gesture.\n\nListen for it from a Video Game Object instance using `Video.on('unlocked', listener)`.", + "Phaser.GameObjects.Events.VIDEO_UNSUPPORTED": "The Video Game Object Unsupported Event.\n\nThis event is dispatched by a Video Game Object if the media source\n(which may be specified as a MediaStream, MediaSource, Blob, or File,\nfor example) doesn't represent a supported media format.\n\nListen for it from a Video Game Object instance using `Video.on('unsupported', listener)`.", + "Phaser.GameObjects.Particles.Events.COMPLETE": "The Particle Emitter Complete Event.\n\nThis event is dispatched when the final particle, emitted from a Particle Emitter that\nhas been stopped, dies. Upon receipt of this event you know that no particles are\nstill rendering at this point in time.\n\nListen for it on a Particle Emitter instance using `ParticleEmitter.on('complete', listener)`.", + "Phaser.GameObjects.Particles.Events.DEATH_ZONE": "The Particle Emitter Death Zone Event.\n\nThis event is dispatched when a Death Zone kills a Particle instance.\n\nListen for it on a Particle Emitter instance using `ParticleEmitter.on('deathzone', listener)`.\n\nIf you wish to know when the final particle is killed, see the `COMPLETE` event.", + "Phaser.GameObjects.Particles.Events.EXPLODE": "The Particle Emitter Explode Event.\n\nThis event is dispatched when a Particle Emitter explodes a set of particles.\n\nListen for it on a Particle Emitter instance using `ParticleEmitter.on('explode', listener)`.", + "Phaser.GameObjects.Particles.Events.START": "The Particle Emitter Start Event.\n\nThis event is dispatched when a Particle Emitter starts emission of particles.\n\nListen for it on a Particle Emitter instance using `ParticleEmitter.on('start', listener)`.", + "Phaser.GameObjects.Particles.Events.STOP": "The Particle Emitter Stop Event.\n\nThis event is dispatched when a Particle Emitter is stopped. This can happen either\nwhen you directly call the `ParticleEmitter.stop` method, or if the emitter has\nbeen configured to stop after a set time via the `duration` property, or after a\nset number of particles via the `stopAfter` property.\n\nListen for it on a Particle Emitter instance using `ParticleEmitter.on('stop', listener)`.\n\nNote that just because the emitter has stopped, that doesn't mean there aren't still\nparticles alive and rendering. It just means the emitter has stopped emitting particles.\n\nIf you wish to know when the final particle is killed, see the `COMPLETE` event.", + "Phaser.Input.Events.BOOT": "The Input Plugin Boot Event.\n\nThis internal event is dispatched by the Input Plugin when it boots, signalling to all of its systems to create themselves.", + "Phaser.Input.Events.DESTROY": "The Input Plugin Destroy Event.\n\nThis internal event is dispatched by the Input Plugin when it is destroyed, signalling to all of its systems to destroy themselves.", + "Phaser.Input.Events.DRAG_END": "The Pointer Drag End Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer stops dragging a Game Object.\n\nListen to this event from within a Scene using: `this.input.on('dragend', listener)`.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG_END]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG_END} event instead.", + "Phaser.Input.Events.DRAG_ENTER": "The Pointer Drag Enter Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer drags a Game Object into a Drag Target.\n\nListen to this event from within a Scene using: `this.input.on('dragenter', listener)`.\n\nA Pointer can only drag a single Game Object at once.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG_ENTER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG_ENTER} event instead.", + "Phaser.Input.Events.DRAG": "The Pointer Drag Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer moves while dragging a Game Object.\n\nListen to this event from within a Scene using: `this.input.on('drag', listener)`.\n\nA Pointer can only drag a single Game Object at once.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG} event instead.", + "Phaser.Input.Events.DRAG_LEAVE": "The Pointer Drag Leave Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer drags a Game Object out of a Drag Target.\n\nListen to this event from within a Scene using: `this.input.on('dragleave', listener)`.\n\nA Pointer can only drag a single Game Object at once.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG_LEAVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG_LEAVE} event instead.", + "Phaser.Input.Events.DRAG_OVER": "The Pointer Drag Over Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer drags a Game Object over a Drag Target.\n\nWhen the Game Object first enters the drag target it will emit a `dragenter` event. If it then moves while within\nthe drag target, it will emit this event instead.\n\nListen to this event from within a Scene using: `this.input.on('dragover', listener)`.\n\nA Pointer can only drag a single Game Object at once.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG_OVER} event instead.", + "Phaser.Input.Events.DRAG_START": "The Pointer Drag Start Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer starts to drag any Game Object.\n\nListen to this event from within a Scene using: `this.input.on('dragstart', listener)`.\n\nA Pointer can only drag a single Game Object at once.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DRAG_START]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DRAG_START} event instead.", + "Phaser.Input.Events.DROP": "The Pointer Drop Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer drops a Game Object on a Drag Target.\n\nListen to this event from within a Scene using: `this.input.on('drop', listener)`.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_DROP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DROP} event instead.", + "Phaser.Input.Events.GAMEOBJECT_DOWN": "The Game Object Down Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is pressed down on _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectdown', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_DOWN} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_DOWN}\n2. [GAMEOBJECT_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DOWN}\n3. [POINTER_DOWN]{@linkcode Phaser.Input.Events#event:POINTER_DOWN} or [POINTER_DOWN_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_DOWN_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_DRAG_END": "The Game Object Drag End Event.\n\nThis event is dispatched by an interactive Game Object if a pointer stops dragging it.\n\nListen to this event from a Game Object using: `gameObject.on('dragend', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive](Phaser.GameObjects.GameObject#setInteractive) for more details.", + "Phaser.Input.Events.GAMEOBJECT_DRAG_ENTER": "The Game Object Drag Enter Event.\n\nThis event is dispatched by an interactive Game Object if a pointer drags it into a drag target.\n\nListen to this event from a Game Object using: `gameObject.on('dragenter', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.", + "Phaser.Input.Events.GAMEOBJECT_DRAG": "The Game Object Drag Event.\n\nThis event is dispatched by an interactive Game Object if a pointer moves while dragging it.\n\nListen to this event from a Game Object using: `gameObject.on('drag', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.", + "Phaser.Input.Events.GAMEOBJECT_DRAG_LEAVE": "The Game Object Drag Leave Event.\n\nThis event is dispatched by an interactive Game Object if a pointer drags it out of a drag target.\n\nListen to this event from a Game Object using: `gameObject.on('dragleave', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.", + "Phaser.Input.Events.GAMEOBJECT_DRAG_OVER": "The Game Object Drag Over Event.\n\nThis event is dispatched by an interactive Game Object if a pointer drags it over a drag target.\n\nWhen the Game Object first enters the drag target it will emit a `dragenter` event. If it then moves while within\nthe drag target, it will emit this event instead.\n\nListen to this event from a Game Object using: `gameObject.on('dragover', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.", + "Phaser.Input.Events.GAMEOBJECT_DRAG_START": "The Game Object Drag Start Event.\n\nThis event is dispatched by an interactive Game Object if a pointer starts to drag it.\n\nListen to this event from a Game Object using: `gameObject.on('dragstart', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThere are lots of useful drag related properties that are set within the Game Object when dragging occurs.\nFor example, `gameObject.input.dragStartX`, `dragStartY` and so on.", + "Phaser.Input.Events.GAMEOBJECT_DROP": "The Game Object Drop Event.\n\nThis event is dispatched by an interactive Game Object if a pointer drops it on a Drag Target.\n\nListen to this event from a Game Object using: `gameObject.on('drop', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive and enabled for drag.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.", + "Phaser.Input.Events.GAMEOBJECT_MOVE": "The Game Object Move Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is moved across _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectmove', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_MOVE} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_MOVE}\n2. [GAMEOBJECT_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_MOVE}\n3. [POINTER_MOVE]{@linkcode Phaser.Input.Events#event:POINTER_MOVE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_OUT": "The Game Object Out Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer moves out of _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectout', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OUT} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OUT}\n2. [GAMEOBJECT_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OUT}\n3. [POINTER_OUT]{@linkcode Phaser.Input.Events#event:POINTER_OUT}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.\n\nIf the pointer leaves the game canvas itself, it will not trigger an this event. To handle those cases,\nplease listen for the [GAME_OUT]{@linkcode Phaser.Input.Events#event:GAME_OUT} event.", + "Phaser.Input.Events.GAMEOBJECT_OVER": "The Game Object Over Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer moves over _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectover', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OVER} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OVER}\n2. [GAMEOBJECT_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OVER}\n3. [POINTER_OVER]{@linkcode Phaser.Input.Events#event:POINTER_OVER}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_DOWN": "The Game Object Pointer Down Event.\n\nThis event is dispatched by an interactive Game Object if a pointer is pressed down on it.\n\nListen to this event from a Game Object using: `gameObject.on('pointerdown', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_DOWN}\n2. [GAMEOBJECT_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DOWN}\n3. [POINTER_DOWN]{@linkcode Phaser.Input.Events#event:POINTER_DOWN} or [POINTER_DOWN_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_DOWN_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_MOVE": "The Game Object Pointer Move Event.\n\nThis event is dispatched by an interactive Game Object if a pointer is moved while over it.\n\nListen to this event from a Game Object using: `gameObject.on('pointermove', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_MOVE}\n2. [GAMEOBJECT_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_MOVE}\n3. [POINTER_MOVE]{@linkcode Phaser.Input.Events#event:POINTER_MOVE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_OUT": "The Game Object Pointer Out Event.\n\nThis event is dispatched by an interactive Game Object if a pointer moves out of it.\n\nListen to this event from a Game Object using: `gameObject.on('pointerout', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OUT}\n2. [GAMEOBJECT_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OUT}\n3. [POINTER_OUT]{@linkcode Phaser.Input.Events#event:POINTER_OUT}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.\n\nIf the pointer leaves the game canvas itself, it will not trigger an this event. To handle those cases,\nplease listen for the [GAME_OUT]{@linkcode Phaser.Input.Events#event:GAME_OUT} event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_OVER": "The Game Object Pointer Over Event.\n\nThis event is dispatched by an interactive Game Object if a pointer moves over it.\n\nListen to this event from a Game Object using: `gameObject.on('pointerover', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OVER}\n2. [GAMEOBJECT_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OVER}\n3. [POINTER_OVER]{@linkcode Phaser.Input.Events#event:POINTER_OVER}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_UP": "The Game Object Pointer Up Event.\n\nThis event is dispatched by an interactive Game Object if a pointer is released while over it.\n\nListen to this event from a Game Object using: `gameObject.on('pointerup', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_UP}\n2. [GAMEOBJECT_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_UP}\n3. [POINTER_UP]{@linkcode Phaser.Input.Events#event:POINTER_UP} or [POINTER_UP_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_UP_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_POINTER_WHEEL": "The Game Object Pointer Wheel Event.\n\nThis event is dispatched by an interactive Game Object if a pointer has its wheel moved while over it.\n\nListen to this event from a Game Object using: `gameObject.on('wheel', listener)`.\nNote that the scope of the listener is automatically set to be the Game Object instance itself.\n\nTo receive this event, the Game Object must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_WHEEL}\n2. [GAMEOBJECT_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_WHEEL}\n3. [POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:POINTER_WHEEL}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_UP": "The Game Object Up Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is released while over _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectup', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_UP} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_UP}\n2. [GAMEOBJECT_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_UP}\n3. [POINTER_UP]{@linkcode Phaser.Input.Events#event:POINTER_UP} or [POINTER_UP_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_UP_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAMEOBJECT_WHEEL": "The Game Object Wheel Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer has its wheel moved while over _any_ interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('gameobjectwheel', listener)`.\n\nTo receive this event, the Game Objects must have been set as interactive.\nSee [GameObject.setInteractive]{@link Phaser.GameObjects.GameObject#setInteractive} for more details.\n\nTo listen for this event from a _specific_ Game Object, use the [GAMEOBJECT_POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_WHEEL} event instead.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_WHEEL}\n2. [GAMEOBJECT_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_WHEEL}\n3. [POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:POINTER_WHEEL}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.GAME_OUT": "The Input Plugin Game Out Event.\n\nThis event is dispatched by the Input Plugin if the active pointer leaves the game canvas and is now\noutside of it, elsewhere on the web page.\n\nListen to this event from within a Scene using: `this.input.on('gameout', listener)`.", + "Phaser.Input.Events.GAME_OVER": "The Input Plugin Game Over Event.\n\nThis event is dispatched by the Input Plugin if the active pointer enters the game canvas and is now\nover of it, having previously been elsewhere on the web page.\n\nListen to this event from within a Scene using: `this.input.on('gameover', listener)`.", + "Phaser.Input.Events.MANAGER_BOOT": "The Input Manager Boot Event.\n\nThis internal event is dispatched by the Input Manager when it boots.", + "Phaser.Input.Events.MANAGER_PROCESS": "The Input Manager Process Event.\n\nThis internal event is dispatched by the Input Manager when not using the legacy queue system,\nand it wants the Input Plugins to update themselves.", + "Phaser.Input.Events.MANAGER_UPDATE": "The Input Manager Update Event.\n\nThis internal event is dispatched by the Input Manager as part of its update step.", + "Phaser.Input.Events.POINTERLOCK_CHANGE": "The Input Manager Pointer Lock Change Event.\n\nThis event is dispatched by the Input Manager when it is processing a native Pointer Lock Change DOM Event.", + "Phaser.Input.Events.POINTER_DOWN": "The Pointer Down Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is pressed down anywhere.\n\nListen to this event from within a Scene using: `this.input.on('pointerdown', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_DOWN}\n2. [GAMEOBJECT_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DOWN}\n3. [POINTER_DOWN]{@linkcode Phaser.Input.Events#event:POINTER_DOWN} or [POINTER_DOWN_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_DOWN_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_DOWN_OUTSIDE": "The Pointer Down Outside Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is pressed down anywhere outside of the game canvas.\n\nListen to this event from within a Scene using: `this.input.on('pointerdownoutside', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_DOWN}\n2. [GAMEOBJECT_DOWN]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_DOWN}\n3. [POINTER_DOWN]{@linkcode Phaser.Input.Events#event:POINTER_DOWN} or [POINTER_DOWN_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_DOWN_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_MOVE": "The Pointer Move Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is moved anywhere.\n\nListen to this event from within a Scene using: `this.input.on('pointermove', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_MOVE}\n2. [GAMEOBJECT_MOVE]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_MOVE}\n3. [POINTER_MOVE]{@linkcode Phaser.Input.Events#event:POINTER_MOVE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_OUT": "The Pointer Out Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer moves out of any interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('pointerout', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OUT}\n2. [GAMEOBJECT_OUT]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OUT}\n3. [POINTER_OUT]{@linkcode Phaser.Input.Events#event:POINTER_OUT}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.\n\nIf the pointer leaves the game canvas itself, it will not trigger an this event. To handle those cases,\nplease listen for the [GAME_OUT]{@linkcode Phaser.Input.Events#event:GAME_OUT} event.", + "Phaser.Input.Events.POINTER_OVER": "The Pointer Over Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer moves over any interactive Game Object.\n\nListen to this event from within a Scene using: `this.input.on('pointerover', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_OVER}\n2. [GAMEOBJECT_OVER]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_OVER}\n3. [POINTER_OVER]{@linkcode Phaser.Input.Events#event:POINTER_OVER}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_UP": "The Pointer Up Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is released anywhere.\n\nListen to this event from within a Scene using: `this.input.on('pointerup', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_UP}\n2. [GAMEOBJECT_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_UP}\n3. [POINTER_UP]{@linkcode Phaser.Input.Events#event:POINTER_UP} or [POINTER_UP_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_UP_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_UP_OUTSIDE": "The Pointer Up Outside Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer is released anywhere outside of the game canvas.\n\nListen to this event from within a Scene using: `this.input.on('pointerupoutside', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_UP}\n2. [GAMEOBJECT_UP]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_UP}\n3. [POINTER_UP]{@linkcode Phaser.Input.Events#event:POINTER_UP} or [POINTER_UP_OUTSIDE]{@linkcode Phaser.Input.Events#event:POINTER_UP_OUTSIDE}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.POINTER_WHEEL": "The Pointer Wheel Input Event.\n\nThis event is dispatched by the Input Plugin belonging to a Scene if a pointer has its wheel updated.\n\nListen to this event from within a Scene using: `this.input.on('wheel', listener)`.\n\nThe event hierarchy is as follows:\n\n1. [GAMEOBJECT_POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_POINTER_WHEEL}\n2. [GAMEOBJECT_WHEEL]{@linkcode Phaser.Input.Events#event:GAMEOBJECT_WHEEL}\n3. [POINTER_WHEEL]{@linkcode Phaser.Input.Events#event:POINTER_WHEEL}\n\nWith the top event being dispatched first and then flowing down the list. Note that higher-up event handlers can stop\nthe propagation of this event.", + "Phaser.Input.Events.PRE_UPDATE": "The Input Plugin Pre-Update Event.\n\nThis internal event is dispatched by the Input Plugin at the start of its `preUpdate` method.\nThis hook is designed specifically for input plugins, but can also be listened to from user-land code.", + "Phaser.Input.Events.SHUTDOWN": "The Input Plugin Shutdown Event.\n\nThis internal event is dispatched by the Input Plugin when it shuts down, signalling to all of its systems to shut themselves down.", + "Phaser.Input.Events.START": "The Input Plugin Start Event.\n\nThis internal event is dispatched by the Input Plugin when it has finished setting-up,\nsignalling to all of its internal systems to start.", + "Phaser.Input.Events.UPDATE": "The Input Plugin Update Event.\n\nThis internal event is dispatched by the Input Plugin at the start of its `update` method.\nThis hook is designed specifically for input plugins, but can also be listened to from user-land code.", + "Phaser.Input.Gamepad.Events.BUTTON_DOWN": "The Gamepad Button Down Event.\n\nThis event is dispatched by the Gamepad Plugin when a button has been pressed on any active Gamepad.\n\nListen to this event from within a Scene using: `this.input.gamepad.on('down', listener)`.\n\nYou can also listen for a DOWN event from a Gamepad instance. See the [GAMEPAD_BUTTON_DOWN]{@linkcode Phaser.Input.Gamepad.Events#event:GAMEPAD_BUTTON_DOWN} event for details.", + "Phaser.Input.Gamepad.Events.BUTTON_UP": "The Gamepad Button Up Event.\n\nThis event is dispatched by the Gamepad Plugin when a button has been released on any active Gamepad.\n\nListen to this event from within a Scene using: `this.input.gamepad.on('up', listener)`.\n\nYou can also listen for an UP event from a Gamepad instance. See the [GAMEPAD_BUTTON_UP]{@linkcode Phaser.Input.Gamepad.Events#event:GAMEPAD_BUTTON_UP} event for details.", + "Phaser.Input.Gamepad.Events.CONNECTED": "The Gamepad Connected Event.\n\nThis event is dispatched by the Gamepad Plugin when a Gamepad has been connected.\n\nListen to this event from within a Scene using: `this.input.gamepad.once('connected', listener)`.\n\nNote that the browser may require you to press a button on a gamepad before it will allow you to access it,\nthis is for security reasons. However, it may also trust the page already, in which case you won't get the\n'connected' event and instead should check `GamepadPlugin.total` to see if it thinks there are any gamepads\nalready connected.", + "Phaser.Input.Gamepad.Events.DISCONNECTED": "The Gamepad Disconnected Event.\n\nThis event is dispatched by the Gamepad Plugin when a Gamepad has been disconnected.\n\nListen to this event from within a Scene using: `this.input.gamepad.once('disconnected', listener)`.", + "Phaser.Input.Gamepad.Events.GAMEPAD_BUTTON_DOWN": "The Gamepad Button Down Event.\n\nThis event is dispatched by a Gamepad instance when a button has been pressed on it.\n\nListen to this event from a Gamepad instance. Once way to get this is from the `pad1`, `pad2`, etc properties on the Gamepad Plugin:\n`this.input.gamepad.pad1.on('down', listener)`.\n\nNote that you will not receive any Gamepad button events until the browser considers the Gamepad as being 'connected'.\n\nYou can also listen for a DOWN event from the Gamepad Plugin. See the [BUTTON_DOWN]{@linkcode Phaser.Input.Gamepad.Events#event:BUTTON_DOWN} event for details.", + "Phaser.Input.Gamepad.Events.GAMEPAD_BUTTON_UP": "The Gamepad Button Up Event.\n\nThis event is dispatched by a Gamepad instance when a button has been released on it.\n\nListen to this event from a Gamepad instance. Once way to get this is from the `pad1`, `pad2`, etc properties on the Gamepad Plugin:\n`this.input.gamepad.pad1.on('up', listener)`.\n\nNote that you will not receive any Gamepad button events until the browser considers the Gamepad as being 'connected'.\n\nYou can also listen for an UP event from the Gamepad Plugin. See the [BUTTON_UP]{@linkcode Phaser.Input.Gamepad.Events#event:BUTTON_UP} event for details.", + "Phaser.Input.Keyboard.Events.ANY_KEY_DOWN": "The Global Key Down Event.\n\nThis event is dispatched by the Keyboard Plugin when any key on the keyboard is pressed down.\n\nListen to this event from within a Scene using: `this.input.keyboard.on('keydown', listener)`.\n\nYou can also listen for a specific key being pressed. See [Keyboard.Events.KEY_DOWN]{@linkcode Phaser.Input.Keyboard.Events#event:KEY_DOWN} for details.\n\nFinally, you can create Key objects, which you can also listen for events from. See [Keyboard.Events.DOWN]{@linkcode Phaser.Input.Keyboard.Events#event:DOWN} for details.\n\n_Note_: Many keyboards are unable to process certain combinations of keys due to hardware limitations known as ghosting.\nRead [this article on ghosting]{@link http://www.html5gamedevs.com/topic/4876-impossible-to-use-more-than-2-keyboard-input-buttons-at-the-same-time/} for details.\n\nAlso, please be aware that some browser extensions can disable or override Phaser keyboard handling.\nFor example, the Chrome extension vimium is known to disable Phaser from using the D key, while EverNote disables the backtick key.\nThere are others. So, please check your extensions if you find you have specific keys that don't work.", + "Phaser.Input.Keyboard.Events.ANY_KEY_UP": "The Global Key Up Event.\n\nThis event is dispatched by the Keyboard Plugin when any key on the keyboard is released.\n\nListen to this event from within a Scene using: `this.input.keyboard.on('keyup', listener)`.\n\nYou can also listen for a specific key being released. See [Keyboard.Events.KEY_UP]{@linkcode Phaser.Input.Keyboard.Events#event:KEY_UP} for details.\n\nFinally, you can create Key objects, which you can also listen for events from. See [Keyboard.Events.UP]{@linkcode Phaser.Input.Keyboard.Events#event:UP} for details.", + "Phaser.Input.Keyboard.Events.COMBO_MATCH": "The Key Combo Match Event.\n\nThis event is dispatched by the Keyboard Plugin when a [Key Combo]{@link Phaser.Input.Keyboard.KeyCombo} is matched.\n\nListen for this event from the Key Plugin after a combo has been created:\n\n```javascript\nthis.input.keyboard.createCombo([ 38, 38, 40, 40, 37, 39, 37, 39, 66, 65, 13 ], { resetOnMatch: true });\n\nthis.input.keyboard.on('keycombomatch', function (event) {\n console.log('Konami Code entered!');\n});\n```", + "Phaser.Input.Keyboard.Events.DOWN": "The Key Down Event.\n\nThis event is dispatched by a [Key]{@link Phaser.Input.Keyboard.Key} object when it is pressed.\n\nListen for this event from the Key object instance directly:\n\n```javascript\nvar spaceBar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);\n\nspaceBar.on('down', listener)\n```\n\nYou can also create a generic 'global' listener. See [Keyboard.Events.ANY_KEY_DOWN]{@linkcode Phaser.Input.Keyboard.Events#event:ANY_KEY_DOWN} for details.", + "Phaser.Input.Keyboard.Events.KEY_DOWN": "The Key Down Event.\n\nThis event is dispatched by the Keyboard Plugin when any key on the keyboard is pressed down.\n\nUnlike the `ANY_KEY_DOWN` event, this one has a special dynamic event name. For example, to listen for the `A` key being pressed\nuse the following from within a Scene: `this.input.keyboard.on('keydown-A', listener)`. You can replace the `-A` part of the event\nname with any valid [Key Code string]{@link Phaser.Input.Keyboard.KeyCodes}. For example, this will listen for the space bar:\n`this.input.keyboard.on('keydown-SPACE', listener)`.\n\nYou can also create a generic 'global' listener. See [Keyboard.Events.ANY_KEY_DOWN]{@linkcode Phaser.Input.Keyboard.Events#event:ANY_KEY_DOWN} for details.\n\nFinally, you can create Key objects, which you can also listen for events from. See [Keyboard.Events.DOWN]{@linkcode Phaser.Input.Keyboard.Events#event:DOWN} for details.\n\n_Note_: Many keyboards are unable to process certain combinations of keys due to hardware limitations known as ghosting.\nRead [this article on ghosting]{@link http://www.html5gamedevs.com/topic/4876-impossible-to-use-more-than-2-keyboard-input-buttons-at-the-same-time/} for details.\n\nAlso, please be aware that some browser extensions can disable or override Phaser keyboard handling.\nFor example, the Chrome extension vimium is known to disable Phaser from using the D key, while EverNote disables the backtick key.\nThere are others. So, please check your extensions if you find you have specific keys that don't work.", + "Phaser.Input.Keyboard.Events.KEY_UP": "The Key Up Event.\n\nThis event is dispatched by the Keyboard Plugin when any key on the keyboard is released.\n\nUnlike the `ANY_KEY_UP` event, this one has a special dynamic event name. For example, to listen for the `A` key being released\nuse the following from within a Scene: `this.input.keyboard.on('keyup-A', listener)`. You can replace the `-A` part of the event\nname with any valid [Key Code string]{@link Phaser.Input.Keyboard.KeyCodes}. For example, this will listen for the space bar:\n`this.input.keyboard.on('keyup-SPACE', listener)`.\n\nYou can also create a generic 'global' listener. See [Keyboard.Events.ANY_KEY_UP]{@linkcode Phaser.Input.Keyboard.Events#event:ANY_KEY_UP} for details.\n\nFinally, you can create Key objects, which you can also listen for events from. See [Keyboard.Events.UP]{@linkcode Phaser.Input.Keyboard.Events#event:UP} for details.", + "Phaser.Input.Keyboard.Events.UP": "The Key Up Event.\n\nThis event is dispatched by a [Key]{@link Phaser.Input.Keyboard.Key} object when it is released.\n\nListen for this event from the Key object instance directly:\n\n```javascript\nvar spaceBar = this.input.keyboard.addKey(Phaser.Input.Keyboard.KeyCodes.SPACE);\n\nspaceBar.on('up', listener)\n```\n\nYou can also create a generic 'global' listener. See [Keyboard.Events.ANY_KEY_UP]{@linkcode Phaser.Input.Keyboard.Events#event:ANY_KEY_UP} for details.", + "Phaser.Loader.Events.ADD": "The Loader Plugin Add File Event.\n\nThis event is dispatched when a new file is successfully added to the Loader and placed into the load queue.\n\nListen to it from a Scene using: `this.load.on('addfile', listener)`.\n\nIf you add lots of files to a Loader from a `preload` method, it will dispatch this event for each one of them.", + "Phaser.Loader.Events.COMPLETE": "The Loader Plugin Complete Event.\n\nThis event is dispatched when the Loader has fully processed everything in the load queue.\nBy this point every loaded file will now be in its associated cache and ready for use.\n\nListen to it from a Scene using: `this.load.on('complete', listener)`.", + "Phaser.Loader.Events.FILE_COMPLETE": "The File Load Complete Event.\n\nThis event is dispatched by the Loader Plugin when _any_ file in the queue finishes loading.\n\nListen to it from a Scene using: `this.load.on('filecomplete', listener)`.\n\nMake sure you remove this listener when you have finished, or it will continue to fire if the Scene reloads.\n\nYou can also listen for the completion of a specific file. See the [FILE_KEY_COMPLETE]{@linkcode Phaser.Loader.Events#event:FILE_KEY_COMPLETE} event.", + "Phaser.Loader.Events.FILE_KEY_COMPLETE": "The File Load Complete Event.\n\nThis event is dispatched by the Loader Plugin when any file in the queue finishes loading.\n\nIt uses a special dynamic event name constructed from the key and type of the file.\n\nFor example, if you have loaded an `image` with a key of `monster`, you can listen for it\nusing the following:\n\n```javascript\nthis.load.on('filecomplete-image-monster', function (key, type, data) {\n // Your handler code\n});\n```\n\nOr, if you have loaded a texture `atlas` with a key of `Level1`:\n\n```javascript\nthis.load.on('filecomplete-atlasjson-Level1', function (key, type, data) {\n // Your handler code\n});\n```\n\nOr, if you have loaded a sprite sheet with a key of `Explosion` and a prefix of `GAMEOVER`:\n\n```javascript\nthis.load.on('filecomplete-spritesheet-GAMEOVERExplosion', function (key, type, data) {\n // Your handler code\n});\n```\n\nMake sure you remove your listeners when you have finished, or they will continue to fire if the Scene reloads.\n\nYou can also listen for the generic completion of files. See the [FILE_COMPLETE]{@linkcode Phaser.Loader.Events#event:FILE_COMPLETE} event.", + "Phaser.Loader.Events.FILE_LOAD_ERROR": "The File Load Error Event.\n\nThis event is dispatched by the Loader Plugin when a file fails to load.\n\nListen to it from a Scene using: `this.load.on('loaderror', listener)`.", + "Phaser.Loader.Events.FILE_LOAD": "The File Load Event.\n\nThis event is dispatched by the Loader Plugin when a file finishes loading,\nbut _before_ it is processed and added to the internal Phaser caches.\n\nListen to it from a Scene using: `this.load.on('load', listener)`.", + "Phaser.Loader.Events.FILE_PROGRESS": "The File Load Progress Event.\n\nThis event is dispatched by the Loader Plugin during the load of a file, if the browser receives a DOM ProgressEvent and\nthe `lengthComputable` event property is true. Depending on the size of the file and browser in use, this may, or may not happen.\n\nListen to it from a Scene using: `this.load.on('fileprogress', listener)`.", + "Phaser.Loader.Events.POST_PROCESS": "The Loader Plugin Post Process Event.\n\nThis event is dispatched by the Loader Plugin when the Loader has finished loading everything in the load queue.\nIt is dispatched before the internal lists are cleared and each File is destroyed.\n\nUse this hook to perform any last minute processing of files that can only happen once the\nLoader has completed, but prior to it emitting the `complete` event.\n\nListen to it from a Scene using: `this.load.on('postprocess', listener)`.", + "Phaser.Loader.Events.PROGRESS": "The Loader Plugin Progress Event.\n\nThis event is dispatched when the Loader updates its load progress, typically as a result of a file having completed loading.\n\nListen to it from a Scene using: `this.load.on('progress', listener)`.", + "Phaser.Loader.Events.START": "The Loader Plugin Start Event.\n\nThis event is dispatched when the Loader starts running. At this point load progress is zero.\n\nThis event is dispatched even if there aren't any files in the load queue.\n\nListen to it from a Scene using: `this.load.on('start', listener)`.", + "Phaser.Physics.Arcade.Events.COLLIDE": "The Arcade Physics World Collide Event.\n\nThis event is dispatched by an Arcade Physics World instance if two bodies collide _and_ at least\none of them has their [onCollide]{@link Phaser.Physics.Arcade.Body#onCollide} property set to `true`.\n\nIt provides an alternative means to handling collide events rather than using the callback approach.\n\nListen to it from a Scene using: `this.physics.world.on('collide', listener)`.\n\nPlease note that 'collide' and 'overlap' are two different things in Arcade Physics.", + "Phaser.Physics.Arcade.Events.OVERLAP": "The Arcade Physics World Overlap Event.\n\nThis event is dispatched by an Arcade Physics World instance if two bodies overlap _and_ at least\none of them has their [onOverlap]{@link Phaser.Physics.Arcade.Body#onOverlap} property set to `true`.\n\nIt provides an alternative means to handling overlap events rather than using the callback approach.\n\nListen to it from a Scene using: `this.physics.world.on('overlap', listener)`.\n\nPlease note that 'collide' and 'overlap' are two different things in Arcade Physics.", + "Phaser.Physics.Arcade.Events.PAUSE": "The Arcade Physics World Pause Event.\n\nThis event is dispatched by an Arcade Physics World instance when it is paused.\n\nListen to it from a Scene using: `this.physics.world.on('pause', listener)`.", + "Phaser.Physics.Arcade.Events.RESUME": "The Arcade Physics World Resume Event.\n\nThis event is dispatched by an Arcade Physics World instance when it resumes from a paused state.\n\nListen to it from a Scene using: `this.physics.world.on('resume', listener)`.", + "Phaser.Physics.Arcade.Events.TILE_COLLIDE": "The Arcade Physics Tile Collide Event.\n\nThis event is dispatched by an Arcade Physics World instance if a body collides with a Tile _and_\nhas its [onCollide]{@link Phaser.Physics.Arcade.Body#onCollide} property set to `true`.\n\nIt provides an alternative means to handling collide events rather than using the callback approach.\n\nListen to it from a Scene using: `this.physics.world.on('tilecollide', listener)`.\n\nPlease note that 'collide' and 'overlap' are two different things in Arcade Physics.", + "Phaser.Physics.Arcade.Events.TILE_OVERLAP": "The Arcade Physics Tile Overlap Event.\n\nThis event is dispatched by an Arcade Physics World instance if a body overlaps with a Tile _and_\nhas its [onOverlap]{@link Phaser.Physics.Arcade.Body#onOverlap} property set to `true`.\n\nIt provides an alternative means to handling overlap events rather than using the callback approach.\n\nListen to it from a Scene using: `this.physics.world.on('tileoverlap', listener)`.\n\nPlease note that 'collide' and 'overlap' are two different things in Arcade Physics.", + "Phaser.Physics.Arcade.Events.WORLD_BOUNDS": "The Arcade Physics World Bounds Event.\n\nThis event is dispatched by an Arcade Physics World instance if a body makes contact with the world bounds _and_\nit has its [onWorldBounds]{@link Phaser.Physics.Arcade.Body#onWorldBounds} property set to `true`.\n\nIt provides an alternative means to handling collide events rather than using the callback approach.\n\nListen to it from a Scene using: `this.physics.world.on('worldbounds', listener)`.", + "Phaser.Physics.Arcade.Events.WORLD_STEP": "The Arcade Physics World Step Event.\n\nThis event is dispatched by an Arcade Physics World instance whenever a physics step is run.\nIt is emitted _after_ the bodies and colliders have been updated.\n\nIn high framerate settings this can be multiple times per game frame.\n\nListen to it from a Scene using: `this.physics.world.on('worldstep', listener)`.", + "Phaser.Physics.Matter.Events.AFTER_ADD": "The Matter Physics After Add Event.\n\nThis event is dispatched by a Matter Physics World instance at the end of the process when a new Body\nor Constraint has just been added to the world.\n\nListen to it from a Scene using: `this.matter.world.on('afteradd', listener)`.", + "Phaser.Physics.Matter.Events.AFTER_REMOVE": "The Matter Physics After Remove Event.\n\nThis event is dispatched by a Matter Physics World instance at the end of the process when a\nBody or Constraint was removed from the world.\n\nListen to it from a Scene using: `this.matter.world.on('afterremove', listener)`.", + "Phaser.Physics.Matter.Events.AFTER_UPDATE": "The Matter Physics After Update Event.\n\nThis event is dispatched by a Matter Physics World instance after the engine has updated and all collision events have resolved.\n\nListen to it from a Scene using: `this.matter.world.on('afterupdate', listener)`.", + "Phaser.Physics.Matter.Events.BEFORE_ADD": "The Matter Physics Before Add Event.\n\nThis event is dispatched by a Matter Physics World instance at the start of the process when a new Body\nor Constraint is being added to the world.\n\nListen to it from a Scene using: `this.matter.world.on('beforeadd', listener)`.", + "Phaser.Physics.Matter.Events.BEFORE_REMOVE": "The Matter Physics Before Remove Event.\n\nThis event is dispatched by a Matter Physics World instance at the start of the process when a\nBody or Constraint is being removed from the world.\n\nListen to it from a Scene using: `this.matter.world.on('beforeremove', listener)`.", + "Phaser.Physics.Matter.Events.BEFORE_UPDATE": "The Matter Physics Before Update Event.\n\nThis event is dispatched by a Matter Physics World instance right before all the collision processing takes place.\n\nListen to it from a Scene using: `this.matter.world.on('beforeupdate', listener)`.", + "Phaser.Physics.Matter.Events.COLLISION_ACTIVE": "The Matter Physics Collision Active Event.\n\nThis event is dispatched by a Matter Physics World instance after the engine has updated.\nIt provides a list of all pairs that are colliding in the current tick (if any).\n\nListen to it from a Scene using: `this.matter.world.on('collisionactive', listener)`.", + "Phaser.Physics.Matter.Events.COLLISION_END": "The Matter Physics Collision End Event.\n\nThis event is dispatched by a Matter Physics World instance after the engine has updated.\nIt provides a list of all pairs that have finished colliding in the current tick (if any).\n\nListen to it from a Scene using: `this.matter.world.on('collisionend', listener)`.", + "Phaser.Physics.Matter.Events.COLLISION_START": "The Matter Physics Collision Start Event.\n\nThis event is dispatched by a Matter Physics World instance after the engine has updated.\nIt provides a list of all pairs that have started to collide in the current tick (if any).\n\nListen to it from a Scene using: `this.matter.world.on('collisionstart', listener)`.", + "Phaser.Physics.Matter.Events.DRAG_END": "The Matter Physics Drag End Event.\n\nThis event is dispatched by a Matter Physics World instance when a Pointer Constraint\nstops dragging a body.\n\nListen to it from a Scene using: `this.matter.world.on('dragend', listener)`.", + "Phaser.Physics.Matter.Events.DRAG": "The Matter Physics Drag Event.\n\nThis event is dispatched by a Matter Physics World instance when a Pointer Constraint\nis actively dragging a body. It is emitted each time the pointer moves.\n\nListen to it from a Scene using: `this.matter.world.on('drag', listener)`.", + "Phaser.Physics.Matter.Events.DRAG_START": "The Matter Physics Drag Start Event.\n\nThis event is dispatched by a Matter Physics World instance when a Pointer Constraint\nstarts dragging a body.\n\nListen to it from a Scene using: `this.matter.world.on('dragstart', listener)`.", + "Phaser.Physics.Matter.Events.PAUSE": "The Matter Physics World Pause Event.\n\nThis event is dispatched by an Matter Physics World instance when it is paused.\n\nListen to it from a Scene using: `this.matter.world.on('pause', listener)`.", + "Phaser.Physics.Matter.Events.RESUME": "The Matter Physics World Resume Event.\n\nThis event is dispatched by an Matter Physics World instance when it resumes from a paused state.\n\nListen to it from a Scene using: `this.matter.world.on('resume', listener)`.", + "Phaser.Physics.Matter.Events.SLEEP_END": "The Matter Physics Sleep End Event.\n\nThis event is dispatched by a Matter Physics World instance when a Body stop sleeping.\n\nListen to it from a Scene using: `this.matter.world.on('sleepend', listener)`.", + "Phaser.Physics.Matter.Events.SLEEP_START": "The Matter Physics Sleep Start Event.\n\nThis event is dispatched by a Matter Physics World instance when a Body goes to sleep.\n\nListen to it from a Scene using: `this.matter.world.on('sleepstart', listener)`.", + "Phaser.Renderer.Events.POST_RENDER": "The Post-Render Event.\n\nThis event is dispatched by the Renderer when all rendering, for all cameras in all Scenes,\nhas completed, but before any pending snap shots have been taken.", + "Phaser.Renderer.Events.PRE_RENDER": "The Pre-Render Event.\n\nThis event is dispatched by the Phaser Renderer. This happens right at the start of the render\nprocess, after the context has been cleared, the scissors enabled (WebGL only) and everything has been\nreset ready for the render.", + "Phaser.Renderer.Events.RENDER": "The Render Event.\n\nThis event is dispatched by the Phaser Renderer for every camera in every Scene.\n\nIt is dispatched before any of the children in the Scene have been rendered.", + "Phaser.Renderer.Events.RESIZE": "The Renderer Resize Event.\n\nThis event is dispatched by the Phaser Renderer when it is resized, usually as a result\nof the Scale Manager resizing.", + "Phaser.Renderer.WebGL.Pipelines.Events.AFTER_FLUSH": "The WebGLPipeline After Flush Event.\n\nThis event is dispatched by a WebGLPipeline right after it has issued a drawArrays command\nand cleared its vertex count.", + "Phaser.Renderer.WebGL.Pipelines.Events.BEFORE_FLUSH": "The WebGLPipeline Before Flush Event.\n\nThis event is dispatched by a WebGLPipeline right before it is about to\nflush and issue a bufferData and drawArrays command.", + "Phaser.Renderer.WebGL.Pipelines.Events.BIND": "The WebGLPipeline Bind Event.\n\nThis event is dispatched by a WebGLPipeline when it is bound by the Pipeline Manager.", + "Phaser.Renderer.WebGL.Pipelines.Events.BOOT": "The WebGLPipeline Boot Event.\n\nThis event is dispatched by a WebGLPipeline when it has completed its `boot` phase.", + "Phaser.Renderer.WebGL.Pipelines.Events.DESTROY": "The WebGLPipeline Destroy Event.\n\nThis event is dispatched by a WebGLPipeline when it is starting its destroy process.", + "Phaser.Renderer.WebGL.Pipelines.Events.REBIND": "The WebGLPipeline ReBind Event.\n\nThis event is dispatched by a WebGLPipeline when it is re-bound by the Pipeline Manager.", + "Phaser.Renderer.WebGL.Pipelines.Events.RESIZE": "The WebGLPipeline Resize Event.\n\nThis event is dispatched by a WebGLPipeline when it is resized, usually as a result\nof the Renderer resizing.", + "Phaser.Scale.Events.ENTER_FULLSCREEN": "The Scale Manager has successfully entered fullscreen mode.", + "Phaser.Scale.Events.FULLSCREEN_FAILED": "The Scale Manager tried to enter fullscreen mode but failed.", + "Phaser.Scale.Events.FULLSCREEN_UNSUPPORTED": "The Scale Manager tried to enter fullscreen mode, but it is unsupported by the browser.", + "Phaser.Scale.Events.LEAVE_FULLSCREEN": "The Scale Manager was in fullscreen mode, but has since left, either directly via game code,\nor via a user gestured, such as pressing the ESC key.", + "Phaser.Scale.Events.ORIENTATION_CHANGE": "The Scale Manager Orientation Change Event.\n\nThis event is dispatched whenever the Scale Manager detects an orientation change event from the browser.", + "Phaser.Scale.Events.RESIZE": "The Scale Manager Resize Event.\n\nThis event is dispatched whenever the Scale Manager detects a resize event from the browser.\nIt sends three parameters to the callback, each of them being Size components. You can read\nthe `width`, `height`, `aspectRatio` and other properties of these components to help with\nscaling your own game content.", + "Phaser.Scenes.Events.ADDED_TO_SCENE": "The Game Object Added to Scene Event.\n\nThis event is dispatched when a Game Object is added to a Scene.\n\nListen for it from a Scene using `this.events.on('addedtoscene', listener)`.", + "Phaser.Scenes.Events.BOOT": "The Scene Systems Boot Event.\n\nThis event is dispatched by a Scene during the Scene Systems boot process. Primarily used by Scene Plugins.\n\nListen to it from a Scene using `this.events.on('boot', listener)`.", + "Phaser.Scenes.Events.CREATE": "The Scene Create Event.\n\nThis event is dispatched by a Scene after it has been created by the Scene Manager.\n\nIf a Scene has a `create` method then this event is emitted _after_ that has run.\n\nIf there is a transition, this event will be fired after the `TRANSITION_START` event.\n\nListen to it from a Scene using `this.events.on('create', listener)`.", + "Phaser.Scenes.Events.DESTROY": "The Scene Systems Destroy Event.\n\nThis event is dispatched by a Scene during the Scene Systems destroy process.\n\nListen to it from a Scene using `this.events.on('destroy', listener)`.\n\nYou should destroy any resources that may be in use by your Scene in this event handler.", + "Phaser.Scenes.Events.PAUSE": "The Scene Systems Pause Event.\n\nThis event is dispatched by a Scene when it is paused, either directly via the `pause` method, or as an\naction from another Scene.\n\nListen to it from a Scene using `this.events.on('pause', listener)`.", + "Phaser.Scenes.Events.POST_UPDATE": "The Scene Systems Post Update Event.\n\nThis event is dispatched by a Scene during the main game loop step.\n\nThe event flow for a single step of a Scene is as follows:\n\n1. [PRE_UPDATE]{@linkcode Phaser.Scenes.Events#event:PRE_UPDATE}\n2. [UPDATE]{@linkcode Phaser.Scenes.Events#event:UPDATE}\n3. The `Scene.update` method is called, if it exists\n4. [POST_UPDATE]{@linkcode Phaser.Scenes.Events#event:POST_UPDATE}\n5. [PRE_RENDER]{@linkcode Phaser.Scenes.Events#event:PRE_RENDER}\n6. [RENDER]{@linkcode Phaser.Scenes.Events#event:RENDER}\n\nListen to it from a Scene using `this.events.on('postupdate', listener)`.\n\nA Scene will only run its step if it is active.", + "Phaser.Scenes.Events.PRE_RENDER": "The Scene Systems Pre-Render Event.\n\nThis event is dispatched by a Scene during the main game loop step.\n\nThe event flow for a single step of a Scene is as follows:\n\n1. [PRE_UPDATE]{@linkcode Phaser.Scenes.Events#event:PRE_UPDATE}\n2. [UPDATE]{@linkcode Phaser.Scenes.Events#event:UPDATE}\n3. The `Scene.update` method is called, if it exists\n4. [POST_UPDATE]{@linkcode Phaser.Scenes.Events#event:POST_UPDATE}\n5. [PRE_RENDER]{@linkcode Phaser.Scenes.Events#event:PRE_RENDER}\n6. [RENDER]{@linkcode Phaser.Scenes.Events#event:RENDER}\n\nListen to this event from a Scene using `this.events.on('prerender', listener)`.\n\nA Scene will only render if it is visible.\n\nThis event is dispatched after the Scene Display List is sorted and before the Scene is rendered.", + "Phaser.Scenes.Events.PRE_UPDATE": "The Scene Systems Pre Update Event.\n\nThis event is dispatched by a Scene during the main game loop step.\n\nThe event flow for a single step of a Scene is as follows:\n\n1. [PRE_UPDATE]{@linkcode Phaser.Scenes.Events#event:PRE_UPDATE}\n2. [UPDATE]{@linkcode Phaser.Scenes.Events#event:UPDATE}\n3. The `Scene.update` method is called, if it exists\n4. [POST_UPDATE]{@linkcode Phaser.Scenes.Events#event:POST_UPDATE}\n5. [PRE_RENDER]{@linkcode Phaser.Scenes.Events#event:PRE_RENDER}\n6. [RENDER]{@linkcode Phaser.Scenes.Events#event:RENDER}\n\nListen to it from a Scene using `this.events.on('preupdate', listener)`.\n\nA Scene will only run its step if it is active.", + "Phaser.Scenes.Events.READY": "The Scene Systems Ready Event.\n\nThis event is dispatched by a Scene during the Scene Systems start process.\nBy this point in the process the Scene is now fully active and rendering.\nThis event is meant for your game code to use, as all plugins have responded to the earlier 'start' event.\n\nListen to it from a Scene using `this.events.on('ready', listener)`.", + "Phaser.Scenes.Events.REMOVED_FROM_SCENE": "The Game Object Removed from Scene Event.\n\nThis event is dispatched when a Game Object is removed from a Scene.\n\nListen for it from a Scene using `this.events.on('removedfromscene', listener)`.", + "Phaser.Scenes.Events.RENDER": "The Scene Systems Render Event.\n\nThis event is dispatched by a Scene during the main game loop step.\n\nThe event flow for a single step of a Scene is as follows:\n\n1. [PRE_UPDATE]{@linkcode Phaser.Scenes.Events#event:PRE_UPDATE}\n2. [UPDATE]{@linkcode Phaser.Scenes.Events#event:UPDATE}\n3. The `Scene.update` method is called, if it exists\n4. [POST_UPDATE]{@linkcode Phaser.Scenes.Events#event:POST_UPDATE}\n5. [PRE_RENDER]{@linkcode Phaser.Scenes.Events#event:PRE_RENDER}\n6. [RENDER]{@linkcode Phaser.Scenes.Events#event:RENDER}\n\nListen to it from a Scene using `this.events.on('render', listener)`.\n\nA Scene will only render if it is visible.\n\nBy the time this event is dispatched, the Scene will have already been rendered.", + "Phaser.Scenes.Events.RESUME": "The Scene Systems Resume Event.\n\nThis event is dispatched by a Scene when it is resumed from a paused state, either directly via the `resume` method,\nor as an action from another Scene.\n\nListen to it from a Scene using `this.events.on('resume', listener)`.", + "Phaser.Scenes.Events.SHUTDOWN": "The Scene Systems Shutdown Event.\n\nThis event is dispatched by a Scene during the Scene Systems shutdown process.\n\nListen to it from a Scene using `this.events.on('shutdown', listener)`.\n\nYou should free-up any resources that may be in use by your Scene in this event handler, on the understanding\nthat the Scene may, at any time, become active again. A shutdown Scene is not 'destroyed', it's simply not\ncurrently active. Use the [DESTROY]{@linkcode Phaser.Scenes.Events#event:DESTROY} event to completely clear resources.", + "Phaser.Scenes.Events.SLEEP": "The Scene Systems Sleep Event.\n\nThis event is dispatched by a Scene when it is sent to sleep, either directly via the `sleep` method,\nor as an action from another Scene.\n\nListen to it from a Scene using `this.events.on('sleep', listener)`.", + "Phaser.Scenes.Events.START": "The Scene Systems Start Event.\n\nThis event is dispatched by a Scene during the Scene Systems start process. Primarily used by Scene Plugins.\n\nListen to it from a Scene using `this.events.on('start', listener)`.", + "Phaser.Scenes.Events.TRANSITION_COMPLETE": "The Scene Transition Complete Event.\n\nThis event is dispatched by the Target Scene of a transition.\n\nIt happens when the transition process has completed. This occurs when the duration timer equals or exceeds the duration\nof the transition.\n\nListen to it from a Scene using `this.events.on('transitioncomplete', listener)`.\n\nThe Scene Transition event flow is as follows:\n\n1. [TRANSITION_OUT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_OUT} - the Scene that started the transition will emit this event.\n2. [TRANSITION_INIT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_INIT} - the Target Scene will emit this event if it has an `init` method.\n3. [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} - the Target Scene will emit this event after its `create` method is called, OR ...\n4. [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} - the Target Scene will emit this event if it was asleep and has been woken-up to be transitioned to.\n5. [TRANSITION_COMPLETE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_COMPLETE} - the Target Scene will emit this event when the transition finishes.", + "Phaser.Scenes.Events.TRANSITION_INIT": "The Scene Transition Init Event.\n\nThis event is dispatched by the Target Scene of a transition.\n\nIt happens immediately after the `Scene.init` method is called. If the Scene does not have an `init` method,\nthis event is not dispatched.\n\nListen to it from a Scene using `this.events.on('transitioninit', listener)`.\n\nThe Scene Transition event flow is as follows:\n\n1. [TRANSITION_OUT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_OUT} - the Scene that started the transition will emit this event.\n2. [TRANSITION_INIT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_INIT} - the Target Scene will emit this event if it has an `init` method.\n3. [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} - the Target Scene will emit this event after its `create` method is called, OR ...\n4. [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} - the Target Scene will emit this event if it was asleep and has been woken-up to be transitioned to.\n5. [TRANSITION_COMPLETE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_COMPLETE} - the Target Scene will emit this event when the transition finishes.", + "Phaser.Scenes.Events.TRANSITION_OUT": "The Scene Transition Out Event.\n\nThis event is dispatched by a Scene when it initiates a transition to another Scene.\n\nListen to it from a Scene using `this.events.on('transitionout', listener)`.\n\nThe Scene Transition event flow is as follows:\n\n1. [TRANSITION_OUT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_OUT} - the Scene that started the transition will emit this event.\n2. [TRANSITION_INIT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_INIT} - the Target Scene will emit this event if it has an `init` method.\n3. [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} - the Target Scene will emit this event after its `create` method is called, OR ...\n4. [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} - the Target Scene will emit this event if it was asleep and has been woken-up to be transitioned to.\n5. [TRANSITION_COMPLETE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_COMPLETE} - the Target Scene will emit this event when the transition finishes.", + "Phaser.Scenes.Events.TRANSITION_START": "The Scene Transition Start Event.\n\nThis event is dispatched by the Target Scene of a transition, only if that Scene was not asleep.\n\nIt happens immediately after the `Scene.create` method is called. If the Scene does not have a `create` method,\nthis event is dispatched anyway.\n\nIf the Target Scene was sleeping then the [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} event is\ndispatched instead of this event.\n\nListen to it from a Scene using `this.events.on('transitionstart', listener)`.\n\nThe Scene Transition event flow is as follows:\n\n1. [TRANSITION_OUT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_OUT} - the Scene that started the transition will emit this event.\n2. [TRANSITION_INIT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_INIT} - the Target Scene will emit this event if it has an `init` method.\n3. [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} - the Target Scene will emit this event after its `create` method is called, OR ...\n4. [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} - the Target Scene will emit this event if it was asleep and has been woken-up to be transitioned to.\n5. [TRANSITION_COMPLETE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_COMPLETE} - the Target Scene will emit this event when the transition finishes.", + "Phaser.Scenes.Events.TRANSITION_WAKE": "The Scene Transition Wake Event.\n\nThis event is dispatched by the Target Scene of a transition, only if that Scene was asleep before\nthe transition began. If the Scene was not asleep the [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} event is dispatched instead.\n\nListen to it from a Scene using `this.events.on('transitionwake', listener)`.\n\nThe Scene Transition event flow is as follows:\n\n1. [TRANSITION_OUT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_OUT} - the Scene that started the transition will emit this event.\n2. [TRANSITION_INIT]{@linkcode Phaser.Scenes.Events#event:TRANSITION_INIT} - the Target Scene will emit this event if it has an `init` method.\n3. [TRANSITION_START]{@linkcode Phaser.Scenes.Events#event:TRANSITION_START} - the Target Scene will emit this event after its `create` method is called, OR ...\n4. [TRANSITION_WAKE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_WAKE} - the Target Scene will emit this event if it was asleep and has been woken-up to be transitioned to.\n5. [TRANSITION_COMPLETE]{@linkcode Phaser.Scenes.Events#event:TRANSITION_COMPLETE} - the Target Scene will emit this event when the transition finishes.", + "Phaser.Scenes.Events.UPDATE": "The Scene Systems Update Event.\n\nThis event is dispatched by a Scene during the main game loop step.\n\nThe event flow for a single step of a Scene is as follows:\n\n1. [PRE_UPDATE]{@linkcode Phaser.Scenes.Events#event:PRE_UPDATE}\n2. [UPDATE]{@linkcode Phaser.Scenes.Events#event:UPDATE}\n3. The `Scene.update` method is called, if it exists and the Scene is in a Running state, otherwise this is skipped.\n4. [POST_UPDATE]{@linkcode Phaser.Scenes.Events#event:POST_UPDATE}\n5. [PRE_RENDER]{@linkcode Phaser.Scenes.Events#event:PRE_RENDER}\n6. [RENDER]{@linkcode Phaser.Scenes.Events#event:RENDER}\n\nListen to it from a Scene using `this.events.on('update', listener)`.\n\nA Scene will only run its step if it is active.", + "Phaser.Scenes.Events.WAKE": "The Scene Systems Wake Event.\n\nThis event is dispatched by a Scene when it is woken from sleep, either directly via the `wake` method,\nor as an action from another Scene.\n\nListen to it from a Scene using `this.events.on('wake', listener)`.", + "Phaser.Sound.Events.COMPLETE": "The Sound Complete Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they complete playback.\n\nListen to it from a Sound instance using `Sound.on('complete', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('complete', listener);\nmusic.play();\n```", + "Phaser.Sound.Events.DECODED_ALL": "The Audio Data Decoded All Event.\n\nThis event is dispatched by the Web Audio Sound Manager as a result of calling the `decodeAudio` method,\nonce all files passed to the method have been decoded (or errored).\n\nUse `Phaser.Sound.Events#DECODED` to listen for single sounds being decoded, and `DECODED_ALL` to\nlisten for them all completing.\n\nListen to it from the Sound Manager in a Scene using `this.sound.on('decodedall', listener)`, i.e.:\n\n```javascript\nthis.sound.once('decodedall', handler);\nthis.sound.decodeAudio([ audioFiles ]);\n```", + "Phaser.Sound.Events.DECODED": "The Audio Data Decoded Event.\n\nThis event is dispatched by the Web Audio Sound Manager as a result of calling the `decodeAudio` method.\n\nListen to it from the Sound Manager in a Scene using `this.sound.on('decoded', listener)`, i.e.:\n\n```javascript\nthis.sound.on('decoded', handler);\nthis.sound.decodeAudio(key, audioData);\n```", + "Phaser.Sound.Events.DESTROY": "The Sound Destroy Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are destroyed, either\ndirectly or via a Sound Manager.\n\nListen to it from a Sound instance using `Sound.on('destroy', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('destroy', listener);\nmusic.destroy();\n```", + "Phaser.Sound.Events.DETUNE": "The Sound Detune Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their detune value changes.\n\nListen to it from a Sound instance using `Sound.on('detune', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('detune', listener);\nmusic.play();\nmusic.setDetune(200);\n```", + "Phaser.Sound.Events.GLOBAL_DETUNE": "The Sound Manager Global Detune Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched when the `detune` property of the Sound Manager is changed, which globally\nadjusts the detuning of all active sounds.\n\nListen to it from a Scene using: `this.sound.on('rate', listener)`.", + "Phaser.Sound.Events.GLOBAL_MUTE": "The Sound Manager Global Mute Event.\n\nThis event is dispatched by the Sound Manager when its `mute` property is changed, either directly\nor via the `setMute` method. This changes the mute state of all active sounds.\n\nListen to it from a Scene using: `this.sound.on('mute', listener)`.", + "Phaser.Sound.Events.GLOBAL_RATE": "The Sound Manager Global Rate Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched when the `rate` property of the Sound Manager is changed, which globally\nadjusts the playback rate of all active sounds.\n\nListen to it from a Scene using: `this.sound.on('rate', listener)`.", + "Phaser.Sound.Events.GLOBAL_VOLUME": "The Sound Manager Global Volume Event.\n\nThis event is dispatched by the Sound Manager when its `volume` property is changed, either directly\nor via the `setVolume` method. This changes the volume of all active sounds.\n\nListen to it from a Scene using: `this.sound.on('volume', listener)`.", + "Phaser.Sound.Events.LOOPED": "The Sound Looped Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they loop during playback.\n\nListen to it from a Sound instance using `Sound.on('looped', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('looped', listener);\nmusic.setLoop(true);\nmusic.play();\n```\n\nThis is not to be confused with the [LOOP]{@linkcode Phaser.Sound.Events#event:LOOP} event, which only emits when the loop state of a Sound is changed.", + "Phaser.Sound.Events.LOOP": "The Sound Loop Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their loop state is changed.\n\nListen to it from a Sound instance using `Sound.on('loop', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('loop', listener);\nmusic.setLoop(true);\n```\n\nThis is not to be confused with the [LOOPED]{@linkcode Phaser.Sound.Events#event:LOOPED} event, which emits each time a Sound loops during playback.", + "Phaser.Sound.Events.MUTE": "The Sound Mute Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their mute state changes.\n\nListen to it from a Sound instance using `Sound.on('mute', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('mute', listener);\nmusic.play();\nmusic.setMute(true);\n```", + "Phaser.Sound.Events.PAN": "The Sound Pan Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their pan changes.\n\nListen to it from a Sound instance using `Sound.on('pan', listener)`, i.e.:\n\n```javascript\nvar sound = this.sound.add('key');\nsound.on('pan', listener);\nsound.play();\nsound.setPan(0.5);\n```", + "Phaser.Sound.Events.PAUSE_ALL": "The Pause All Sounds Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched when the `pauseAll` method is invoked and after all current Sounds\nhave been paused.\n\nListen to it from a Scene using: `this.sound.on('pauseall', listener)`.", + "Phaser.Sound.Events.PAUSE": "The Sound Pause Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are paused.\n\nListen to it from a Sound instance using `Sound.on('pause', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('pause', listener);\nmusic.play();\nmusic.pause();\n```", + "Phaser.Sound.Events.PLAY": "The Sound Play Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are played.\n\nListen to it from a Sound instance using `Sound.on('play', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('play', listener);\nmusic.play();\n```", + "Phaser.Sound.Events.RATE": "The Sound Rate Change Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their rate changes.\n\nListen to it from a Sound instance using `Sound.on('rate', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('rate', listener);\nmusic.play();\nmusic.setRate(0.5);\n```", + "Phaser.Sound.Events.RESUME_ALL": "The Resume All Sounds Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched when the `resumeAll` method is invoked and after all current Sounds\nhave been resumed.\n\nListen to it from a Scene using: `this.sound.on('resumeall', listener)`.", + "Phaser.Sound.Events.RESUME": "The Sound Resume Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are resumed from a paused state.\n\nListen to it from a Sound instance using `Sound.on('resume', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('resume', listener);\nmusic.play();\nmusic.pause();\nmusic.resume();\n```", + "Phaser.Sound.Events.SEEK": "The Sound Seek Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are seeked to a new position.\n\nListen to it from a Sound instance using `Sound.on('seek', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('seek', listener);\nmusic.play();\nmusic.setSeek(5000);\n```", + "Phaser.Sound.Events.STOP_ALL": "The Stop All Sounds Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched when the `stopAll` method is invoked and after all current Sounds\nhave been stopped.\n\nListen to it from a Scene using: `this.sound.on('stopall', listener)`.", + "Phaser.Sound.Events.STOP": "The Sound Stop Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when they are stopped.\n\nListen to it from a Sound instance using `Sound.on('stop', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('stop', listener);\nmusic.play();\nmusic.stop();\n```", + "Phaser.Sound.Events.UNLOCKED": "The Sound Manager Unlocked Event.\n\nThis event is dispatched by the Base Sound Manager, or more typically, an instance of the Web Audio Sound Manager,\nor the HTML5 Audio Manager. It is dispatched during the update loop when the Sound Manager becomes unlocked. For\nWeb Audio this is on the first user gesture on the page.\n\nListen to it from a Scene using: `this.sound.on('unlocked', listener)`.", + "Phaser.Sound.Events.VOLUME": "The Sound Volume Event.\n\nThis event is dispatched by both Web Audio and HTML5 Audio Sound objects when their volume changes.\n\nListen to it from a Sound instance using `Sound.on('volume', listener)`, i.e.:\n\n```javascript\nvar music = this.sound.add('key');\nmusic.on('volume', listener);\nmusic.play();\nmusic.setVolume(0.5);\n```", + "Phaser.Structs.Events.PROCESS_QUEUE_ADD": "The Process Queue Add Event.\n\nThis event is dispatched by a Process Queue when a new item is successfully moved to its active list.\n\nYou will most commonly see this used by a Scene's Update List when a new Game Object has been added.\n\nIn that instance, listen to this event from within a Scene using: `this.sys.updateList.on('add', listener)`.", + "Phaser.Structs.Events.PROCESS_QUEUE_REMOVE": "The Process Queue Remove Event.\n\nThis event is dispatched by a Process Queue when a new item is successfully removed from its active list.\n\nYou will most commonly see this used by a Scene's Update List when a Game Object has been removed.\n\nIn that instance, listen to this event from within a Scene using: `this.sys.updateList.on('remove', listener)`.", + "Phaser.Textures.Events.ADD": "The Texture Add Event.\n\nThis event is dispatched by the Texture Manager when a texture is added to it.\n\nListen to this event from within a Scene using: `this.textures.on('addtexture', listener)`.", + "Phaser.Textures.Events.ADD_KEY": "The Texture Add Key Event.\n\nThis event is dispatched by the Texture Manager when a texture with the given key is added to it.\n\nListen to this event from within a Scene using: `this.textures.on('addtexture-key', listener)`.", + "Phaser.Textures.Events.ERROR": "The Texture Load Error Event.\n\nThis event is dispatched by the Texture Manager when a texture it requested to load failed.\nThis only happens when base64 encoded textures fail. All other texture types are loaded via the Loader Plugin.\n\nListen to this event from within a Scene using: `this.textures.on('onerror', listener)`.", + "Phaser.Textures.Events.LOAD": "The Texture Load Event.\n\nThis event is dispatched by the Texture Manager when a texture has finished loading on it.\nThis only happens for base64 encoded textures. All other texture types are loaded via the Loader Plugin.\n\nListen to this event from within a Scene using: `this.textures.on('onload', listener)`.\n\nThis event is dispatched after the [ADD]{@linkcode Phaser.Textures.Events#event:ADD} event.", + "Phaser.Textures.Events.READY": "This internal event signifies that the Texture Manager is now ready and the Game can continue booting.\n\nWhen a Phaser Game instance is booting for the first time, the Texture Manager has to wait on a couple of non-blocking\nasync events before it's fully ready to carry on. When those complete the Texture Manager emits this event via the Game\ninstance, which tells the Game to carry on booting.", + "Phaser.Textures.Events.REMOVE": "The Texture Remove Event.\n\nThis event is dispatched by the Texture Manager when a texture is removed from it.\n\nListen to this event from within a Scene using: `this.textures.on('removetexture', listener)`.\n\nIf you have any Game Objects still using the removed texture, they will start throwing\nerrors the next time they try to render. Be sure to clear all use of the texture in this event handler.", + "Phaser.Textures.Events.REMOVE_KEY": "The Texture Remove Key Event.\n\nThis event is dispatched by the Texture Manager when a texture with the given key is removed from it.\n\nListen to this event from within a Scene using: `this.textures.on('removetexture-key', listener)`.\n\nIf you have any Game Objects still using the removed texture, they will start throwing\nerrors the next time they try to render. Be sure to clear all use of the texture in this event handler.", + "Phaser.Tweens.Events.TWEEN_ACTIVE": "The Tween Active Event.\n\nThis event is dispatched by a Tween when it becomes active within the Tween Manager.\n\nAn 'active' Tween is one that is now progressing, although it may not yet be updating\nany target properties, due to settings such as `delay`. If you need an event for when\nthe Tween starts actually updating its first property, see `TWEEN_START`.\n\nListen to it from a Tween instance using `Tween.on('active', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.create({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000\n});\ntween.on('active', listener);\nthis.tweens.existing(tween);\n```\n\nNote that this event is usually dispatched already by the time you call `this.tweens.add()`, and is\nmeant for use with `tweens.create()` and/or `tweens.existing()`.", + "Phaser.Tweens.Events.TWEEN_COMPLETE": "The Tween Complete Event.\n\nThis event is dispatched by a Tween when it completes playback entirely, factoring in repeats and loops.\n\nIf the Tween has been set to loop or repeat infinitely, this event will not be dispatched\nunless the `Tween.stop` method is called.\n\nIf a Tween has a `completeDelay` set, this event will fire after that delay expires.\n\nListen to it from a Tween instance using `Tween.on('complete', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000\n});\ntween.on('complete', listener);\n```", + "Phaser.Tweens.Events.TWEEN_LOOP": "The Tween Loop Event.\n\nThis event is dispatched by a Tween when it loops.\n\nThis event will only be dispatched if the Tween has a loop count set.\n\nIf a Tween has a `loopDelay` set, this event will fire after that delay expires.\n\nThe difference between `loop` and `repeat` is that `repeat` is a property setting,\nwhere-as `loop` applies to the entire Tween.\n\nListen to it from a Tween instance using `Tween.on('loop', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000,\n loop: 6\n});\ntween.on('loop', listener);\n```", + "Phaser.Tweens.Events.TWEEN_PAUSE": "The Tween Pause Event.\n\nThis event is dispatched by a Tween when it is paused.\n\nListen to it from a Tween instance using `Tween.on('pause', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n ease: 'Power1',\n duration: 3000,\n x: 600\n});\ntween.on('pause', listener);\n// At some point later ...\ntween.pause();\n```", + "Phaser.Tweens.Events.TWEEN_REPEAT": "The Tween Repeat Event.\n\nThis event is dispatched by a Tween when one of the properties it is tweening repeats.\n\nThis event will only be dispatched if the Tween has a property with a repeat count set.\n\nIf a Tween has a `repeatDelay` set, this event will fire after that delay expires.\n\nThe difference between `loop` and `repeat` is that `repeat` is a property setting,\nwhere-as `loop` applies to the entire Tween.\n\nListen to it from a Tween instance using `Tween.on('repeat', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000,\n repeat: 4\n});\ntween.on('repeat', listener);\n```", + "Phaser.Tweens.Events.TWEEN_RESUME": "The Tween Resume Event.\n\nThis event is dispatched by a Tween when it is resumed from a paused state.\n\nListen to it from a Tween instance using `Tween.on('resume', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n ease: 'Power1',\n duration: 3000,\n x: 600\n});\ntween.on('resume', listener);\n// At some point later ...\ntween.resume();\n```", + "Phaser.Tweens.Events.TWEEN_START": "The Tween Start Event.\n\nThis event is dispatched by a Tween when it starts tweening its first property.\n\nA Tween will only emit this event once, as it can only start once.\n\nIf a Tween has a `delay` set, this event will fire after that delay expires.\n\nListen to it from a Tween instance using `Tween.on('start', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000\n});\ntween.on('start', listener);\n```", + "Phaser.Tweens.Events.TWEEN_STOP": "The Tween Stop Event.\n\nThis event is dispatched by a Tween when it is stopped.\n\nListen to it from a Tween instance using `Tween.on('stop', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000\n});\ntween.on('stop', listener);\n```", + "Phaser.Tweens.Events.TWEEN_UPDATE": "The Tween Update Event.\n\nThis event is dispatched by a Tween every time it updates _any_ of the properties it is tweening.\n\nA Tween that is changing 3 properties of a target will emit this event 3 times per change, once per property.\n\n**Note:** This is a very high frequency event and may be dispatched multiple times, every single frame.\n\nListen to it from a Tween instance using `Tween.on('update', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000,\n});\ntween.on('update', listener);\n```", + "Phaser.Tweens.Events.TWEEN_YOYO": "The Tween Yoyo Event.\n\nThis event is dispatched by a Tween whenever a property it is tweening yoyos.\n\nThis event will only be dispatched if the Tween has a property with `yoyo` set.\n\nIf the Tween has a `hold` value, this event is dispatched when the hold expires.\n\nThis event is dispatched for every property, and for every target, that yoyos.\nFor example, if a Tween was updating 2 properties and had 10 targets, this event\nwould be dispatched 20 times (twice per target). So be careful how you use it!\n\nListen to it from a Tween instance using `Tween.on('yoyo', listener)`, i.e.:\n\n```javascript\nvar tween = this.tweens.add({\n targets: image,\n x: 500,\n ease: 'Power1',\n duration: 3000,\n yoyo: true\n});\ntween.on('yoyo', listener);\n```" +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/plugin.json b/source/editor/plugins/phasereditor2d.scene/plugin.json index 2787e9f33..eb22703ff 100644 --- a/source/editor/plugins/phasereditor2d.scene/plugin.json +++ b/source/editor/plugins/phasereditor2d.scene/plugin.json @@ -1,7 +1,8 @@ { "id": "phasereditor2d.scene", "styles": [ - "styles/SceneEditor.css" + "styles/SceneEditor.css", + "styles/EventPropertyDialog.css" ], "scripts": [ "libs/localforage.js", diff --git a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts index 7f42def3e..14f912bcb 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ScenePlugin.ts @@ -87,6 +87,7 @@ namespace phasereditor2d.scene { private _sceneFinder: core.json.SceneFinder; private _docs: phasereditor2d.ide.core.PhaserDocs; + private _eventsDocs: phasereditor2d.ide.core.PhaserDocs; static getInstance() { return this._instance; @@ -95,7 +96,12 @@ namespace phasereditor2d.scene { private constructor() { super("phasereditor2d.scene"); - this._docs = new phasereditor2d.ide.core.PhaserDocs(this, "data/phaser-docs.json"); + this._docs = new phasereditor2d.ide.core.PhaserDocs( + this, + "data/phaser-docs.json", + "data/events-docs.json"); + + this._eventsDocs = new phasereditor2d.ide.core.PhaserDocs(this, "data/events-docs.json"); } async starting() { @@ -130,9 +136,15 @@ namespace phasereditor2d.scene { } getPhaserDocs() { + return this._docs; } + getPhaserEventsDocs() { + + return this._eventsDocs; + } + registerExtensions(reg: colibri.ExtensionRegistry) { this._sceneFinder = new core.json.SceneFinder(); @@ -150,6 +162,11 @@ namespace phasereditor2d.scene { await ScenePlugin.getInstance().getPhaserDocs().preload(); })); + reg.addExtension(new ide.PluginResourceLoaderExtension(async () => { + + await ScenePlugin.getInstance().getPhaserEventsDocs().preload(); + })); + // preload UserComponent files reg.addExtension(new ide.PluginResourceLoaderExtension(async () => { @@ -355,10 +372,16 @@ namespace phasereditor2d.scene { reg.addExtension(new ui.editor.properties.SceneEditorPropertySectionExtension( page => new ui.sceneobjects.GameObjectVariableSection(page), page => new ui.sceneobjects.PrefabObjectVariableSection(page), - page => new ui.sceneobjects.NestedPrefabObjectVariableSection(page), - page => new ui.sceneobjects.PrefabInstanceSection(page), - page => new ui.sceneobjects.ObjectUserComponentsSection(page), - page => new ui.sceneobjects.ObjectSingleUserComponentSection(page), + page => new ui.sceneobjects.NestedPrefabObjectVariableSection(page) + )); + + // dynamic component sections + + reg.addExtension(new ui.editor.properties.DynamicUserSectionExtension()); + + // more property sections + + reg.addExtension(new ui.editor.properties.SceneEditorPropertySectionExtension( page => new ui.sceneobjects.ListVariableSection(page), page => new ui.sceneobjects.GameObjectListSection(page), page => new ui.sceneobjects.ChildrenSection(page), @@ -384,7 +407,6 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.ArcadeGeometrySection(page), page => new ui.sceneobjects.ArcadeBodyMovementSection(page), page => new ui.sceneobjects.ArcadeBodyCollisionSection(page), - page => new ui.sceneobjects.TextureSection(page), page => new ui.sceneobjects.TextContentSection(page), page => new ui.sceneobjects.TextSection(page), page => new ui.sceneobjects.BitmapTextSection(page), @@ -399,7 +421,8 @@ namespace phasereditor2d.scene { page => new ui.sceneobjects.TriangleSection(page), page => new ui.sceneobjects.PolygonSection(page), page => new ui.sceneobjects.ColliderSection(page), - page => new ui.sceneobjects.KeyboardKeySection(page) + page => new ui.sceneobjects.KeyboardKeySection(page), + page => new ui.sceneobjects.TextureSection(page), )); // scene tools @@ -469,7 +492,7 @@ namespace phasereditor2d.scene { return settings; } - createUserPropertyTypes() { + getUserPropertyTypes() { // TODO: we should do this via extension return [ @@ -477,10 +500,12 @@ namespace phasereditor2d.scene { new ui.sceneobjects.StringPropertyType(), new ui.sceneobjects.BooleanPropertyType(), new ui.sceneobjects.ColorPropertyType(), + new ui.sceneobjects.KeyCodePropertyType(), new ui.sceneobjects.ExpressionPropertyType(), new ui.sceneobjects.OptionPropertyType(), new ui.sceneobjects.ObjectVarPropertyType(), new ui.sceneobjects.ObjectConstructorPropertyType(), + new ui.sceneobjects.EventPropertyType(), new ui.sceneobjects.TextureConfigPropertyType(), new ui.sceneobjects.AnimationKeyPropertyType(), new ui.sceneobjects.AudioKeyPropertyType(), @@ -489,9 +514,9 @@ namespace phasereditor2d.scene { ]; } - createUserPropertyType(typeId: string) { + getUserPropertyType(typeId: string) { - return this.createUserPropertyTypes().find(t => t.getId() === typeId); + return this.getUserPropertyTypes().find(t => t.getId() === typeId); } getPrefabColor() { diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts index e24e970e7..b3afd7b91 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/code/SceneCodeDOMBuilder.ts @@ -983,7 +983,8 @@ namespace phasereditor2d.scene.core.code { objectVarName: varname, prefabSerializer: prefabSerializer, unit: this._unit, - sceneFile: this._sceneFile + sceneFile: this._sceneFile, + obj }); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts index b5ba65213..07c7c8cd4 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/core/json/SceneFinder.ts @@ -38,6 +38,12 @@ namespace phasereditor2d.scene.core.json { component: usercomponent.UserComponent } + export interface IUserEvent { + name: string; + help: string; + file: io.FilePath; + } + export class SceneFinder { private _prefabObjectId_ObjectData_Map: Map; @@ -245,7 +251,7 @@ namespace phasereditor2d.scene.core.json { sceneFiles.push(file); } catch (e) { - + console.error(`SceneDataTable: parsing file ${file.getFullName()}. Error: ${(e as Error).message}`); } @@ -440,13 +446,13 @@ namespace phasereditor2d.scene.core.json { return this.getPrefabHierarchy2(prefabId, []); } - isPrefabVariant(basePrefabFile: io.FilePath, superPrefanFile: io.FilePath) { + isPrefabVariant(basePrefabFile: io.FilePath, superPrefabFile: io.FilePath) { const basePrefabId = this.getPrefabId(basePrefabFile); const result = this.getPrefabHierarchy(basePrefabId); - if (result.indexOf(superPrefanFile) >= 0) { + if (result.indexOf(superPrefabFile) >= 0) { return true; } @@ -538,5 +544,44 @@ namespace phasereditor2d.scene.core.json { console.log(this._prefabObjectId_ObjectData_Map.get(id)); } } + + async findUserEvents(): Promise { + + const result: IUserEvent[] = []; + + for (const file of colibri.ui.ide.FileUtils.getAllFiles()) { + + if (file.getName() === "events.txt") { + + const content = await colibri.ui.ide.FileUtils.preloadAndGetFileString(file); + const lines = content.split("\n"); + + for (let line of lines) { + + line = line.trim(); + + if (line.length === 0 || line.startsWith("#")) { + + continue; + } + + let name = line; + let help = ""; + + const i = line.indexOf(" "); + + if (i > 0) { + + name = line.substring(0, i); + help = line.substring(i + 1); + } + + result.push({ name, help, file }); + } + } + } + + return result; + } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts index 2e91883c5..3e04363ce 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/Scene.ts @@ -84,20 +84,29 @@ namespace phasereditor2d.scene.ui { for (const objData of list) { - const ext = ScenePlugin.getInstance().getPlainObjectExtensionByObjectType(objData.type); + this.readPlainObject(objData); + } + } - if (ext) { + readPlainObject(objData: core.json.IScenePlainObjectData) { - const plainObject = ext.createPlainObjectWithData({ - scene: this, - data: objData - }); + const ext = ScenePlugin.getInstance().getPlainObjectExtensionByObjectType(objData.type); - plainObject.getEditorSupport().readJSON(objData); + if (ext) { - this.addPlainObject(plainObject); - } + const plainObject = ext.createPlainObjectWithData({ + scene: this, + data: objData + }); + + plainObject.getEditorSupport().readJSON(objData); + + this.addPlainObject(plainObject); + + return plainObject; } + + return undefined; } removePlainObjects(objects: sceneobjects.IScenePlainObject[]) { @@ -371,9 +380,11 @@ namespace phasereditor2d.scene.ui { const nameMaker = new colibri.ui.ide.utils.NameMaker((obj: any) => { - if (sceneobjects.isGameObject(obj)) { + const objES = sceneobjects.EditorSupport.getEditorSupport(obj); - return (obj as sceneobjects.ISceneGameObject).getEditorSupport().getLabel(); + if (objES) { + + return objES.getLabel(); } return (obj as sceneobjects.ObjectList).getLabel(); @@ -389,6 +400,8 @@ namespace phasereditor2d.scene.ui { || objES.isPrefeabInstanceAppendedChild;; }); + nameMaker.update(this._plainObjects); + for (const list of this._objectLists.getLists()) { nameMaker.update([list]); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts index 9c9364ff1..68e307866 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/SceneMaker.ts @@ -72,12 +72,13 @@ namespace phasereditor2d.scene.ui { return undefined; } - afterDropObjects(prefabObj: sceneobjects.ISceneGameObject, dropObjects: sceneobjects.ISceneGameObject[]) { + afterDropObjects(prefabObj: sceneobjects.ISceneGameObject, dropObjects: sceneobjects.ISceneGameObject[], alternativeSelection?: sceneobjects.ISceneGameObject[]) { let dropInContainer: sceneobjects.Container; let dropInObj: sceneobjects.ISceneGameObject; - const selection = this._editorScene.getEditor().getSelectedGameObjects(); + const selection = alternativeSelection + || this._editorScene.getEditor().getSelectedGameObjects(); const areDropingScriptNodes = dropObjects.filter(obj => obj instanceof sceneobjects.ScriptNode).length === dropObjects.length; @@ -318,7 +319,9 @@ namespace phasereditor2d.scene.ui { label: "temporal" }); - if (obj.getEditorSupport().isUnlockedProperty(sceneobjects.TransformComponent.x)) { + const objES = obj.getEditorSupport(); + + if (objES.isUnlockedProperty(sceneobjects.TransformComponent.x)) { const { x, y } = this.getCanvasCenterPoint(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AbstractAddPrefabPropertyDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AbstractAddPrefabPropertyDialog.ts new file mode 100644 index 000000000..460198a55 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AbstractAddPrefabPropertyDialog.ts @@ -0,0 +1,49 @@ +namespace phasereditor2d.scene.ui.dialogs { + + import controls = colibri.ui.controls; + + export abstract class AbstractAddPrefabPropertyDialog extends controls.dialogs.ViewerDialog { + + constructor() { + super(new UserPropertyTypesViewer(), false); + + this.setSize(300, 400, true); + } + + create(hideParentDialog?: boolean): void { + + super.create(hideParentDialog); + + this.setTitle("Add Property"); + + this.enableButtonOnlyWhenOneElementIsSelected(this.addOpenButton("Add Property", sel => { + + this.addProperty(sel[0] as sceneobjects.UserPropertyType); + })); + + this.addCancelButton(); + } + + protected abstract addProperty(propType: sceneobjects.UserPropertyType): void; + } + + class UserPropertyTypesViewer extends controls.viewers.TreeViewer { + + constructor() { + super("phasereditor2d.scene.ui.dialogs.UserPropertyTypesViewer"); + + this.setLabelProvider(new UserPropertyTypeLabelProvider()); + this.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + this.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider()); + this.setInput(ScenePlugin.getInstance().getUserPropertyTypes()); + } + } + + class UserPropertyTypeLabelProvider implements controls.viewers.ILabelProvider { + + getLabel(obj: ui.sceneobjects.UserPropertyType): string { + + return `${obj.getName()} Property`; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AddPrefabPropertyDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AddPrefabPropertyDialog.ts new file mode 100644 index 000000000..6c6a3c902 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/dialogs/AddPrefabPropertyDialog.ts @@ -0,0 +1,21 @@ +/// + +namespace phasereditor2d.scene.ui.dialogs { + + export class AddPrefabPropertyDialog extends AbstractAddPrefabPropertyDialog { + + protected override addProperty(propType: sceneobjects.UserPropertyType) { + + const editor = colibri.Platform.getWorkbench().getActiveEditor() as ui.editor.SceneEditor; + + ui.editor.properties.ChangePrefabPropertiesOperation.runPropertiesOperation(editor, (props) => { + + const prop = props.createProperty(propType); + + props.add(prop); + + editor.setSelection([prop]); + }); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/ClipboardManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/ClipboardManager.ts index 41f885d53..2670fc6b6 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/ClipboardManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/ClipboardManager.ts @@ -4,7 +4,7 @@ namespace phasereditor2d.scene.ui.editor { export interface IClipboardItem { - type: string; + type: "ISceneObject" | "IScenePlainObject" | "ObjectList" | "PrefabProperty"; data: object; } @@ -31,12 +31,69 @@ namespace phasereditor2d.scene.ui.editor { ClipboardManager._clipboard = []; + this.copyGameObjects(); + + this.copyPlainObjects(); + + this.copyObjectLists(); + + this.copyPrefabProperties(); + } + + private copyPlainObjects() { + + for(const obj of this._editor.getSelectedPlainObjects()) { + + const data: any = {}; + + obj.getEditorSupport().writeJSON(data); + + ClipboardManager._clipboard.push({ + data, + type: "IScenePlainObject" + }); + } + } + + private copyPrefabProperties() { + + for (const prop of this._editor.getSelectedPrefabProperties()) { + + const data = {}; + + prop.writeJSON(data); + + ClipboardManager._clipboard.push({ + data, + type: "PrefabProperty" + }); + } + } + + private copyObjectLists() { + + for (const list of this._editor.getSelectedLists()) { + + const listData = {} as any; + list.writeJSON(listData); + + ClipboardManager._clipboard.push({ + type: "ObjectList", + data: listData + }); + } + } + + private copyGameObjects() { + let minX = Number.MAX_SAFE_INTEGER; let minY = Number.MAX_SAFE_INTEGER; const p = new Phaser.Math.Vector2(); - for (const obj of this._editor.getSelectedGameObjects()) { + const gameObjects = this._editor.getSelectedGameObjects(); + + for (const obj of gameObjects) { const sprite = obj as unknown as Phaser.GameObjects.Sprite; @@ -54,7 +111,7 @@ namespace phasereditor2d.scene.ui.editor { minY = Math.min(minY, p.y); } - for (const obj of this._editor.getSelectedGameObjects()) { + for (const obj of gameObjects) { const objData = {} as any; @@ -83,17 +140,6 @@ namespace phasereditor2d.scene.ui.editor { data: objData }); } - - for (const list of this._editor.getSelectedLists()) { - - const listData = {} as any; - list.writeJSON(listData); - - ClipboardManager._clipboard.push({ - type: "ObjectList", - data: listData - }); - } } paste(pasteInPlace: boolean) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/DropManager.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/DropManager.ts index 25ab1fc0c..f3353f522 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/DropManager.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/DropManager.ts @@ -65,7 +65,9 @@ namespace phasereditor2d.scene.ui.editor { ide.Workbench.getWorkbench().setActivePart(this._editor); } - async createWithDropEvent(dropAssetArray: any[], offsetX: number, offsetY: number) { + async createWithDropEvent( + dropAssetArray: any[], offsetX: number, offsetY: number, + alternativeSelection?: sceneobjects.ISceneGameObject[]) { const scene = this._editor.getScene(); @@ -134,11 +136,12 @@ namespace phasereditor2d.scene.ui.editor { if (sceneMaker.isPrefabFile(data)) { const sprite = await sceneMaker.createPrefabInstanceWithFile(data); + const spriteES = sprite.getEditorSupport(); - if (sprite.getEditorSupport().hasComponent(sceneobjects.TransformComponent)) { + if (spriteES.hasComponent(sceneobjects.TransformComponent)) { - sprite.getEditorSupport().setUnlockedProperty(sceneobjects.TransformComponent.x, true); - sprite.getEditorSupport().setUnlockedProperty(sceneobjects.TransformComponent.y, true); + spriteES.setUnlockedProperty(sceneobjects.TransformComponent.x, true); + spriteES.setUnlockedProperty(sceneobjects.TransformComponent.y, true); (sprite as any as Phaser.GameObjects.Image).setPosition(x, y); } @@ -238,7 +241,7 @@ namespace phasereditor2d.scene.ui.editor { sceneObjectES.setLabel(label); } - scene.getMaker().afterDropObjects(prefabObj, newSprites); + scene.getMaker().afterDropObjects(prefabObj, newSprites, alternativeSelection); sceneobjects.sortGameObjects(newSprites); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts index 4c4b68a56..ff4167fe9 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/SceneEditorMenuCreator.ts @@ -27,7 +27,7 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createPrefabMenu()); - menu.addMenu(this.createScriptsMenu()); + menu.addMenu(this.createScriptingMenu()); menu.addMenu(this.createTypeMenu()); @@ -39,10 +39,6 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createParentMenu()); - menu.addMenu(this.createDepthMenu()); - - menu.addMenu(this.createListMenu()); - menu.addSeparator(); menu.addMenu(this.createSnappingMenu()); @@ -56,13 +52,18 @@ namespace phasereditor2d.scene.ui.editor { menu.addMenu(this.createCompilerMenu()); } - createScriptsMenu(): controls.Menu { + createScriptingMenu(): controls.Menu { - const menu = new controls.Menu("Scripts"); + const menu = new controls.Menu("Scripting"); menu.addCommand(commands.CMD_OPEN_ADD_SCRIPT_DIALOG); menu.addCommand(commands.CMD_OPEN_SCRIPT_DIALOG); + menu.addSeparator(); + + menu.addCommand(commands.CMD_ADD_USER_COMPONENT); + menu.addCommand(commands.CMD_BROWSE_USER_COMPONENTS); + return menu; } @@ -170,34 +171,6 @@ namespace phasereditor2d.scene.ui.editor { return menu; } - private createDepthMenu(): controls.Menu { - - const menu = new controls.Menu("Depth"); - - for (const move of ["Up", "Down", "Top", "Bottom"]) { - - const id = "phasereditor2d.scene.ui.editor.commands.Depth" + move; - - menu.addCommand(id); - } - - return menu; - } - - private createListMenu(): controls.Menu { - - const menu = new controls.Menu("Object List"); - - for (const move of ["Up", "Down", "Top", "Bottom"]) { - - const id = "phasereditor2d.scene.ui.editor.commands.ListOrder" + move; - - menu.addCommand(id); - } - - return menu; - } - private createEditMenu() { const menu = new controls.Menu("Edit"); @@ -213,6 +186,13 @@ namespace phasereditor2d.scene.ui.editor { menu.addCommand(commands.CMD_PASTE_IN_PLACE); menu.addCommand(colibri.ui.ide.actions.CMD_DELETE); + menu.addSeparator(); + + menu.addCommand(commands.CMD_SORT_OBJ_UP); + menu.addCommand(commands.CMD_SORT_OBJ_DOWN); + menu.addCommand(commands.CMD_SORT_OBJ_TOP); + menu.addCommand(commands.CMD_SORT_OBJ_BOTTOM); + return menu; } @@ -317,6 +297,8 @@ namespace phasereditor2d.scene.ui.editor { menu.addCommand(commands.CMD_OPEN_PREFAB); menu.addCommand(commands.CMD_CREATE_PREFAB_WITH_OBJECT); + menu.addSeparator(); + menu.addCommand(commands.CMD_ADD_PREFAB_PROPERTY); return menu; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts index eb27e94de..e456bea34 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/commands/SceneEditorCommands.ts @@ -60,6 +60,19 @@ namespace phasereditor2d.scene.ui.editor.commands { export const CMD_OPEN_ADD_SCRIPT_DIALOG = "phasereditor2d.scene.ui.editor.commands.OpenAddScriptDialog"; export const CMD_PREVIEW_SCENE = "phasereditor2d.scene.ui.editor.commands.PreviewScene"; export const CMD_EDIT_HIT_AREA = "phasereditor2d.scene.ui.editor.commands.ResizeHitArea"; + export const CMD_ADD_PREFAB_PROPERTY = "phasereditor2d.scene.ui.editor.commands.AddPrefabProperty"; + export const CMD_SORT_OBJ_UP = "phasereditor2d.scene.ui.editor.commands.SortObjectUp"; + export const CMD_SORT_OBJ_DOWN = "phasereditor2d.scene.ui.editor.commands.SortObjectDown"; + export const CMD_SORT_OBJ_TOP = "phasereditor2d.scene.ui.editor.commands.SortObjectTop"; + export const CMD_SORT_OBJ_BOTTOM = "phasereditor2d.scene.ui.editor.commands.SortObjectBottom"; + export const CMD_ADD_USER_COMPONENT = "phasereditor2d.scene.ui.editor.commands.AddUserComponent"; + export const CMD_BROWSE_USER_COMPONENTS = "phasereditor2d.scene.ui.editor.commands.BrowseUserComponents"; + + function isCommandDialogActive() { + + return colibri.Platform.getWorkbench() + .getActiveDialog() instanceof controls.dialogs.CommandDialog + } function isSceneScope(args: colibri.ui.ide.commands.HandlerArgs) { @@ -109,6 +122,24 @@ namespace phasereditor2d.scene.ui.editor.commands { return args.activeEditor && args.activeEditor.getSelection().length > 0; } + function onlyGameObjectsSelected(args: colibri.ui.ide.commands.HandlerArgs) { + + if (args.activeEditor instanceof SceneEditor) { + + for (const obj of args.activeEditor.getSelection()) { + + if (!sceneobjects.isGameObject(obj)) { + + return false; + } + } + + return args.activeEditor.getSelection().length > 0; + } + + return false; + } + export class SceneEditorCommands { static registerCommands(manager: colibri.ui.ide.commands.CommandManager) { @@ -138,13 +169,15 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerOriginCommands(manager); - this.registerDepthCommands(manager); + this.registerGameObjectDepthCommands(manager); + + this.registerPlainObjectOrderCommands(manager); this.registerListCommands(manager); this.registerTypeCommands(manager); - this.registerMoveObjectCommands(manager); + this.registerTranslateObjectCommands(manager); this.registerTextureCommands(manager); @@ -153,6 +186,228 @@ namespace phasereditor2d.scene.ui.editor.commands { this.registerArcadeCommands(manager); this.registerScriptNodeCommands(manager); + + this.registerUserComponentCommands(manager); + + this.registerPrefabCommands(manager); + + this.registerPropertiesCommands(manager); + } + + static registerPlainObjectOrderCommands(manager: colibri.ui.ide.commands.CommandManager) { + + const moves: [undo.DepthMove, string][] = [ + ["Up", CMD_SORT_OBJ_UP], + ["Down", CMD_SORT_OBJ_DOWN], + ["Top", CMD_SORT_OBJ_TOP], + ["Bottom", CMD_SORT_OBJ_BOTTOM] + ]; + + for (const tuple of moves) { + + const move = tuple[0]; + const cmd = tuple[1]; + + manager.addHandlerHelper(cmd, + // testFunc + args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && undo.PlainObjectOrderOperation.allow(args.activeEditor as any, move), + // execFunc + args => args.activeEditor.getUndoManager().add( + new undo.PlainObjectOrderOperation(args.activeEditor as editor.SceneEditor, move) + )); + } + } + + private static registerPrefabCommands(manager: colibri.ui.ide.commands.CommandManager) { + + manager.add({ + command: { + id: CMD_ADD_PREFAB_PROPERTY, + name: "Add Prefab Property", + category: CAT_SCENE_EDITOR, + tooltip: "Add a new property to the current prefab" + }, + handler: { + testFunc: args => { + + if (isSceneScope(args)) { + + const editor = args.activeEditor as SceneEditor; + + return editor.getScene().isPrefabSceneType(); + } + + return false; + }, + executeFunc: args => { + + const editor = args.activeEditor as SceneEditor; + + const dialog = new ui.dialogs.AddPrefabPropertyDialog(); + dialog.create(); + + // ui.editor.properties.PrefabPropertySection.runPropertiesOperation(editor, () => { + + // // TODO: show the Add Property dialog + + // }, true); + } + } + }) + } + + private static registerUserComponentCommands(manager: colibri.ui.ide.commands.CommandManager) { + + // add user component + + manager.add({ + + command: { + id: CMD_ADD_USER_COMPONENT, + category: CAT_SCENE_EDITOR, + name: "Add User Component", + tooltip: "Pick a User Component and add it to the selected objects" + }, + keys: { + key: "KeyM", + keyLabel: "M" + }, + handler: { + testFunc: onlyGameObjectsSelected, + executeFunc: args => { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + const editor = args.activeEditor as SceneEditor; + + const editorCompList = args.activeEditor.getSelection() + .map(obj => sceneobjects.GameObjectEditorSupport.getObjectComponent(obj, sceneobjects.UserComponentsEditorComponent) as sceneobjects.UserComponentsEditorComponent); + + const used = new Set( + [...editorCompList + .flatMap(editorComp => editorComp.getLocalUserComponents()) + .map(info => info.component.getName()), + + ...editorCompList.flatMap(editorComp => editorComp.getPrefabUserComponents()) + .flatMap(info => info.components) + .map(c => c.getName()) + ] + ); + + class ContentProvider implements controls.viewers.ITreeContentProvider { + + getRoots(input: any): any[] { + + return finder.getUserComponentsModels() + .filter(info => info.model.getComponents().filter(c => !used.has(c.getName())).length > 0); + } + + getChildren(parentObj: core.json.IUserComponentsModelInfo | usercomponent.UserComponent): any[] { + + if (parentObj instanceof usercomponent.UserComponent) { + + return []; + } + + return parentObj.model.getComponents().filter(c => !used.has(c.getName())); + } + } + + const viewer = new controls.viewers.TreeViewer("UserComponentInstancePropertySection.addComponentDialogViewer"); + + viewer.setStyledLabelProvider({ + getStyledTexts: (obj: usercomponent.UserComponent | core.json.IUserComponentsModelInfo, dark) => { + + const theme = controls.Controls.getTheme(); + + if (obj instanceof usercomponent.UserComponent) { + + return [{ + text: obj.getName(), + color: theme.viewerForeground + }]; + } + + return [{ + text: obj.file.getNameWithoutExtension(), + color: theme.viewerForeground + }, { + text: " - " + obj.file.getParent().getProjectRelativeName() + .split("/").filter(s => s !== "").reverse().join("/"), + color: theme.viewerForeground + "90" + }]; + } + }); + + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider( + (obj: core.json.IUserComponentsModelInfo | usercomponent.UserComponent) => + new controls.viewers.IconImageCellRenderer( + obj instanceof usercomponent.UserComponent ? + ScenePlugin.getInstance().getIcon(ICON_USER_COMPONENT) + : colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER)))); + + viewer.setContentProvider(new ContentProvider()); + + viewer.setInput([]); + + viewer.expandRoots(false); + + const dlg = new controls.dialogs.ViewerDialog(viewer, false); + + dlg.setSize(undefined, 400, true); + + dlg.create(); + + dlg.setTitle("Add User Component"); + + dlg.enableButtonOnlyWhenOneElementIsSelected(dlg.addOpenButton("Add Component", () => { + + const selComp = viewer.getSelectionFirstElement() as usercomponent.UserComponent; + + if (selComp) { + + + editor.getUndoManager().add(new ui.editor.undo.SimpleSceneSnapshotOperation(editor, () => { + + for (const editorComp of editorCompList) { + + editorComp.addUserComponent(selComp.getName()); + } + })); + + // section.updateWithSelection(); + editor.dispatchSelectionChanged(); + } + }), obj => obj instanceof usercomponent.UserComponent); + + dlg.addCancelButton(); + } + } + }); + + // browse user component + manager.add({ + command: { + id: CMD_BROWSE_USER_COMPONENTS, + category: CAT_SCENE_EDITOR, + name: "Browse User Components", + tooltip: "Browse all user components in the scene's objects." + }, + keys: { + key: "KeyM", + shift: true, + keyLabel: "M" + }, + handler: { + testFunc: isSceneScope, + executeFunc: args => { + + const dlg = new sceneobjects.BrowseUserComponentsDialog( + args.activeEditor as SceneEditor); + dlg.create(); + } + } + }) } private static registerScriptNodeCommands(manager: colibri.ui.ide.commands.CommandManager) { @@ -897,7 +1152,8 @@ namespace phasereditor2d.scene.ui.editor.commands { } }, keys: { - key: "KeyM" + shift: true, + key: "KeyF" } }); } @@ -1038,9 +1294,59 @@ namespace phasereditor2d.scene.ui.editor.commands { args => args.activeEditor.getUndoManager() .add(new undo.DeleteOperation(args.activeEditor as SceneEditor)) ); + + // sort + + manager.add({ + command: { + id: CMD_SORT_OBJ_UP, + name: "Move Up", + tooltip: "Move up object in the list.", + category: CAT_SCENE_EDITOR + }, + keys: { + key: "PageUp" + } + }); + + manager.add({ + command: { + id: CMD_SORT_OBJ_DOWN, + name: "Move Down", + tooltip: "Move down object in the list.", + category: CAT_SCENE_EDITOR + }, + keys: { + key: "PageDown" + } + }); + + manager.add({ + command: { + id: CMD_SORT_OBJ_TOP, + name: "Move Top", + tooltip: "Move top object in the list.", + category: CAT_SCENE_EDITOR + }, + keys: { + key: "Home" + } + }); + + manager.add({ + command: { + id: CMD_SORT_OBJ_BOTTOM, + name: "Move Bottom", + tooltip: "Move bottom object in the list.", + category: CAT_SCENE_EDITOR + }, + keys: { + key: "End" + } + }); } - private static registerMoveObjectCommands(manager: colibri.ui.ide.commands.CommandManager) { + private static registerTranslateObjectCommands(manager: colibri.ui.ide.commands.CommandManager) { class Operation extends undo.SceneSnapshotOperation { @@ -1106,6 +1412,11 @@ namespace phasereditor2d.scene.ui.editor.commands { return false; } + if (isCommandDialogActive()) { + + return false; + } + if (args.activeEditor.getSelection().length === 0) { return false; @@ -1985,36 +2296,57 @@ namespace phasereditor2d.scene.ui.editor.commands { }); } - private static registerDepthCommands(manager: colibri.ui.ide.commands.CommandManager) { + private static registerGameObjectDepthCommands(manager: colibri.ui.ide.commands.CommandManager) { - const moves: [undo.DepthMove, string][] = [["Up", "PageUp"], ["Down", "PageDown"], ["Top", "Home"], ["Bottom", "End"]]; + const moves: [undo.DepthMove, string][] = [ + ["Up", CMD_SORT_OBJ_UP], + ["Down", CMD_SORT_OBJ_DOWN], + ["Top", CMD_SORT_OBJ_TOP], + ["Bottom", CMD_SORT_OBJ_BOTTOM] + ]; for (const tuple of moves) { const move = tuple[0]; - const key = tuple[1]; + const cmd = tuple[1]; + + manager.addHandlerHelper(cmd, + // testFunc + args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && undo.GameObjectDepthOperation.allow(args.activeEditor as any, move), + // execFunc + args => args.activeEditor.getUndoManager().add( + new undo.GameObjectDepthOperation(args.activeEditor as editor.SceneEditor, move) + )); + } + } - manager.add({ + private static registerPropertiesCommands(manager: colibri.ui.ide.commands.CommandManager) { - command: { - id: "phasereditor2d.scene.ui.editor.commands.Depth" + move, - name: "Move Object Depth " + move, - category: CAT_SCENE_EDITOR, - tooltip: "Move the object in its container to " + move + "." - }, + // order commands - handler: { - testFunc: args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 - && undo.DepthOperation.allow(args.activeEditor as any, move), + const moves: [undo.DepthMove, string][] = [ + ["Up", CMD_SORT_OBJ_UP], + ["Down", CMD_SORT_OBJ_DOWN], + ["Top", CMD_SORT_OBJ_TOP], + ["Bottom", CMD_SORT_OBJ_BOTTOM] + ]; - executeFunc: args => args.activeEditor.getUndoManager().add( - new undo.DepthOperation(args.activeEditor as editor.SceneEditor, move)) - }, + for (const tuple of moves) { - keys: { - key - } - }); + const move = tuple[0]; + const cmd = tuple[1]; + + manager.addHandlerHelper(cmd, + // testFunc + args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && properties.PrefabPropertyOrderAction.allow(args.activeEditor as any, move), + // execFunc + args => properties.ChangePrefabPropertiesOperation.runPropertiesOperation(args.activeEditor as SceneEditor, props => { + + properties.PrefabPropertyOrderAction.execute(args.activeEditor as SceneEditor, move); + }) + ); } } @@ -2022,34 +2354,26 @@ namespace phasereditor2d.scene.ui.editor.commands { // order commands - const moves: [undo.DepthMove, string][] = [["Up", "PageUp"], ["Down", "PageDown"], ["Top", "Home"], ["Bottom", "End"]]; + const moves: [undo.DepthMove, string][] = [ + ["Up", CMD_SORT_OBJ_UP], + ["Down", CMD_SORT_OBJ_DOWN], + ["Top", CMD_SORT_OBJ_TOP], + ["Bottom", CMD_SORT_OBJ_BOTTOM] + ]; for (const tuple of moves) { const move = tuple[0]; - const key = tuple[1]; - - manager.add({ - - command: { - id: "phasereditor2d.scene.ui.editor.commands.ListOrder" + move, - name: "Move " + move, - category: CAT_SCENE_EDITOR, - tooltip: "Move the object in its list to " + move + "." - }, - - handler: { - testFunc: args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 - && sceneobjects.ListOrderOperation.allow(args.activeEditor as any, move), - - executeFunc: args => args.activeEditor.getUndoManager().add( - new sceneobjects.ListOrderOperation(args.activeEditor as editor.SceneEditor, move)) - }, - - keys: { - key - } - }); + const cmd = tuple[1]; + + manager.addHandlerHelper(cmd, + // testFunc + args => isSceneScope(args) && args.activeEditor.getSelection().length > 0 + && sceneobjects.ListOrderOperation.allow(args.activeEditor as any, move), + // execFunc + args => args.activeEditor.getUndoManager().add( + new sceneobjects.ListOrderOperation(args.activeEditor as editor.SceneEditor, move)) + ); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts index c96c9b86e..a00a8a16b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/outline/SceneEditorOutlineContentProvider.ts @@ -5,10 +5,12 @@ namespace phasereditor2d.scene.ui.editor.outline { export class SceneEditorOutlineContentProvider implements controls.viewers.ITreeContentProvider { protected _editor: SceneEditor; + private _includeUserComponents: boolean; - constructor(editor: SceneEditor) { + constructor(editor: SceneEditor, includeUserComponents = false) { this._editor = editor; + this._includeUserComponents = includeUserComponents; } getRoots(input: any): any[] { @@ -86,14 +88,17 @@ namespace phasereditor2d.scene.ui.editor.outline { list.reverse(); } - // prepend the user components + if (this._includeUserComponents) { - const compNodes = parentES - .getUserComponentsComponent() - .getUserComponentNodes() - .filter(n => n.isPublished()); + // prepend the user components - list = [...compNodes, ...list]; + const compNodes = parentES + .getUserComponentsComponent() + .getUserComponentNodes() + .filter(n => n.isPublished()); + + list = [...compNodes, ...list]; + } return list; } @@ -113,7 +118,7 @@ namespace phasereditor2d.scene.ui.editor.outline { } else if (parent instanceof sceneobjects.ObjectList) { const scene = this._editor.getScene(); - + return parent.getItemsWithObjects(scene); } else if (typeof parent === "string") { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/ChangePrefabPropertiesOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/ChangePrefabPropertiesOperation.ts index e8206a689..38bb0049c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/ChangePrefabPropertiesOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/ChangePrefabPropertiesOperation.ts @@ -22,6 +22,29 @@ namespace phasereditor2d.scene.ui.editor.properties { return data; } + static runPropertiesOperation(editor: SceneEditor, action: (props?: sceneobjects.UserPropertiesManager) => void, updateSelection?: boolean) { + + const scene = editor.getScene(); + + const before = this.snapshot(editor); + + action(scene.getPrefabUserProperties()); + + const after = this.snapshot(editor); + + editor.getUndoManager() + .add(new ChangePrefabPropertiesOperation(editor, before, after)); + + editor.setDirty(true); + + editor.refreshOutline(); + + if (updateSelection) { + + editor.getSelectionManager().refreshSelection(); + } + } + private load(data: any) { const editor = this.getEditor(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserComponentSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserComponentSection.ts new file mode 100644 index 000000000..988ba44bf --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserComponentSection.ts @@ -0,0 +1,351 @@ +/// +namespace phasereditor2d.scene.ui.editor.properties { + + import controls = colibri.ui.controls; + import io = colibri.core.io; + + export class DynamicUserComponentSection extends sceneobjects.SceneGameObjectSection { + + private _componentName: string; + + constructor(page: controls.properties.PropertyPage, componentName: string, hash: string) { + super(page, + DynamicUserComponentSection.computeId(componentName, hash), + componentName, false, true, + ScenePlugin.getInstance().getIcon(ICON_USER_COMPONENT), + `DynamicUserComponentSection_${componentName}}`); + + this._componentName = componentName; + } + + private static computeId(compName: string, hash: string) { + + return `phasereditor2d.scene.ui.editor.properties.DynamicPropertySection_${compName}_${hash}`; + } + + isDynamicTitle(): boolean { + + return true; + } + + getTitle(): string { + + const prefabNames = this.getSelection() + .flatMap((obj: sceneobjects.ISceneGameObject) => obj.getEditorSupport() + .getUserComponentsComponent() + .getPrefabUserComponents()) + .filter(i => i.components.find(c => c.getName() === this._componentName)) + .map(i => i.prefabFile.getNameWithoutExtension()); + + const distinctPrefabNames: string[] = []; + + if (prefabNames.length > 0) { + + const used = new Set(); + + for (const prefabName of prefabNames) { + + if (used.has(prefabName)) { + + continue; + } + + used.add(prefabName); + distinctPrefabNames.push(prefabName); + } + + return `${this._componentName} ← ${distinctPrefabNames.join(" & ")}`; + } + + return this._componentName; + } + + createMenu(menu: controls.Menu): void { + + const obj = this.getSelectionFirstElement(); + const objES = obj.getEditorSupport(); + + menu.addAction({ + text: `Select Objects With ${this._componentName}`, + callback: () => { + + const sel = []; + + this.getEditor().getScene().visitAll(obj => { + + if (sceneobjects.GameObjectEditorSupport.hasObjectComponent(obj, sceneobjects.UserComponentsEditorComponent)) { + + const userComp = sceneobjects.GameObjectEditorSupport + .getObjectComponent(obj, sceneobjects.UserComponentsEditorComponent) as sceneobjects.UserComponentsEditorComponent; + + if (userComp.hasUserComponent(this._componentName)) { + + sel.push(obj); + } + } + }); + + this.getEditor().setSelection(sel); + } + }); + + menu.addAction({ + text: "Open Definition Of " + this._componentName, + callback: () => this.openComponentEditor() + }); + + // the Reveal In Prefab File options + { + + const prefabFiles: io.FilePath[] = []; + + for (const obj of this.getSelection()) { + + const objES = obj.getEditorSupport(); + + if (objES.isPrefabInstance()) { + + const file = objES.getPrefabFile(); + + if (prefabFiles.indexOf(file) < 0) { + + prefabFiles.push(file); + } + } + } + + for (const prefabFile of prefabFiles) { + + menu.addAction({ + text: `Reveal In ${prefabFile.getNameWithoutExtension()} File`, + callback: () => this.openPrefabDefInSceneEditor(prefabFile) + }); + } + } + + const allLocalNodes = this.getSelection() + .filter(obj => obj.getEditorSupport() + .getUserComponentsComponent() + .hasLocalUserComponent(this._componentName)) + .length === this.getSelection().length; + + if (allLocalNodes) { + + if (this.getSelection().length === 1) { + + const editorComp = objES.getUserComponentsComponent(); + + menu.addAction({ + text: "Move Up", + callback: () => { + + this.runOperation(() => { + + editorComp.moveUpUserComponent(this._componentName) + }); + + this.updateWithSelection(); + } + }); + + menu.addAction({ + text: "Move Down", + callback: () => { + + this.runOperation(() => { + + editorComp.moveDownUserComponent(this._componentName) + }); + + this.updateWithSelection(); + } + }); + } + + + menu.addAction({ + text: "Delete", + callback: () => { + + const editor = this.getEditor(); + + const selIds = editor.getSelectionManager().getSelectionIds(); + + this.runOperation(() => { + + for (const obj of this.getSelection()) { + + const objEs = obj.getEditorSupport(); + objEs.getUserComponentsComponent() + .removeUserComponent(this._componentName); + } + }); + + editor.getSelectionManager().setSelectionByIds(selIds); + } + }); + } + } + + private runOperation(action: () => void) { + + const editor = this.getEditor(); + + editor.getUndoManager().add(new ui.editor.undo.SimpleSceneSnapshotOperation(editor, action)); + } + + private openPrefabDefInSceneEditor(prefabFile: io.FilePath) { + + const prefabEditor = colibri.Platform.getWorkbench().openEditor(prefabFile); + + if (prefabEditor && prefabEditor instanceof ui.editor.SceneEditor) { + + setTimeout(() => { + + const obj = this.getSelectionFirstElement(); + const objES = obj.getEditorSupport(); + + let selObj: sceneobjects.ISceneGameObject; + + if (objES.isNestedPrefabInstance()) { + + selObj = prefabEditor.getScene().getByEditorId(objES.getPrefabId()); + + } else { + + selObj = prefabEditor.getScene().getPrefabObject(); + } + + if (selObj) { + + prefabEditor.setSelection([selObj]); + } + + }, 10); + } + } + + private openComponentEditor() { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const info = finder.getUserComponentByName(this._componentName); + + const editor = colibri.Platform.getWorkbench().openEditor(info.file) as editor.usercomponent.UserComponentsEditor; + + editor.revealComponent(this._componentName); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent); + comp.style.gridTemplateColumns = "auto auto 1fr"; + + { + // export property + + const result = this.createBooleanField(comp, this.getExportProperty(), false); + result.labelElement.style.gridColumn = "2"; + + this.addUpdater(() => { + + const scene = this.getEditor().getScene(); + + const values = this.getSelection().map(obj => { + + const objES = obj.getEditorSupport(); + + if (scene.isPrefabSceneType() + && (objES.isScenePrefabObject() || objES.isNestedPrefabScope())) { + + if (objES.getUserComponentsComponent() + .hasLocalUserComponent(this._componentName)) { + + return true; + } + } + + return false; + }) + + const visible = this.flatValues_BooleanAnd(values); + + result.labelElement.style.display = visible ? "" : "none"; + result.checkElement.style.display = visible ? "" : "none"; + }); + } + + // user properties + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const compInfo = finder.getUserComponentByName(this._componentName); + + { + const props = compInfo.component.getUserProperties().getProperties(); + + if (props.length > 0) { + + const atLeastOnePrefab = this.getSelection() + .map(obj => obj.getEditorSupport()) + .filter(objES => objES.isPrefabInstance() + && !objES.getUserComponentsComponent() + .hasLocalUserComponent(this._componentName)) + .length > 0; + + for (const prop of props) { + + prop.getType().createInspectorPropertyEditor(this, comp, prop, atLeastOnePrefab); + } + } + } + } + + private getExportProperty(): sceneobjects.IProperty { + + return { + name: "isExported", + label: "Export", + getValue: obj => { + + const value = obj.getEditorSupport() + .getUserComponentsComponent().isExportComponent(this._componentName); + + return value; + }, + setValue: (obj: sceneobjects.ISceneGameObject, value: boolean) => { + + const compName = this._componentName; + + obj.getEditorSupport() + .getUserComponentsComponent() + .setExportComponent(compName, value); + }, + defValue: true, + }; + } + + canEdit(obj: any, n: number): boolean { + + if (sceneobjects.isGameObject(obj)) { + + const objES = sceneobjects.GameObjectEditorSupport.getEditorSupport(obj); + + const userComp = objES.getUserComponentsComponent(); + + if (userComp.hasUserComponent(this._componentName)) { + + return true; + } + } + + return false; + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserSectionExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserSectionExtension.ts new file mode 100644 index 000000000..3353b6b64 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/DynamicUserSectionExtension.ts @@ -0,0 +1,97 @@ +/// +namespace phasereditor2d.scene.ui.editor.properties { + + import io = colibri.core.io; + + export class DynamicUserSectionExtension extends SceneEditorPropertySectionExtension { + + getSectionProviders(editor: SceneEditor): GetPropertySection[] { + + const result: GetPropertySection[] = []; + + const visitedPrefabs = new Set(); + const visitedComps = new Set(); + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + // add local user components + + for (const obj of editor.getSelectedGameObjects()) { + + const objES = obj.getEditorSupport(); + + const localComps = objES.getUserComponentsComponent() + .getLocalUserComponents(); + + for (const compInfo of localComps) { + + const compName = compInfo.component.getName(); + + visitedComps.add(compName); + + result.push(page => new DynamicUserComponentSection( + page, compName, `${compInfo.file.getModTime()}`)); + } + } + + + for (const obj of editor.getSelectedGameObjects()) { + + const objES = obj.getEditorSupport(); + + if (!objES.isPrefabInstance()) { + + continue; + } + + const prefabFile = objES.getPrefabFile(); + + if (visitedPrefabs.has(prefabFile)) { + + continue; + } + + visitedPrefabs.add(prefabFile); + + const prefabUserProps = objES.getComponent(sceneobjects.PrefabUserPropertyComponent) as sceneobjects.PrefabUserPropertyComponent; + const prefabInfoList = prefabUserProps.getPropertiesByPrefab(); + + // add all properties from prefabs + + for (const prefabInfo of prefabInfoList) { + + // add section for the current prefab info + + result.push(page => new sceneobjects.DynamicPrefabInstanceSection( + page, prefabInfo.prefabFile, prefabInfo.properties)); + + // add all user component properties defined in the current prefab + + const userComps = objES.getUserComponentsComponent(); + + const compNames = userComps + .getPrefabUserComponents() + .filter(i => i.prefabFile === prefabInfo.prefabFile) + .flatMap(i => i.components) + .map(c => c.getName()) + .filter(name => !visitedComps.has(name)); + + for (const compName of compNames) { + + visitedComps.add(compName); + + const findResult = finder.getUserComponentByName(compName); + + if (findResult) { + + result.push(page => new DynamicUserComponentSection( + page, compName, `${findResult.file.getModTime()}`)); + } + } + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertiesSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertiesSection.ts index 2dbb0a616..b83258ff1 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertiesSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertiesSection.ts @@ -23,12 +23,12 @@ namespace phasereditor2d.scene.ui.editor.properties { this.getEditor().setSelection([obj]); }; - SingleUserPropertySection.createAddComponentButton(comp, this, action => this.runOperation(action), selector); + SingleUserPropertySection.createAddProprtyButton(comp, this, action => this.runOperation(action), selector); } - runOperation(action: (props?: sceneobjects.UserProperties) => void, updateSelection = true) { + runOperation(action: (props?: sceneobjects.UserPropertiesManager) => void, updateSelection = true) { - PrefabPropertySection.runPropertiesOperation(this.getEditor(), action, updateSelection); + ui.editor.properties.ChangePrefabPropertiesOperation.runPropertiesOperation(this.getEditor(), action, updateSelection); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertyOrderAction.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertyOrderAction.ts new file mode 100644 index 000000000..c3198940b --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertyOrderAction.ts @@ -0,0 +1,121 @@ +namespace phasereditor2d.scene.ui.editor.properties { + + export class PrefabPropertyOrderAction { + + static allow(editor: SceneEditor, move: ui.editor.undo.DepthMove) { + + const sel = this.sortedSelection(editor); + + if (sel.length === 0) { + + return false; + } + + for (const prop of sel) { + + if (!(prop instanceof ui.sceneobjects.UserProperty)) { + + return false; + } + } + + const siblings = sel[0].getManager().getProperties(); + + for (const prop of sel) { + + const index = siblings.indexOf(prop); + + const len = siblings.length; + + if (move === "Bottom" || move === "Down") { + + if (index === len - 1) { + + return false; + } + + } else { // Top || Up + + if (index === 0) { + + return false; + } + } + } + + return true; + } + + static execute(editor: SceneEditor, depthMove: undo.DepthMove): void { + + const sel = this.sortedSelection(editor); + + switch (depthMove) { + + case "Bottom": + + for (const prop of sel) { + + const siblings = prop.getManager().getProperties(); + + Phaser.Utils.Array.BringToTop(siblings, prop); + } + + break; + + case "Top": + + for (let i = 0; i < sel.length; i++) { + + const prop = sel[sel.length - i - 1]; + + const siblings = prop.getManager().getProperties(); + + Phaser.Utils.Array.SendToBack(siblings, prop) + } + + break; + + case "Down": + + for (let i = 0; i < sel.length; i++) { + + const prop = sel[sel.length - i - 1]; + + const siblings = prop.getManager().getProperties(); + + Phaser.Utils.Array.MoveUp(siblings, prop); + } + + break; + + case "Up": + + for (const prop of sel) { + + const siblings = prop.getManager().getProperties(); + + Phaser.Utils.Array.MoveDown(siblings, prop); + } + + break; + } + } + + private static sortedSelection(editor: SceneEditor) { + + const sel = editor.getSelection() as any as sceneobjects.UserProperty[]; + const props = editor.getScene().getPrefabUserProperties().getProperties(); + + sel.sort((a, b) => { + + const aa = props.indexOf(a); + const bb = props.indexOf(b); + + return aa - bb; + }); + + return sel; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertySection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertySection.ts index 205531bb2..0e3e2eed7 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertySection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/PrefabPropertySection.ts @@ -14,9 +14,9 @@ namespace phasereditor2d.scene.ui.editor.properties { return "scene-editor/prefab-user-properties.html"; } - protected getUserProperties(): sceneobjects.UserProperties { + protected getUserProperties(): sceneobjects.UserPropertiesManager { - return this.getProperty().getAllProperties(); + return this.getProperty().getManager(); } protected getProperty(): sceneobjects.UserProperty { @@ -34,32 +34,9 @@ namespace phasereditor2d.scene.ui.editor.properties { return colibri.ui.ide.Workbench.getWorkbench().getActiveEditor() as SceneEditor; } - static runPropertiesOperation(editor: SceneEditor, action: (props?: sceneobjects.UserProperties) => void, updateSelection?: boolean) { + protected runOperation(action: (props?: sceneobjects.UserPropertiesManager) => void, updateSelection?: boolean) { - const scene = editor.getScene(); - - const before = ui.editor.properties.ChangePrefabPropertiesOperation.snapshot(editor); - - action(scene.getPrefabUserProperties()); - - const after = ui.editor.properties.ChangePrefabPropertiesOperation.snapshot(editor); - - editor.getUndoManager() - .add(new ChangePrefabPropertiesOperation(editor, before, after)); - - editor.setDirty(true); - - editor.refreshOutline(); - - if (updateSelection) { - - editor.getSelectionManager().refreshSelection(); - } - } - - protected runOperation(action: (props?: sceneobjects.UserProperties) => void, updateSelection?: boolean) { - - PrefabPropertySection.runPropertiesOperation(this.getEditor(), action, updateSelection); + ui.editor.properties.ChangePrefabPropertiesOperation.runPropertiesOperation(this.getEditor(), action, updateSelection); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPopertyProvider.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPopertyProvider.ts index 1b2d315e3..3f937e674 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPopertyProvider.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPopertyProvider.ts @@ -34,7 +34,9 @@ namespace phasereditor2d.scene.ui.editor.properties { for (const ext of exts) { - for (const provider of ext.getSectionProviders()) { + const providers = ext.getSectionProviders(this._editor); + + for (const provider of providers) { sections.push(provider(page)); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPropertySectionExtension.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPropertySectionExtension.ts index dc63df964..6d97b9f41 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPropertySectionExtension.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SceneEditorPropertySectionExtension.ts @@ -16,7 +16,7 @@ namespace phasereditor2d.scene.ui.editor.properties { this._sectionProviders = sectionProviders; } - getSectionProviders() { + getSectionProviders(editor?: SceneEditor) { return this._sectionProviders; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SingleUserPropertySection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SingleUserPropertySection.ts index f4d2c1c7e..fd9c6ba7a 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SingleUserPropertySection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/SingleUserPropertySection.ts @@ -7,37 +7,41 @@ namespace phasereditor2d.scene.ui.editor.properties { protected abstract getSectionHelpPath(): string; - protected abstract getUserProperties(): sceneobjects.UserProperties; + protected abstract getUserProperties(): sceneobjects.UserPropertiesManager; protected abstract getProperty(): sceneobjects.UserProperty; protected abstract componentTitleUpdated(): void; - protected abstract runOperation(action: (props?: sceneobjects.UserProperties) => void, updateSelection?: boolean); + protected abstract runOperation(action: (props?: sceneobjects.UserPropertiesManager) => void, updateSelection?: boolean); - static createAddComponentButton( + static createAddProprtyButton( comp: HTMLDivElement, formBuilder: colibri.ui.controls.properties.FormBuilder, runOperation: ( action: TUserPropertiesAction) => void, - selector: (obj:any) => void) { + selector: (obj: any) => void) { - const propTypes = ScenePlugin.getInstance().createUserPropertyTypes(); + const propTypes = ScenePlugin.getInstance().getUserPropertyTypes(); - const buttonElement = formBuilder.createMenuButton(comp, "Add Property", () => propTypes.map(t => ({ - name: t.getName() + " Property", - value: t.getId() - })), (typeId: string) => { + const buttonElement = formBuilder.createButton(comp, "Add Property", () => { - const newType = ScenePlugin.getInstance().createUserPropertyType(typeId); + class Dlg extends ui.dialogs.AbstractAddPrefabPropertyDialog { - runOperation(userProps => { + protected addProperty(propType: sceneobjects.UserPropertyType): void { - const prop = userProps.createProperty(newType); - userProps.add(prop); + runOperation(userProps => { - selector(prop); - }); + const prop = userProps.createProperty(propType); + userProps.add(prop); + + selector(prop); + }); + } + } + + const dlg = new Dlg(); + dlg.create(); }); return { buttonElement }; @@ -52,57 +56,15 @@ namespace phasereditor2d.scene.ui.editor.properties { const prop = this.getProperty(); - menu.addAction({ - text: "Move Up", - callback: () => { - this.runOperation(userProps => { - - const list = userProps.getProperties(); - - const i = list.indexOf(prop); - - if (i > 0) { - - const temp = list[i - 1]; - list[i - 1] = prop; - list[i] = temp; - } - }, true); - } - }); - - menu.addAction({ - text: "Move Down", - callback: () => { - this.runOperation(userProps => { - - const list = userProps.getProperties(); - - const i = list.indexOf(prop); - - if (i < list.length - 1) { - - const temp = list[i + 1]; - list[i + 1] = prop; - list[i] = temp; - } - }, true); - } - }); - - menu.addSeparator(); - menu.addMenu(this.createMorphMenu(prop)); - menu.addSeparator(); - menu.addAction({ text: "Delete", callback: () => { this.runOperation(userProps => { userProps.deleteProperty(prop.getName()); - + }, true); } }); @@ -267,7 +229,7 @@ namespace phasereditor2d.scene.ui.editor.properties { const menu = new controls.Menu("Change Type"); - const propTypes = ScenePlugin.getInstance().createUserPropertyTypes(); + const propTypes = ScenePlugin.getInstance().getUserPropertyTypes(); for (const propType of propTypes) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/UserPropertiesSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/UserPropertiesSection.ts index df18254d9..6550b7414 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/UserPropertiesSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/properties/UserPropertiesSection.ts @@ -2,7 +2,7 @@ namespace phasereditor2d.scene.ui.editor.properties { import controls = colibri.ui.controls; - export declare type TUserPropertiesAction = (props?: sceneobjects.UserProperties) => void + export declare type TUserPropertiesAction = (props?: sceneobjects.UserPropertiesManager) => void export abstract class UserPropertiesSection extends controls.properties.PropertySection { @@ -10,7 +10,7 @@ namespace phasereditor2d.scene.ui.editor.properties { protected abstract getSectionHelpPath(): string; - protected abstract getUserProperties(): sceneobjects.UserProperties; + protected abstract getUserProperties(): sceneobjects.UserPropertiesManager; protected abstract runOperation(action: TUserPropertiesAction, updateSelection?: boolean); @@ -32,14 +32,14 @@ namespace phasereditor2d.scene.ui.editor.properties { this._propArea = this.createGridElement(comp, 2); comp.appendChild(this._propArea); - const propTypes = ScenePlugin.getInstance().createUserPropertyTypes(); + const propTypes = ScenePlugin.getInstance().getUserPropertyTypes(); const btn = this.createMenuButton(comp, "Add Property", () => propTypes.map(t => ({ name: t.getName() + " Property", value: t.getId() })), (typeId: string) => { - const newType = ScenePlugin.getInstance().createUserPropertyType(typeId); + const newType = ScenePlugin.getInstance().getUserPropertyType(typeId); this.runOperation(userProps => { @@ -259,7 +259,7 @@ namespace phasereditor2d.scene.ui.editor.properties { const menu = new controls.Menu("Change Type"); - const propTypes = ScenePlugin.getInstance().createUserPropertyTypes(); + const propTypes = ScenePlugin.getInstance().getUserPropertyTypes(); for (const propType of propTypes) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CreateObjectWithAssetOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CreateObjectWithAssetOperation.ts index 1e493c757..42413ff3f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CreateObjectWithAssetOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CreateObjectWithAssetOperation.ts @@ -1,6 +1,8 @@ /// namespace phasereditor2d.scene.ui.editor.undo { + import io = colibri.core.io; + export class CreateObjectWithAssetOperation extends SceneSnapshotOperation { private _offsetX: number; @@ -17,9 +19,52 @@ namespace phasereditor2d.scene.ui.editor.undo { protected async performModification() { - const sprites = await this.getEditor().getDropManager().createWithDropEvent(this._data, this._offsetX, this._offsetY); + const editor = this.getEditor(); + + let isScriptNode = false; + + if (this._data.length === 1) { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const asset = this._data[0]; + + isScriptNode = asset instanceof io.FilePath + && finder.isScriptPrefabFile(asset) + || asset instanceof sceneobjects.ScriptNodeExtension; + } + + const sel = editor.getSelectedGameObjects(); + + if (isScriptNode && sel.length > 0) { + + // We are dropping a script node, + // so we should go for every object selected in the scene + // and add the script. + // It is different from adding a regular game object, + // where only one instance is created + + const newSprites = []; + + const script = this._data[0]; + + for (const obj of sel) { + + const sprites = await editor.getDropManager() + .createWithDropEvent([script], 0, 0, [obj]); + + newSprites.push(...sprites); + } + + editor.setSelection(newSprites); + + } else { + + const sprites = await editor.getDropManager() + .createWithDropEvent(this._data, this._offsetX, this._offsetY); - this.getEditor().setSelection(sprites); + editor.setSelection(sprites); + } } } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CutOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CutOperation.ts index ab3e73653..bda5af882 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CutOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/CutOperation.ts @@ -12,24 +12,45 @@ namespace phasereditor2d.scene.ui.editor.undo { this._editor.getClipboardManager().copy(); + const scene = this.getScene(); + const lists = this._editor.getScene().getObjectLists(); - for (const obj of this._editor.getSelection()) { + // delete game objects + + for (const obj of this._editor.getSelectedGameObjects()) { - if (sceneobjects.isGameObject(obj)) { + const objES = obj.getEditorSupport(); + + objES.destroy(); + lists.removeObjectById(objES.getId()); + } - const sprite = obj as sceneobjects.ISceneGameObject; + // delete plain objects - sprite.getEditorSupport().destroy(); - lists - .removeObjectById(sprite.getEditorSupport().getId()); + const plainObjects = this._editor.getSelectedPlainObjects(); - } else if (obj instanceof sceneobjects.ObjectList) { + if (plainObjects.length > 0) { - lists.removeListById(obj.getId()); - } + scene.removePlainObjects(plainObjects); } + // delete ObjectLists + + for (const objectList of this._editor.getSelectedLists()) { + + lists.removeListById(objectList.getId()); + } + + // delete prefab properties + + for (const prop of this._editor.getSelectedPrefabProperties()) { + + prop.getManager().deleteProperty(prop.getName()); + } + + // clear selection + this._editor.setSelection([]); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts index eaf453c2e..8103fc6c0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DeleteOperation.ts @@ -40,7 +40,7 @@ namespace phasereditor2d.scene.ui.editor.undo { for(const obj of editor.getSelectedPrefabProperties()) { - obj.getAllProperties().deleteProperty(obj.getName()); + obj.getManager().deleteProperty(obj.getName()); } scene.removePlainObjects(editor.getSelectedPlainObjects()); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/GameObjectDepthOperation.ts similarity index 96% rename from source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/GameObjectDepthOperation.ts index 78d59750f..cc91742b3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/DepthOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/GameObjectDepthOperation.ts @@ -2,7 +2,7 @@ namespace phasereditor2d.scene.ui.editor.undo { export declare type DepthMove = "Up" | "Down" | "Top" | "Bottom"; - export class DepthOperation extends SceneSnapshotOperation { + export class GameObjectDepthOperation extends SceneSnapshotOperation { private _depthMove: DepthMove; @@ -80,7 +80,7 @@ namespace phasereditor2d.scene.ui.editor.undo { protected async performModification() { - const sel = DepthOperation.sortedSelection(this.getEditor()); + const sel = GameObjectDepthOperation.sortedSelection(this.getEditor()); switch (this._depthMove) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PasteOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PasteOperation.ts index 27b41627a..984a8d940 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PasteOperation.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PasteOperation.ts @@ -29,23 +29,111 @@ namespace phasereditor2d.scene.ui.editor.undo { const items = ClipboardManager.getClipboardCopy(); - const maker = this._editor.getSceneMaker(); - const sel = []; + await this.pasteGameObjects(items, sel); + + await this.pastePlainObjects(items, sel); + + this.pastePrefaProperties(items, sel); + + this._editor.setSelection(sel); + } + + private async pastePlainObjects(clipboardItems: IClipboardItem[], sel: any[]) { + const scene = this._editor.getScene(); const nameMaker = scene.createNameMaker(); + const plainObjects: sceneobjects.IScenePlainObject[] = []; + + const dataList = clipboardItems.filter(i => i.type === "IScenePlainObject").map(i => i.data as json.IScenePlainObjectData); + + await scene.getMaker().updateLoaderWithData([], dataList); + + for (const data of dataList) { + + this.setNewObjectId(data); + + const obj =scene.readPlainObject(data); + + if (obj) { + + plainObjects.push(obj); + + sel.push(obj); + } + } + + for (const newObj of plainObjects) { + + const oldLabel = newObj.getEditorSupport().getLabel(); + + const newLabel = nameMaker.makeName(oldLabel); + + newObj.getEditorSupport().setLabel(newLabel); + } + } + + private pastePrefaProperties(clipboardItems: IClipboardItem[], sel: any[]) { + + const scene = this._editor.getScene(); + + if (!scene.isPrefabSceneType()) { + + return; + } + + for (const item of clipboardItems) { + + if (item.type === "PrefabProperty") { + + const data = item.data as any; + + const id = data.type.id; + + const propType = ScenePlugin.getInstance().getUserPropertyType(id); + + if (propType) { + + const userProps = scene.getPrefabUserProperties(); + + const dataName = colibri.ui.ide.utils.NameMaker.trimNumbering(data.name); + const dataLabel = colibri.ui.ide.utils.NameMaker.trimNumbering(data.label); + + const { name, label } = userProps.createNewPropertyNameInfo(dataName, dataLabel); + + data.name = name; + data.label = label; + + const prop = userProps.createPropertyFromData(data); + + userProps.add(prop); + + sel.push(prop); + } + } + } + } + + private async pasteGameObjects(clipboardItems: IClipboardItem[], sel: any[]) { + + const scene = this._editor.getScene(); + + const maker = this._editor.getSceneMaker(); + + const nameMaker = scene.createNameMaker(); + const prefabObj = scene.getPrefabObject(); const sprites: sceneobjects.ISceneGameObject[] = []; - const displayList = items.filter(i => i.type === "ISceneObject").map(i => i.data as json.IObjectData); + const displayList = clipboardItems.filter(i => i.type === "ISceneObject").map(i => i.data as json.IObjectData); await scene.getMaker().updateLoaderWithData([], displayList); - for (const item of items) { + for (const item of clipboardItems) { if (item.type === "ISceneObject") { @@ -89,8 +177,6 @@ namespace phasereditor2d.scene.ui.editor.undo { } maker.afterDropObjects(prefabObj, sprites); - - this._editor.setSelection(sel); } private setNewObjectId(data: json.IObjectData) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PlainObjectOrderOperation.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PlainObjectOrderOperation.ts new file mode 100644 index 000000000..d63542c92 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/undo/PlainObjectOrderOperation.ts @@ -0,0 +1,146 @@ +namespace phasereditor2d.scene.ui.editor.undo { + + export class PlainObjectOrderOperation extends SceneSnapshotOperation { + + private _depthMove: DepthMove; + + constructor(editor: SceneEditor, depthMove: DepthMove) { + super(editor); + + this._depthMove = depthMove; + } + + private static getSibling(editor: SceneEditor, obj: sceneobjects.IScenePlainObject) { + + const objES = obj.getEditorSupport(); + + const category = objES.getExtension().getCategory(); + + const siblings = editor.getScene().getPlainObjectsByCategory(category); + + return siblings; + } + + static allow(editor: SceneEditor, move: DepthMove) { + + // sort the selection and filter off non-game-objects + let sel = this.sortedSelection(editor); + + // if the sorted selection contains all the selected objects + if (sel.length !== editor.getSelection().length) { + + return false; + } + + for (const obj of sel) { + + const siblings = this.getSibling(editor, obj); + + const index = siblings.indexOf(obj); + + let bottomIndex = 0; + const len = siblings.length; + + if (move === "Bottom" || move === "Down") { + + if (index === len - 1) { + + return false; + } + + } else { // Top || Up + + if (index === bottomIndex) { + + return false; + } + } + } + + return true; + } + + protected async performModification() { + + const editor = this.getEditor(); + + const sel = PlainObjectOrderOperation.sortedSelection(editor); + + const plainObjects = editor.getScene().getPlainObjects(); + + switch (this._depthMove) { + + case "Bottom": + + for (const obj of sel) { + + const siblings = PlainObjectOrderOperation.getSibling(editor, obj); + + const start = plainObjects.indexOf(siblings[0]); + + Phaser.Utils.Array.BringToTop(siblings, obj); + + plainObjects.splice(start, siblings.length, ...siblings); + } + + break; + + case "Top": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + const siblings = PlainObjectOrderOperation.getSibling(editor, obj); + + const start = plainObjects.indexOf(siblings[0]); + + Phaser.Utils.Array.SendToBack(siblings, obj); + + plainObjects.splice(start, siblings.length, ...siblings); + } + + break; + + case "Down": + + for (let i = 0; i < sel.length; i++) { + + const obj = sel[sel.length - i - 1]; + + Phaser.Utils.Array.MoveUp(plainObjects, obj); + } + + break; + + case "Up": + + for (const obj of sel) { + + Phaser.Utils.Array.MoveDown(plainObjects, obj); + } + + break; + } + + this.getEditor().repaint(); + } + + private static sortedSelection(editor: SceneEditor) { + + const sel = editor.getSelectedPlainObjects(); + + const plainObjects = editor.getScene().getPlainObjects(); + + sel.sort((a, b) => { + + const aa = plainObjects.indexOf(a); + const bb = plainObjects.indexOf(b); + + return aa - bb; + }); + + return sel; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponent.ts index a975e7439..c09180d4c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponent.ts @@ -7,7 +7,7 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { private _name: string; private _baseClass: string; private _gameObjectType: string; - private _properties: sceneobjects.UserProperties; + private _properties: sceneobjects.UserPropertiesManager; constructor(name: string) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentProperties.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentProperties.ts index e09605e10..f3cf55b36 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentProperties.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentProperties.ts @@ -1,9 +1,9 @@ -/// +/// /// /// namespace phasereditor2d.scene.ui.editor.usercomponent { - export class UserComponentProperties extends sceneobjects.UserProperties { + export class UserComponentProperties extends sceneobjects.UserPropertiesManager { private _userComponent: UserComponent; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentPropertySection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentPropertySection.ts index 244fa9961..b0d9a1147 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentPropertySection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentPropertySection.ts @@ -23,9 +23,9 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { .getSelectedEditor() as UserComponentsEditor; } - protected getUserProperties(): sceneobjects.UserProperties { + protected getUserProperties(): sceneobjects.UserPropertiesManager { - return this.getSelectionFirstElement().getAllProperties(); + return this.getSelectionFirstElement().getManager(); } protected getProperty(): sceneobjects.UserProperty { @@ -38,7 +38,7 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { this.getEditor().refreshViewers(); } - runOperation(action: (props?: sceneobjects.UserProperties) => void, updateSelection?: boolean) { + runOperation(action: (props?: sceneobjects.UserPropertiesManager) => void, updateSelection?: boolean) { this.getEditor().runOperation(() => action(this.getUserProperties())); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentSection.ts index ec8571170..c8487879b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentSection.ts @@ -61,7 +61,7 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { }; const { buttonElement } = editor.properties.SingleUserPropertySection - .createAddComponentButton(comp, this, op, selector); + .createAddProprtyButton(comp, this, op, selector); buttonElement.style.marginTop = "10px"; buttonElement.style.width = "100%"; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentsEditor.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentsEditor.ts index b7085ba31..58580a0c3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentsEditor.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/editor/usercomponent/UserComponentsEditor.ts @@ -208,7 +208,7 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { if (obj instanceof sceneobjects.UserProperty) { - const comp = (obj.getAllProperties() as UserComponentProperties).getUserComponent(); + const comp = (obj.getManager() as UserComponentProperties).getUserComponent(); return { component: comp.getName(), @@ -452,7 +452,7 @@ namespace phasereditor2d.scene.ui.editor.usercomponent { } else if (obj instanceof sceneobjects.UserProperty) { - obj.getAllProperties().deleteProperty(obj.getName()); + obj.getManager().deleteProperty(obj.getName()); } else if (typeof obj === "string") { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts index 0a7db2c38..6c6aeb729 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Component.ts @@ -11,7 +11,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { objectVarName: string; prefabSerializer: core.json.Serializer; unit: core.code.UnitCodeDOM; - sceneFile: io.FilePath + sceneFile: io.FilePath, + obj: ISceneGameObject } export interface IBuildPrefabExtraTypeScriptDefinitionsCodeDOMArgs { @@ -74,7 +75,11 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const prop of properties) { - ser.write(prop.name, prop.getValue(this._obj), this.getPropertyDefaultValue(prop)); + const value = prop.getValue(this._obj); + + const defValue = this.getPropertyDefaultValue(prop); + + ser.write(prop.name, value, defValue); } } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts index 6d9e18016..84a9f3cc0 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/EditorSupport.ts @@ -27,9 +27,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { const support = obj["getEditorSupport"](); - if (support instanceof EditorSupport) + if (support instanceof EditorSupport) { return support; + } } return null; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Utils.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Utils.ts index 11a3e12c6..f01fb282c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Utils.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/Utils.ts @@ -72,4 +72,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { return GameObjectEditorSupport.hasEditorSupport(obj); } + + export function isPlainObject(obj: any) { + + return ScenePlainObjectEditorSupport.hasEditorSupport(obj); + } } \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts index 368d775d2..94a391dd3 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/BaseHitAreaComponent.ts @@ -35,7 +35,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { super.readJSON(ser); } - protected abstract _setDefaultValues(width: number, height: number): void; + writeJSON(ser: core.json.Serializer): void { + + // only writes this component data if its shape is selected + const shape = HitAreaComponent.hitAreaShape.getValue(this.getObject()); + + if (shape === this._shape) { + + super.writeJSON(ser); + } + } + + protected abstract _setDefaultValues(x: number, y: number, width: number, height: number): void; setDefaultValues() { @@ -43,6 +54,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const objES = this.getEditorSupport(); let width = 0, height = 0; + let x = 0, y = 0; let [widthProp, heightProp] = objES.getSizeProperties(); @@ -60,13 +72,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { width = b.width; height = b.height; + const origin = c.getEditorSupport().computeDisplayOrigin(); + + x = -origin.displayOriginX; + y = -origin.displayOriginY; + } else if (obj.width && obj.height) { width = obj.width; height = obj.height; } - this._setDefaultValues(width, height); + this._setDefaultValues(x, y, width, height); } buildSetObjectPropertiesCodeDOM(args: ISetObjectPropertiesCodeDOMArgs): void { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts index 1121d070f..2d5091c57 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/CircleHitAreaComponent.ts @@ -35,10 +35,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - protected _setDefaultValues(width: number, height: number): void { + protected _setDefaultValues(x:number, y: number, width: number, height: number): void { - this.x = width / 2; - this.y = height / 2; + this.x = x + width / 2; + this.y = y + height / 2; this.radius = Math.min(width, height) / 2; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts index e8d5d0b08..c6c00369f 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/EllipseHitAreaComponent.ts @@ -44,10 +44,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - protected _setDefaultValues(width: number, height: number): void { + protected _setDefaultValues(x: number, y: number, width: number, height: number): void { - this.x = width / 2; - this.y = height / 2; + this.x = x + width / 2; + this.y = y + height / 2; this.width = width; this.height = height; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts index 1cbe18fb6..ac359cc05 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PixelPerfectHitAreaComponent.ts @@ -23,7 +23,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - protected _setDefaultValues(width: number, height: number): void { + protected _setDefaultValues(x:number, y: number, width: number, height: number): void { // nothing } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts index 6b79d76f1..fb772857b 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaComponent.ts @@ -5,7 +5,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { export class PolygonHitAreaComponent extends BaseHitAreaComponent { static points = HitAreaProperty(PolygonHitAreaComponent, "points", "Points", "The polygon's points, in a string format `X1 Y1 Y2 X2...`", ""); - + public points: string; constructor(obj: ISceneGameObject) { @@ -42,9 +42,9 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - protected _setDefaultValues(w: number, h: number): void { + protected _setDefaultValues(x: number, y: number, w: number, h: number): void { - this.points = `0 ${h * 0.25} ${w/2} 0 ${w} ${h * 0.25} ${w} ${h} 0 ${h}`; + this.points = `${x} ${y + h * 0.25} ${x + w / 2} ${y} ${x + w} ${y + h * 0.25} ${x + w} ${y + h} ${x} ${y + h}`; } protected override buildSetInteractiveCodeCOM( diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts index 6b55224ce..6037edd90 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/PolygonHitAreaToolItem.ts @@ -219,7 +219,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const obj = comp.getObject() as Sprite; - const { displayOriginX, displayOriginY } = obj.getEditorSupport().computeDisplayOrigin(); + const { displayOriginX, displayOriginY } = this.getDisplayOrigin(obj); const tx = obj.getWorldTransformMatrix(); @@ -366,7 +366,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { sprite.getWorldTransformMatrix().applyInverse(newPoint.x, newPoint.y, newPoint); - const {displayOriginX, displayOriginY} = sprite.getEditorSupport().computeDisplayOrigin(); + let { displayOriginX, displayOriginY } = this.getDisplayOrigin(sprite); point.x = newPoint.x + displayOriginX; point.y = newPoint.y + displayOriginY; @@ -376,6 +376,16 @@ namespace phasereditor2d.scene.ui.sceneobjects { args.editor.updateInspectorViewSection(PolygonHitAreaSection.ID); } + private getDisplayOrigin(sprite: ISceneGameObject) { + + if (sprite instanceof Container) { + + return { displayOriginX: 0, displayOriginY: 0 }; + } + + return sprite.getEditorSupport().computeDisplayOrigin(); + } + onStopDrag(args: editor.tools.ISceneToolDragEventArgs): void { this._newPoint = undefined; diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts index f10ddb978..2f4877039 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/hitArea/RectangleHitAreaComponent.ts @@ -41,10 +41,10 @@ namespace phasereditor2d.scene.ui.sceneobjects { return comp; } - protected _setDefaultValues(width: number, height: number): void { + protected _setDefaultValues(x:number, y: number, width: number, height: number): void { - this.x = 0; - this.y = 0; + this.x = x; + this.y = y; this.width = width; this.height = height; } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeySection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeySection.ts index eaa67cb32..1f3eeb276 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeySection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeySection.ts @@ -17,40 +17,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.createLabel(comp, "Key Code", "The keycode of this key"); - const btn = this.createButton(comp, "KeyCode", e => { - - const viewer = new controls.viewers.TreeViewer(KeyboardKeySection.ID + ".keyCodes"); - viewer.setLabelProvider(new controls.viewers.LabelProvider(obj => obj)); - viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); - viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider()); - viewer.setInput(KeyboardKeyExtension.getInstance().getKeyCodes()); - viewer.revealAndSelect(this.getSelectionFirstElement().keyCode); - - const dlg = new controls.dialogs.ViewerDialog(viewer, false); - - dlg.create(); - - dlg.setTitle("Select Key Code"); - - dlg.addOpenButton("Select", (sel) => { - - const value = sel[0]; - const keys = this.getSelection(); - - console.log("set", value, "to", keys[0].getEditorSupport().getId()); - - this.getEditor().getUndoManager().add( - new SimpleOperation(this.getEditor(), keys, KeyboardKeyComponent.keyCode, value)); - - }, false); - - dlg.addCancelButton(); - }); - - this.addUpdater(() => { - - btn.textContent = this.flatValues_StringOneOrNothing(this.getSelection().map(k => k.keyCode)); - }); + this.createKeyCodeField(comp, KeyboardKeyComponent.keyCode); } canEdit(obj: any, n: number): boolean { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeyViewer.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeyViewer.ts new file mode 100644 index 000000000..06ab32e2f --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/keyboard/KeyboardKeyViewer.ts @@ -0,0 +1,16 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class KeyboardKeysViewer extends controls.viewers.TreeViewer { + + constructor() { + super("KeyboardKeysViewer"); + + this.setLabelProvider(new controls.viewers.LabelProvider(obj => obj)); + this.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + this.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider()); + this.setInput(KeyboardKeyExtension.getInstance().getKeyCodes()); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/BrowseUserComponentsDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/BrowseUserComponentsDialog.ts new file mode 100644 index 000000000..f657b73c6 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/BrowseUserComponentsDialog.ts @@ -0,0 +1,116 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class BrowseUserComponentsDialog extends controls.dialogs.ViewerDialog { + + private static createViewer(editor: ui.editor.SceneEditor) { + + const viewer = new controls.viewers.TreeViewer("BrowseScriptsDialog"); + + viewer.setLabelProvider(new ui.editor.outline.SceneEditorOutlineLabelProvider()); + viewer.setStyledLabelProvider(new ui.editor.outline.SceneEditorOutlineStyledLabelProvider()); + viewer.setCellRendererProvider(new ui.editor.outline.SceneEditorOutlineRendererProvider()); + viewer.setContentProvider(new UserCompponentsDialogContentProvider(editor)); + viewer.setInput([]) + viewer.expandRoots(); + + return viewer; + } + + private _editor: ui.editor.SceneEditor; + + constructor(editor: ui.editor.SceneEditor) { + super(BrowseUserComponentsDialog.createViewer(editor), true); + + this._editor = editor; + } + + create(): void { + + super.create(); + + this.setTitle("Browse User Components"); + + this.addOpenButton("Select", sel => { + + const selSet = new Set(sel.map(obj => { + + if (obj instanceof sceneobjects.UserComponentNode) { + + return obj.getObject() + } + + return obj; + })); + + + this._editor.setSelection([...selSet]); + }); + + this.addCancelButton(); + } + } + + class UserCompponentsDialogContentProvider extends ui.editor.outline.SceneEditorOutlineContentProvider { + + constructor(editor: ui.editor.SceneEditor) { + super(editor, true); + } + + getRoots(input: any): any[] { + + return [this._editor.getScene().sys.displayList]; + } + + getChildren(parent: any): any[] { + + const children = super.getChildren(parent); + + let result = []; + + for (const obj of children) { + + if (obj instanceof sceneobjects.UserComponentNode) { + + result.push(obj); + + } else if (isGameObject(obj)) { + + if (this.hasUserComponents(obj)) { + + result.push(obj); + } + } + } + + return result; + } + + private hasUserComponents(obj: ISceneGameObject) { + + let result = obj.getEditorSupport().getUserComponentsComponent() + .getUserComponentNodes().length > 0; + + if (!result) { + + const children = super.getChildren(obj); + + for (const child of children) { + + if (isGameObject(child)) { + + result = this.hasUserComponents(child); + + if (result) { + + return true; + } + } + } + } + + return result; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/DynamicPrefabInstanceSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/DynamicPrefabInstanceSection.ts new file mode 100644 index 000000000..656048334 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/DynamicPrefabInstanceSection.ts @@ -0,0 +1,141 @@ +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + import io = colibri.core.io; + + export class DynamicPrefabInstanceSection extends SceneGameObjectSection { + + private _prefabFile: io.FilePath; + private _properties: UserProperty[]; + + constructor( + page: controls.properties.PropertyPage, + prefabFile: io.FilePath, + properties: UserProperty[]) { + super(page, + DynamicPrefabInstanceSection.computeId(prefabFile), + prefabFile.getNameWithoutExtension(), + false, true, ScenePlugin.getInstance().getIcon(ICON_GROUP), + DynamicPrefabInstanceSection.computeTypeHash(prefabFile)); + + this._prefabFile = prefabFile; + this._properties = properties; + } + + private static computeTypeHash(prefabFile: io.FilePath): string { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const prefabId = finder.getPrefabId(prefabFile); + + return `DynamicPrefabInstanceSection_${prefabFile.getNameWithoutExtension()}_${prefabId}` + } + + private static computeId(prefabFile: io.FilePath) { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + const id = finder.getPrefabId(prefabFile); + const hash = prefabFile.getModTime(); + + return `phasereditor2d.scene.ui.sceneobjects.DynamicPrefabInstanceSection_${id}_${hash}`; + } + + getSectionHelpPath() { + + return "scene-editor/prefab-user-properties.html#user-properties-in-a-prefab-instance"; + } + + createMenu(menu: controls.Menu) { + + menu.addCommand(editor.commands.CMD_OPEN_PREFAB); + + const prefabName = this._prefabFile.getNameWithoutExtension(); + + menu.addAction({ + text: `Select All ${prefabName}`, + callback: () => { + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const sel = []; + + this.getEditor().getScene().visitAll(obj2 => { + + if (GameObjectEditorSupport.hasEditorSupport(obj2)) { + + const editorSupport = GameObjectEditorSupport.getEditorSupport(obj2); + + if (editorSupport.isPrefabInstance()) { + + const prefabFiles = finder.getPrefabHierarchy(editorSupport.getPrefabId()); + + if (prefabFiles.indexOf(this._prefabFile) >= 0) { + + sel.push(obj2); + } + } + } + }); + + this.getEditor().setSelection(sel); + } + }); + + menu.addSeparator(); + + super.createMenu(menu); + } + + createForm(parent: HTMLDivElement) { + + const comp = this.createGridElement(parent); + comp.style.gridTemplateColumns = "auto auto 1fr"; + + for (const prop of this._properties) { + + prop.getType().createInspectorPropertyEditor(this, comp, prop, true); + } + } + + canEdit(obj: sceneobjects.ISceneGameObject, n: number): boolean { + + if (sceneobjects.isGameObject(obj)) { + + const objES = obj.getEditorSupport(); + + if (objES.isPrefabInstance()) { + + const objPrefabFile = objES.getPrefabFile(); + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + if (objES.isNestedPrefabInstance()) { + + const objPrefabId = finder.getFirstNonNestedPrefabId(objES.getPrefabId()); + + if (objPrefabId) { + + const sectionPrefabId = finder.getPrefabId(this._prefabFile); + + return objPrefabId === sectionPrefabId; + } + + } else { + + if (finder.isPrefabVariant(objPrefabFile, this._prefabFile)) { + + return true; + } + } + } + } + + return false; + } + + canEditNumber(n: number): boolean { + + return n > 0; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectSingleUserComponentSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectSingleUserComponentSection.ts deleted file mode 100644 index d0e4f5e8b..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectSingleUserComponentSection.ts +++ /dev/null @@ -1,497 +0,0 @@ -namespace phasereditor2d.scene.ui.sceneobjects { - - import controls = colibri.ui.controls; - import io = colibri.core.io; - - class GameObjectSectionAdapter extends SceneGameObjectSection { - - constructor(page: controls.properties.PropertyPage) { - super(page, "id", "title"); - } - - createForm(parent: HTMLDivElement) { - // nothing - } - - canEdit(obj: any, n: number): boolean { - // nothing - return false; - } - - canEditNumber(n: number): boolean { - // nothing - return false; - } - - getSelection(): ISceneGameObject[] { - - const page = this.getPage(); - - const sel = page.getSelection(); - - return sel.map((n: UserComponentNode) => n.getObject()); - } - } - - export class ObjectSingleUserComponentSection extends editor.properties.BaseSceneSection { - - private _propArea: HTMLDivElement; - - constructor(page: controls.properties.PropertyPage) { - super(page, - "phasereditor2d.scene.ui.sceneobjects.ObjectSingleUserComponentSection", "User Component", false, false); - } - - getSectionHelpPath() { - return "scene-editor/user-components-instancing.html"; - } - - createForm(parent: HTMLDivElement) { - - const sectionAdapter = new GameObjectSectionAdapter(this.getPage()); - - { - const commonPropsComp = this.createGridElement(parent, 2); - - { - // name property - - this.createLabel(commonPropsComp, "Component"); - - const text = this.createText(commonPropsComp, true); - - this.addUpdater(() => { - - text.value = this.buildComponentName(); - }); - } - - { - // export property - - const result = sectionAdapter.createBooleanField(commonPropsComp, this.getExportProperty(), false); - - this.addUpdater(() => { - - const values = this.getSelection().map(n => { - - if (n.getObject().getEditorSupport().isScenePrefabObject()) { - - if (n.getUserComponentsComponent().hasLocalUserComponent(n.getComponentName())) { - - return true; - } - } - - return false; - }); - - const visible = this.flatValues_BooleanAnd(values); - - result.labelElement.style.display = visible ? "" : "none"; - result.checkElement.style.display = visible ? "" : "none"; - }); - } - } - - this._propArea = this.createGridElement(parent); - this._propArea.style.gridTemplateColumns = "1fr"; - - this.addUpdater(() => { - - this._propArea.innerHTML = ""; - - const finder = ScenePlugin.getInstance().getSceneFinder(); - - const nodes = this.getSelection(); - const node = nodes[0]; - - const compName = node.getComponentName(); - const compInfo = finder.getUserComponentByName(compName); - - // properties - - { - const props = compInfo.component.getUserProperties().getProperties(); - - if (props.length > 0) { - - const compPropArea = this.createGridElement(this._propArea); - compPropArea.style.gridTemplateColumns = "auto auto 1fr"; - - const atLeastOneDefinedInAPrefab = nodes - .filter(n => n.isPrefabDefined()) - .length > 0; - - for (const prop of props) { - - prop.getType().createInspectorPropertyEditor(sectionAdapter, compPropArea, prop, atLeastOneDefinedInAPrefab); - } - } - } - - const btn = this.createButton(this._propArea, "Select Parent Game Object", () => { - - this.getEditor().setSelection(this.getSelection().map(node => node.getObject())); - }); - btn.style.width = "100%"; - btn.style.justifySelf = "self-center"; - btn.style.marginTop = "10px"; - }); - - // it is important to update the adapter at the end, - // because it should update the adapter selection after the dynamic prop elements - // are created - - this.addUpdater(() => { - - sectionAdapter.updateWithSelection(); - }); - } - - private getExportProperty(): IProperty { - - return { - name: "isExported", - label: "Export", - getValue: obj => { - - const compName = this.getSelectionFirstElement().getComponentName(); - - const value = obj.getEditorSupport() - .getUserComponentsComponent().isExportComponent(compName); - - return value; - }, - setValue: (obj: ISceneGameObject, value: boolean) => { - - const compName = this.getSelectionFirstElement().getComponentName(); - - obj.getEditorSupport() - .getUserComponentsComponent().setExportComponent(compName, value); - }, - defValue: true, - }; - } - - private static openComponentEditor(node: UserComponentNode) { - - const finder = ScenePlugin.getInstance().getSceneFinder(); - - const compName = node.getComponentName(); - - const info = finder.getUserComponentByName(compName); - - const editor = colibri.Platform.getWorkbench().openEditor(info.file) as editor.usercomponent.UserComponentsEditor; - - editor.revealComponent(compName); - } - - static createComponentIcon(section: colibri.ui.controls.properties.FormBuilder, headerDiv: HTMLDivElement) { - - section.createIcon(headerDiv, ScenePlugin.getInstance().getIcon(ICON_USER_COMPONENT)); - } - - static selectAllComponentNodesFor(editor: ui.editor.SceneEditor, node: UserComponentNode) { - - const compName = node.getComponentName(); - - const nodes = editor.getSelectedGameObjects() - .flatMap(obj => obj.getEditorSupport() - .getUserComponentsComponent().getUserComponentNodes()) - .filter(node => node.getComponentName() === compName); - - editor.setSelection(nodes); - } - - private static openPrefabLinkInSceneEditor(node: UserComponentNode) { - - const prefabFile = node.getPrefabFile(); - const prefabEditor = colibri.Platform.getWorkbench().openEditor(prefabFile); - - if (prefabEditor && prefabEditor instanceof ui.editor.SceneEditor) { - - setTimeout(() => { - - const obj = node.getObject(); - const objES = obj.getEditorSupport(); - - let selObj: ISceneGameObject; - - if (objES.isNestedPrefabInstance()) { - - selObj = prefabEditor.getScene().getByEditorId(objES.getPrefabId()); - - } else { - - selObj = prefabEditor.getScene().getPrefabObject(); - } - - if (selObj) { - - const selNode = selObj.getEditorSupport().getUserComponentsComponent().getUserComponentNodes().find(n => n.getComponentName() === node.getComponentName()); - - if (selNode) { - - prefabEditor.setSelection([selNode]); - } - } - - }, 10); - } - } - - private buildComponentName() { - - const nodes = this.getSelection(); - - let name = this.getSelectionFirstElement().getComponentName(); - - const prefabNodes = [...new Set( - nodes - .filter(n => n.isPrefabDefined()) - .map(node => node.getPrefabFile().getNameWithoutExtension())) - ]; - - const prefabNames = prefabNodes.join(", "); - - if (prefabNodes.length === 1) { - - name += " ← " + prefabNames; - - } else if (prefabNodes.length > 1) { - - name += ` ← (${prefabNames})`; - } - - return name; - } - - static buildPrefabLinks(nodes: UserComponentNode[], headerDiv: HTMLDivElement) { - - const nodesInPrefabs = nodes.filter(n => n.isPrefabDefined()); - const nodesInPrefabsLen = nodesInPrefabs.length; - const atLeastOneDefinedInAPrefab = nodesInPrefabsLen > 0; - - if (atLeastOneDefinedInAPrefab) { - - const elem = document.createElement("span"); - elem.innerHTML = " ← "; - headerDiv.appendChild(elem); - - if (nodesInPrefabsLen > 1) { - - const elem = document.createElement("label"); - elem.innerHTML = "("; - headerDiv.appendChild(elem); - } - - for (let i = 0; i < nodesInPrefabsLen; i++) { - - const node = nodesInPrefabs[i]; - - const prefabFile = nodesInPrefabs[i].getPrefabFile(); - const prefabBtn = document.createElement("a"); - headerDiv.appendChild(prefabBtn); - prefabBtn.href = "#"; - prefabBtn.innerHTML = prefabFile.getNameWithoutExtension(); - prefabBtn.addEventListener("click", e => { - - this.openPrefabLinkInSceneEditor(node); - }); - - if (i < nodesInPrefabsLen - 1) { - - const elem = document.createElement("label"); - elem.innerHTML = ", "; - headerDiv.appendChild(elem); - } - } - - if (nodesInPrefabs.length > 1) { - - const elem = document.createElement("label"); - elem.innerHTML = ")"; - headerDiv.appendChild(elem); - } - } - return { atLeastOneDefinedInAPrefab }; - } - - static createComponentMenu( - nodes: UserComponentNode[], - menu: controls.Menu, - section: ObjectSingleUserComponentSection | ObjectUserComponentsSection) { - - const firstNode = nodes[0]; - const comp = firstNode.getUserComponent(); - const compName = comp.getName(); - - menu.addAction({ - text: `Select Objects With ${compName}`, - callback: () => { - - const sel = []; - - section.getEditor().getScene().visitAll(obj => { - - if (GameObjectEditorSupport.hasObjectComponent(obj, UserComponentsEditorComponent)) { - - const userComp = GameObjectEditorSupport - .getObjectComponent(obj, UserComponentsEditorComponent) as UserComponentsEditorComponent; - - if (userComp.hasUserComponent(compName)) { - - sel.push(obj); - } - } - }); - - section.getEditor().setSelection(sel); - } - }); - - menu.addAction({ - text: "Open Definition Of " + firstNode.getComponentName(), - callback: () => this.openComponentEditor(firstNode) - }); - - // the Reveal In Prefab File options - { - const fileNodeMap = new Map(); - - for (const node of nodes) { - - if (node.isPrefabDefined()) { - - fileNodeMap.set(node.getPrefabFile(), node); - } - } - - for (const prefabFile of fileNodeMap.keys()) { - - const node = fileNodeMap.get(prefabFile); - - menu.addAction({ - text: `Reveal In ${prefabFile.getNameWithoutExtension()} File`, - callback: () => this.openPrefabLinkInSceneEditor(node) - }); - } - } - - if (section instanceof ObjectUserComponentsSection) { - - menu.addAction({ - text: "Edit Values", - callback: () => { - - ObjectSingleUserComponentSection.selectAllComponentNodesFor(section.getEditor(), firstNode); - } - }); - } - - if (!firstNode.isPrefabDefined()) { - - if (nodes.length === 1) { - - const editorComp = firstNode.getUserComponentsComponent(); - - menu.addAction({ - text: "Move Up", - callback: () => { - - section.runOperation(() => { - - editorComp.moveUpUserComponent(compName) - }); - - section.updateWithSelection(); - } - }); - - menu.addAction({ - text: "Move Down", - callback: () => { - - section.runOperation(() => { - - editorComp.moveDownUserComponent(compName) - }); - - section.updateWithSelection(); - } - }); - } - - const allLocalNodes = nodes.filter(n => n.isPrefabDefined()).length === 0; - - if (allLocalNodes) { - - menu.addAction({ - text: "Delete", - callback: () => { - - const editor = section.getEditor(); - - const selIds = editor.getSelectionManager().getSelectionIds(); - - section.runOperation(() => { - - for (const node of nodes) { - - node.getUserComponentsComponent().removeUserComponent(compName); - } - }); - - editor.getSelectionManager().setSelectionByIds(selIds); - } - }); - } - } - } - - createMenu(menu: controls.Menu): void { - - ObjectSingleUserComponentSection.createComponentMenu(this.getSelection(), menu, this); - - menu.addSeparator(); - - super.createMenu(menu); - } - - runOperation(action: () => void) { - - const editor = this.getEditor(); - - editor.getUndoManager().add(new ui.editor.undo.SimpleSceneSnapshotOperation(editor, action)); - } - - canEditAll(selection: UserComponentNode[]) { - - const first = selection[0]; - const firstComp = first.getUserComponent(); - - for (const node of selection) { - - const comp = node.getUserComponent(); - - if (comp.getName() !== firstComp.getName()) { - - return false; - } - } - - return true; - } - - canEdit(obj: any, n: number): boolean { - - return obj instanceof sceneobjects.UserComponentNode; - } - - canEditNumber(n: number): boolean { - - return n > 0; - } - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectUserComponentsSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectUserComponentsSection.ts deleted file mode 100644 index 4d0cb8f55..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/ObjectUserComponentsSection.ts +++ /dev/null @@ -1,260 +0,0 @@ -namespace phasereditor2d.scene.ui.sceneobjects { - - import controls = colibri.ui.controls; - import UserComponent = editor.usercomponent.UserComponent; - - export class ObjectUserComponentsSection extends SceneGameObjectSection { - - private _propArea: HTMLDivElement; - - constructor(page: controls.properties.PropertyPage) { - super(page, - "phasereditor2d.scene.ui.sceneobjects.ObjectUserComponentsSection", "User Components", false, true); - } - - getSectionHelpPath() { - return "scene-editor/user-components-instancing.html"; - } - - private getCommonComponentNodes() { - - const nameCountMap = new Map(); - - let selection = this.getSelection() - .flatMap(obj => obj.getEditorSupport().getUserComponentsComponent().getUserComponentNodes()); - - let nodes: UserComponentNode[] = []; - - for (const node of selection) { - - if (!node.isPublished()) { - - continue; - } - - const name = node.getComponentName(); - - if (nameCountMap.has(name)) { - - const count = nameCountMap.get(name); - nameCountMap.set(name, count + 1); - - } else { - - nameCountMap.set(name, 1); - nodes.push(node); - } - } - - const total = this.getSelection().length; - - nodes = nodes - .filter(node => nameCountMap.get(node.getComponentName()) === total); - - nodes.sort((a, b) => { - - const aa = a.isPrefabDefined() ? 1 : 0; - const bb = b.isPrefabDefined() ? 1 : 0; - - return aa - bb; - }); - - return nodes; - } - - createForm(parent: HTMLDivElement) { - - const comp = this.createGridElement(parent); - comp.style.gridTemplateColumns = "1fr"; - - this._propArea = this.createGridElement(comp); - this._propArea.style.gridTemplateColumns = "1fr"; - - comp.appendChild(this._propArea); - - this.addUpdater(() => { - - this._propArea.innerHTML = ""; - - const finder = ScenePlugin.getInstance().getSceneFinder(); - - const editorCompList = this.getSelection() - .map(obj => GameObjectEditorSupport.getObjectComponent(obj, UserComponentsEditorComponent) as UserComponentsEditorComponent); - - const commonNodes = this.getCommonComponentNodes(); - - for (const node of commonNodes) { - - const compName = node.getComponentName(); - - const headerDiv = document.createElement("div"); - headerDiv.classList.add("PrefabLink"); - - this._propArea.appendChild(headerDiv); - - ObjectSingleUserComponentSection.createComponentIcon(this, headerDiv); - - const compBtn = document.createElement("a"); - headerDiv.appendChild(compBtn); - compBtn.href = "#"; - compBtn.innerHTML = compName; - compBtn.addEventListener("click", e => { - - ObjectSingleUserComponentSection.selectAllComponentNodesFor(this.getEditor(), node); - }); - - // get all nodes (from the selected objects) - // with the same user component of this current node - const sameNodes = this.getSelection() - .flatMap(obj => obj.getEditorSupport() - .getUserComponentsComponent().getUserComponentNodes()) - .filter(n => n.getComponentName() === compName); - - const samePrefabNodes = sameNodes.filter(n => n.isPrefabDefined()); - - ObjectSingleUserComponentSection.buildPrefabLinks(samePrefabNodes, headerDiv); - - - this.createComponentMenuIcon(headerDiv, sameNodes); - } - - // Add Components button - - const btn = this.createButton(this._propArea, "Add Component", () => { - - const used = new Set( - [...editorCompList - .flatMap(editorComp => editorComp.getLocalUserComponents()) - .map(info => info.component.getName()), - - ...editorCompList.flatMap(editorComp => editorComp.getPrefabUserComponents()) - .flatMap(info => info.components) - .map(c => c.getName()) - ] - ); - - class ContentProvider implements controls.viewers.ITreeContentProvider { - - getRoots(input: any): any[] { - - return finder.getUserComponentsModels() - .filter(info => info.model.getComponents().filter(c => !used.has(c.getName())).length > 0); - } - - getChildren(parentObj: core.json.IUserComponentsModelInfo | UserComponent): any[] { - - if (parentObj instanceof UserComponent) { - - return []; - } - - return parentObj.model.getComponents().filter(c => !used.has(c.getName())); - } - } - - const viewer = new controls.viewers.TreeViewer("UserComponentInstancePropertySection.addComponentDialogViewer"); - - viewer.setStyledLabelProvider({ - getStyledTexts: (obj: UserComponent | core.json.IUserComponentsModelInfo, dark) => { - - const theme = controls.Controls.getTheme(); - - if (obj instanceof UserComponent) { - - return [{ - text: obj.getName(), - color: theme.viewerForeground - }]; - } - - return [{ - text: obj.file.getNameWithoutExtension(), - color: theme.viewerForeground - }, { - text: " - " + obj.file.getParent().getProjectRelativeName() - .split("/").filter(s => s !== "").reverse().join("/"), - color: theme.viewerForeground + "90" - }]; - } - }); - - viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider( - (obj: core.json.IUserComponentsModelInfo | UserComponent) => - new controls.viewers.IconImageCellRenderer( - obj instanceof UserComponent ? - ScenePlugin.getInstance().getIcon(ICON_USER_COMPONENT) - : colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER)))); - - viewer.setContentProvider(new ContentProvider()); - - viewer.setInput([]); - - viewer.expandRoots(false); - - const dlg = new controls.dialogs.ViewerDialog(viewer, false); - - dlg.setSize(undefined, 400, true); - - dlg.create(); - - dlg.setTitle("User Component"); - - dlg.enableButtonOnlyWhenOneElementIsSelected(dlg.addOpenButton("Add Component", () => { - - const selComp = viewer.getSelectionFirstElement() as UserComponent; - - if (selComp) { - - this.runOperation(() => { - - for (const editorComp of editorCompList) { - - editorComp.addUserComponent(selComp.getName()); - } - }); - - this.updateWithSelection(); - } - }), obj => obj instanceof UserComponent); - - dlg.addCancelButton(); - }); - - btn.style.width = "100%"; - btn.style.justifySelf = "self-center"; - btn.style.marginTop = "10px"; - }); - } - - private createComponentMenuIcon( - headerDiv: HTMLElement, nodes: UserComponentNode[]) { - - this.createMenuIcon(headerDiv, () => { - - const menu = new controls.Menu(); - - ObjectSingleUserComponentSection.createComponentMenu(nodes, menu, this); - - return menu; - }); - } - - runOperation(action: () => void) { - - const editor = this.getEditor(); - - editor.getUndoManager().add(new ui.editor.undo.SimpleSceneSnapshotOperation(editor, action)); - } - - canEdit(obj: any, n: number): boolean { - - return GameObjectEditorSupport.hasEditorSupport(obj) - && (obj as ISceneGameObject).getEditorSupport().isDisplayObject(); - } - - canEditNumber(n: number): boolean { - - return n > 0; - } - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/PrefabInstanceUserPropertySection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/PrefabInstanceUserPropertySection.ts deleted file mode 100644 index 6712d7ba8..000000000 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/PrefabInstanceUserPropertySection.ts +++ /dev/null @@ -1,192 +0,0 @@ -namespace phasereditor2d.scene.ui.sceneobjects { - - import controls = colibri.ui.controls; - - export class PrefabInstanceSection extends SceneGameObjectSection { - - private _propArea: HTMLDivElement; - - constructor(page: controls.properties.PropertyPage) { - super(page, - "phasereditor2d.scene.ui.sceneobjects.PrefabInstanceUserPropertySection", "Prefab Instance"); - } - - getSectionHelpPath() { - - return "scene-editor/prefab-user-properties.html#user-properties-in-a-prefab-instance"; - } - - createMenu(menu: controls.Menu) { - - menu.addCommand(editor.commands.CMD_OPEN_PREFAB); - - menu.addSeparator(); - - super.createMenu(menu); - } - - createForm(parent: HTMLDivElement) { - - const comp = this.createGridElement(parent); - comp.style.gridTemplateColumns = "1fr"; - - this._propArea = this.createGridElement(comp); - this._propArea.style.gridTemplateColumns = "auto auto 1fr"; - - comp.appendChild(this._propArea); - - this.addUpdater(() => { - - this._propArea.innerHTML = ""; - - const obj = this.getSelectionFirstElement() as ISceneGameObject; - - // properties of non-nested prefabs - - const userPropsComponent = GameObjectEditorSupport - .getObjectComponent(obj, PrefabUserPropertyComponent) as PrefabUserPropertyComponent; - - const propsByPrefabList = userPropsComponent.getPropertiesByPrefab(); - - for (const propsByPrefab of propsByPrefabList) { - - const prefabFile = propsByPrefab.prefabFile; - const prefabName = prefabFile.getNameWithoutExtension(); - - const headerDiv = this.createPrefabLink(prefabFile); - - this.createMenuIcon(headerDiv, () => { - - const menu = new controls.Menu(); - - menu.addAction({ - text: `Select All ${prefabName}`, - callback: () => { - - const finder = ScenePlugin.getInstance().getSceneFinder(); - - const sel = []; - - this.getEditor().getScene().visitAll(obj2 => { - - if (GameObjectEditorSupport.hasEditorSupport(obj2)) { - - const editorSupport = GameObjectEditorSupport.getEditorSupport(obj2); - - if (editorSupport.isPrefabInstance()) { - - const prefabFiles = finder.getPrefabHierarchy(editorSupport.getPrefabId()); - - if (prefabFiles.indexOf(prefabFile) >= 0) { - - sel.push(obj2); - } - } - } - }); - - this.getEditor().setSelection(sel); - } - }); - - menu.addAction({ - text: `Open ${prefabName} File`, - callback: () => colibri.Platform.getWorkbench().openEditor(prefabFile) - }) - - return menu; - }); - - for (const prop of propsByPrefab.properties) { - - prop.getType().createInspectorPropertyEditor(this, this._propArea, prop, true); - } - } - - // link to nested prefab - - const objES = obj.getEditorSupport(); - - if (objES.isNestedPrefabInstance()) { - - const file = objES.getPrefabFile(); - - this.createPrefabLink(file, `${file.getNameWithoutExtension()} (nested in)`); - } - }); - } - - private createPrefabLink(file: colibri.core.io.FilePath, btnText?:string) { - - const headerDiv = document.createElement("div"); - headerDiv.classList.add("PrefabLink"); - headerDiv.style.gridColumn = "1 / span 3"; - this._propArea.appendChild(headerDiv); - - const prefabBtn = document.createElement("a"); - prefabBtn.href = "#"; - prefabBtn.innerHTML = btnText ?? file.getNameWithoutExtension(); - headerDiv.appendChild(prefabBtn); - - prefabBtn.addEventListener("click", () => colibri.Platform.getWorkbench().openEditor(file)); - - return headerDiv; - } - - canEdit(obj: any, n: number): boolean { - - return true; - } - - canEditNumber(n: number): boolean { - - if (n === 0) { - - return false; - } - - const obj = this.getSelectionFirstElement(); - - if (GameObjectEditorSupport.hasEditorSupport(obj)) { - - const support = GameObjectEditorSupport.getEditorSupport(obj); - - if (support.isPrefabInstance()) { - - const prefabFile = support.getPrefabFile(); - - for (const obj2 of this.getSelection()) { - - if (GameObjectEditorSupport.hasEditorSupport(obj2)) { - - const support2 = GameObjectEditorSupport.getEditorSupport(obj2); - - if (support2.isPrefabInstance()) { - - const prefabFile2 = support2.getPrefabFile(); - - if (prefabFile !== prefabFile2) { - - return false; - } - - } else { - - return false; - } - - } else { - - return false; - } - } - - return true; - } - } - - - return false; - } - } -} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts index 6ed9555ea..7eb807ca2 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneGameObjectSection.ts @@ -204,6 +204,21 @@ namespace phasereditor2d.scene.ui.sceneobjects { return text; } + createKeyCodeRow(parent: HTMLElement, prop: IProperty, lockIcon: boolean = true) { + + if (lockIcon) { + + this.createLock(parent, prop); + } + + const labelElement = this.createLabel(parent, prop.label, PhaserHelp(prop.tooltip)); + labelElement.style.gridColumn = "2"; + + const buttonElement = this.createKeyCodeField(parent, prop); + + return { labelElement, buttonElement }; + } + createPropertyEnumRow(parent: HTMLElement, prop: IEnumProperty, lockIcon: boolean = true, filter?: (v: any) => boolean) { if (lockIcon) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts index 90c95fc02..5dfa2c403 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/object/properties/SceneObjectSection.ts @@ -197,6 +197,43 @@ namespace phasereditor2d.scene.ui.sceneobjects { return colorElement; } + createKeyCodeField(parent: HTMLElement, property: IProperty) { + + const btn = this.createButton(parent, "KeyCode", e => { + + const viewer = new KeyboardKeysViewer(); + + const selObj = this.getSelectionFirstElement(); + const keyCode = property.getValue(selObj); + + viewer.revealAndSelect(keyCode); + + const dlg = new controls.dialogs.ViewerDialog(viewer, false); + + dlg.create(); + + dlg.setTitle("Select Key Code"); + + dlg.addOpenButton("Select", (sel) => { + + const value = sel[0]; + const keys = this.getSelection(); + + this.getEditor().getUndoManager().add( + new SimpleOperation(this.getEditor(), keys, KeyboardKeyComponent.keyCode, value)); + + }, false); + + dlg.addCancelButton(); + }); + + this.addUpdater(() => { + + btn.textContent = this.flatValues_StringOneOrNothing( + this.getSelection().map(obj => property.getValue(obj))); + }); + } + createBooleanField(parent: HTMLElement, property: IProperty, checkUnlock = true) { const labelElement = this.createLabel(parent, property.label, PhaserHelp(property.tooltip)); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/scriptNode/AddScriptDialog.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/scriptNode/AddScriptDialog.ts index 7335d06dc..472351e39 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/scriptNode/AddScriptDialog.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/scriptNode/AddScriptDialog.ts @@ -70,7 +70,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { this.addOpenButton("Add Script", sel => { this.addScript(sel[0]); - })); this.addCancelButton(); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSection.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSection.ts index 1a13d4aca..9cf1ed92c 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSection.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/texture/TextureSection.ts @@ -7,7 +7,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { static SECTION_ID = "phasereditor2d.scene.ui.sceneobjects.TextureSection"; constructor(page: controls.properties.PropertyPage) { - super(page, TextureSection.SECTION_ID, "Texture", false, true); + super(page, TextureSection.SECTION_ID, "Texture", false, false); } getSectionHelpPath() { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts index be98a9949..1622df293 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/AbstractDialogPropertyType.ts @@ -52,7 +52,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const text = section.createStringField(comp, prop); - const btn = this.createSearchButton(() => prop.getValue(section.getSelectionFirstElement()), value => { + const { buttonElement, iconControl } = this.createSearchButton(() => prop.getValue(section.getSelectionFirstElement()), value => { text.value = value; @@ -64,10 +64,14 @@ namespace phasereditor2d.scene.ui.sceneobjects { section.addUpdater(() => { - btn.disabled = !section.isUnlocked(prop); + buttonElement.disabled = !section.isUnlocked(prop); + + const value = prop.getValue(section.getSelectionFirstElement()); + + this.updateIcon(iconControl, value); }); - comp.appendChild(btn); + comp.appendChild(buttonElement); } private createEditorComp() { @@ -83,6 +87,8 @@ namespace phasereditor2d.scene.ui.sceneobjects { private createSearchButton(getValue: () => any, callback: (value: string) => void) { + console.log("createSearchButton") + const iconControl = new controls.IconControl(colibri.ColibriPlugin.getInstance().getIcon(colibri.ICON_FOLDER)); const btn = document.createElement("button"); @@ -102,7 +108,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { } - return btn; + return { buttonElement: btn, iconControl }; } protected async updateIcon(iconControl: controls.IconControl, value: any) { @@ -136,7 +142,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { viewer.setInput([]); - const dlg = new controls.dialogs.ViewerDialog(viewer, true); + const dlg = this.createDialogInstance(viewer, true); const size = this.getDialogSize(); @@ -156,16 +162,22 @@ namespace phasereditor2d.scene.ui.sceneobjects { dlg.addCancelButton(); - this.loadViewerInput(viewer); + await this.loadViewerInput(viewer); this.revealValue(viewer, revealValue); controls.viewers.GridTreeViewerRenderer.expandSections(viewer); } + protected createDialogInstance(viewer: controls.viewers.TreeViewer, showZoomControls: boolean) + : controls.dialogs.AbstractViewerDialog { + + return new controls.dialogs.ViewerDialog(viewer, showZoomControls) + } + protected abstract valueToString(viewer: controls.viewers.TreeViewer, value: any): string; - protected abstract loadViewerInput(viewer: controls.viewers.TreeViewer): void; + protected abstract loadViewerInput(viewer: controls.viewers.TreeViewer): Promise; protected revealValue(viewer: controls.viewers.TreeViewer, value: string) { @@ -192,14 +204,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { setValue(inputElement.value); }); + const { buttonElement, iconControl } = this.createSearchButton(getValue, setValue); + + comp.appendChild(buttonElement); + const update = () => { - inputElement.value = getValue(); - }; + const value = getValue(); - const btn = this.createSearchButton(getValue, setValue); + inputElement.value = value; - comp.appendChild(btn); + this.updateIcon(iconControl, value); + }; return { element: comp, diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ColorPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ColorPropertyType.ts index 1c3a431a0..13d3e6182 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ColorPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ColorPropertyType.ts @@ -37,17 +37,6 @@ namespace phasereditor2d.scene.ui.sceneobjects { }; } - private createEditorComp() { - - const comp = document.createElement("div"); - comp.style.display = "grid"; - comp.style.gridTemplateColumns = "1fr auto"; - comp.style.gap = "5px"; - comp.style.alignItems = "center"; - - return comp; - } - createInspectorPropertyEditor(section: SceneGameObjectSection, parent: HTMLElement, userProp: UserProperty, lockIcon: boolean): void { section.createPropertyColorRow(parent, userProp.getComponentProperty(), true, lockIcon); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts new file mode 100644 index 000000000..83c8ac0c0 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/EventPropertyType.ts @@ -0,0 +1,225 @@ +/// + +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + + export class EventPropertyType extends AbstractDialogPropertyType { + + constructor() { + super({ + id: "event", + dialogTitle: "Select Event", + name: "Event Dialog", + hasCustomIcon: false + }); + } + + getName() { + + return "Event"; + } + + renderValue(value: any): string { + + return value; + } + + private isPhaserBuiltIn(value: string) { + + return value.startsWith("Phaser."); + } + + private getCodeValue(value: string) { + + if (this.isPhaserBuiltIn(value)) { + + return value; + } + + return `"${value}"`; + } + + protected createDialogInstance(viewer: controls.viewers.TreeViewer, zoom: boolean) { + + return new EventPropertyDialog(viewer); + } + + buildDeclarePropertyCodeDOM(prop: UserProperty, value: string): core.code.FieldDeclCodeDOM { + + const codeValue = this.getCodeValue(value); + + return this.buildExpressionFieldCode(prop, "string", codeValue); + } + + buildSetObjectPropertyCodeDOM(comp: Component, args: ISetObjectPropertiesCodeDOMArgs, userProp: UserProperty): void { + + const prop = userProp.getComponentProperty(); + const value = prop.getValue(args.obj); + + if (this.isPhaserBuiltIn(value)) { + + comp.buildSetObjectPropertyCodeDOM_StringVerbatimProperty(args, prop); + + } else { + + comp.buildSetObjectPropertyCodeDOM_StringProperty(args, prop); + } + } + + protected async createViewer() { + + const viewer = new controls.viewers.TreeViewer("phasereditor2d.scene.editor.EventPropertyType.Dialog"); + viewer.setCellRendererProvider(new controls.viewers.EmptyCellRendererProvider( + () => new controls.viewers.EmptyCellRenderer(false))); + viewer.setLabelProvider(new controls.viewers.LabelProvider()); + viewer.setStyledLabelProvider(new EventPropertyStyleLabelProider()); + viewer.setContentProvider(new controls.viewers.ArrayTreeContentProvider()); + + return viewer; + } + + protected valueToString(viewer: colibri.ui.controls.viewers.TreeViewer, value: string): string { + + return value; + } + + protected async loadViewerInput(viewer: colibri.ui.controls.viewers.TreeViewer) { + + // Phaser events + const docs = ScenePlugin.getInstance().getPhaserEventsDocs(); + + const phaserNames = docs.getKeys(); + + const finder = ScenePlugin.getInstance().getSceneFinder(); + + const events = await finder.findUserEvents(); + + // user events + + const userNames = events.map(e => e.name); + + // Phaser animation dynamic events + + const packFinder = new pack.core.PackFinder(); + + await packFinder.preload(); + + const animEvents = packFinder + .getAssets(i => i instanceof pack.core.AnimationsAssetPackItem) + .map(i => i as pack.core.AnimationsAssetPackItem) + .flatMap(i => i.getAnimations()) + .map(anim => anim.getKey()) + .map(k => `animationcomplete-${k}`); + + // Phaser keyboard dynamic events + + const keyboardEvents = []; + + for(const k of Object.keys(Phaser.Input.Keyboard.KeyCodes)) { + + keyboardEvents.push(`keydown-${k}`, `keyup-${k}`); + } + + console.log(keyboardEvents); + + viewer.setInput([ + ...userNames, + ...phaserNames, + ...keyboardEvents, + ...animEvents]); + } + } + + class EventPropertyDialog extends controls.dialogs.ViewerFormDialog { + + constructor(viewer: controls.viewers.TreeViewer) { + super(viewer, false); + + this.setSize(undefined, 600, true); + } + + protected createFormArea(formArea: HTMLDivElement): void { + + const docsArea = document.createElement("div"); + docsArea.innerHTML = ""; + + formArea.appendChild(docsArea); + formArea.classList.add("EventPropertyDialogHelpPane"); + + const viewer = this.getViewer(); + + const userEvents: core.json.IUserEvent[] = []; + + ScenePlugin.getInstance().getSceneFinder().findUserEvents().then(events => { + + userEvents.push(...events); + }) + + viewer.eventSelectionChanged.addListener(sel => { + + const [eventName] = sel as string[]; + + const phaserDocs = ScenePlugin.getInstance().getPhaserDocs(); + const eventsDocs = ScenePlugin.getInstance().getPhaserEventsDocs(); + + if (eventName) { + + let help = ""; + + if (eventName.startsWith("Phaser.")) { + + help = eventsDocs.getDoc(eventName, false) || ""; + + } else if (eventName.startsWith("animationcomplete-")) { + + help = eventsDocs.getDoc("Phaser.Animations.Events.ANIMATION_COMPLETE_KEY", false); + + } else if (eventName.startsWith("keydown-")) { + + help = eventsDocs.getDoc("Phaser.Input.Keyboard.Events.KEY_DOWN", false); + + } if (eventName.startsWith("keyup-")) { + + help = eventsDocs.getDoc("Phaser.Input.Keyboard.Events.KEY_UP", false); + + } else { + + const event = userEvents.find(e => e.name === eventName); + + if (event) { + + help = phasereditor2d.ide.core.PhaserDocs.markdownToHtml(event.help) + } + } + + docsArea.innerHTML = help; + } + }); + } + } + + class EventPropertyStyleLabelProider implements controls.viewers.IStyledLabelProvider { + + getStyledTexts(obj: any, dark: boolean): controls.viewers.IStyledText[] { + + const label = obj as string; + const i = label.lastIndexOf("."); + const namespace_ = label.substring(0, i + 1); + const name = label.substring(i + 1); + + const theme = controls.Controls.getTheme(); + + return [ + { + color: theme.viewerForeground + "90", + text: namespace_ + }, + { + color: theme.viewerForeground, + text: name + } + ]; + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/KeyCodePropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/KeyCodePropertyType.ts new file mode 100644 index 000000000..34c9faa02 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/KeyCodePropertyType.ts @@ -0,0 +1,84 @@ +/// +namespace phasereditor2d.scene.ui.sceneobjects { + + import controls = colibri.ui.controls; + import code = core.code; + + export class KeyCodePropertyType extends UserPropertyType { + + constructor(typeId = "keycode") { + super(typeId, "SPACE"); + } + + getName(): string { + + return "Key Code"; + } + + createEditorElement(getValue: () => any, setValue: (value: any) => void): IPropertyEditor { + + const formBuilder = new controls.properties.FormBuilder(); + + const btn = formBuilder.createButton(undefined, "Key Code", e => { + + const viewer = new KeyboardKeysViewer(); + + const keyCode = getValue(); + viewer.revealAndSelect(keyCode); + + const dlg = new controls.dialogs.ViewerDialog(viewer, false); + + dlg.create(); + + dlg.setTitle("Select Key Code"); + + dlg.addOpenButton("Select", (sel) => { + + const value = sel[0]; + + setValue(value); + + }, false); + + dlg.addCancelButton(); + }); + + const update = () => { + + btn.textContent = getValue(); + }; + + return { + element: btn, + update + }; + } + + createInspectorPropertyEditor(section: SceneGameObjectSection, parent: HTMLElement, userProp: UserProperty, lockIcon: boolean): void { + + section.createKeyCodeRow(parent, userProp.getComponentProperty(), lockIcon); + } + + renderValue(value: string): string { + + return value; + } + + buildDeclarePropertyCodeDOM(prop: UserProperty, value: string): core.code.FieldDeclCodeDOM { + + return this.buildExpressionFieldCode(prop, "number", `Phaser.Input.Keyboard.KeyCodes.${value}`); + } + + buildSetObjectPropertyCodeDOM(comp: Component, args: ISetObjectPropertiesCodeDOMArgs, userProp: UserProperty): void { + + comp.buildSetObjectPropertyCodeDOM([userProp.getComponentProperty()], args2 => { + + const dom = new code.AssignPropertyCodeDOM(args2.fieldCodeName, args.objectVarName); + + dom.value(`Phaser.Input.Keyboard.KeyCodes.${args2.value}`); + + args.statements.push(dom); + }); + } + } +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts index 5c0c3023c..8097ac048 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/ObjectVarPropertyType.ts @@ -64,15 +64,23 @@ namespace phasereditor2d.scene.ui.sceneobjects { return core.code.SceneCodeDOMBuilder.getPrefabInstanceVarName(value); } - return objES.getLabel(); + return core.code.formatToValidVarName(objES.getLabel()); } - return viewer.getLabelProvider().getLabel(value); + return core.code.formatToValidVarName(viewer.getLabelProvider().getLabel(value)); } - protected loadViewerInput(viewer: colibri.ui.controls.viewers.TreeViewer): void { + protected async loadViewerInput(viewer: colibri.ui.controls.viewers.TreeViewer): Promise { - viewer.setInput(this.getEditor().getScene().getGameObjects()); + const scene = this.getEditor().getScene(); + + const input = [ + ...scene.getGameObjects(), + ...scene.getPlainObjects(), + ...scene.getObjectLists().getLists() + ]; + + viewer.setInput(input); } protected async updateIcon(iconControl: controls.IconControl, value: string): Promise { @@ -109,7 +117,19 @@ namespace phasereditor2d.scene.ui.sceneobjects { } }); - const found = foundElement[0]; + let found = foundElement[0]; + + if (!found) { + + found = scene.getPlainObjects().find( + obj => core.code.formatToValidVarName(obj.getEditorSupport().getLabel()) === value); + } + + if (!found) { + + found = scene.getObjectLists().getLists() + .find(l => core.code.formatToValidVarName(l.getLabel()) === value); + } if (found) { diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/PrefabUserProperties.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/PrefabUserProperties.ts index 27c5015c9..b5661b4a6 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/PrefabUserProperties.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/PrefabUserProperties.ts @@ -1,4 +1,4 @@ -/// +/// namespace phasereditor2d.scene.ui.sceneobjects { export function PrefabUserPropertyBuilder(prop: UserProperty) { @@ -12,7 +12,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { return new PrefabUserPropertyWrapper(prop); } - export class PrefabUserProperties extends UserProperties { + export class PrefabUserProperties extends UserPropertiesManager { constructor() { super(PrefabUserPropertyBuilder); diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperties.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserPropertiesManager.ts similarity index 66% rename from source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperties.ts rename to source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserPropertiesManager.ts index b80a32339..fb0993100 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperties.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserPropertiesManager.ts @@ -1,12 +1,12 @@ namespace phasereditor2d.scene.ui.sceneobjects { - export abstract class UserProperties { - + export abstract class UserPropertiesManager { + private _properties: UserProperty[]; private _componentPropertyBuilder: TComponentPropertyBuilder; constructor(componentPropertyBuilder: TComponentPropertyBuilder) { - + this._componentPropertyBuilder = componentPropertyBuilder; this._properties = []; } @@ -17,7 +17,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { } deleteProperty(propName: string) { - + const prop = this._properties.find(p => p.getName() === propName); const i = this._properties.indexOf(prop); @@ -32,26 +32,54 @@ namespace phasereditor2d.scene.ui.sceneobjects { createProperty(propType: UserPropertyType) { + const { name, label } = this.createNewPropertyNameInfo("property", "Property"); + + const prop = new UserProperty(this, this._componentPropertyBuilder, { + defValue: propType.getDefaultValue(), + name, + label, + tooltip: "", + customDefinition: false, + type: propType + }); + + return prop; + } + + createNewPropertyNameInfo(baseName: string, baseLabel: string) { + + const p = this._properties.find(p2 => p2.getInfo().name === baseName); + + if (!p) { + + return { name: baseName, label: baseLabel }; + } + let i = 0; while (true) { + i++; - const p = this._properties.find(p2 => p2.getInfo().name === "property" + i) + const p = this._properties.find(p2 => p2.getInfo().name === `${baseName}_${i}`); if (!p) { + break; } } - const prop = new UserProperty(this, this._componentPropertyBuilder, { - defValue: propType.getDefaultValue(), - label: "Property " + i, - name: "property" + i, - tooltip: "Property " + i, - customDefinition: false, - type: propType - }); + return { + name: `${baseName}_${i}`, + label: `${baseLabel} ${i}` + } + } + + createPropertyFromData(data: any) { + + const prop = new UserProperty(this, this._componentPropertyBuilder); + + prop.readJSON(data); return prop; } @@ -62,9 +90,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { for (const propData of data) { - const prop = new UserProperty(this, this._componentPropertyBuilder); - - prop.readJSON(propData); + const prop = this.createPropertyFromData(propData); this._properties.push(prop); } diff --git a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperty.ts b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperty.ts index 7af24d578..e21b91718 100644 --- a/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperty.ts +++ b/source/editor/plugins/phasereditor2d.scene/src/ui/sceneobjects/userProperties/UserProperty.ts @@ -16,18 +16,18 @@ namespace phasereditor2d.scene.ui.sceneobjects { private _info: IUserPropertyInfo; private _componentProperty: IProperty; private _componentPropertyBuilder: TComponentPropertyBuilder; - private _allProperties: UserProperties; + private _manager: UserPropertiesManager; - constructor(allProperties: UserProperties, componentPropertyBuilder: TComponentPropertyBuilder, info?: IUserPropertyInfo) { + constructor(allProperties: UserPropertiesManager, componentPropertyBuilder: TComponentPropertyBuilder, info?: IUserPropertyInfo) { - this._allProperties = allProperties; + this._manager = allProperties; this._componentPropertyBuilder = componentPropertyBuilder; this._info = info; } - getAllProperties() { + getManager() { - return this._allProperties; + return this._manager; } getComponentProperty(): IProperty { @@ -91,7 +91,7 @@ namespace phasereditor2d.scene.ui.sceneobjects { const typeData = data.type; const typeId = typeData.id; - const propType = ScenePlugin.getInstance().createUserPropertyType(typeId); + const propType = ScenePlugin.getInstance().getUserPropertyType(typeId); propType.readJSON(typeData); this._info = { diff --git a/source/editor/plugins/phasereditor2d.scene/styles/EventPropertyDialog.css b/source/editor/plugins/phasereditor2d.scene/styles/EventPropertyDialog.css new file mode 100644 index 000000000..ff469ba58 --- /dev/null +++ b/source/editor/plugins/phasereditor2d.scene/styles/EventPropertyDialog.css @@ -0,0 +1,11 @@ +.EventPropertyDialogHelpPane { + height: 10rem; + overflow-y: scroll; + align-items: start !important; +} + +.dark .EventPropertyDialogHelpPane { + border-top-style: solid; + border-top-width: 1px; + border-radius: 0px; +} \ No newline at end of file diff --git a/source/editor/plugins/phasereditor2d.scene/styles/SceneEditor.css b/source/editor/plugins/phasereditor2d.scene/styles/SceneEditor.css index b335a5cee..8cd797191 100644 --- a/source/editor/plugins/phasereditor2d.scene/styles/SceneEditor.css +++ b/source/editor/plugins/phasereditor2d.scene/styles/SceneEditor.css @@ -24,6 +24,12 @@ border-radius: 3px; } +#InspectorView .UserComponentTitle_PrefabsPart { + font-size: small; + font-style: italic; + opacity: 0.5; +} + #InspectorView label { min-width: unset; }