///
module BABYLON.GUI {
export class InputText extends Control implements IFocusableControl {
private _text = "";
private _placeholderText = "";
private _background = "black";
private _focusedBackground = "black";
private _placeholderColor = "gray";
private _thickness = 1;
private _margin = new ValueAndUnit(10, ValueAndUnit.UNITMODE_PIXEL);
private _autoStretchWidth = true;
private _maxWidth = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
private _isFocused = false;
private _blinkTimeout: number;
private _blinkIsEven = false;
private _cursorOffset = 0;
private _scrollLeft: number;
public promptMessage = "Please enter text:";
public onTextChangedObservable = new Observable();
public onFocusObservable = new Observable();
public onBlurObservable = new Observable();
public get maxWidth(): string | number {
return this._maxWidth.toString(this._host);
}
public set maxWidth(value: string | number ) {
if (this._maxWidth.toString(this._host) === value) {
return;
}
if (this._maxWidth.fromString(value)) {
this._markAsDirty();
}
}
public get margin(): string {
return this._margin.toString(this._host);
}
public set margin(value: string) {
if (this._margin.toString(this._host) === value) {
return;
}
if (this._margin.fromString(value)) {
this._markAsDirty();
}
}
public get autoStretchWidth(): boolean {
return this._autoStretchWidth;
}
public set autoStretchWidth(value: boolean) {
if (this._autoStretchWidth === value) {
return;
}
this._autoStretchWidth = value;
this._markAsDirty();
}
public get thickness(): number {
return this._thickness;
}
public set thickness(value: number) {
if (this._thickness === value) {
return;
}
this._thickness = value;
this._markAsDirty();
}
public get focusedBackground(): string {
return this._focusedBackground;
}
public set focusedBackground(value: string) {
if (this._focusedBackground === value) {
return;
}
this._focusedBackground = value;
this._markAsDirty();
}
public get background(): string {
return this._background;
}
public set background(value: string) {
if (this._background === value) {
return;
}
this._background = value;
this._markAsDirty();
}
public get placeholderColor(): string {
return this._placeholderColor;
}
public set placeholderColor(value: string) {
if (this._placeholderColor === value) {
return;
}
this._placeholderColor = value;
this._markAsDirty();
}
public get placeholderText(): string {
return this._placeholderText;
}
public set placeholderText(value: string) {
if (this._placeholderText === value) {
return;
}
this._placeholderText = value;
this._markAsDirty();
}
public get text(): string {
return this._text;
}
public set text(value: string) {
if (this._text === value) {
return;
}
this._text = value;
this._markAsDirty();
this.onTextChangedObservable.notifyObservers(this);
}
constructor(public name?: string, text: string = "") {
super(name);
this.text = text;
}
public onBlur(): void {
this._isFocused = false;
this._scrollLeft = null;
this._cursorOffset = 0;
clearTimeout(this._blinkTimeout);
this._markAsDirty();
this.onBlurObservable.notifyObservers(this);
}
public onFocus(): void {
this._scrollLeft = null;
this._isFocused = true;
this._blinkIsEven = false;
this._cursorOffset = 0;
this._markAsDirty();
this.onFocusObservable.notifyObservers(this);
if (navigator.userAgent.indexOf("Mobile") !== -1) {
this.text = prompt(this.promptMessage);
this._host.focusedControl = null;
return;
}
}
protected _getTypeName(): string {
return "InputText";
}
public processKey(keyCode: number, key?: string) {
// Specific cases
switch (keyCode) {
case 8: // BACKSPACE
if (this._text && this._text.length > 0) {
if (this._cursorOffset === 0) {
this.text = this._text.substr(0, this._text.length - 1);
} else {
let deletePosition = this._text.length - this._cursorOffset;
if (deletePosition > 0) {
this.text = this._text.slice(0, deletePosition - 1) + this._text.slice(deletePosition);
}
}
}
return;
case 46: // DELETE
if (this._text && this._text.length > 0) {
let deletePosition = this._text.length - this._cursorOffset;
this.text = this._text.slice(0, deletePosition) + this._text.slice(deletePosition + 1);
this._cursorOffset--;
}
return;
case 13: // RETURN
this._host.focusedControl = null;
return;
case 35: // END
this._cursorOffset = 0;
this._blinkIsEven = false;
this._markAsDirty();
return;
case 36: // HOME
this._cursorOffset = this._text.length;
this._blinkIsEven = false;
this._markAsDirty();
return;
case 37: // LEFT
this._cursorOffset++;
if (this._cursorOffset > this._text.length) {
this._cursorOffset = this._text.length;
}
this._blinkIsEven = false;
this._markAsDirty();
return;
case 39: // RIGHT
this._cursorOffset--;
if (this._cursorOffset < 0) {
this._cursorOffset = 0;
}
this._blinkIsEven = false;
this._markAsDirty();
return;
}
// Printable characters
if (
(keyCode === -1) || // Direct access
(keyCode === 32) || // Space
(keyCode > 47 && keyCode < 58) || // Numbers
(keyCode > 64 && keyCode < 91) || // Letters
(keyCode > 185 && keyCode < 193) || // Special characters
(keyCode > 218 && keyCode < 223) || // Special characters
(keyCode > 95 && keyCode < 112)) { // Numpad
if (this._cursorOffset === 0) {
this.text += key;
} else {
let insertPosition = this._text.length - this._cursorOffset;
this.text = this._text.slice(0, insertPosition) + key + this._text.slice(insertPosition);
}
}
}
public processKeyboard(evt: KeyboardEvent): void {
this.processKey(evt.keyCode, evt.key);
}
public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
context.save();
this._applyStates(context);
if (this._processMeasures(parentMeasure, context)) {
// Background
if (this._isFocused) {
if (this._focusedBackground) {
context.fillStyle = this._focusedBackground;
context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
}
} else if (this._background) {
context.fillStyle = this._background;
context.fillRect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
}
if (!this._fontOffset) {
this._fontOffset = Control._GetFontOffset(context.font);
}
// Text
let clipTextLeft = this._currentMeasure.left + this._margin.getValueInPixel(this._host, parentMeasure.width);
if (this.color) {
context.fillStyle = this.color;
}
let text = this._text;
if (!this._isFocused && !this._text && this._placeholderText) {
text = this._placeholderText;
if (this._placeholderColor) {
context.fillStyle = this._placeholderColor;
}
}
let textWidth = context.measureText(text).width;
let marginWidth = this._margin.getValueInPixel(this._host, parentMeasure.width) * 2;
if (this._autoStretchWidth) {
this.width = Math.min(this._maxWidth.getValueInPixel(this._host, parentMeasure.width), textWidth + marginWidth) + "px";
}
let rootY = this._fontOffset.ascent + (this._currentMeasure.height - this._fontOffset.height) / 2;
let availableWidth = this._width.getValueInPixel(this._host, parentMeasure.width) - marginWidth;
context.save();
context.beginPath();
context.rect(clipTextLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, availableWidth + 2, this._currentMeasure.height);
context.clip();
if (this._isFocused && textWidth > availableWidth) {
let textLeft = clipTextLeft - textWidth + availableWidth;
if (!this._scrollLeft) {
this._scrollLeft = textLeft;
}
} else {
this._scrollLeft = clipTextLeft;
}
context.fillText(text, this._scrollLeft, this._currentMeasure.top + rootY);
// Cursor
if (this._isFocused) {
if (!this._blinkIsEven) {
let cursorOffsetText = this.text.substr(this._text.length - this._cursorOffset);
let cursorOffsetWidth = context.measureText(cursorOffsetText).width;
let cursorLeft = this._scrollLeft + textWidth - cursorOffsetWidth;
if (cursorLeft < clipTextLeft) {
this._scrollLeft += (clipTextLeft - cursorLeft);
cursorLeft = clipTextLeft;
this._markAsDirty();
} else if (cursorLeft > clipTextLeft + availableWidth) {
this._scrollLeft += (clipTextLeft + availableWidth - cursorLeft);
cursorLeft = clipTextLeft + availableWidth;
this._markAsDirty();
}
context.fillRect(cursorLeft, this._currentMeasure.top + (this._currentMeasure.height - this._fontOffset.height) / 2, 2, this._fontOffset.height);
}
clearTimeout(this._blinkTimeout);
this._blinkTimeout = setTimeout(() => {
this._blinkIsEven = !this._blinkIsEven;
this._markAsDirty();
}, 500);
}
context.restore();
// Border
if (this._thickness) {
if (this.color) {
context.strokeStyle = this.color;
}
context.lineWidth = this._thickness;
context.strokeRect(this._currentMeasure.left + this._thickness / 2, this._currentMeasure.top + this._thickness / 2,
this._currentMeasure.width - this._thickness, this._currentMeasure.height - this._thickness);
}
}
context.restore();
}
protected _onPointerDown(coordinates: Vector2): boolean {
if (!super._onPointerDown(coordinates)) {
return false;
}
this._host.focusedControl = this;
return true;
}
protected _onPointerUp(coordinates: Vector2): void {
super._onPointerUp(coordinates);
}
public dispose() {
super.dispose();
this.onBlurObservable.clear();
this.onFocusObservable.clear();
this.onTextChangedObservable.clear();
}
}
}