|
@@ -2,11 +2,31 @@
|
|
|
|
|
|
module BABYLON.GUI {
|
|
|
/**
|
|
|
+ * Enum that determines the text-wrapping mode to use.
|
|
|
+ */
|
|
|
+ export enum TextWrapping {
|
|
|
+ /**
|
|
|
+ * Clip the text when it's larger than Control.width; this is the default mode.
|
|
|
+ */
|
|
|
+ Clip = 0,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Wrap the text word-wise, i.e. try to add line-breaks at word boundary to fit within Control.width.
|
|
|
+ */
|
|
|
+ WordWrap = 1,
|
|
|
+
|
|
|
+ /**
|
|
|
+ * Ellipsize the text, i.e. shrink with trailing … when text is larger than Control.width.
|
|
|
+ */
|
|
|
+ Ellipsis,
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
* Class used to create text block control
|
|
|
*/
|
|
|
export class TextBlock extends Control {
|
|
|
private _text = "";
|
|
|
- private _textWrapping = false;
|
|
|
+ private _textWrapping = TextWrapping.Clip;
|
|
|
private _textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
|
|
|
private _textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
|
|
|
|
|
@@ -54,18 +74,18 @@ module BABYLON.GUI {
|
|
|
/**
|
|
|
* Gets or sets a boolean indicating if text must be wrapped
|
|
|
*/
|
|
|
- public get textWrapping(): boolean {
|
|
|
+ public get textWrapping(): TextWrapping | boolean {
|
|
|
return this._textWrapping;
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* Gets or sets a boolean indicating if text must be wrapped
|
|
|
*/
|
|
|
- public set textWrapping(value: boolean) {
|
|
|
+ public set textWrapping(value: TextWrapping | boolean) {
|
|
|
if (this._textWrapping === value) {
|
|
|
return;
|
|
|
}
|
|
|
- this._textWrapping = value;
|
|
|
+ this._textWrapping = +value;
|
|
|
this._markAsDirty();
|
|
|
}
|
|
|
|
|
@@ -249,29 +269,54 @@ module BABYLON.GUI {
|
|
|
}
|
|
|
|
|
|
protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
|
|
|
- this._lines = [];
|
|
|
+ this._lines = this._breakLines(this._currentMeasure.width, context);
|
|
|
+ this.onLinesReadyObservable.notifyObservers(this);
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _breakLines(refWidth: number, context: CanvasRenderingContext2D): object[] {
|
|
|
+ var lines = [];
|
|
|
var _lines = this.text.split("\n");
|
|
|
|
|
|
- if (this._textWrapping && !this._resizeToFit) {
|
|
|
+ if (this._textWrapping === TextWrapping.Ellipsis && !this._resizeToFit) {
|
|
|
+ for (var _line of _lines) {
|
|
|
+ lines.push(this._parseLineEllipsis(_line, refWidth, context));
|
|
|
+ }
|
|
|
+ } else if (this._textWrapping === TextWrapping.WordWrap && !this._resizeToFit) {
|
|
|
for (var _line of _lines) {
|
|
|
- this._lines.push(this._parseLineWithTextWrapping(_line, context));
|
|
|
+ lines.push(...this._parseLineWordWrap(_line, refWidth, context));
|
|
|
}
|
|
|
} else {
|
|
|
for (var _line of _lines) {
|
|
|
- this._lines.push(this._parseLine(_line, context));
|
|
|
+ lines.push(this._parseLine(_line, context));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
- this.onLinesReadyObservable.notifyObservers(this);
|
|
|
+ return lines;
|
|
|
}
|
|
|
|
|
|
protected _parseLine(line: string = '', context: CanvasRenderingContext2D): object {
|
|
|
return { text: line, width: context.measureText(line).width };
|
|
|
}
|
|
|
|
|
|
- protected _parseLineWithTextWrapping(line: string = '', context: CanvasRenderingContext2D): object {
|
|
|
+ protected _parseLineEllipsis(line: string = '', width: number,
|
|
|
+ context: CanvasRenderingContext2D): object {
|
|
|
+ var lineWidth = context.measureText(line).width;
|
|
|
+
|
|
|
+ if (lineWidth > width) {
|
|
|
+ line += '…';
|
|
|
+ }
|
|
|
+ while (line.length > 2 && lineWidth > width) {
|
|
|
+ line = line.slice(0, -2) + '…';
|
|
|
+ lineWidth = context.measureText(line).width;
|
|
|
+ }
|
|
|
+
|
|
|
+ return { text: line, width: lineWidth };
|
|
|
+ }
|
|
|
+
|
|
|
+ protected _parseLineWordWrap(line: string = '', width: number,
|
|
|
+ context: CanvasRenderingContext2D): object[] {
|
|
|
+ var lines = [];
|
|
|
var words = line.split(' ');
|
|
|
- var width = this._currentMeasure.width;
|
|
|
var lineWidth = 0;
|
|
|
|
|
|
for (var n = 0; n < words.length; n++) {
|
|
@@ -279,7 +324,7 @@ module BABYLON.GUI {
|
|
|
var metrics = context.measureText(testLine);
|
|
|
var testWidth = metrics.width;
|
|
|
if (testWidth > width && n > 0) {
|
|
|
- this._lines.push({ text: line, width: lineWidth });
|
|
|
+ lines.push({ text: line, width: lineWidth });
|
|
|
line = words[n];
|
|
|
lineWidth = context.measureText(line).width;
|
|
|
}
|
|
@@ -288,8 +333,9 @@ module BABYLON.GUI {
|
|
|
line = testLine;
|
|
|
}
|
|
|
}
|
|
|
+ lines.push({ text: line, width: lineWidth });
|
|
|
|
|
|
- return { text: line, width: lineWidth };
|
|
|
+ return lines;
|
|
|
}
|
|
|
|
|
|
protected _renderLines(context: CanvasRenderingContext2D): void {
|
|
@@ -339,6 +385,26 @@ module BABYLON.GUI {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+ /**
|
|
|
+ * Given a width constraint applied on the text block, find the expected height
|
|
|
+ * @returns expected height
|
|
|
+ */
|
|
|
+ public computeExpectedHeight(): number {
|
|
|
+ if (this.text && this.widthInPixels) {
|
|
|
+ const context = document.createElement('canvas').getContext('2d');
|
|
|
+ if (context) {
|
|
|
+ this._applyStates(context);
|
|
|
+ if (!this._fontOffset) {
|
|
|
+ this._fontOffset = Control._GetFontOffset(context.font);
|
|
|
+ }
|
|
|
+ const lines = this._lines ? this._lines : this._breakLines(
|
|
|
+ this.widthInPixels - this.paddingLeftInPixels - this.paddingRightInPixels, context);
|
|
|
+ return this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * lines.length;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+
|
|
|
dispose(): void {
|
|
|
super.dispose();
|
|
|
|