import { Vector2, Object3D, Audio } from "three"; import { RelationType, PopupDirection } from "./FieldTypes"; import { GComponent } from "./GComponent"; import { GGraph } from "./GGraph"; import { GObject, Decls } from "./GObject"; import { UIConfig } from "./UIConfig"; import { UIPackage } from "./UIPackage"; import { Window } from "./Window" import { Stage } from "../core/Stage"; import { Color4 } from "../utils/Color"; import { DisplayObject } from "../core/DisplayObject"; import { UIContentScaler } from "./UIContentScaler"; var _inst: GRoot; export class GRoot extends GComponent { private _modalLayer: GGraph; private _popupStack: GObject[]; private _justClosedPopups: GObject[]; private _modalWaitPane: GObject; private _tooltipWin: GObject; private _defaultTooltipWin: GObject; public static get inst(): GRoot { if (!_inst) { _inst = new GRoot(); Stage.scene.add(_inst.displayObject.obj3D); } return _inst; } public static findFor(obj: GObject): GRoot { if (obj instanceof GRoot) return obj; if (!obj) return _inst; var p: GObject = obj._parent; while (p) { if (p instanceof GRoot) return p; p = p.parent; } return _inst; } constructor() { super(); if (!_inst) _inst = this; this.opaque = false; this._popupStack = []; this._justClosedPopups = []; this.on("touch_begin", this.__stageTouchBegin, this, true); this._modalLayer = new GGraph(); this._modalLayer.setSize(this.width, this.height); this._modalLayer.shape.drawRect(0, new Color4(0, 0), UIConfig.modalLayerColor); this._modalLayer.addRelation(this, RelationType.Size); this.applyScaleFactor(); this.on("content_scale_factor_changed", this.applyScaleFactor, this); } private applyScaleFactor() { this.setSize(Math.ceil(Stage.width / UIContentScaler.scaleFactor), Math.ceil(Stage.height / UIContentScaler.scaleFactor)); this.setScale(UIContentScaler.scaleFactor, UIContentScaler.scaleFactor); } public showWindow(win: Window): void { this.addChild(win); if (win.x > this.width) win.x = this.width - win.width; else if (win.x + win.width < 0) win.x = 0; if (win.y > this.height) win.y = this.height - win.height; else if (win.y + win.height < 0) win.y = 0; this.adjustModalLayer(); } public hideWindow(win: Window): void { win.hide(); } public hideWindowImmediately(win: Window): void { if (win.parent == this) this.removeChild(win); this.adjustModalLayer(); } public bringToFront(win: Window): void { var cnt: number = this.numChildren; var i: number; if (this._modalLayer.parent && !win.modal) i = this.getChildIndex(this._modalLayer) - 1; else i = cnt - 1; for (; i >= 0; i--) { var g: GObject = this.getChildAt(i); if (g == win) return; if (g instanceof Window) break; } if (i >= 0) this.setChildIndex(win, i); } public showModalWait(msg?: string): void { if (UIConfig.globalModalWaiting) { if (this._modalWaitPane == null) this._modalWaitPane = UIPackage.createObjectFromURL(UIConfig.globalModalWaiting); this._modalWaitPane.setSize(this.width, this.height); this._modalWaitPane.addRelation(this, RelationType.Size); this.addChild(this._modalWaitPane); this._modalWaitPane.text = msg || ""; } } public closeModalWait(): void { if (this._modalWaitPane && this._modalWaitPane.parent) this.removeChild(this._modalWaitPane); } public closeAllExceptModals(): void { var arr: GObject[] = this._children.slice(); var cnt: number = arr.length; for (var i: number = 0; i < cnt; i++) { var g: GObject = arr[i]; if ((g instanceof Window) && !g.modal) g.hide(); } } public closeAllWindows(): void { var arr: GObject[] = this._children.slice(); var cnt: number = arr.length; for (var i: number = 0; i < cnt; i++) { var g: GObject = arr[i]; if (g instanceof Window) g.hide(); } } public getTopWindow(): Window { var cnt: number = this.numChildren; for (var i: number = cnt - 1; i >= 0; i--) { var g: GObject = this.getChildAt(i); if (g instanceof Window) { return g; } } return null; } public get modalLayer(): GObject { return this._modalLayer; } public get hasModalWindow(): boolean { return this._modalLayer.parent != null; } public get modalWaiting(): boolean { return this._modalWaitPane && this._modalWaitPane.onStage; } public showPopup(popup: GObject, target?: GObject, dir?: PopupDirection): void { if (this._popupStack.length > 0) { var k: number = this._popupStack.indexOf(popup); if (k != -1) { for (var i: number = this._popupStack.length - 1; i >= k; i--) this.removeChild(this._popupStack.pop()); } } this._popupStack.push(popup); if (target) { var p: GObject = target; while (p) { if (p.parent == this) { if (popup.sortingOrder < p.sortingOrder) { popup.sortingOrder = p.sortingOrder; } break; } p = p.parent; } } this.addChild(popup); this.adjustModalLayer(); var pos: Vector2; var sizeW: number = 0, sizeH: number = 0; if (target) { pos = target.localToRoot(0, 0); let size = target.localToRoot(target.width, target.height); sizeW = size.x - pos.x; sizeH = size.y - pos.y; } else { pos = Stage.getTouchPos(); pos = this.globalToLocal(pos.x, pos.y); } var xx: number, yy: number; xx = pos.x; if (xx + popup.width > this.width) xx = xx + sizeW - popup.width; yy = pos.y + sizeH; if (((dir === undefined || dir === PopupDirection.Auto) && yy + popup.height > this.height) || dir === PopupDirection.Up) { yy = pos.y - popup.height - 1; if (yy < 0) { yy = 0; xx += sizeW / 2; } } popup.setPosition(xx, yy); } public togglePopup(popup: GObject, target?: GObject, dir?: PopupDirection): void { if (this._justClosedPopups.indexOf(popup) != -1) return; this.showPopup(popup, target, dir); } public hidePopup(popup?: GObject): void { if (popup) { var k: number = this._popupStack.indexOf(popup); if (k != -1) { for (var i: number = this._popupStack.length - 1; i >= k; i--) this.closePopup(this._popupStack.pop()); } } else { var cnt: number = this._popupStack.length; for (i = cnt - 1; i >= 0; i--) this.closePopup(this._popupStack[i]); this._popupStack.length = 0; } } public get hasAnyPopup(): boolean { return this._popupStack.length != 0; } private closePopup(target: GObject): void { if (target.parent) { if (target instanceof Window) target.hide(); else this.removeChild(target); } } public showTooltips(msg: string): void { if (this._defaultTooltipWin == null) { var resourceURL: string = UIConfig.tooltipsWin; if (!resourceURL) { console.warn("UIConfig.tooltipsWin not defined"); return; } this._defaultTooltipWin = UIPackage.createObjectFromURL(resourceURL); } this._defaultTooltipWin.text = msg; this.showTooltipsWin(this._defaultTooltipWin); } public showTooltipsWin(tooltipWin: GObject, xx?: number, yy?: number): void { this.hideTooltips(); this._tooltipWin = tooltipWin; if (xx == null || yy == null) { xx = Stage.touchPos.x + 10; yy = Stage.touchPos.y + 20; } var pt: Vector2 = this.globalToLocal(xx, yy); xx = pt.x; yy = pt.y; if (xx + this._tooltipWin.width > this.width) { xx = xx - this._tooltipWin.width - 1; if (xx < 0) xx = 10; } if (yy + this._tooltipWin.height > this.height) { yy = yy - this._tooltipWin.height - 1; if (xx - this._tooltipWin.width - 1 > 0) xx = xx - this._tooltipWin.width - 1; if (yy < 0) yy = 10; } this._tooltipWin.x = xx; this._tooltipWin.y = yy; this.addChild(this._tooltipWin); } public hideTooltips(): void { if (this._tooltipWin) { if (this._tooltipWin.parent) this.removeChild(this._tooltipWin); this._tooltipWin = null; } } public playOneShotSound(url: string, volumeScale?: number): void { if (!Stage.audioListener) return; if (volumeScale == null) volumeScale = 1; let pi = UIPackage.getItemByURL(url); if (pi && pi.audioBuffer) { if (!pi.sound) { pi.sound = new Audio(Stage.audioListener); pi.sound.setBuffer(pi.audioBuffer); pi.sound.setLoop(false); } pi.sound.setVolume(volumeScale); pi.sound.play(); } } private adjustModalLayer(): void { var cnt: number = this.numChildren; if (this._modalWaitPane && this._modalWaitPane.parent) this.setChildIndex(this._modalWaitPane, cnt - 1); for (var i: number = cnt - 1; i >= 0; i--) { var g: GObject = this.getChildAt(i); if ((g instanceof Window) && g.modal) { if (this._modalLayer.parent == null) this.addChildAt(this._modalLayer, i); else this.setChildIndexBefore(this._modalLayer, i); return; } } if (this._modalLayer.parent) this.removeChild(this._modalLayer); } public checkPopups(): void { this._justClosedPopups.length = 0; if (this._popupStack.length > 0) { let mc: DisplayObject | Object3D = Stage.touchTarget; let handled = false; while (mc) { let gobj = GObject.cast(mc); if (gobj) { let k = this._popupStack.indexOf(gobj); if (k != -1) { for (let i = this._popupStack.length - 1; i > k; i--) { let last = this._popupStack.length - 1; let popup: GObject = this._popupStack[last]; this.closePopup(popup); this._justClosedPopups.push(popup); this._popupStack.splice(last, 1); } handled = true; break; } } mc = mc.parent; } if (!handled) { for (let i = this._popupStack.length - 1; i >= 0; i--) { let popup = this._popupStack[i]; this.closePopup(popup); this._justClosedPopups.push(popup); this._popupStack.splice(i, 1); } } } } private __stageTouchBegin(): void { if (this._tooltipWin) this.hideTooltips(); this.checkPopups(); } } Decls.GRoot = GRoot;