control.ts 50 KB

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