Преглед изворни кода

Canvas2D: StackPanel Layout fixing

nockawa пре 9 година
родитељ
комит
edaae2712a

+ 13 - 2
src/Canvas2d/babylon.canvas2dLayoutEngine.ts

@@ -129,6 +129,9 @@
 
         private _isHorizontal: boolean = true;
 
+        private static dstOffset = Vector2.Zero();
+        private static dstArea = Size.Zero();
+
         public updateLayout(prim: Prim2DBase) {
             if (prim._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
 
@@ -138,8 +141,16 @@
                 let max = 0;
 
                 for (let child of prim.children) {
-                    let layoutArea = child.layoutArea;
-                    child.margin.computeArea(child.actualSize, layoutArea);
+
+                    let layoutArea: Size;
+                    if (child._hasMargin) {
+                        child.margin.computeWithAlignment(prim.layoutArea, child.actualSize, child.marginAlignment, StackPanelLayoutEngine.dstOffset, StackPanelLayoutEngine.dstArea, true);
+                        layoutArea = StackPanelLayoutEngine.dstArea;
+                        child.layoutArea = layoutArea;
+                    } else {
+                        layoutArea = child.layoutArea;
+                        child.margin.computeArea(child.actualSize, layoutArea);
+                    }
 
                     max = Math.max(max, h ? layoutArea.height : layoutArea.width);
 

+ 12 - 2
src/Canvas2d/babylon.group2d.ts

@@ -262,14 +262,19 @@
             // The computed size will be floor on both width and height
             let actualSize: Size;
 
+            // Return the actualSize if set
+            if (this._actualSize) {
+                return this._actualSize;
+            }
+
             // Return the size if set by the user
             if (this._size) {
                 actualSize = new Size(Math.ceil(this._size.width), Math.ceil(this._size.height));
             }
 
-            // Otherwise the size is computed based on the boundingInfo
+            // Otherwise the size is computed based on the boundingInfo of the layout (or bounding info) content
             else {
-                let m = this.boundingInfo.max();
+                let m = this.layoutBoundingInfo.max();
                 actualSize = new Size(Math.ceil(m.x), Math.ceil(m.y));
             }
 
@@ -283,6 +288,11 @@
             return actualSize;
         }
 
+        public set actualSize(value: Size) {
+            this._actualSize = value;
+        }
+
+
         /**
          * Get/set the Cache Behavior, used in case the Canvas Cache Strategy is set to CACHESTRATEGY_ALLGROUPS. Can be either GROUPCACHEBEHAVIOR_CACHEINPARENTGROUP, GROUPCACHEBEHAVIOR_DONTCACHEOVERRIDE or GROUPCACHEBEHAVIOR_FOLLOWCACHESTRATEGY. See their documentation for more information.
          * GROUPCACHEBEHAVIOR_NORESIZEONSCALE can also be set if you set it at creation time.

+ 75 - 19
src/Canvas2d/babylon.prim2dBase.ts

@@ -1033,13 +1033,13 @@
 
         /**
          * Compute the positioning/size of an area considering the thickness of this object and a given alignment
-         * @param sourceArea the source area
+         * @param sourceArea the source area where the content must be sized/positioned
          * @param contentSize the content size to position/resize
          * @param alignment the alignment setting
          * @param dstOffset the position of the content
          * @param dstArea the new size of the content
          */
-        public computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, dstOffset: Vector2, dstArea: Size) {
+        public computeWithAlignment(sourceArea: Size, contentSize: Size, alignment: PrimitiveAlignment, dstOffset: Vector2, dstArea: Size, computeLayoutArea = false) {
             // Fetch some data
             let topType = this._getType(0, true);
             let leftType = this._getType(1, true);
@@ -1064,6 +1064,9 @@
                             dstOffset.x = this.leftPixels;
                         }
                         dstArea.width = width;
+                        if (computeLayoutArea) {
+                            dstArea.width += this.leftPixels;
+                        }
                         break;
 
                     }
@@ -1076,6 +1079,9 @@
                             dstOffset.x = Math.round(sourceArea.width - (width + this.rightPixels));
                         }
                         dstArea.width = width;
+                        if (computeLayoutArea) {
+                            dstArea.width += this.rightPixels;
+                        }
                         break;
                     }
                 case PrimitiveAlignment.AlignStretch:
@@ -1121,6 +1127,9 @@
                             dstOffset.y = Math.round(sourceArea.height - (height + this.topPixels));
                         }
                         dstArea.height = height;
+                        if (computeLayoutArea) {
+                            dstArea.height += this.topPixels;
+                        }
                         break;
 
                     }
@@ -1133,6 +1142,9 @@
                             dstOffset.y = this.bottomPixels;
                         }
                         dstArea.height = height;
+                        if (computeLayoutArea) {
+                            dstArea.height += this.bottomPixels;
+                        }
                         break;
 
                     }
@@ -1364,7 +1376,8 @@
             this._actualSize = null;
             this._boundingSize = Size.Zero();
             this._layoutArea = Size.Zero();
-            this._layoutAreaPos = Vector2.Zero();
+            this._layoutAreaPos = null;
+            this._layoutBoundingInfo = null;
             this._marginOffset = Vector2.Zero();
             this._paddingOffset = Vector2.Zero();
             this._parentPaddingOffset = Vector2.Zero();
@@ -1405,7 +1418,7 @@
             if (settings.dontInheritParentScale) {
                 this._setFlags(SmartPropertyPrim.flagDontInheritParentScale);
             }
-            this._setFlags((isPickable ? SmartPropertyPrim.flagIsPickable : 0) | SmartPropertyPrim.flagBoundingInfoDirty | SmartPropertyPrim.flagActualOpacityDirty | (isContainer ? SmartPropertyPrim.flagIsContainer : 0) | SmartPropertyPrim.flagActualScaleDirty);
+            this._setFlags((isPickable ? SmartPropertyPrim.flagIsPickable : 0) | SmartPropertyPrim.flagBoundingInfoDirty | SmartPropertyPrim.flagActualOpacityDirty | (isContainer ? SmartPropertyPrim.flagIsContainer : 0) | SmartPropertyPrim.flagActualScaleDirty | SmartPropertyPrim.flagLayoutBoundingInfoDirty);
 
             if (settings.opacity != null) {
                 this._opacity = settings.opacity;
@@ -2050,7 +2063,7 @@
             return this._margin;
         }
 
-        private get _hasMargin(): boolean {
+        public get _hasMargin(): boolean {
             return (this._margin !== null) || (this._marginAlignment !== null);
         }
 
@@ -2231,6 +2244,9 @@
                 return;
             }
             this._positioningDirty();
+            if (this.parent) {
+                this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
+            }
             this._layoutArea = val;
         }
 
@@ -2239,13 +2255,16 @@
          * The setter should only be called by a Layout Engine class.
          */
         public get layoutAreaPos(): Vector2 {
-            return this._layoutAreaPos;
+            return this._layoutAreaPos || Prim2DBase._nullPosition;
         }
 
         public set layoutAreaPos(val: Vector2) {
-            if (this._layoutAreaPos.equals(val)) {
+            if (this._layoutAreaPos && this._layoutAreaPos.equals(val)) {
                 return;
             }
+            if (this.parent) {
+                this.parent._setFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
+            }
             this._positioningDirty();
             this._layoutAreaPos = val;
         }
@@ -2332,7 +2351,7 @@
         }
 
         private static _bMax = Vector2.Zero();
-
+        private static _tpsBB = new BoundingInfo2D();
         /**
          * Get the boundingInfo associated to the primitive and its children.
          * The value is supposed to be always up to date
@@ -2351,7 +2370,8 @@
 
                 var tps = new BoundingInfo2D();
                 for (let curChild of this._children) {
-                    curChild.boundingInfo.transformToRef(curChild.localTransform, tps);
+                    let bb = curChild.boundingInfo;
+                    bb.transformToRef(curChild.localTransform, tps);
                     bi.unionToRef(tps, bi);
                 }
 
@@ -2366,6 +2386,41 @@
         }
 
         /**
+         * Get the boundingInfo of the primitive's content arranged by a layout Engine
+         * If a particular child is not arranged by layout, it's boundingInfo is used instead to produce something as accurate as possible
+         */
+        public get layoutBoundingInfo(): BoundingInfo2D {
+            if (this._isFlagSet(SmartPropertyPrim.flagLayoutBoundingInfoDirty)) {
+                if (!this._layoutBoundingInfo) {
+                    this._layoutBoundingInfo = new BoundingInfo2D();
+                }
+                if (this.isSizedByContent) {
+                    this._layoutBoundingInfo.clear();
+                } else {
+                    this._layoutBoundingInfo.copyFrom(this.levelBoundingInfo);
+                }
+                let bi = this._layoutBoundingInfo;
+
+                var tps = new BoundingInfo2D();
+                for (let curChild of this._children) {
+                    let bb: BoundingInfo2D;
+                    if (curChild._layoutAreaPos) {
+                        let s = curChild._layoutArea;
+                        BoundingInfo2D.CreateFromMinMaxToRef(0, s.width, 0, s.height, Prim2DBase._tpsBB);
+                        bb = Prim2DBase._tpsBB;
+                    } else {
+                        bb = curChild.boundingInfo;
+                    }
+                    bb.transformToRef(curChild.localTransform, tps);
+                    bi.unionToRef(tps, bi);
+                }
+
+                this._clearFlags(SmartPropertyPrim.flagLayoutBoundingInfoDirty);
+            }
+            return this._layoutBoundingInfo;
+        }
+
+        /**
          * Determine if the size is automatically computed or fixed because manually specified.
          * Use the actualSize property to get the final/real size of the primitive
          * @returns true if the size is automatically computed, false if it were manually specified.
@@ -2772,7 +2827,7 @@
 
                 var rot = Quaternion.RotationAxis(new Vector3(0, 0, 1), this._rotation);
                 var local: Matrix;
-                let pos = this.position;
+                let pos = this._position ? this.position : this.layoutAreaPos;
 
                 if (this._origin.x === 0 && this._origin.y === 0) {
                     local = Matrix.Compose(new Vector3(this._scale.x, this._scale.y, 1), rot, new Vector3(pos.x, pos.y, 0));
@@ -2837,8 +2892,10 @@
                 }
             }
 
-            // Check for layout update
             let positioningDirty = this._isFlagSet(SmartPropertyPrim.flagPositioningDirty);
+            let positioningComputed = positioningDirty && !this._isFlagSet(SmartPropertyPrim.flagPositioningDirty);
+
+            // Check for layout update
             if (this._isFlagSet(SmartPropertyPrim.flagLayoutDirty)) {
                 this.owner.addUpdateLayoutCounter(1);
                 this._layoutEngine.updateLayout(this);
@@ -2846,13 +2903,12 @@
                 this._clearFlags(SmartPropertyPrim.flagLayoutDirty);
             }
 
-            let positioningComputed = positioningDirty && !this._isFlagSet(SmartPropertyPrim.flagPositioningDirty);
             let autoContentChanged = false;
             if (this.isSizeAuto) {
                 if (!this._lastAutoSizeArea) {
-                    autoContentChanged = this.size!==null;
+                    autoContentChanged = this.actualSize!==null;
                 } else {
-                    autoContentChanged = (!this._lastAutoSizeArea.equals(this.size));
+                    autoContentChanged = (!this._lastAutoSizeArea.equals(this.actualSize));
                 }
             }
 
@@ -2900,8 +2956,8 @@
 
                     let localTransform: Matrix;
                     Prim2DBase._transMtx.copyFrom(this._localTransform);
-                    Prim2DBase._transMtx.m[12] += this._layoutAreaPos.x + this._marginOffset.x + parentPaddingOffset.x;
-                    Prim2DBase._transMtx.m[13] += this._layoutAreaPos.y + this._marginOffset.y + parentPaddingOffset.y;
+                    Prim2DBase._transMtx.m[12] += this._marginOffset.x + parentPaddingOffset.x;
+                    Prim2DBase._transMtx.m[13] += this._marginOffset.y + parentPaddingOffset.y;
                     localTransform = Prim2DBase._transMtx;
 
                     this._globalTransform = this._parent ? localTransform.multiply(globalTransform) : localTransform.clone();
@@ -2949,7 +3005,7 @@
 
             // Apply margin
             if (this._hasMargin) {
-                this.margin.computeWithAlignment(this.layoutArea, this.size, this.marginAlignment, this._marginOffset, Prim2DBase._size);
+                this.margin.computeWithAlignment(this.layoutArea, this.size || this.actualSize, this.marginAlignment, this._marginOffset, Prim2DBase._size);
                 this.actualSize = Prim2DBase._size.clone();
             }
 
@@ -2986,11 +3042,11 @@
             }
 
             if (!this._position) {
-                let aPos = new Vector2(this._layoutAreaPos.x + this._marginOffset.x, this._layoutAreaPos.y + this._marginOffset.y);
+                let aPos = new Vector2(this.layoutAreaPos.x + this._marginOffset.x, this.layoutAreaPos.y + this._marginOffset.y);
                 this.actualPosition = aPos;
             }
             if (isSizeAuto) {
-                this._lastAutoSizeArea = this.size;                
+                this._lastAutoSizeArea = this.actualSize;
             }
         }
 

+ 31 - 29
src/Canvas2d/babylon.smartPropertyPrim.ts

@@ -607,35 +607,37 @@
             }
         }
 
-        public static flagIsDisposed             = 0x0000001;    // set if the object is already disposed
-        public static flagLevelBoundingInfoDirty = 0x0000002;    // set if the primitive's level bounding box (not including children) is dirty
-        public static flagModelDirty             = 0x0000004;    // set if the model must be changed
-        public static flagLayoutDirty            = 0x0000008;    // set if the layout must be computed
-        public static flagLevelVisible           = 0x0000010;    // set if the primitive is set as visible for its level only
-        public static flagBoundingInfoDirty      = 0x0000020;    // set if the primitive's overall bounding box (including children) is dirty
-        public static flagIsPickable             = 0x0000040;    // set if the primitive can be picked during interaction
-        public static flagIsVisible              = 0x0000080;    // set if the primitive is concretely visible (use the levelVisible of parents)
-        public static flagVisibilityChanged      = 0x0000100;    // set if there was a transition between visible/hidden status
-        public static flagPositioningDirty       = 0x0000200;    // set if the primitive positioning must be computed
-        public static flagTrackedGroup           = 0x0000400;    // set if the group2D is tracking a scene node
-        public static flagWorldCacheChanged      = 0x0000800;    // set if the cached bitmap of a world space canvas changed
-        public static flagChildrenFlatZOrder     = 0x0001000;    // set if all the children (direct and indirect) will share the same Z-Order
-        public static flagZOrderDirty            = 0x0002000;    // set if the Z-Order for this prim and its children must be recomputed
-        public static flagActualOpacityDirty     = 0x0004000;    // set if the actualOpactity should be recomputed
-        public static flagPrimInDirtyList        = 0x0008000;    // set if the primitive is in the primDirtyList
-        public static flagIsContainer            = 0x0010000;    // set if the primitive is a container
-        public static flagNeedRefresh            = 0x0020000;    // set if the primitive wasn't successful at refresh
-        public static flagActualScaleDirty       = 0x0040000;    // set if the actualScale property needs to be recomputed
-        public static flagDontInheritParentScale = 0x0080000;    // set if the actualScale must not use its parent's scale to be computed
-        public static flagGlobalTransformDirty   = 0x0100000;    // set if the global transform must be recomputed due to a local transform change
-
-        private   _flags             : number;
-        private   _externalData      : StringDictionary<Object>;
-        private   _modelKey          : string;
-        private   _propInfo          : StringDictionary<Prim2DPropInfo>;
-        protected _levelBoundingInfo : BoundingInfo2D;
-        protected _boundingInfo      : BoundingInfo2D;
-        protected _instanceDirtyFlags: number;
+        public static flagIsDisposed              = 0x0000001;    // set if the object is already disposed
+        public static flagLevelBoundingInfoDirty  = 0x0000002;    // set if the primitive's level bounding box (not including children) is dirty
+        public static flagModelDirty              = 0x0000004;    // set if the model must be changed
+        public static flagLayoutDirty             = 0x0000008;    // set if the layout must be computed
+        public static flagLevelVisible            = 0x0000010;    // set if the primitive is set as visible for its level only
+        public static flagBoundingInfoDirty       = 0x0000020;    // set if the primitive's overall bounding box (including children) is dirty
+        public static flagIsPickable              = 0x0000040;    // set if the primitive can be picked during interaction
+        public static flagIsVisible               = 0x0000080;    // set if the primitive is concretely visible (use the levelVisible of parents)
+        public static flagVisibilityChanged       = 0x0000100;    // set if there was a transition between visible/hidden status
+        public static flagPositioningDirty        = 0x0000200;    // set if the primitive positioning must be computed
+        public static flagTrackedGroup            = 0x0000400;    // set if the group2D is tracking a scene node
+        public static flagWorldCacheChanged       = 0x0000800;    // set if the cached bitmap of a world space canvas changed
+        public static flagChildrenFlatZOrder      = 0x0001000;    // set if all the children (direct and indirect) will share the same Z-Order
+        public static flagZOrderDirty             = 0x0002000;    // set if the Z-Order for this prim and its children must be recomputed
+        public static flagActualOpacityDirty      = 0x0004000;    // set if the actualOpactity should be recomputed
+        public static flagPrimInDirtyList         = 0x0008000;    // set if the primitive is in the primDirtyList
+        public static flagIsContainer             = 0x0010000;    // set if the primitive is a container
+        public static flagNeedRefresh             = 0x0020000;    // set if the primitive wasn't successful at refresh
+        public static flagActualScaleDirty        = 0x0040000;    // set if the actualScale property needs to be recomputed
+        public static flagDontInheritParentScale  = 0x0080000;    // set if the actualScale must not use its parent's scale to be computed
+        public static flagGlobalTransformDirty    = 0x0100000;    // set if the global transform must be recomputed due to a local transform change
+        public static flagLayoutBoundingInfoDirty = 0x0100000;    // set if the layout bounding info is dirty
+
+        private   _flags              : number;
+        private   _externalData       : StringDictionary<Object>;
+        private   _modelKey           : string;
+        private   _propInfo           : StringDictionary<Prim2DPropInfo>;
+        protected _levelBoundingInfo  : BoundingInfo2D;
+        protected _boundingInfo       : BoundingInfo2D;
+        protected _layoutBoundingInfo : BoundingInfo2D;
+        protected _instanceDirtyFlags : number;
     }
 
     export function modelLevelProperty<T>(propId: number, piStore: (pi: Prim2DPropInfo) => void, typeLevelCompare = false, dirtyBoundingInfo = false, dirtyParentBoundingBox = false): (target: Object, propName: string | symbol, descriptor: TypedPropertyDescriptor<T>) => void {