control.ts 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494
  1. import { Container } from "./container";
  2. import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
  3. import { ValueAndUnit } from "../valueAndUnit";
  4. import { Nullable, Observer, Vector2, AbstractMesh, Observable, Vector3, Scene, Tools, Matrix, PointerEventTypes } from "babylonjs";
  5. import { Measure } from "../measure";
  6. import { Style } from "../style";
  7. import { Matrix2D, Vector2WithInfo } from "../math2D";
  8. /**
  9. * Root class used for all 2D controls
  10. * @see http://doc.babylonjs.com/how_to/gui#controls
  11. */
  12. export class Control {
  13. private _alpha = 1;
  14. private _alphaSet = false;
  15. private _zIndex = 0;
  16. /** @hidden */
  17. public _root: Nullable<Container>;
  18. /** @hidden */
  19. public _host: AdvancedDynamicTexture;
  20. /** Gets or sets the control parent */
  21. public parent: Nullable<Container>;
  22. /** @hidden */
  23. public _currentMeasure = Measure.Empty();
  24. private _fontFamily = "Arial";
  25. private _fontStyle = "";
  26. private _fontWeight = "";
  27. private _fontSize = new ValueAndUnit(18, ValueAndUnit.UNITMODE_PIXEL, false);
  28. private _font: string;
  29. /** @hidden */
  30. public _width = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
  31. /** @hidden */
  32. public _height = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
  33. /** @hidden */
  34. protected _fontOffset: { ascent: number, height: number, descent: number };
  35. private _color = "";
  36. private _style: Nullable<Style> = null;
  37. private _styleObserver: Nullable<Observer<Style>>;
  38. /** @hidden */
  39. protected _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
  40. /** @hidden */
  41. protected _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
  42. private _isDirty = true;
  43. /** @hidden */
  44. public _tempParentMeasure = Measure.Empty();
  45. /** @hidden */
  46. protected _cachedParentMeasure = Measure.Empty();
  47. private _paddingLeft = new ValueAndUnit(0);
  48. private _paddingRight = new ValueAndUnit(0);
  49. private _paddingTop = new ValueAndUnit(0);
  50. private _paddingBottom = new ValueAndUnit(0);
  51. /** @hidden */
  52. public _left = new ValueAndUnit(0);
  53. /** @hidden */
  54. public _top = new ValueAndUnit(0);
  55. private _scaleX = 1.0;
  56. private _scaleY = 1.0;
  57. private _rotation = 0;
  58. private _transformCenterX = 0.5;
  59. private _transformCenterY = 0.5;
  60. private _transformMatrix = Matrix2D.Identity();
  61. /** @hidden */
  62. protected _invertTransformMatrix = Matrix2D.Identity();
  63. /** @hidden */
  64. protected _transformedPosition = Vector2.Zero();
  65. private _onlyMeasureMode = false;
  66. private _isMatrixDirty = true;
  67. private _cachedOffsetX: number;
  68. private _cachedOffsetY: number;
  69. private _isVisible = true;
  70. /** @hidden */
  71. public _linkedMesh: Nullable<AbstractMesh>;
  72. private _fontSet = false;
  73. private _dummyVector2 = Vector2.Zero();
  74. private _downCount = 0;
  75. private _enterCount = -1;
  76. private _doNotRender = false;
  77. private _downPointerIds: { [id: number]: boolean } = {};
  78. protected _isEnabled = true;
  79. protected _disabledColor = "#9a9a9a";
  80. /** @hidden */
  81. public _tag: any;
  82. /** Gets or sets a boolean indicating if the control can be hit with pointer events */
  83. public isHitTestVisible = true;
  84. /** Gets or sets a boolean indicating if the control can block pointer events */
  85. public isPointerBlocker = false;
  86. /** Gets or sets a boolean indicating if the control can be focusable */
  87. public isFocusInvisible = false;
  88. /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
  89. public shadowOffsetX = 0;
  90. /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
  91. public shadowOffsetY = 0;
  92. /** Gets or sets a value indicating the amount of blur to use to render the shadow */
  93. public shadowBlur = 0;
  94. /** Gets or sets a value indicating the color of the shadow (black by default ie. "#000") */
  95. public shadowColor = '#000';
  96. /** Gets or sets the cursor to use when the control is hovered */
  97. public hoverCursor = "";
  98. /** @hidden */
  99. protected _linkOffsetX = new ValueAndUnit(0);
  100. /** @hidden */
  101. protected _linkOffsetY = new ValueAndUnit(0);
  102. // Properties
  103. /** Gets the control type name */
  104. public get typeName(): string {
  105. return this._getTypeName();
  106. }
  107. /**
  108. * An event triggered when the pointer move over the control.
  109. */
  110. public onPointerMoveObservable = new Observable<Vector2>();
  111. /**
  112. * An event triggered when the pointer move out of the control.
  113. */
  114. public onPointerOutObservable = new Observable<Control>();
  115. /**
  116. * An event triggered when the pointer taps the control
  117. */
  118. public onPointerDownObservable = new Observable<Vector2WithInfo>();
  119. /**
  120. * An event triggered when pointer up
  121. */
  122. public onPointerUpObservable = new Observable<Vector2WithInfo>();
  123. /**
  124. * An event triggered when a control is clicked on
  125. */
  126. public onPointerClickObservable = new Observable<Vector2WithInfo>();
  127. /**
  128. * An event triggered when pointer enters the control
  129. */
  130. public onPointerEnterObservable = new Observable<Control>();
  131. /**
  132. * An event triggered when the control is marked as dirty
  133. */
  134. public onDirtyObservable = new Observable<Control>();
  135. /**
  136. * An event triggered after the control is drawn
  137. */
  138. public onAfterDrawObservable = new Observable<Control>();
  139. /** Gets or set information about font offsets (used to render and align text) */
  140. public get fontOffset(): { ascent: number, height: number, descent: number } {
  141. return this._fontOffset;
  142. }
  143. public set fontOffset(offset: { ascent: number, height: number, descent: number }) {
  144. this._fontOffset = offset;
  145. }
  146. /** Gets or sets alpha value for the control (1 means opaque and 0 means entirely transparent) */
  147. public get alpha(): number {
  148. return this._alpha;
  149. }
  150. public set alpha(value: number) {
  151. if (this._alpha === value) {
  152. return;
  153. }
  154. this._alphaSet = true;
  155. this._alpha = value;
  156. this._markAsDirty();
  157. }
  158. /** Gets or sets a value indicating the scale factor on X axis (1 by default)
  159. * @see http://doc.babylonjs.com/how_to/gui#rotation-and-scaling
  160. */
  161. public get scaleX(): number {
  162. return this._scaleX;
  163. }
  164. public set scaleX(value: number) {
  165. if (this._scaleX === value) {
  166. return;
  167. }
  168. this._scaleX = value;
  169. this._markAsDirty();
  170. this._markMatrixAsDirty();
  171. }
  172. /** Gets or sets a value indicating the scale factor on Y axis (1 by default)
  173. * @see http://doc.babylonjs.com/how_to/gui#rotation-and-scaling
  174. */
  175. public get scaleY(): number {
  176. return this._scaleY;
  177. }
  178. public set scaleY(value: number) {
  179. if (this._scaleY === value) {
  180. return;
  181. }
  182. this._scaleY = value;
  183. this._markAsDirty();
  184. this._markMatrixAsDirty();
  185. }
  186. /** Gets or sets the rotation angle (0 by default)
  187. * @see http://doc.babylonjs.com/how_to/gui#rotation-and-scaling
  188. */
  189. public get rotation(): number {
  190. return this._rotation;
  191. }
  192. public set rotation(value: number) {
  193. if (this._rotation === value) {
  194. return;
  195. }
  196. this._rotation = value;
  197. this._markAsDirty();
  198. this._markMatrixAsDirty();
  199. }
  200. /** Gets or sets the transformation center on Y axis (0 by default)
  201. * @see http://doc.babylonjs.com/how_to/gui#rotation-and-scaling
  202. */
  203. public get transformCenterY(): number {
  204. return this._transformCenterY;
  205. }
  206. public set transformCenterY(value: number) {
  207. if (this._transformCenterY === value) {
  208. return;
  209. }
  210. this._transformCenterY = value;
  211. this._markAsDirty();
  212. this._markMatrixAsDirty();
  213. }
  214. /** Gets or sets the transformation center on X axis (0 by default)
  215. * @see http://doc.babylonjs.com/how_to/gui#rotation-and-scaling
  216. */
  217. public get transformCenterX(): number {
  218. return this._transformCenterX;
  219. }
  220. public set transformCenterX(value: number) {
  221. if (this._transformCenterX === value) {
  222. return;
  223. }
  224. this._transformCenterX = value;
  225. this._markAsDirty();
  226. this._markMatrixAsDirty();
  227. }
  228. /**
  229. * Gets or sets the horizontal alignment
  230. * @see http://doc.babylonjs.com/how_to/gui#alignments
  231. */
  232. public get horizontalAlignment(): number {
  233. return this._horizontalAlignment;
  234. }
  235. public set horizontalAlignment(value: number) {
  236. if (this._horizontalAlignment === value) {
  237. return;
  238. }
  239. this._horizontalAlignment = value;
  240. this._markAsDirty();
  241. }
  242. /**
  243. * Gets or sets the vertical alignment
  244. * @see http://doc.babylonjs.com/how_to/gui#alignments
  245. */
  246. public get verticalAlignment(): number {
  247. return this._verticalAlignment;
  248. }
  249. public set verticalAlignment(value: number) {
  250. if (this._verticalAlignment === value) {
  251. return;
  252. }
  253. this._verticalAlignment = value;
  254. this._markAsDirty();
  255. }
  256. /**
  257. * Gets or sets control width
  258. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  259. */
  260. public get width(): string | number {
  261. return this._width.toString(this._host);
  262. }
  263. /**
  264. * Gets control width in pixel
  265. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  266. */
  267. public get widthInPixels(): number {
  268. return this._width.getValueInPixel(this._host, this._cachedParentMeasure.width);
  269. }
  270. public set width(value: string | number) {
  271. if (this._width.toString(this._host) === value) {
  272. return;
  273. }
  274. if (this._width.fromString(value)) {
  275. this._markAsDirty();
  276. }
  277. }
  278. /**
  279. * Gets or sets control height
  280. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  281. */
  282. public get height(): string | number {
  283. return this._height.toString(this._host);
  284. }
  285. /**
  286. * Gets control height in pixel
  287. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  288. */
  289. public get heightInPixels(): number {
  290. return this._height.getValueInPixel(this._host, this._cachedParentMeasure.height);
  291. }
  292. public set height(value: string | number) {
  293. if (this._height.toString(this._host) === value) {
  294. return;
  295. }
  296. if (this._height.fromString(value)) {
  297. this._markAsDirty();
  298. }
  299. }
  300. /** Gets or set font family */
  301. public get fontFamily(): string {
  302. return this._fontFamily;
  303. }
  304. public set fontFamily(value: string) {
  305. if (this._fontFamily === value) {
  306. return;
  307. }
  308. this._fontFamily = value;
  309. this._resetFontCache();
  310. }
  311. /** Gets or sets font style */
  312. public get fontStyle(): string {
  313. return this._fontStyle;
  314. }
  315. public set fontStyle(value: string) {
  316. if (this._fontStyle === value) {
  317. return;
  318. }
  319. this._fontStyle = value;
  320. this._resetFontCache();
  321. }
  322. /** Gets or sets font weight */
  323. public get fontWeight(): string {
  324. return this._fontWeight;
  325. }
  326. public set fontWeight(value: string) {
  327. if (this._fontWeight === value) {
  328. return;
  329. }
  330. this._fontWeight = value;
  331. this._resetFontCache();
  332. }
  333. /**
  334. * Gets or sets style
  335. * @see http://doc.babylonjs.com/how_to/gui#styles
  336. */
  337. public get style(): Nullable<Style> {
  338. return this._style;
  339. }
  340. public set style(value: Nullable<Style>) {
  341. if (this._style) {
  342. this._style.onChangedObservable.remove(this._styleObserver);
  343. this._styleObserver = null;
  344. }
  345. this._style = value;
  346. if (this._style) {
  347. this._styleObserver = this._style.onChangedObservable.add(() => {
  348. this._markAsDirty();
  349. this._resetFontCache();
  350. });
  351. }
  352. this._markAsDirty();
  353. this._resetFontCache();
  354. }
  355. /** @hidden */
  356. public get _isFontSizeInPercentage(): boolean {
  357. return this._fontSize.isPercentage;
  358. }
  359. /** Gets font size in pixels */
  360. public get fontSizeInPixels(): number {
  361. let fontSizeToUse = this._style ? this._style._fontSize : this._fontSize;
  362. if (fontSizeToUse.isPixel) {
  363. return fontSizeToUse.getValue(this._host);
  364. }
  365. return fontSizeToUse.getValueInPixel(this._host, this._tempParentMeasure.height || this._cachedParentMeasure.height);
  366. }
  367. /** Gets or sets font size */
  368. public get fontSize(): string | number {
  369. return this._fontSize.toString(this._host);
  370. }
  371. public set fontSize(value: string | number) {
  372. if (this._fontSize.toString(this._host) === value) {
  373. return;
  374. }
  375. if (this._fontSize.fromString(value)) {
  376. this._markAsDirty();
  377. this._resetFontCache();
  378. }
  379. }
  380. /** Gets or sets foreground color */
  381. public get color(): string {
  382. return this._color;
  383. }
  384. public set color(value: string) {
  385. if (this._color === value) {
  386. return;
  387. }
  388. this._color = value;
  389. this._markAsDirty();
  390. }
  391. /** Gets or sets z index which is used to reorder controls on the z axis */
  392. public get zIndex(): number {
  393. return this._zIndex;
  394. }
  395. public set zIndex(value: number) {
  396. if (this.zIndex === value) {
  397. return;
  398. }
  399. this._zIndex = value;
  400. if (this._root) {
  401. this._root._reOrderControl(this);
  402. }
  403. }
  404. /** Gets or sets a boolean indicating if the control can be rendered */
  405. public get notRenderable(): boolean {
  406. return this._doNotRender;
  407. }
  408. public set notRenderable(value: boolean) {
  409. if (this._doNotRender === value) {
  410. return;
  411. }
  412. this._doNotRender = value;
  413. this._markAsDirty();
  414. }
  415. /** Gets or sets a boolean indicating if the control is visible */
  416. public get isVisible(): boolean {
  417. return this._isVisible;
  418. }
  419. public set isVisible(value: boolean) {
  420. if (this._isVisible === value) {
  421. return;
  422. }
  423. this._isVisible = value;
  424. this._markAsDirty();
  425. }
  426. /** Gets a boolean indicating that the control needs to update its rendering */
  427. public get isDirty(): boolean {
  428. return this._isDirty;
  429. }
  430. /**
  431. * Gets or sets a value indicating the padding to use on the left of the control
  432. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  433. */
  434. public get paddingLeft(): string | number {
  435. return this._paddingLeft.toString(this._host);
  436. }
  437. /**
  438. * Gets a value indicating the padding in pixels to use on the left of the control
  439. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  440. */
  441. public get paddingLeftInPixels(): number {
  442. return this._paddingLeft.getValueInPixel(this._host, this._cachedParentMeasure.width);
  443. }
  444. public set paddingLeft(value: string | number) {
  445. if (this._paddingLeft.fromString(value)) {
  446. this._markAsDirty();
  447. }
  448. }
  449. /**
  450. * Gets or sets a value indicating the padding to use on the right of the control
  451. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  452. */
  453. public get paddingRight(): string | number {
  454. return this._paddingRight.toString(this._host);
  455. }
  456. /**
  457. * Gets a value indicating the padding in pixels to use on the right of the control
  458. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  459. */
  460. public get paddingRightInPixels(): number {
  461. return this._paddingRight.getValueInPixel(this._host, this._cachedParentMeasure.width);
  462. }
  463. public set paddingRight(value: string | number) {
  464. if (this._paddingRight.fromString(value)) {
  465. this._markAsDirty();
  466. }
  467. }
  468. /**
  469. * Gets or sets a value indicating the padding to use on the top of the control
  470. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  471. */
  472. public get paddingTop(): string | number {
  473. return this._paddingTop.toString(this._host);
  474. }
  475. /**
  476. * Gets a value indicating the padding in pixels to use on the top of the control
  477. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  478. */
  479. public get paddingTopInPixels(): number {
  480. return this._paddingTop.getValueInPixel(this._host, this._cachedParentMeasure.height);
  481. }
  482. public set paddingTop(value: string | number) {
  483. if (this._paddingTop.fromString(value)) {
  484. this._markAsDirty();
  485. }
  486. }
  487. /**
  488. * Gets or sets a value indicating the padding to use on the bottom of the control
  489. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  490. */
  491. public get paddingBottom(): string | number {
  492. return this._paddingBottom.toString(this._host);
  493. }
  494. /**
  495. * Gets a value indicating the padding in pixels to use on the bottom of the control
  496. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  497. */
  498. public get paddingBottomInPixels(): number {
  499. return this._paddingBottom.getValueInPixel(this._host, this._cachedParentMeasure.height);
  500. }
  501. public set paddingBottom(value: string | number) {
  502. if (this._paddingBottom.fromString(value)) {
  503. this._markAsDirty();
  504. }
  505. }
  506. /**
  507. * Gets or sets a value indicating the left coordinate of the control
  508. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  509. */
  510. public get left(): string | number {
  511. return this._left.toString(this._host);
  512. }
  513. /**
  514. * Gets a value indicating the left coordinate in pixels of the control
  515. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  516. */
  517. public get leftInPixels(): number {
  518. return this._left.getValueInPixel(this._host, this._cachedParentMeasure.width);
  519. }
  520. public set left(value: string | number) {
  521. if (this._left.fromString(value)) {
  522. this._markAsDirty();
  523. }
  524. }
  525. /**
  526. * Gets or sets a value indicating the top coordinate of the control
  527. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  528. */
  529. public get top(): string | number {
  530. return this._top.toString(this._host);
  531. }
  532. /**
  533. * Gets a value indicating the top coordinate in pixels of the control
  534. * @see http://doc.babylonjs.com/how_to/gui#position-and-size
  535. */
  536. public get topInPixels(): number {
  537. return this._top.getValueInPixel(this._host, this._cachedParentMeasure.height);
  538. }
  539. public set top(value: string | number) {
  540. if (this._top.fromString(value)) {
  541. this._markAsDirty();
  542. }
  543. }
  544. /**
  545. * Gets or sets a value indicating the offset on X axis to the linked mesh
  546. * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
  547. */
  548. public get linkOffsetX(): string | number {
  549. return this._linkOffsetX.toString(this._host);
  550. }
  551. /**
  552. * Gets a value indicating the offset in pixels on X axis to the linked mesh
  553. * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
  554. */
  555. public get linkOffsetXInPixels(): number {
  556. return this._linkOffsetX.getValueInPixel(this._host, this._cachedParentMeasure.width);
  557. }
  558. public set linkOffsetX(value: string | number) {
  559. if (this._linkOffsetX.fromString(value)) {
  560. this._markAsDirty();
  561. }
  562. }
  563. /**
  564. * Gets or sets a value indicating the offset on Y axis to the linked mesh
  565. * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
  566. */
  567. public get linkOffsetY(): string | number {
  568. return this._linkOffsetY.toString(this._host);
  569. }
  570. /**
  571. * Gets a value indicating the offset in pixels on Y axis to the linked mesh
  572. * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
  573. */
  574. public get linkOffsetYInPixels(): number {
  575. return this._linkOffsetY.getValueInPixel(this._host, this._cachedParentMeasure.height);
  576. }
  577. public set linkOffsetY(value: string | number) {
  578. if (this._linkOffsetY.fromString(value)) {
  579. this._markAsDirty();
  580. }
  581. }
  582. /** Gets the center coordinate on X axis */
  583. public get centerX(): number {
  584. return this._currentMeasure.left + this._currentMeasure.width / 2;
  585. }
  586. /** Gets the center coordinate on Y axis */
  587. public get centerY(): number {
  588. return this._currentMeasure.top + this._currentMeasure.height / 2;
  589. }
  590. /** Gets or sets if control is Enabled*/
  591. public get isEnabled(): boolean {
  592. return this._isEnabled;
  593. }
  594. public set isEnabled(value: boolean) {
  595. if(this._isEnabled === value){
  596. return;
  597. }
  598. this._isEnabled = value;
  599. this._markAsDirty();
  600. }
  601. /** Gets or sets background color of control if it's disabled*/
  602. public get disabledColor(): string {
  603. return this._disabledColor;
  604. }
  605. public set disabledColor(value: string) {
  606. if(this._disabledColor === value){
  607. return;
  608. }
  609. this._disabledColor = value;
  610. this._markAsDirty();
  611. }
  612. // Functions
  613. /**
  614. * Creates a new control
  615. * @param name defines the name of the control
  616. */
  617. constructor(
  618. /** defines the name of the control */
  619. public name?: string) {
  620. }
  621. /** @hidden */
  622. protected _getTypeName(): string {
  623. return "Control";
  624. }
  625. /** @hidden */
  626. public _resetFontCache(): void {
  627. this._fontSet = true;
  628. this._markAsDirty();
  629. }
  630. /**
  631. * Determines if a container is an ascendant of the current control
  632. * @param container defines the container to look for
  633. * @returns true if the container is one of the ascendant of the control
  634. */
  635. public isAscendant(container: Control): boolean {
  636. if (!this.parent) {
  637. return false;
  638. }
  639. if (this.parent === container) {
  640. return true;
  641. }
  642. return this.parent.isAscendant(container);
  643. }
  644. /**
  645. * Gets coordinates in local control space
  646. * @param globalCoordinates defines the coordinates to transform
  647. * @returns the new coordinates in local space
  648. */
  649. public getLocalCoordinates(globalCoordinates: Vector2): Vector2 {
  650. var result = Vector2.Zero();
  651. this.getLocalCoordinatesToRef(globalCoordinates, result);
  652. return result;
  653. }
  654. /**
  655. * Gets coordinates in local control space
  656. * @param globalCoordinates defines the coordinates to transform
  657. * @param result defines the target vector2 where to store the result
  658. * @returns the current control
  659. */
  660. public getLocalCoordinatesToRef(globalCoordinates: Vector2, result: Vector2): Control {
  661. result.x = globalCoordinates.x - this._currentMeasure.left;
  662. result.y = globalCoordinates.y - this._currentMeasure.top;
  663. return this;
  664. }
  665. /**
  666. * Gets coordinates in parent local control space
  667. * @param globalCoordinates defines the coordinates to transform
  668. * @returns the new coordinates in parent local space
  669. */
  670. public getParentLocalCoordinates(globalCoordinates: Vector2): Vector2 {
  671. var result = Vector2.Zero();
  672. result.x = globalCoordinates.x - this._cachedParentMeasure.left;
  673. result.y = globalCoordinates.y - this._cachedParentMeasure.top;
  674. return result;
  675. }
  676. /**
  677. * Move the current control to a vector3 position projected onto the screen.
  678. * @param position defines the target position
  679. * @param scene defines the hosting scene
  680. */
  681. public moveToVector3(position: Vector3, scene: Scene): void {
  682. if (!this._host || this._root !== this._host._rootContainer) {
  683. Tools.Error("Cannot move a control to a vector3 if the control is not at root level");
  684. return;
  685. }
  686. this.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  687. this.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  688. var globalViewport = this._host._getGlobalViewport(scene);
  689. var projectedPosition = Vector3.Project(position, Matrix.Identity(), scene.getTransformMatrix(), globalViewport);
  690. this._moveToProjectedPosition(projectedPosition);
  691. if (projectedPosition.z < 0 || projectedPosition.z > 1) {
  692. this.notRenderable = true;
  693. return;
  694. }
  695. this.notRenderable = false;
  696. }
  697. /**
  698. * Link current control with a target mesh
  699. * @param mesh defines the mesh to link with
  700. * @see http://doc.babylonjs.com/how_to/gui#tracking-positions
  701. */
  702. public linkWithMesh(mesh: Nullable<AbstractMesh>): void {
  703. if (!this._host || this._root && this._root !== this._host._rootContainer) {
  704. if (mesh) {
  705. Tools.Error("Cannot link a control to a mesh if the control is not at root level");
  706. }
  707. return;
  708. }
  709. var index = this._host._linkedControls.indexOf(this);
  710. if (index !== -1) {
  711. this._linkedMesh = mesh;
  712. if (!mesh) {
  713. this._host._linkedControls.splice(index, 1);
  714. }
  715. return;
  716. } else if (!mesh) {
  717. return;
  718. }
  719. this.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
  720. this.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
  721. this._linkedMesh = mesh;
  722. this._onlyMeasureMode = this._currentMeasure.width === 0 || this._currentMeasure.height === 0;
  723. this._host._linkedControls.push(this);
  724. }
  725. /** @hidden */
  726. public _moveToProjectedPosition(projectedPosition: Vector3): void {
  727. let oldLeft = this._left.getValue(this._host);
  728. let oldTop = this._top.getValue(this._host);
  729. var newLeft = ((projectedPosition.x + this._linkOffsetX.getValue(this._host)) - this._currentMeasure.width / 2);
  730. var newTop = ((projectedPosition.y + this._linkOffsetY.getValue(this._host)) - this._currentMeasure.height / 2);
  731. if (this._left.ignoreAdaptiveScaling && this._top.ignoreAdaptiveScaling) {
  732. if (Math.abs(newLeft - oldLeft) < 0.5) {
  733. newLeft = oldLeft;
  734. }
  735. if (Math.abs(newTop - oldTop) < 0.5) {
  736. newTop = oldTop;
  737. }
  738. }
  739. this.left = newLeft + "px";
  740. this.top = newTop + "px";
  741. this._left.ignoreAdaptiveScaling = true;
  742. this._top.ignoreAdaptiveScaling = true;
  743. }
  744. /** @hidden */
  745. public _markMatrixAsDirty(): void {
  746. this._isMatrixDirty = true;
  747. this._markAsDirty();
  748. }
  749. /** @hidden */
  750. public _markAsDirty(): void {
  751. this._isDirty = true;
  752. if (!this._host) {
  753. return; // Not yet connected
  754. }
  755. this._host.markAsDirty();
  756. }
  757. /** @hidden */
  758. public _markAllAsDirty(): void {
  759. this._markAsDirty();
  760. if (this._font) {
  761. this._prepareFont();
  762. }
  763. }
  764. /** @hidden */
  765. public _link(root: Nullable<Container>, host: AdvancedDynamicTexture): void {
  766. this._root = root;
  767. this._host = host;
  768. }
  769. /** @hidden */
  770. protected _transform(context: CanvasRenderingContext2D): void {
  771. if (!this._isMatrixDirty && this._scaleX === 1 && this._scaleY === 1 && this._rotation === 0) {
  772. return;
  773. }
  774. // postTranslate
  775. var offsetX = this._currentMeasure.width * this._transformCenterX + this._currentMeasure.left;
  776. var offsetY = this._currentMeasure.height * this._transformCenterY + this._currentMeasure.top;
  777. context.translate(offsetX, offsetY);
  778. // rotate
  779. context.rotate(this._rotation);
  780. // scale
  781. context.scale(this._scaleX, this._scaleY);
  782. // preTranslate
  783. context.translate(-offsetX, -offsetY);
  784. // Need to update matrices?
  785. if (this._isMatrixDirty || this._cachedOffsetX !== offsetX || this._cachedOffsetY !== offsetY) {
  786. this._cachedOffsetX = offsetX;
  787. this._cachedOffsetY = offsetY;
  788. this._isMatrixDirty = false;
  789. Matrix2D.ComposeToRef(-offsetX, -offsetY, this._rotation, this._scaleX, this._scaleY, this._root ? this._root._transformMatrix : null, this._transformMatrix);
  790. this._transformMatrix.invertToRef(this._invertTransformMatrix);
  791. }
  792. }
  793. /** @hidden */
  794. protected _applyStates(context: CanvasRenderingContext2D): void {
  795. if (this._fontSet) {
  796. this._prepareFont();
  797. this._fontSet = false;
  798. }
  799. if (this._font) {
  800. context.font = this._font;
  801. }
  802. if (this._color) {
  803. context.fillStyle = this._color;
  804. }
  805. if (this._alphaSet) {
  806. context.globalAlpha = this.parent ? this.parent.alpha * this._alpha : this._alpha;
  807. }
  808. }
  809. /** @hidden */
  810. protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
  811. if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
  812. this._isDirty = false;
  813. this._currentMeasure.copyFrom(parentMeasure);
  814. // Let children take some pre-measurement actions
  815. this._preMeasure(parentMeasure, context);
  816. this._measure();
  817. this._computeAlignment(parentMeasure, context);
  818. // Convert to int values
  819. this._currentMeasure.left = this._currentMeasure.left | 0;
  820. this._currentMeasure.top = this._currentMeasure.top | 0;
  821. this._currentMeasure.width = this._currentMeasure.width | 0;
  822. this._currentMeasure.height = this._currentMeasure.height | 0;
  823. // Let children add more features
  824. this._additionalProcessing(parentMeasure, context);
  825. this._cachedParentMeasure.copyFrom(parentMeasure);
  826. if (this.onDirtyObservable.hasObservers()) {
  827. this.onDirtyObservable.notifyObservers(this);
  828. }
  829. }
  830. if (this._currentMeasure.left > parentMeasure.left + parentMeasure.width) {
  831. return false;
  832. }
  833. if (this._currentMeasure.left + this._currentMeasure.width < parentMeasure.left) {
  834. return false;
  835. }
  836. if (this._currentMeasure.top > parentMeasure.top + parentMeasure.height) {
  837. return false;
  838. }
  839. if (this._currentMeasure.top + this._currentMeasure.height < parentMeasure.top) {
  840. return false;
  841. }
  842. // Transform
  843. this._transform(context);
  844. if (this._onlyMeasureMode) {
  845. this._onlyMeasureMode = false;
  846. return false; // We do not want rendering for this frame as they are measure dependant information that need to be gathered
  847. }
  848. // Clip
  849. this._clip(context);
  850. context.clip();
  851. return true;
  852. }
  853. /** @hidden */
  854. protected _clip(context: CanvasRenderingContext2D) {
  855. context.beginPath();
  856. if (this.shadowBlur || this.shadowOffsetX || this.shadowOffsetY) {
  857. var shadowOffsetX = this.shadowOffsetX;
  858. var shadowOffsetY = this.shadowOffsetY;
  859. var shadowBlur = this.shadowBlur;
  860. var leftShadowOffset = Math.min(Math.min(shadowOffsetX, 0) - shadowBlur * 2, 0);
  861. var rightShadowOffset = Math.max(Math.max(shadowOffsetX, 0) + shadowBlur * 2, 0);
  862. var topShadowOffset = Math.min(Math.min(shadowOffsetY, 0) - shadowBlur * 2, 0);
  863. var bottomShadowOffset = Math.max(Math.max(shadowOffsetY, 0) + shadowBlur * 2, 0);
  864. context.rect(this._currentMeasure.left + leftShadowOffset,
  865. this._currentMeasure.top + topShadowOffset,
  866. this._currentMeasure.width + rightShadowOffset - leftShadowOffset,
  867. this._currentMeasure.height + bottomShadowOffset - topShadowOffset);
  868. } else {
  869. context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
  870. }
  871. }
  872. /** @hidden */
  873. public _measure(): void {
  874. // Width / Height
  875. if (this._width.isPixel) {
  876. this._currentMeasure.width = this._width.getValue(this._host);
  877. } else {
  878. this._currentMeasure.width *= this._width.getValue(this._host);
  879. }
  880. if (this._height.isPixel) {
  881. this._currentMeasure.height = this._height.getValue(this._host);
  882. } else {
  883. this._currentMeasure.height *= this._height.getValue(this._host);
  884. }
  885. }
  886. /** @hidden */
  887. protected _computeAlignment(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  888. var width = this._currentMeasure.width;
  889. var height = this._currentMeasure.height;
  890. var parentWidth = parentMeasure.width;
  891. var parentHeight = parentMeasure.height;
  892. // Left / top
  893. var x = 0;
  894. var y = 0;
  895. switch (this.horizontalAlignment) {
  896. case Control.HORIZONTAL_ALIGNMENT_LEFT:
  897. x = 0
  898. break;
  899. case Control.HORIZONTAL_ALIGNMENT_RIGHT:
  900. x = parentWidth - width;
  901. break;
  902. case Control.HORIZONTAL_ALIGNMENT_CENTER:
  903. x = (parentWidth - width) / 2;
  904. break;
  905. }
  906. switch (this.verticalAlignment) {
  907. case Control.VERTICAL_ALIGNMENT_TOP:
  908. y = 0;
  909. break;
  910. case Control.VERTICAL_ALIGNMENT_BOTTOM:
  911. y = parentHeight - height;
  912. break;
  913. case Control.VERTICAL_ALIGNMENT_CENTER:
  914. y = (parentHeight - height) / 2;
  915. break;
  916. }
  917. if (this._paddingLeft.isPixel) {
  918. this._currentMeasure.left += this._paddingLeft.getValue(this._host);
  919. this._currentMeasure.width -= this._paddingLeft.getValue(this._host);
  920. } else {
  921. this._currentMeasure.left += parentWidth * this._paddingLeft.getValue(this._host);
  922. this._currentMeasure.width -= parentWidth * this._paddingLeft.getValue(this._host);
  923. }
  924. if (this._paddingRight.isPixel) {
  925. this._currentMeasure.width -= this._paddingRight.getValue(this._host);
  926. } else {
  927. this._currentMeasure.width -= parentWidth * this._paddingRight.getValue(this._host);
  928. }
  929. if (this._paddingTop.isPixel) {
  930. this._currentMeasure.top += this._paddingTop.getValue(this._host);
  931. this._currentMeasure.height -= this._paddingTop.getValue(this._host);
  932. } else {
  933. this._currentMeasure.top += parentHeight * this._paddingTop.getValue(this._host);
  934. this._currentMeasure.height -= parentHeight * this._paddingTop.getValue(this._host);
  935. }
  936. if (this._paddingBottom.isPixel) {
  937. this._currentMeasure.height -= this._paddingBottom.getValue(this._host);
  938. } else {
  939. this._currentMeasure.height -= parentHeight * this._paddingBottom.getValue(this._host);
  940. }
  941. if (this._left.isPixel) {
  942. this._currentMeasure.left += this._left.getValue(this._host);
  943. } else {
  944. this._currentMeasure.left += parentWidth * this._left.getValue(this._host);
  945. }
  946. if (this._top.isPixel) {
  947. this._currentMeasure.top += this._top.getValue(this._host);
  948. } else {
  949. this._currentMeasure.top += parentHeight * this._top.getValue(this._host);
  950. }
  951. this._currentMeasure.left += x;
  952. this._currentMeasure.top += y;
  953. }
  954. /** @hidden */
  955. protected _preMeasure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  956. // Do nothing
  957. }
  958. /** @hidden */
  959. protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  960. // Do nothing
  961. }
  962. /** @hidden */
  963. public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
  964. // Do nothing
  965. }
  966. /**
  967. * Tests if a given coordinates belong to the current control
  968. * @param x defines x coordinate to test
  969. * @param y defines y coordinate to test
  970. * @returns true if the coordinates are inside the control
  971. */
  972. public contains(x: number, y: number): boolean {
  973. // Invert transform
  974. this._invertTransformMatrix.transformCoordinates(x, y, this._transformedPosition);
  975. x = this._transformedPosition.x;
  976. y = this._transformedPosition.y;
  977. // Check
  978. if (x < this._currentMeasure.left) {
  979. return false;
  980. }
  981. if (x > this._currentMeasure.left + this._currentMeasure.width) {
  982. return false;
  983. }
  984. if (y < this._currentMeasure.top) {
  985. return false;
  986. }
  987. if (y > this._currentMeasure.top + this._currentMeasure.height) {
  988. return false;
  989. }
  990. if (this.isPointerBlocker) {
  991. this._host._shouldBlockPointer = true;
  992. }
  993. return true;
  994. }
  995. /** @hidden */
  996. public _processPicking(x: number, y: number, type: number, pointerId: number, buttonIndex: number): boolean {
  997. if(!this._isEnabled){
  998. return false;
  999. }
  1000. if (!this.isHitTestVisible || !this.isVisible || this._doNotRender) {
  1001. return false;
  1002. }
  1003. if (!this.contains(x, y)) {
  1004. return false;
  1005. }
  1006. this._processObservables(type, x, y, pointerId, buttonIndex);
  1007. return true;
  1008. }
  1009. /** @hidden */
  1010. public _onPointerMove(target: Control, coordinates: Vector2): void {
  1011. var canNotify: boolean = this.onPointerMoveObservable.notifyObservers(coordinates, -1, target, this);
  1012. if (canNotify && this.parent != null) this.parent._onPointerMove(target, coordinates);
  1013. }
  1014. /** @hidden */
  1015. public _onPointerEnter(target: Control): boolean {
  1016. if(!this._isEnabled){
  1017. return false;
  1018. }
  1019. if (this._enterCount > 0) {
  1020. return false;
  1021. }
  1022. if (this._enterCount === -1) { // -1 is for touch input, we are now sure we are with a mouse or pencil
  1023. this._enterCount = 0;
  1024. }
  1025. this._enterCount++;
  1026. var canNotify: boolean = this.onPointerEnterObservable.notifyObservers(this, -1, target, this);
  1027. if (canNotify && this.parent != null) this.parent._onPointerEnter(target);
  1028. return true;
  1029. }
  1030. /** @hidden */
  1031. public _onPointerOut(target: Control): void {
  1032. if(!this._isEnabled){
  1033. return;
  1034. }
  1035. this._enterCount = 0;
  1036. var canNotify: boolean = this.onPointerOutObservable.notifyObservers(this, -1, target, this);
  1037. if (canNotify && this.parent != null) this.parent._onPointerOut(target);
  1038. }
  1039. /** @hidden */
  1040. public _onPointerDown(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number): boolean {
  1041. // Prevent pointerout to lose control context.
  1042. // Event redundancy is checked inside the function.
  1043. this._onPointerEnter(this);
  1044. if (this._downCount !== 0) {
  1045. return false;
  1046. }
  1047. this._downCount++;
  1048. this._downPointerIds[pointerId] = true;
  1049. var canNotify: boolean = this.onPointerDownObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
  1050. if (canNotify && this.parent != null) this.parent._onPointerDown(target, coordinates, pointerId, buttonIndex);
  1051. return true;
  1052. }
  1053. /** @hidden */
  1054. public _onPointerUp(target: Control, coordinates: Vector2, pointerId: number, buttonIndex: number, notifyClick: boolean): void {
  1055. if(!this._isEnabled){
  1056. return;
  1057. }
  1058. this._downCount = 0;
  1059. delete this._downPointerIds[pointerId];
  1060. var canNotifyClick: boolean = notifyClick;
  1061. if (notifyClick && (this._enterCount > 0 || this._enterCount === -1)) {
  1062. canNotifyClick = this.onPointerClickObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
  1063. }
  1064. var canNotify: boolean = this.onPointerUpObservable.notifyObservers(new Vector2WithInfo(coordinates, buttonIndex), -1, target, this);
  1065. if (canNotify && this.parent != null) this.parent._onPointerUp(target, coordinates, pointerId, buttonIndex, canNotifyClick);
  1066. }
  1067. /** @hidden */
  1068. public _forcePointerUp(pointerId: Nullable<number> = null) {
  1069. if (pointerId !== null) {
  1070. this._onPointerUp(this, Vector2.Zero(), pointerId, 0, true);
  1071. } else {
  1072. for (var key in this._downPointerIds) {
  1073. this._onPointerUp(this, Vector2.Zero(), +key as number, 0, true);
  1074. }
  1075. }
  1076. }
  1077. /** @hidden */
  1078. public _processObservables(type: number, x: number, y: number, pointerId: number, buttonIndex: number): boolean {
  1079. if(!this._isEnabled){
  1080. return false;
  1081. }
  1082. this._dummyVector2.copyFromFloats(x, y);
  1083. if (type === PointerEventTypes.POINTERMOVE) {
  1084. this._onPointerMove(this, this._dummyVector2);
  1085. var previousControlOver = this._host._lastControlOver[pointerId];
  1086. if (previousControlOver && previousControlOver !== this) {
  1087. previousControlOver._onPointerOut(this);
  1088. }
  1089. if (previousControlOver !== this) {
  1090. this._onPointerEnter(this);
  1091. }
  1092. this._host._lastControlOver[pointerId] = this;
  1093. return true;
  1094. }
  1095. if (type === PointerEventTypes.POINTERDOWN) {
  1096. this._onPointerDown(this, this._dummyVector2, pointerId, buttonIndex);
  1097. this._host._lastControlDown[pointerId] = this;
  1098. this._host._lastPickedControl = this;
  1099. return true;
  1100. }
  1101. if (type === PointerEventTypes.POINTERUP) {
  1102. if (this._host._lastControlDown[pointerId]) {
  1103. this._host._lastControlDown[pointerId]._onPointerUp(this, this._dummyVector2, pointerId, buttonIndex, true);
  1104. }
  1105. delete this._host._lastControlDown[pointerId];
  1106. return true;
  1107. }
  1108. return false;
  1109. }
  1110. private _prepareFont() {
  1111. if (!this._font && !this._fontSet) {
  1112. return;
  1113. }
  1114. if (this._style) {
  1115. this._font = this._style.fontStyle + " " + this._style.fontWeight + " " + this.fontSizeInPixels + "px " + this._style.fontFamily;
  1116. } else {
  1117. this._font = this._fontStyle + " " + this._fontWeight + " " + this.fontSizeInPixels + "px " + this._fontFamily;
  1118. }
  1119. this._fontOffset = Control._GetFontOffset(this._font);
  1120. }
  1121. /** Releases associated resources */
  1122. public dispose() {
  1123. this.onDirtyObservable.clear();
  1124. this.onAfterDrawObservable.clear();
  1125. this.onPointerDownObservable.clear();
  1126. this.onPointerEnterObservable.clear();
  1127. this.onPointerMoveObservable.clear();
  1128. this.onPointerOutObservable.clear();
  1129. this.onPointerUpObservable.clear();
  1130. this.onPointerClickObservable.clear();
  1131. if (this._styleObserver && this._style) {
  1132. this._style.onChangedObservable.remove(this._styleObserver);
  1133. this._styleObserver = null;
  1134. }
  1135. if (this._root) {
  1136. this._root.removeControl(this);
  1137. this._root = null;
  1138. }
  1139. if (this._host) {
  1140. var index = this._host._linkedControls.indexOf(this);
  1141. if (index > -1) {
  1142. this.linkWithMesh(null);
  1143. }
  1144. }
  1145. }
  1146. // Statics
  1147. private static _HORIZONTAL_ALIGNMENT_LEFT = 0;
  1148. private static _HORIZONTAL_ALIGNMENT_RIGHT = 1;
  1149. private static _HORIZONTAL_ALIGNMENT_CENTER = 2;
  1150. private static _VERTICAL_ALIGNMENT_TOP = 0;
  1151. private static _VERTICAL_ALIGNMENT_BOTTOM = 1;
  1152. private static _VERTICAL_ALIGNMENT_CENTER = 2;
  1153. /** HORIZONTAL_ALIGNMENT_LEFT */
  1154. public static get HORIZONTAL_ALIGNMENT_LEFT(): number {
  1155. return Control._HORIZONTAL_ALIGNMENT_LEFT;
  1156. }
  1157. /** HORIZONTAL_ALIGNMENT_RIGHT */
  1158. public static get HORIZONTAL_ALIGNMENT_RIGHT(): number {
  1159. return Control._HORIZONTAL_ALIGNMENT_RIGHT;
  1160. }
  1161. /** HORIZONTAL_ALIGNMENT_CENTER */
  1162. public static get HORIZONTAL_ALIGNMENT_CENTER(): number {
  1163. return Control._HORIZONTAL_ALIGNMENT_CENTER;
  1164. }
  1165. /** VERTICAL_ALIGNMENT_TOP */
  1166. public static get VERTICAL_ALIGNMENT_TOP(): number {
  1167. return Control._VERTICAL_ALIGNMENT_TOP;
  1168. }
  1169. /** VERTICAL_ALIGNMENT_BOTTOM */
  1170. public static get VERTICAL_ALIGNMENT_BOTTOM(): number {
  1171. return Control._VERTICAL_ALIGNMENT_BOTTOM;
  1172. }
  1173. /** VERTICAL_ALIGNMENT_CENTER */
  1174. public static get VERTICAL_ALIGNMENT_CENTER(): number {
  1175. return Control._VERTICAL_ALIGNMENT_CENTER;
  1176. }
  1177. private static _FontHeightSizes: { [key: string]: { ascent: number, height: number, descent: number } } = {};
  1178. /** @hidden */
  1179. public static _GetFontOffset(font: string): { ascent: number, height: number, descent: number } {
  1180. if (Control._FontHeightSizes[font]) {
  1181. return Control._FontHeightSizes[font];
  1182. }
  1183. var text = document.createElement("span");
  1184. text.innerHTML = "Hg";
  1185. text.style.font = font;
  1186. var block = document.createElement("div");
  1187. block.style.display = "inline-block";
  1188. block.style.width = "1px";
  1189. block.style.height = "0px";
  1190. block.style.verticalAlign = "bottom";
  1191. var div = document.createElement("div");
  1192. div.appendChild(text);
  1193. div.appendChild(block);
  1194. document.body.appendChild(div);
  1195. var fontAscent = 0;
  1196. var fontHeight = 0;
  1197. try {
  1198. fontHeight = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
  1199. block.style.verticalAlign = "baseline";
  1200. fontAscent = block.getBoundingClientRect().top - text.getBoundingClientRect().top;
  1201. } finally {
  1202. document.body.removeChild(div);
  1203. }
  1204. var result = { ascent: fontAscent, height: fontHeight, descent: fontHeight - fontAscent };
  1205. Control._FontHeightSizes[font] = result;
  1206. return result;
  1207. };
  1208. /**
  1209. * Creates a stack panel that can be used to render headers
  1210. * @param control defines the control to associate with the header
  1211. * @param text defines the text of the header
  1212. * @param size defines the size of the header
  1213. * @param options defines options used to configure the header
  1214. * @returns a new StackPanel
  1215. * @ignore
  1216. * @hidden
  1217. */
  1218. public static AddHeader: (control: Control, text: string, size: string | number, options: { isHorizontal: boolean, controlFirst: boolean }) => any = () => { };
  1219. /** @hidden */
  1220. protected static drawEllipse(x: number, y: number, width: number, height: number, context: CanvasRenderingContext2D): void {
  1221. context.translate(x, y);
  1222. context.scale(width, height);
  1223. context.beginPath();
  1224. context.arc(0, 0, 1, 0, 2 * Math.PI);
  1225. context.closePath();
  1226. context.scale(1 / width, 1 / height);
  1227. context.translate(-x, -y);
  1228. }
  1229. }