babylon.canvas2dLayoutEngine.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. module BABYLON {
  2. @className("LayoutEngineBase")
  3. /**
  4. * This is the base class you have to extend in order to implement your own Layout Engine.
  5. * Note that for performance reason, each different Layout Engine type can be exposed as one/many singleton or must be instanced each time.
  6. * If data has to be associated to a given primitive you can use the SmartPropertyPrim.addExternalData API to do it.
  7. */
  8. export class LayoutEngineBase implements ILockable {
  9. constructor() {
  10. this.layoutDirtyOnPropertyChangedMask = 0;
  11. }
  12. public updateLayout(prim: Prim2DBase) {
  13. }
  14. public get isChildPositionAllowed(): boolean {
  15. return false;
  16. }
  17. isLocked(): boolean {
  18. return this._isLocked;
  19. }
  20. lock(): boolean {
  21. if (this._isLocked) {
  22. return false;
  23. }
  24. this._isLocked = true;
  25. return true;
  26. }
  27. public layoutDirtyOnPropertyChangedMask;
  28. private _isLocked: boolean;
  29. }
  30. @className("CanvasLayoutEngine")
  31. /**
  32. * The default Layout Engine, primitive are positioning into a Canvas, using their x/y coordinates.
  33. * This layout must be used as a Singleton through the CanvasLayoutEngine.Singleton property.
  34. */
  35. export class CanvasLayoutEngine extends LayoutEngineBase {
  36. public static Singleton: CanvasLayoutEngine = new CanvasLayoutEngine();
  37. // A very simple (no) layout computing...
  38. // The Canvas and its direct children gets the Canvas' size as Layout Area
  39. // Indirect children have their Layout Area to the actualSize (margin area) of their parent
  40. public updateLayout(prim: Prim2DBase) {
  41. // If this prim is layoutDiry we update its layoutArea and also the one of its direct children
  42. if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
  43. for (let child of prim.children) {
  44. this._doUpdate(child);
  45. }
  46. prim._clearFlags(SmartPropertyPrim.flagLayoutDirty);
  47. }
  48. }
  49. private _doUpdate(prim: Prim2DBase) {
  50. // Canvas ?
  51. if (prim instanceof Canvas2D) {
  52. prim.layoutArea = prim.actualSize;
  53. }
  54. // Direct child of Canvas ?
  55. else if (prim.parent instanceof Canvas2D) {
  56. prim.layoutArea = prim.owner.actualSize;
  57. }
  58. // Indirect child of Canvas
  59. else {
  60. prim.layoutArea = prim.parent.contentArea;
  61. }
  62. }
  63. get isChildPositionAllowed(): boolean {
  64. return true;
  65. }
  66. }
  67. @className("StackPanelLayoutEngine")
  68. /**
  69. * A stack panel layout. Primitive will be stack either horizontally or vertically.
  70. * This Layout type must be used as a Singleton, use the StackPanelLayoutEngine.Horizontal for an horizontal stack panel or StackPanelLayoutEngine.Vertical for a vertical one.
  71. */
  72. export class StackPanelLayoutEngine extends LayoutEngineBase {
  73. constructor() {
  74. super();
  75. this.layoutDirtyOnPropertyChangedMask = Prim2DBase.sizeProperty.flagId;
  76. }
  77. public static get Horizontal(): StackPanelLayoutEngine {
  78. if (!StackPanelLayoutEngine._horizontal) {
  79. StackPanelLayoutEngine._horizontal = new StackPanelLayoutEngine();
  80. StackPanelLayoutEngine._horizontal.isHorizontal = true;
  81. StackPanelLayoutEngine._horizontal.lock();
  82. }
  83. return StackPanelLayoutEngine._horizontal;
  84. }
  85. public static get Vertical(): StackPanelLayoutEngine {
  86. if (!StackPanelLayoutEngine._vertical) {
  87. StackPanelLayoutEngine._vertical = new StackPanelLayoutEngine();
  88. StackPanelLayoutEngine._vertical.isHorizontal = false;
  89. StackPanelLayoutEngine._vertical.lock();
  90. }
  91. return StackPanelLayoutEngine._vertical;
  92. }
  93. private static _horizontal: StackPanelLayoutEngine = null;
  94. private static _vertical: StackPanelLayoutEngine = null;
  95. get isHorizontal(): boolean {
  96. return this._isHorizontal;
  97. }
  98. set isHorizontal(val: boolean) {
  99. if (this.isLocked()) {
  100. return;
  101. }
  102. this._isHorizontal = val;
  103. }
  104. private _isHorizontal: boolean = true;
  105. private static dstOffset = Vector2.Zero();
  106. private static dstArea = Size.Zero();
  107. public updateLayout(prim: Prim2DBase) {
  108. if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
  109. let x = 0;
  110. let y = 0;
  111. let h = this.isHorizontal;
  112. let max = 0;
  113. for (let child of prim.children) {
  114. let layoutArea: Size;
  115. if (child._hasMargin) {
  116. child.margin.computeWithAlignment(prim.layoutArea, child.actualSize, child.marginAlignment, StackPanelLayoutEngine.dstOffset, StackPanelLayoutEngine.dstArea, true);
  117. layoutArea = StackPanelLayoutEngine.dstArea.clone();
  118. child.layoutArea = layoutArea;
  119. } else {
  120. layoutArea = child.layoutArea;
  121. child.margin.computeArea(child.actualSize, layoutArea);
  122. }
  123. max = Math.max(max, h ? layoutArea.height : layoutArea.width);
  124. }
  125. for (let child of prim.children) {
  126. child.layoutAreaPos = new Vector2(x, y);
  127. let layoutArea = child.layoutArea;
  128. if (h) {
  129. x += layoutArea.width;
  130. child.layoutArea = new Size(layoutArea.width, max);
  131. } else {
  132. y += layoutArea.height;
  133. child.layoutArea = new Size(max, layoutArea.height);
  134. }
  135. }
  136. prim._clearFlags(SmartPropertyPrim.flagLayoutDirty);
  137. }
  138. }
  139. get isChildPositionAllowed(): boolean {
  140. return false;
  141. }
  142. }
  143. }