///
module BABYLON.GUI {
export class Control {
private _zIndex = 0;
public _root: Container;
public _host: AdvancedDynamicTexture;
public _currentMeasure = Measure.Empty();
private _fontFamily: string;
private _fontSize = 18;
private _font: string;
private _width = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
private _height = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
private _lastMeasuredFont: string;
protected _fontOffset: {ascent: number, height: number, descent: number};
private _color: string;
private _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
private _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
private _isDirty = true;
private _cachedParentMeasure = Measure.Empty();
private _marginLeft = new ValueAndUnit(0);
private _marginRight = new ValueAndUnit(0);
private _marginTop = new ValueAndUnit(0);
private _marginBottom = new ValueAndUnit(0);
private _left = new ValueAndUnit(0);
private _top = new ValueAndUnit(0);
// Properties
/**
* An event triggered when the pointer move over the control.
* @type {BABYLON.Observable}
*/
public onPointerMoveObservable = new Observable();
/**
* An event triggered when the pointer move out of the control.
* @type {BABYLON.Observable}
*/
public onPointerOutObservable = new Observable();
/**
* An event triggered when the pointer taps the control
* @type {BABYLON.Observable}
*/
public onPointerDownObservable = new Observable();
/**
* An event triggered when pointer up
* @type {BABYLON.Observable}
*/
public onPointerUpObservable = new Observable();
public get horizontalAlignment(): number {
return this._horizontalAlignment;
}
public set horizontalAlignment(value: number) {
if (this._horizontalAlignment === value) {
return;
}
this._horizontalAlignment = value;
this._markAsDirty();
}
public get verticalAlignment(): number {
return this._verticalAlignment;
}
public set verticalAlignment(value: number) {
if (this._verticalAlignment === value) {
return;
}
this._verticalAlignment = value;
this._markAsDirty();
}
public get width(): string {
return this._width.toString();
}
public set width(value: string) {
if (this._width.toString() === value) {
return;
}
if (this._width.fromString(value)) {
this._markAsDirty();
}
}
public get height(): string {
return this._height.toString();
}
public set height(value: string) {
if (this._height.toString() === value) {
return;
}
if (this._height.fromString(value)) {
this._markAsDirty();
}
}
public get fontFamily(): string {
return this._fontFamily;
}
public set fontFamily(value: string) {
if (this._fontFamily === value) {
return;
}
this._fontFamily = value;
this._prepareFont();
}
public get fontSize(): number {
return this._fontSize;
}
public set fontSize(value: number) {
if (this._fontSize === value) {
return;
}
this._fontSize = value;
this._prepareFont();
}
public get color(): string {
return this._color;
}
public set color(value: string) {
if (this._color === value) {
return;
}
this._color = value;
this._markAsDirty();
}
public get zIndex(): number {
return this._zIndex;
}
public set zIndex(value: number) {
if (this.zIndex === value) {
return;
}
this._zIndex = value;
this._root._reOrderControl(this);
}
public get isDirty(): boolean {
return this._isDirty;
}
public get marginLeft(): string {
return this._marginLeft.toString();
}
public set marginLeft(value: string) {
if (this._marginLeft.fromString(value)) {
this._markAsDirty();
}
}
public get marginRight(): string {
return this._marginRight.toString();
}
public set marginRight(value: string) {
if (this._marginRight.fromString(value)) {
this._markAsDirty();
}
}
public get marginTop(): string {
return this._marginTop.toString();
}
public set marginTop(value: string) {
if (this._marginTop.fromString(value)) {
this._markAsDirty();
}
}
public get marginBottom(): string {
return this._marginBottom.toString();
}
public set marginBottom(value: string) {
if (this._marginBottom.fromString(value)) {
this._markAsDirty();
}
}
public get left(): string {
return this._left.toString();
}
public set left(value: string) {
if (this._left.fromString(value)) {
this._markAsDirty();
}
}
public get top(): string {
return this._top.toString();
}
public set top(value: string) {
if (this._top.fromString(value)) {
this._markAsDirty();
}
}
// Functions
constructor(public name: string) {
this.fontFamily = "Arial";
}
protected _markAsDirty(): void {
this._isDirty = true;
if (!this._host) {
return; // Not yet connected
}
this._host.markAsDirty();
}
public _link(root: Container, host: AdvancedDynamicTexture): void {
this._root = root;
this._host = host;
}
protected applyStates(context: CanvasRenderingContext2D): void {
if (this._font) {
context.font = this._font;
}
if (this._color) {
context.fillStyle = this._color;
}
}
protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D) {
if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
this._currentMeasure.copyFrom(parentMeasure);
this._measure(parentMeasure, context);
this._computeAlignment(parentMeasure, context);
// Convert to int values
this._currentMeasure.left = this._currentMeasure.left | 0;
this._currentMeasure.top = this._currentMeasure.top | 0;
this._currentMeasure.width = this._currentMeasure.width | 0;
this._currentMeasure.height = this._currentMeasure.height | 0;
// Let children add more features
this._additionalProcessing(parentMeasure, context);
this._isDirty = false;
this._cachedParentMeasure.copyFrom(parentMeasure);
}
// Clip
this._clip(context);
context.clip();
}
protected _clip( context: CanvasRenderingContext2D) {
context.beginPath();
context.rect(this._currentMeasure.left ,this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
}
protected _measure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
// Width / Height
if (this._width.isPixel) {
this._currentMeasure.width = this._width.value;
} else {
this._currentMeasure.width *= this._width.value;
}
if (this._height.isPixel) {
this._currentMeasure.height = this._height.value;
} else {
this._currentMeasure.height *= this._height.value;
}
}
protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
var width = this._currentMeasure.width;
var height = this._currentMeasure.height;
var parentWidth = parentMeasure.width;
var parentHeight = parentMeasure.height;
// Left / top
var x = 0;
var y = 0;
switch (this.horizontalAlignment) {
case Control.HORIZONTAL_ALIGNMENT_LEFT:
x = 0
break;
case Control.HORIZONTAL_ALIGNMENT_RIGHT:
x = parentWidth - width;
break;
case Control.HORIZONTAL_ALIGNMENT_CENTER:
x = (parentWidth - width) / 2;
break;
}
switch (this.verticalAlignment) {
case Control.VERTICAL_ALIGNMENT_TOP:
y = 0;
break;
case Control.VERTICAL_ALIGNMENT_BOTTOM:
y = parentHeight - height;
break;
case Control.VERTICAL_ALIGNMENT_CENTER:
y = (parentHeight - height) / 2;
break;
}
if (this._marginLeft.isPixel) {
this._currentMeasure.left += this._marginLeft.value;
this._currentMeasure.width -= this._marginRight.value;
} else {
this._currentMeasure.left += parentWidth * this._marginLeft.value;
this._currentMeasure.width -= parentWidth * this._marginLeft.value;
}
if (this._marginRight.isPixel) {
this._currentMeasure.width -= this._marginRight.value;
} else {
this._currentMeasure.width -= parentWidth * this._marginRight.value;
}
if (this._marginTop.isPixel) {
this._currentMeasure.top += this._marginTop.value;
this._currentMeasure.height -= this._marginTop.value;
} else {
this._currentMeasure.top += parentHeight * this._marginTop.value;
this._currentMeasure.height -= parentHeight * this._marginTop.value;
}
if (this._marginBottom.isPixel) {
this._currentMeasure.height -= this._marginBottom.value;
} else {
this._currentMeasure.height -= parentHeight * this._marginBottom.value;
}
if (this._left.isPixel) {
this._currentMeasure.left += this._left.value;
} else {
this._currentMeasure.left += parentWidth * this._left.value;
}
if (this._top.isPixel) {
this._currentMeasure.top += this._top.value;
} else {
this._currentMeasure.top += parentHeight * this._top.value;
}
this._currentMeasure.left += x;
this._currentMeasure.top += y;
}
protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
// Do nothing
}
public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
// Do nothing
}
protected _contains(x: number, y: number) : boolean {
if (x < this._currentMeasure.left) {
return false;
}
if (x > this._currentMeasure.left + this._currentMeasure.width) {
return false;
}
if (y < this._currentMeasure.top) {
return false;
}
if (y > this._currentMeasure.top + this._currentMeasure.height) {
return false;
}
return true;
}
public _processPicking(x: number, y: number, type: number): boolean {
if (!this._contains(x, y)) {
return false;
}
this._processObservables(type);
return true;
}
protected _processObservables(type: number): boolean {
if (type === BABYLON.PointerEventTypes.POINTERMOVE && this.onPointerMoveObservable.hasObservers()) {
this.onPointerMoveObservable.notifyObservers(this);
var previousControlOver = this._host._lastControlOver;
if (previousControlOver && previousControlOver !== this && previousControlOver.onPointerOutObservable.hasObservers()) {
previousControlOver.onPointerOutObservable.notifyObservers(previousControlOver);
}
this._host._lastControlOver = this;
return true;
}
if (type === BABYLON.PointerEventTypes.POINTERDOWN && this.onPointerDownObservable.hasObservers()) {
this.onPointerDownObservable.notifyObservers(this);
return true;
}
if (type === BABYLON.PointerEventTypes.POINTERUP && this.onPointerUpObservable.hasObservers()) {
this.onPointerUpObservable.notifyObservers(this);
return true;
}
return false;
}
private _prepareFont() {
if (!this._fontFamily) {
return;
}
this._font = this._fontSize + "px " + this._fontFamily;
this._fontOffset = Control._GetFontOffset(this._font);
this._markAsDirty();
}
// Statics
private static _HORIZONTAL_ALIGNMENT_LEFT = 0;
private static _HORIZONTAL_ALIGNMENT_RIGHT = 1;
private static _HORIZONTAL_ALIGNMENT_CENTER = 2;
private static _VERTICAL_ALIGNMENT_TOP = 0;
private static _VERTICAL_ALIGNMENT_BOTTOM = 1;
private static _VERTICAL_ALIGNMENT_CENTER = 2;
public static get HORIZONTAL_ALIGNMENT_LEFT(): number {
return Control._HORIZONTAL_ALIGNMENT_LEFT;
}
public static get HORIZONTAL_ALIGNMENT_RIGHT(): number {
return Control._HORIZONTAL_ALIGNMENT_RIGHT;
}
public static get HORIZONTAL_ALIGNMENT_CENTER(): number {
return Control._HORIZONTAL_ALIGNMENT_CENTER;
}
public static get VERTICAL_ALIGNMENT_TOP(): number {
return Control._VERTICAL_ALIGNMENT_TOP;
}
public static get VERTICAL_ALIGNMENT_BOTTOM(): number {
return Control._VERTICAL_ALIGNMENT_BOTTOM;
}
public static get VERTICAL_ALIGNMENT_CENTER(): number {
return Control._VERTICAL_ALIGNMENT_CENTER;
}
private static _FontHeightSizes = {};
public static _GetFontOffset(font: string): {ascent: number, height: number, descent: number} {
if (Control._FontHeightSizes[font]) {
return Control._FontHeightSizes[font];
}
var text = document.createElement("span");
text.innerHTML = "Hg";
text.style.font = font;
var block = document.createElement("div");
block.style.display = "inline-block";
block.style.width = "1px";
block.style.height = "0px";
block.style.verticalAlign = "bottom";
var div = document.createElement("div");
div.appendChild(text);
div.appendChild(block);
document.body.appendChild(div);
var fontAscent = 0;
var fontHeight = 0;
try {
fontHeight = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
block.style.verticalAlign = "baseline";
fontAscent = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
} finally {
div.remove();
}
var result = { ascent: fontAscent, height: fontHeight, descent: fontHeight - fontAscent };
Control._FontHeightSizes[font] = result;
return result;
};
}
}