From 27173f923f79ff17833a87dd15c17fd6039a90ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Lemaire?= Date: Thu, 13 Apr 2017 01:22:34 +0200 Subject: [PATCH] Started UI component --- src/ui/TestGame.ts | 4 ++ src/ui/common/Tools.spec.ts | 2 + src/ui/common/UIComponent.spec.ts | 48 +++++++++++++++++ src/ui/common/UIComponent.ts | 87 +++++++++++++++++++++++++++++++ 4 files changed, 141 insertions(+) create mode 100644 src/ui/common/UIComponent.spec.ts create mode 100644 src/ui/common/UIComponent.ts diff --git a/src/ui/TestGame.ts b/src/ui/TestGame.ts index 23c33b8..ef8849d 100644 --- a/src/ui/TestGame.ts +++ b/src/ui/TestGame.ts @@ -24,6 +24,8 @@ module TS.SpaceTac.UI.Specs { spyOn(console, "log").and.stub(); spyOn(console, "warn").and.stub(); + jasmine.clock().install(); + testgame.ui = new MainUI(true); if (testgame.ui.load) { @@ -50,6 +52,8 @@ module TS.SpaceTac.UI.Specs { ui.destroy(); } }); + + jasmine.clock().uninstall(); }); return testgame; diff --git a/src/ui/common/Tools.spec.ts b/src/ui/common/Tools.spec.ts index df9f917..f11037c 100644 --- a/src/ui/common/Tools.spec.ts +++ b/src/ui/common/Tools.spec.ts @@ -24,6 +24,8 @@ module TS.SpaceTac.UI.Specs { }); it("handles hover and click on desktops and mobile targets", function (done) { + jasmine.clock().uninstall(); + let newButton: () => [Phaser.Button, any] = () => { var button = new Phaser.Button(testgame.ui); var funcs = { diff --git a/src/ui/common/UIComponent.spec.ts b/src/ui/common/UIComponent.spec.ts new file mode 100644 index 0000000..1005df6 --- /dev/null +++ b/src/ui/common/UIComponent.spec.ts @@ -0,0 +1,48 @@ +module TS.SpaceTac.UI.Specs { + describe("UIComponent", () => { + let testgame = setupEmptyView(); + + it("controls visibility", function () { + let component = new UIComponent(testgame.baseview, 50, 50); + + let container = (component).container; + expect(container.visible).toBe(true); + + component.setVisible(false); + expect(container.visible).toBe(false); + + component.setVisible(true); + expect(container.visible).toBe(true); + + // with transition + component.setVisible(false, 500); + expect(container.visible).toBe(true); + expect(testgame.baseview.animations.simulate(container, 'alpha')).toEqual([1, 0.5, 0]); + }); + + it("sets position inside parent", function () { + let comp1 = new UIComponent(testgame.baseview, 100, 100); + expect(comp1.getPosition()).toEqual([0, 0]); + comp1.setPositionInsideParent(1, 1); + expect(comp1.getPosition()).toEqual([1820, 980]); + comp1.setPositionInsideParent(0.5, 0.5); + expect(comp1.getPosition()).toEqual([910, 490]); + + let comp2 = new UIComponent(comp1, 50, 50); + expect(comp2.getPosition()).toEqual([910, 490]); + expect(comp2.getPosition(true)).toEqual([0, 0]); + comp2.setPositionInsideParent(1, 0); + expect(comp2.getPosition()).toEqual([960, 490]); + expect(comp2.getPosition(true)).toEqual([50, 0]); + + comp1.setPositionInsideParent(0, 0); + expect(comp1.getPosition()).toEqual([0, 0]); + expect(comp2.getPosition()).toEqual([50, 0]); + + comp1.setPositionInsideParent(0.654, 0.321, false); + expect(comp1.getPosition()).toEqual([1190.28, 314.58]); + comp1.setPositionInsideParent(0.654, 0.321); + expect(comp1.getPosition()).toEqual([1190, 315]); + }); + }); +} diff --git a/src/ui/common/UIComponent.ts b/src/ui/common/UIComponent.ts new file mode 100644 index 0000000..06204b6 --- /dev/null +++ b/src/ui/common/UIComponent.ts @@ -0,0 +1,87 @@ +module TS.SpaceTac.UI { + /** + * Base class for UI components + */ + export class UIComponent { + private view: BaseView; + private parent: UIComponent | null; + private container: Phaser.Group; + private width: number; + private height: number; + + constructor(parent: BaseView | UIComponent, width: number, height: number) { + if (parent instanceof UIComponent) { + this.view = parent.view; + this.parent = parent; + } else { + this.view = parent; + this.parent = null; + } + + this.container = this.view.add.group(); + + this.width = width; + this.height = height; + } + + /** + * Set the component's visibility, with optional transition (in milliseconds) + */ + setVisible(visible: boolean, transition = 0): void { + if (transition > 0) { + this.view.animations.setVisible(this.container, visible, transition); + } else { + this.container.visible = visible; + } + } + + /** + * Get the component's declared size + */ + getSize(): [number, number] { + return [this.width, this.height]; + } + + /** + * Get the width and height of parent + */ + getParentSize(): [number, number] { + if (this.parent) { + return this.parent.getSize(); + } else { + return [this.view.getWidth(), this.view.getHeight()]; + } + } + + /** + * Return the component's position (either relative to its parent, or absolute in the view) + */ + getPosition(relative = false): [number, number] { + if (relative || !this.parent) { + return [this.container.x, this.container.y]; + } else { + let [px, py] = this.parent.getPosition(); + return [px + this.container.x, py + this.container.y]; + } + } + + /** + * Position the component inside the boundaries of its parent. + * + * (0, 0) is the top-left anchoring, (1, 1) is the bottom-right one. + * + * If *pixelsnap* is true, position will be rounded to pixel. + */ + setPositionInsideParent(x: number, y: number, pixelsnap = true): void { + let [pwidth, pheight] = this.getParentSize(); + let [width, height] = this.getSize(); + let rx = (pwidth - width) * x; + let ry = (pheight - height) * y; + if (pixelsnap) { + this.container.position.set(Math.round(rx), Math.round(ry)); + } else { + this.container.position.set(rx, ry); + } + } + } +}