textBlock.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. /// <reference path="../../../dist/preview release/babylon.d.ts"/>
  2. module BABYLON.GUI {
  3. export class TextBlock extends Control {
  4. private _text = "";
  5. private _textWrapping = false;
  6. private _textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  7. private _textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  8. private _lines: any[];
  9. private _resizeToFit: boolean = false;
  10. private _lineSpacing: ValueAndUnit = new ValueAndUnit(0);
  11. private _outlineWidth: number = 0;
  12. private _outlineColor: string = "white";
  13. /**
  14. * An event triggered after the text is changed
  15. */
  16. public onTextChangedObservable = new Observable<TextBlock>();
  17. /**
  18. * An event triggered after the text was broken up into lines
  19. */
  20. public onLinesReadyObservable = new Observable<TextBlock>();
  21. /**
  22. * Return the line list (you may need to use the onLinesReadyObservable to make sure the list is ready)
  23. */
  24. public get lines(): any[] {
  25. return this._lines;
  26. }
  27. /**
  28. * Gets or sets an boolean indicating that the TextBlock will be resized to fit container
  29. */
  30. public get resizeToFit(): boolean {
  31. return this._resizeToFit;
  32. }
  33. /**
  34. * Gets or sets an boolean indicating that the TextBlock will be resized to fit container
  35. */
  36. public set resizeToFit(value: boolean) {
  37. this._resizeToFit = value;
  38. if (this._resizeToFit) {
  39. this._width.ignoreAdaptiveScaling = true;
  40. this._height.ignoreAdaptiveScaling = true;
  41. }
  42. }
  43. /**
  44. * Gets or sets a boolean indicating if text must be wrapped
  45. */
  46. public get textWrapping(): boolean {
  47. return this._textWrapping;
  48. }
  49. /**
  50. * Gets or sets a boolean indicating if text must be wrapped
  51. */
  52. public set textWrapping(value: boolean) {
  53. if (this._textWrapping === value) {
  54. return;
  55. }
  56. this._textWrapping = value;
  57. this._markAsDirty();
  58. }
  59. /**
  60. * Gets or sets text to display
  61. */
  62. public get text(): string {
  63. return this._text;
  64. }
  65. /**
  66. * Gets or sets text to display
  67. */
  68. public set text(value: string) {
  69. if (this._text === value) {
  70. return;
  71. }
  72. this._text = value;
  73. this._markAsDirty();
  74. this.onTextChangedObservable.notifyObservers(this);
  75. }
  76. /**
  77. * Gets or sets text horizontal alignment (BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER by default)
  78. */
  79. public get textHorizontalAlignment(): number {
  80. return this._textHorizontalAlignment;
  81. }
  82. /**
  83. * Gets or sets text horizontal alignment (BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER by default)
  84. */
  85. public set textHorizontalAlignment(value: number) {
  86. if (this._textHorizontalAlignment === value) {
  87. return;
  88. }
  89. this._textHorizontalAlignment = value;
  90. this._markAsDirty();
  91. }
  92. /**
  93. * Gets or sets text vertical alignment (BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER by default)
  94. */
  95. public get textVerticalAlignment(): number {
  96. return this._textVerticalAlignment;
  97. }
  98. /**
  99. * Gets or sets text vertical alignment (BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER by default)
  100. */
  101. public set textVerticalAlignment(value: number) {
  102. if (this._textVerticalAlignment === value) {
  103. return;
  104. }
  105. this._textVerticalAlignment = value;
  106. this._markAsDirty();
  107. }
  108. /**
  109. * Gets or sets line spacing value
  110. */
  111. public set lineSpacing(value: string | number) {
  112. if (this._lineSpacing.fromString(value)) {
  113. this._markAsDirty();
  114. }
  115. }
  116. /**
  117. * Gets or sets line spacing value
  118. */
  119. public get lineSpacing(): string | number {
  120. return this._lineSpacing.toString(this._host);
  121. }
  122. /**
  123. * Gets or sets outlineWidth of the text to display
  124. */
  125. public get outlineWidth(): number {
  126. return this._outlineWidth;
  127. }
  128. /**
  129. * Gets or sets outlineWidth of the text to display
  130. */
  131. public set outlineWidth(value: number) {
  132. if (this._outlineWidth === value) {
  133. return;
  134. }
  135. this._outlineWidth = value;
  136. this._markAsDirty();
  137. }
  138. /**
  139. * Gets or sets outlineColor of the text to display
  140. */
  141. public get outlineColor(): string {
  142. return this._outlineColor;
  143. }
  144. /**
  145. * Gets or sets outlineColor of the text to display
  146. */
  147. public set outlineColor(value: string) {
  148. if (this._outlineColor === value) {
  149. return;
  150. }
  151. this._outlineColor = value;
  152. this._markAsDirty();
  153. }
  154. /**
  155. * Creates a new TextBlock object
  156. * @param name defines the name of the control
  157. * @param text defines the text to display (emptry string by default)
  158. */
  159. constructor(
  160. /**
  161. * Defines the name of the control
  162. */
  163. public name?: string,
  164. text: string = "") {
  165. super(name);
  166. this.text = text;
  167. }
  168. protected _getTypeName(): string {
  169. return "TextBlock";
  170. }
  171. private _drawText(text: string, textWidth: number, y: number, context: CanvasRenderingContext2D): void {
  172. var width = this._currentMeasure.width;
  173. var x = 0;
  174. switch (this._textHorizontalAlignment) {
  175. case Control.HORIZONTAL_ALIGNMENT_LEFT:
  176. x = 0
  177. break;
  178. case Control.HORIZONTAL_ALIGNMENT_RIGHT:
  179. x = width - textWidth;
  180. break;
  181. case Control.HORIZONTAL_ALIGNMENT_CENTER:
  182. x = (width - textWidth) / 2;
  183. break;
  184. }
  185. if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
  186. context.shadowColor = this.shadowColor;
  187. context.shadowBlur = this.shadowBlur;
  188. context.shadowOffsetX = this.shadowOffsetX;
  189. context.shadowOffsetY = this.shadowOffsetY;
  190. }
  191. if (this.outlineWidth) {
  192. context.strokeText(text, this._currentMeasure.left + x, y);
  193. }
  194. context.fillText(text, this._currentMeasure.left + x, y);
  195. }
  196. /** @ignore */
  197. public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  198. context.save();
  199. this._applyStates(context);
  200. if (this._processMeasures(parentMeasure, context)) {
  201. // Render lines
  202. this._renderLines(context);
  203. }
  204. context.restore();
  205. }
  206. protected _applyStates(context: CanvasRenderingContext2D): void {
  207. super._applyStates(context);
  208. if (this.outlineWidth) {
  209. context.lineWidth = this.outlineWidth;
  210. context.strokeStyle = this.outlineColor;
  211. }
  212. }
  213. protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  214. this._lines = [];
  215. var _lines = this.text.split("\n");
  216. if (this._textWrapping && !this._resizeToFit) {
  217. for (var _line of _lines) {
  218. this._lines.push(this._parseLineWithTextWrapping(_line, context));
  219. }
  220. } else {
  221. for (var _line of _lines) {
  222. this._lines.push(this._parseLine(_line, context));
  223. }
  224. }
  225. this.onLinesReadyObservable.notifyObservers(this);
  226. }
  227. protected _parseLine(line: string = '', context: CanvasRenderingContext2D): object {
  228. return { text: line, width: context.measureText(line).width };
  229. }
  230. protected _parseLineWithTextWrapping(line: string = '', context: CanvasRenderingContext2D): object {
  231. var words = line.split(' ');
  232. var width = this._currentMeasure.width;
  233. var lineWidth = 0;
  234. for (var n = 0; n < words.length; n++) {
  235. var testLine = n > 0 ? line + " " + words[n] : words[0];
  236. var metrics = context.measureText(testLine);
  237. var testWidth = metrics.width;
  238. if (testWidth > width && n > 0) {
  239. this._lines.push({ text: line, width: lineWidth });
  240. line = words[n];
  241. lineWidth = context.measureText(line).width;
  242. }
  243. else {
  244. lineWidth = testWidth;
  245. line = testLine;
  246. }
  247. }
  248. return { text: line, width: lineWidth };
  249. }
  250. protected _renderLines(context: CanvasRenderingContext2D): void {
  251. var height = this._currentMeasure.height;
  252. if (!this._fontOffset) {
  253. this._fontOffset = Control._GetFontOffset(context.font);
  254. }
  255. var rootY = 0;
  256. switch (this._textVerticalAlignment) {
  257. case Control.VERTICAL_ALIGNMENT_TOP:
  258. rootY = this._fontOffset.ascent;
  259. break;
  260. case Control.VERTICAL_ALIGNMENT_BOTTOM:
  261. rootY = height - this._fontOffset.height * (this._lines.length - 1) - this._fontOffset.descent;
  262. break;
  263. case Control.VERTICAL_ALIGNMENT_CENTER:
  264. rootY = this._fontOffset.ascent + (height - this._fontOffset.height * this._lines.length) / 2;
  265. break;
  266. }
  267. rootY += this._currentMeasure.top;
  268. var maxLineWidth: number = 0;
  269. for (let i = 0; i < this._lines.length; i++) {
  270. const line = this._lines[i];
  271. if (i !== 0 && this._lineSpacing.internalValue !== 0) {
  272. if (this._lineSpacing.isPixel) {
  273. rootY += this._lineSpacing.getValue(this._host);
  274. } else {
  275. rootY = rootY + (this._lineSpacing.getValue(this._host) * this._height.getValueInPixel(this._host, this._cachedParentMeasure.height));
  276. }
  277. }
  278. this._drawText(line.text, line.width, rootY, context);
  279. rootY += this._fontOffset.height;
  280. if (line.width > maxLineWidth) maxLineWidth = line.width;
  281. }
  282. if (this._resizeToFit) {
  283. this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
  284. this.height = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length + 'px';
  285. }
  286. }
  287. dispose(): void {
  288. super.dispose();
  289. this.onTextChangedObservable.clear();
  290. }
  291. }
  292. }