/// module BABYLON.GUI { /** * Class used as base class for controls */ export class Control3D implements IDisposable, IBehaviorAware { /** @hidden */ public _host: GUI3DManager; private _node: Nullable; private _downCount = 0; private _enterCount = -1; private _downPointerIds:{[id:number] : boolean} = {}; private _isVisible = true; /** Gets or sets the control position in world space */ public get position(): Vector3 { if (!this._node) { return Vector3.Zero(); } return this._node.position; } public set position(value: Vector3) { if (!this._node) { return; } this._node.position = value;; } /** Gets or sets the control scaling in world space */ public get scaling(): Vector3 { if (!this._node) { return new Vector3(1, 1, 1); } return this._node.scaling; } public set scaling(value: Vector3) { if (!this._node) { return; } this._node.scaling = value;; } /** Callback used to start pointer enter animation */ public pointerEnterAnimation: () => void; /** Callback used to start pointer out animation */ public pointerOutAnimation: () => void; /** Callback used to start pointer down animation */ public pointerDownAnimation: () => void; /** Callback used to start pointer up animation */ public pointerUpAnimation: () => void; /** * An event triggered when the pointer move over the control */ public onPointerMoveObservable = new Observable(); /** * An event triggered when the pointer move out of the control */ public onPointerOutObservable = new Observable(); /** * An event triggered when the pointer taps the control */ public onPointerDownObservable = new Observable(); /** * An event triggered when pointer is up */ public onPointerUpObservable = new Observable(); /** * An event triggered when a control is clicked on (with a mouse) */ public onPointerClickObservable = new Observable(); /** * An event triggered when pointer enters the control */ public onPointerEnterObservable = new Observable(); /** * Gets or sets the parent container */ public parent: Nullable; // Behaviors private _behaviors = new Array>(); /** * Gets the list of attached behaviors * @see http://doc.babylonjs.com/features/behaviour */ public get behaviors(): Behavior[] { return this._behaviors; } /** * Attach a behavior to the control * @see http://doc.babylonjs.com/features/behaviour * @param behavior defines the behavior to attach * @returns the current control */ public addBehavior(behavior: Behavior): Control3D { var index = this._behaviors.indexOf(behavior); if (index !== -1) { return this; } behavior.init(); let scene = this._host.scene; if (scene.isLoading) { // We defer the attach when the scene will be loaded scene.onDataLoadedObservable.addOnce(() => { behavior.attach(this); }); } else { behavior.attach(this); } this._behaviors.push(behavior); return this; } /** * Remove an attached behavior * @see http://doc.babylonjs.com/features/behaviour * @param behavior defines the behavior to attach * @returns the current control */ public removeBehavior(behavior: Behavior): Control3D { var index = this._behaviors.indexOf(behavior); if (index === -1) { return this; } this._behaviors[index].detach(); this._behaviors.splice(index, 1); return this; } /** * Gets an attached behavior by name * @param name defines the name of the behavior to look for * @see http://doc.babylonjs.com/features/behaviour * @returns null if behavior was not found else the requested behavior */ public getBehaviorByName(name: string): Nullable> { for (var behavior of this._behaviors) { if (behavior.name === name) { return behavior; } } return null; } /** Gets or sets a boolean indicating if the control is visible */ public get isVisible(): boolean { return this._isVisible; } public set isVisible(value: boolean) { if (this._isVisible === value) { return; } this._isVisible = value; let mesh = this.mesh; if (mesh) { mesh.setEnabled(value); } } /** * Creates a new control * @param name defines the control name */ constructor( /** Defines the control name */ public name?: string) { } /** * Gets a string representing the class name */ public get typeName(): string { return this._getTypeName(); } protected _getTypeName(): string { return "Control3D"; } /** * Gets the transform node used by this control */ public get node(): Nullable { return this._node; } /** * Gets the mesh used to render this control */ public get mesh(): Nullable { if (this._node instanceof AbstractMesh) { return this._node as AbstractMesh; } return null; } /** * Link the control as child of the given node * @param node defines the node to link to. Use null to unlink the control * @returns the current control */ public linkToTransformNode(node: Nullable): Control3D { if (this._node) { this._node.parent = node; } return this; } /** @hidden **/ public _prepareNode(scene: Scene): void{ if (!this._node) { this._node = this._createNode(scene); if (!this.node) { return; } this._node!.metadata = this; // Store the control on the metadata field in order to get it when picking this._node!.position = this.position; this._node!.scaling = this.scaling; let mesh = this.mesh; if (mesh) { mesh.isPickable = true; this._affectMaterial(mesh); } } } /** * Node creation. * Can be overriden by children * @param scene defines the scene where the node must be attached * @returns the attached node or null if none. Must return a Mesh or AbstractMesh if there is an atttached visible object */ protected _createNode(scene: Scene): Nullable { // Do nothing by default return null; } /** * Affect a material to the given mesh * @param mesh defines the mesh which will represent the control */ protected _affectMaterial(mesh: AbstractMesh) { mesh.material = null; } // Pointers /** @hidden */ public _onPointerMove(target: Control3D, coordinates: Vector3): void { this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this); } /** @hidden */ public _onPointerEnter(target: Control3D): boolean { if (this._enterCount > 0) { return false; } if (this._enterCount === -1) { // -1 is for touch input, we are now sure we are with a mouse or pencil this._enterCount = 0; } this._enterCount++; this.onPointerEnterObservable.notifyObservers(this, -1, target, this); if (this.pointerEnterAnimation) { this.pointerEnterAnimation(); } return true; } /** @hidden */ public _onPointerOut(target: Control3D): void { this._enterCount = 0; this.onPointerOutObservable.notifyObservers(this, -1, target, this); if (this.pointerOutAnimation) { this.pointerOutAnimation(); } } /** @hidden */ public _onPointerDown(target: Control3D, coordinates: Vector3, pointerId:number, buttonIndex: number): boolean { if (this._downCount !== 0) { return false; } this._downCount++; this._downPointerIds[pointerId] = true; this.onPointerDownObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this); if (this.pointerDownAnimation) { this.pointerDownAnimation(); } return true; } /** @hidden */ public _onPointerUp(target: Control3D, coordinates: Vector3, pointerId:number, buttonIndex: number, notifyClick: boolean): void { this._downCount = 0; delete this._downPointerIds[pointerId]; if (notifyClick && (this._enterCount > 0 || this._enterCount === -1)) { this.onPointerClickObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this); } this.onPointerUpObservable.notifyObservers(new Vector3WithInfo(coordinates, buttonIndex), -1, target, this); if (this.pointerUpAnimation) { this.pointerUpAnimation(); } } /** @hidden */ public forcePointerUp(pointerId: Nullable = null) { if(pointerId !== null){ this._onPointerUp(this, Vector3.Zero(), pointerId, 0, true); }else{ for(var key in this._downPointerIds){ this._onPointerUp(this, Vector3.Zero(), +key as number, 0, true); } } } /** @hidden */ public _processObservables(type: number, pickedPoint: Vector3, pointerId:number, buttonIndex: number): boolean { if (type === BABYLON.PointerEventTypes.POINTERMOVE) { this._onPointerMove(this, pickedPoint); var previousControlOver = this._host._lastControlOver[pointerId]; if (previousControlOver && previousControlOver !== this) { previousControlOver._onPointerOut(this); } if (previousControlOver !== this) { this._onPointerEnter(this); } this._host._lastControlOver[pointerId] = this; return true; } if (type === BABYLON.PointerEventTypes.POINTERDOWN) { this._onPointerDown(this, pickedPoint, pointerId, buttonIndex); this._host._lastControlDown[pointerId] = this; this._host._lastPickedControl = this; return true; } if (type === BABYLON.PointerEventTypes.POINTERUP) { if (this._host._lastControlDown[pointerId]) { this._host._lastControlDown[pointerId]._onPointerUp(this, pickedPoint, pointerId, buttonIndex, true); } delete this._host._lastControlDown[pointerId]; return true; } return false; } /** @hidden */ public _disposeNode(): void { if (this._node) { this._node.dispose(); this._node = null; } } /** * Releases all associated resources */ public dispose() { this.onPointerDownObservable.clear(); this.onPointerEnterObservable.clear(); this.onPointerMoveObservable.clear(); this.onPointerOutObservable.clear(); this.onPointerUpObservable.clear(); this.onPointerClickObservable.clear(); this._disposeNode(); // Behaviors for (var behavior of this._behaviors) { behavior.detach(); } } } }