Browse Source

Merge pull request #9514 from BabylonJS/serialization-gui

Fix #9434
David Catuhe 4 years ago
parent
commit
15a82d5d08

+ 23 - 0
gui/src/2D/advancedDynamicTexture.ts

@@ -890,6 +890,29 @@ export class AdvancedDynamicTexture extends DynamicTexture {
             this._lastControlDown = {};
         });
     }
+
+    /**
+     * Serializes the entire GUI system
+     * @returns an object with the JSON serialized data
+     */
+    public serializeContent(): any {
+        let serializationObject = {
+            root: {}
+        };
+
+        this._rootContainer.serialize(serializationObject.root);
+
+        return serializationObject;
+    }
+
+    /**
+     * Recreate the content of the ADT from a JSON object
+     * @param serializedObject define the JSON serialized object to restore from
+     */
+    public parseContent(serializedObject: any) {
+        this._rootContainer = Control.Parse(serializedObject.root, this) as Container;
+    }
+
     // Statics
     /**
      * Creates a new AdvancedDynamicTexture in projected mode (ie. attached to a mesh)

+ 5 - 0
gui/src/2D/controls/checkbox.ts

@@ -8,6 +8,7 @@ import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Nullable } from 'babylonjs/types';
 import { Measure } from '../measure';
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Class used to represent a 2D checkbox
@@ -19,6 +20,7 @@ export class Checkbox extends Control {
     private _thickness = 1;
 
     /** Gets or sets border thickness  */
+    @serialize()
     public get thickness(): number {
         return this._thickness;
     }
@@ -38,6 +40,7 @@ export class Checkbox extends Control {
     public onIsCheckedChangedObservable = new Observable<boolean>();
 
     /** Gets or sets a value indicating the ratio between overall size and check size */
+    @serialize()
     public get checkSizeRatio(): number {
         return this._checkSizeRatio;
     }
@@ -54,6 +57,7 @@ export class Checkbox extends Control {
     }
 
     /** Gets or sets background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -68,6 +72,7 @@ export class Checkbox extends Control {
     }
 
     /** Gets or sets a boolean indicating if the checkbox is checked or not */
+    @serialize()
     public get isChecked(): boolean {
         return this._isChecked;
     }

+ 5 - 0
gui/src/2D/controls/colorpicker.ts

@@ -12,6 +12,7 @@ import { TextBlock } from "../controls/textBlock";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Color3 } from 'babylonjs/Maths/math.color';
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /** Class used to create color pickers */
 export class ColorPicker extends Control {
@@ -40,6 +41,7 @@ export class ColorPicker extends Control {
     public onValueChangedObservable = new Observable<Color3>();
 
     /** Gets or sets the color of the color picker */
+    @serialize()
     public get value(): Color3 {
         return this._value;
     }
@@ -90,6 +92,7 @@ export class ColorPicker extends Control {
      * Gets or sets control width
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get width(): string | number {
         return this._width.toString(this._host);
     }
@@ -109,6 +112,7 @@ export class ColorPicker extends Control {
      * Gets or sets control height
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get height(): string | number {
         return this._height.toString(this._host);
     }
@@ -126,6 +130,7 @@ export class ColorPicker extends Control {
     }
 
     /** Gets or sets control size */
+    @serialize()
     public get size(): string | number {
         return this.width;
     }

+ 38 - 0
gui/src/2D/controls/container.ts

@@ -6,6 +6,7 @@ import { Measure } from "../measure";
 import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Root class for 2D containers
@@ -31,9 +32,11 @@ export class Container extends Control {
     /**
      * Gets or sets the number of layout cycles (a change involved by a control while evaluating the layout) allowed
      */
+    @serialize()
     public maxLayoutCycle = 3;
 
     /** Gets or sets a boolean indicating if the container should try to adapt to its children height */
+    @serialize()
     public get adaptHeightToChildren(): boolean {
         return this._adaptHeightToChildren;
     }
@@ -53,6 +56,7 @@ export class Container extends Control {
     }
 
     /** Gets or sets a boolean indicating if the container should try to adapt to its children width */
+    @serialize()
     public get adaptWidthToChildren(): boolean {
         return this._adaptWidthToChildren;
     }
@@ -72,6 +76,7 @@ export class Container extends Control {
     }
 
     /** Gets or sets background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -453,6 +458,25 @@ export class Container extends Control {
         this._measureForChildren.copyFrom(this._currentMeasure);
     }
 
+     /**
+     * Serializes the current control
+     * @param serializationObject defined the JSON serialized object
+     */
+    public serialize(serializationObject: any) {
+        super.serialize(serializationObject);
+        if (!this.children.length) {
+            return;
+        }
+
+        serializationObject.children = [];
+
+        for (var child of this.children) {
+            let childSerializationObject = {};
+            child.serialize(childSerializationObject);
+            serializationObject.children.push(childSerializationObject);
+        }
+    }
+
     /** Releases associated resources */
     public dispose() {
         super.dispose();
@@ -461,5 +485,19 @@ export class Container extends Control {
             this.children[index].dispose();
         }
     }
+
+    /** @hidden */
+    public _parseFromContent(serializedObject: any, host: AdvancedDynamicTexture) {
+        super._parseFromContent(serializedObject, host);
+        this._link(host);
+
+        if (!serializedObject.children) {
+            return;
+        }
+
+        for (var childData of serializedObject.children) {
+            this.addControl(Control.Parse(childData, host));
+        }
+    }
 }
 _TypeStore.RegisteredTypes["BABYLON.GUI.Container"] = Container;

+ 97 - 3
gui/src/2D/controls/control.ts

@@ -14,6 +14,7 @@ import { Measure } from "../measure";
 import { Style } from "../style";
 import { Matrix2D, Vector2WithInfo } from "../math2D";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { SerializationHelper, serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Root class used for all 2D controls
@@ -120,36 +121,44 @@ export class Control {
     /**
      * Gets or sets an object used to store user defined information for the node
      */
+    @serialize()
     public metadata: any = null;
 
     /** Gets or sets a boolean indicating if the control can be hit with pointer events */
+    @serialize()
     public isHitTestVisible = true;
     /** Gets or sets a boolean indicating if the control can block pointer events */
+    @serialize()
     public isPointerBlocker = false;
     /** Gets or sets a boolean indicating if the control can be focusable */
+    @serialize()
     public isFocusInvisible = false;
 
     /**
      * Gets or sets a boolean indicating if the children are clipped to the current control bounds.
      * Please note that not clipping children may generate issues with adt.useInvalidateRectOptimization so it is recommended to turn this optimization off if you want to use unclipped children
      */
+    @serialize()
     public clipChildren = true;
 
     /**
      * Gets or sets a boolean indicating that control content must be clipped
      * Please note that not clipping children may generate issues with adt.useInvalidateRectOptimization so it is recommended to turn this optimization off if you want to use unclipped children
      */
+    @serialize()
     public clipContent = true;
 
     /**
      * Gets or sets a boolean indicating that the current control should cache its rendering (useful when the control does not change often)
      */
+    @serialize()
     public useBitmapCache = false;
 
     private _cacheData: Nullable<ImageData>;
 
     private _shadowOffsetX = 0;
     /** Gets or sets a value indicating the offset to apply on X axis to render the shadow */
+    @serialize()
     public get shadowOffsetX() {
         return this._shadowOffsetX;
     }
@@ -165,6 +174,7 @@ export class Control {
 
     private _shadowOffsetY = 0;
     /** Gets or sets a value indicating the offset to apply on Y axis to render the shadow */
+    @serialize()
     public get shadowOffsetY() {
         return this._shadowOffsetY;
     }
@@ -180,6 +190,7 @@ export class Control {
 
     private _shadowBlur = 0;
     /** Gets or sets a value indicating the amount of blur to use to render the shadow */
+    @serialize()
     public get shadowBlur() {
         return this._shadowBlur;
     }
@@ -195,6 +206,7 @@ export class Control {
 
     private _shadowColor = 'black';
     /** Gets or sets a value indicating the color of the shadow (black by default ie. "#000") */
+    @serialize()
     public get shadowColor() {
         return this._shadowColor;
     }
@@ -209,6 +221,7 @@ export class Control {
     }
 
     /** Gets or sets the cursor to use when the control is hovered */
+    @serialize()
     public hoverCursor = "";
 
     /** @hidden */
@@ -293,6 +306,7 @@ export class Control {
     }
 
     /** Gets or set information about font offsets (used to render and align text) */
+    @serialize()
     public get fontOffset(): { ascent: number, height: number, descent: number } {
         return this._fontOffset;
     }
@@ -302,6 +316,7 @@ export class Control {
     }
 
     /** Gets or sets alpha value for the control (1 means opaque and 0 means entirely transparent) */
+    @serialize()
     public get alpha(): number {
         return this._alpha;
     }
@@ -334,6 +349,7 @@ export class Control {
     /** Gets or sets a value indicating the scale factor on X axis (1 by default)
      * @see https://doc.babylonjs.com/how_to/gui#rotation-and-scaling
     */
+   @serialize()
     public get scaleX(): number {
         return this._scaleX;
     }
@@ -351,6 +367,7 @@ export class Control {
     /** Gets or sets a value indicating the scale factor on Y axis (1 by default)
      * @see https://doc.babylonjs.com/how_to/gui#rotation-and-scaling
     */
+   @serialize()
     public get scaleY(): number {
         return this._scaleY;
     }
@@ -368,6 +385,7 @@ export class Control {
     /** Gets or sets the rotation angle (0 by default)
      * @see https://doc.babylonjs.com/how_to/gui#rotation-and-scaling
     */
+   @serialize()
     public get rotation(): number {
         return this._rotation;
     }
@@ -385,6 +403,7 @@ export class Control {
     /** Gets or sets the transformation center on Y axis (0 by default)
      * @see https://doc.babylonjs.com/how_to/gui#rotation-and-scaling
     */
+   @serialize()
     public get transformCenterY(): number {
         return this._transformCenterY;
     }
@@ -402,6 +421,7 @@ export class Control {
     /** Gets or sets the transformation center on X axis (0 by default)
      * @see https://doc.babylonjs.com/how_to/gui#rotation-and-scaling
     */
+   @serialize()
     public get transformCenterX(): number {
         return this._transformCenterX;
     }
@@ -420,6 +440,7 @@ export class Control {
      * Gets or sets the horizontal alignment
      * @see https://doc.babylonjs.com/how_to/gui#alignments
      */
+    @serialize()
     public get horizontalAlignment(): number {
         return this._horizontalAlignment;
     }
@@ -437,6 +458,7 @@ export class Control {
      * Gets or sets the vertical alignment
      * @see https://doc.babylonjs.com/how_to/gui#alignments
      */
+    @serialize()
     public get verticalAlignment(): number {
         return this._verticalAlignment;
     }
@@ -456,6 +478,7 @@ export class Control {
      * The first dimension used in the computation is the last one set (by setting width / widthInPixels or height / heightInPixels), and the
      * second dimension is computed as first dimension * fixedRatio
      */
+    @serialize()
     public fixedRatio = 0;
 
     private _fixedRatioMasterIsWidth = true;
@@ -464,6 +487,7 @@ export class Control {
      * Gets or sets control width
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get width(): string | number {
         return this._width.toString(this._host);
     }
@@ -500,6 +524,7 @@ export class Control {
      * Gets or sets control height
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get height(): string | number {
         return this._height.toString(this._host);
     }
@@ -581,6 +606,7 @@ export class Control {
      * Gets or sets style
      * @see https://doc.babylonjs.com/how_to/gui#styles
      */
+    @serialize()
     public get style(): Nullable<Style> {
         return this._style;
     }
@@ -644,6 +670,7 @@ export class Control {
     }
 
     /** Gets or sets foreground color */
+    @serialize()
     public get color(): string {
         return this._color;
     }
@@ -658,6 +685,7 @@ export class Control {
     }
 
     /** Gets or sets z index which is used to reorder controls on the z axis */
+    @serialize()
     public get zIndex(): number {
         return this._zIndex;
     }
@@ -675,6 +703,7 @@ export class Control {
     }
 
     /** Gets or sets a boolean indicating if the control can be rendered */
+    @serialize()
     public get notRenderable(): boolean {
         return this._doNotRender;
     }
@@ -689,6 +718,7 @@ export class Control {
     }
 
     /** Gets or sets a boolean indicating if the control is visible */
+    @serialize()
     public get isVisible(): boolean {
         return this._isVisible;
     }
@@ -718,6 +748,7 @@ export class Control {
      * Gets or sets a value indicating the padding to use on the left of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get paddingLeft(): string | number {
         return this._paddingLeft.toString(this._host);
     }
@@ -747,6 +778,7 @@ export class Control {
      * Gets or sets a value indicating the padding to use on the right of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get paddingRight(): string | number {
         return this._paddingRight.toString(this._host);
     }
@@ -776,6 +808,7 @@ export class Control {
      * Gets or sets a value indicating the padding to use on the top of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get paddingTop(): string | number {
         return this._paddingTop.toString(this._host);
     }
@@ -805,6 +838,7 @@ export class Control {
      * Gets or sets a value indicating the padding to use on the bottom of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get paddingBottom(): string | number {
         return this._paddingBottom.toString(this._host);
     }
@@ -834,6 +868,7 @@ export class Control {
      * Gets or sets a value indicating the left coordinate of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get left(): string | number {
         return this._left.toString(this._host);
     }
@@ -863,6 +898,7 @@ export class Control {
      * Gets or sets a value indicating the top coordinate of the control
      * @see https://doc.babylonjs.com/how_to/gui#position-and-size
      */
+    @serialize()
     public get top(): string | number {
         return this._top.toString(this._host);
     }
@@ -892,6 +928,7 @@ export class Control {
      * Gets or sets a value indicating the offset on X axis to the linked mesh
      * @see https://doc.babylonjs.com/how_to/gui#tracking-positions
      */
+    @serialize()
     public get linkOffsetX(): string | number {
         return this._linkOffsetX.toString(this._host);
     }
@@ -921,6 +958,7 @@ export class Control {
      * Gets or sets a value indicating the offset on Y axis to the linked mesh
      * @see https://doc.babylonjs.com/how_to/gui#tracking-positions
      */
+    @serialize()
     public get linkOffsetY(): string | number {
         return this._linkOffsetY.toString(this._host);
     }
@@ -956,7 +994,8 @@ export class Control {
         return this._currentMeasure.top + this._currentMeasure.height / 2;
     }
 
-    /** Gets or sets if control is Enabled*/
+    /** Gets or sets if control is Enabled */
+    @serialize()
     public get isEnabled(): boolean {
         return this._isEnabled;
     }
@@ -969,7 +1008,8 @@ export class Control {
         this._isEnabled = value;
         this._markAsDirty();
     }
-    /** Gets or sets background color of control if it's disabled*/
+    /** Gets or sets background color of control if it's disabled */
+    @serialize()
     public get disabledColor(): string {
         return this._disabledColor;
     }
@@ -982,7 +1022,8 @@ export class Control {
         this._disabledColor = value;
         this._markAsDirty();
     }
-    /** Gets or sets front color of control if it's disabled*/
+    /** Gets or sets front color of control if it's disabled */
+    @serialize()
     public get disabledColorItem(): string {
         return this._disabledColorItem;
     }
@@ -1972,6 +2013,42 @@ export class Control {
         this._fontOffset = Control._GetFontOffset(this._font);
     }
 
+    /**
+     * Serializes the current control
+     * @param serializationObject defined the JSON serialized object
+     */
+    public serialize(serializationObject: any) {
+        SerializationHelper.Serialize(this, serializationObject);
+        serializationObject.name = this.name;
+        serializationObject.className = this.getClassName();
+
+        if (this._font) {
+            serializationObject.fontFamily = this.fontFamily;
+            serializationObject.fontSize = this.fontSize;
+            serializationObject.fontWeight = this.fontWeight;
+            serializationObject.fontStyle = this.fontStyle;
+        }
+    }
+
+    /** @hidden */
+    public _parseFromContent(serializedObject: any, host: AdvancedDynamicTexture) {
+        if (serializedObject.fontFamily) {
+            this.fontFamily = serializedObject.fontFamily;
+        }
+
+        if (serializedObject.fontSize) {
+            this.fontSize = serializedObject.fontSize;
+        }
+
+        if (serializedObject.fontWeight) {
+            this.fontWeight = serializedObject.fontWeight;
+        }
+
+        if (serializedObject.fontStyle) {
+            this.fontStyle = serializedObject.fontStyle;
+        }
+    }
+
     /** Releases associated resources */
     public dispose() {
         this.onDirtyObservable.clear();
@@ -2088,6 +2165,23 @@ export class Control {
     }
 
     /**
+     * Creates a Control from parsed data
+     * @param serializedObject defines parsed data
+     * @param host defines the hosting AdvancedDynamicTexture
+     * @returns a new Control
+     */
+    public static Parse(serializedObject: any, host: AdvancedDynamicTexture): Control {
+        let controlType = Tools.Instantiate("BABYLON.GUI." + serializedObject.className);
+        let control = SerializationHelper.Parse(() => new controlType(), serializedObject, null);
+
+        control.name = serializedObject.name;
+
+        control._parseFromContent(serializedObject, host);
+
+        return control;
+    }
+
+    /**
      * Creates a stack panel that can be used to render headers
      * @param control defines the control to associate with the header
      * @param text defines the text of the header

+ 11 - 0
gui/src/2D/controls/displayGrid.ts

@@ -2,6 +2,7 @@ import { Control } from "./control";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Nullable } from 'babylonjs/types';
 import { Measure } from '../measure';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /** Class used to render a grid  */
 export class DisplayGrid extends Control {
@@ -22,6 +23,7 @@ export class DisplayGrid extends Control {
     private _displayMinorLines = true;
 
     /** Gets or sets a boolean indicating if minor lines must be rendered (true by default)) */
+    @serialize()
     public get displayMinorLines(): boolean {
         return this._displayMinorLines;
     }
@@ -36,6 +38,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets a boolean indicating if major lines must be rendered (true by default)) */
+    @serialize()
     public get displayMajorLines(): boolean {
         return this._displayMajorLines;
     }
@@ -50,6 +53,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets background color (Black by default) */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -64,6 +68,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the width of each cell (20 by default) */
+    @serialize()
     public get cellWidth(): number {
         return this._cellWidth;
     }
@@ -75,6 +80,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the height of each cell (20 by default) */
+    @serialize()
     public get cellHeight(): number {
         return this._cellHeight;
     }
@@ -86,6 +92,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the tickness of minor lines (1 by default) */
+    @serialize()
     public get minorLineTickness(): number {
         return this._minorLineTickness;
     }
@@ -97,6 +104,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the color of minor lines (DarkGray by default) */
+    @serialize()
     public get minorLineColor(): string {
         return this._minorLineColor;
     }
@@ -108,6 +116,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the tickness of major lines (2 by default) */
+    @serialize()
     public get majorLineTickness(): number {
         return this._majorLineTickness;
     }
@@ -119,6 +128,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the color of major lines (White by default) */
+    @serialize()
     public get majorLineColor(): string {
         return this._majorLineColor;
     }
@@ -130,6 +140,7 @@ export class DisplayGrid extends Control {
     }
 
     /** Gets or sets the frequency of major lines (default is 1 every 5 minor lines)*/
+    @serialize()
     public get majorLineFrequency(): number {
         return this._majorLineFrequency;
     }

+ 2 - 0
gui/src/2D/controls/ellipse.ts

@@ -2,12 +2,14 @@ import { Container } from "./container";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /** Class used to create 2D ellipse containers */
 export class Ellipse extends Container {
     private _thickness = 1;
 
     /** Gets or sets border thickness */
+    @serialize()
     public get thickness(): number {
         return this._thickness;
     }

+ 15 - 0
gui/src/2D/controls/image.ts

@@ -5,6 +5,7 @@ import { Tools } from "babylonjs/Misc/tools";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { expandToProperty, serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Class used to create 2D images
@@ -17,6 +18,8 @@ export class Image extends Control {
     private _imageHeight: number;
     private _loaded = false;
     private _stretch = Image.STRETCH_FILL;
+    @serialize()
+    @expandToProperty("source")
     private _source: Nullable<string>;
     private _autoScale = false;
 
@@ -64,6 +67,7 @@ export class Image extends Control {
     /**
      * Gets or sets a boolean indicating if nine patch slices (left, top, right, bottom) should be read from image data
      */
+    @serialize()
     public get populateNinePatchSlicesFromImage(): boolean {
         return this._populateNinePatchSlicesFromImage;
     }
@@ -84,6 +88,7 @@ export class Image extends Control {
      * Gets or sets a boolean indicating if pointers should only be validated on pixels with alpha > 0.
      * Beware using this as this will comsume more memory as the image has to be stored twice
      */
+    @serialize()
     public get detectPointerOnOpaqueOnly(): boolean {
         return this._detectPointerOnOpaqueOnly;
     }
@@ -99,6 +104,7 @@ export class Image extends Control {
     /**
      * Gets or sets the left value for slicing (9-patch)
      */
+    @serialize()
     public get sliceLeft(): number {
         return this._sliceLeft;
     }
@@ -116,6 +122,7 @@ export class Image extends Control {
     /**
      * Gets or sets the right value for slicing (9-patch)
      */
+    @serialize()
     public get sliceRight(): number {
         return this._sliceRight;
     }
@@ -133,6 +140,7 @@ export class Image extends Control {
     /**
      * Gets or sets the top value for slicing (9-patch)
      */
+    @serialize()
     public get sliceTop(): number {
         return this._sliceTop;
     }
@@ -150,6 +158,7 @@ export class Image extends Control {
     /**
      * Gets or sets the bottom value for slicing (9-patch)
      */
+    @serialize()
     public get sliceBottom(): number {
         return this._sliceBottom;
     }
@@ -167,6 +176,7 @@ export class Image extends Control {
     /**
      * Gets or sets the left coordinate in the source image
      */
+    @serialize()
     public get sourceLeft(): number {
         return this._sourceLeft;
     }
@@ -184,6 +194,7 @@ export class Image extends Control {
     /**
      * Gets or sets the top coordinate in the source image
      */
+    @serialize()
     public get sourceTop(): number {
         return this._sourceTop;
     }
@@ -201,6 +212,7 @@ export class Image extends Control {
     /**
      * Gets or sets the width to capture in the source image
      */
+    @serialize()
     public get sourceWidth(): number {
         return this._sourceWidth;
     }
@@ -218,6 +230,7 @@ export class Image extends Control {
     /**
      * Gets or sets the height to capture in the source image
      */
+    @serialize()
     public get sourceHeight(): number {
         return this._sourceHeight;
     }
@@ -246,6 +259,7 @@ export class Image extends Control {
      * Gets or sets a boolean indicating if the image can force its container to adapt its size
      * @see https://doc.babylonjs.com/how_to/gui#image
      */
+    @serialize()
     public get autoScale(): boolean {
         return this._autoScale;
     }
@@ -263,6 +277,7 @@ export class Image extends Control {
     }
 
     /** Gets or sets the streching mode used by the image */
+    @serialize()
     public get stretch(): number {
         return this._stretch;
     }

+ 18 - 0
gui/src/2D/controls/inputText.ts

@@ -11,6 +11,7 @@ import { VirtualKeyboard } from "./virtualKeyboard";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Measure } from '../measure';
 import { TextWrapper } from './textWrapper';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Class used to create input text control
@@ -52,8 +53,10 @@ export class InputText extends Control implements IFocusableControl {
     public _connectedVirtualKeyboard: Nullable<VirtualKeyboard>;
 
     /** Gets or sets a string representing the message displayed on mobile when the control gets the focus */
+    @serialize()
     public promptMessage = "Please enter text:";
     /** Force disable prompt on mobile device */
+    @serialize()
     public disableMobilePrompt = false;
 
     /** Observable raised when the text changes */
@@ -76,6 +79,7 @@ export class InputText extends Control implements IFocusableControl {
     public onKeyboardEventProcessedObservable = new Observable<KeyboardEvent>();
 
     /** Gets or sets the maximum width allowed by the control */
+    @serialize()
     public get maxWidth(): string | number {
         return this._maxWidth.toString(this._host);
     }
@@ -96,6 +100,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the text highlighter transparency; default: 0.4 */
+    @serialize()
     public get highligherOpacity(): number {
         return this._highligherOpacity;
     }
@@ -108,6 +113,7 @@ export class InputText extends Control implements IFocusableControl {
         this._markAsDirty();
     }
     /** Gets or sets a boolean indicating whether to select complete text by default on input focus */
+    @serialize()
     public get onFocusSelectAll(): boolean {
         return this._onFocusSelectAll;
     }
@@ -122,6 +128,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the text hightlight color */
+    @serialize()
     public get textHighlightColor(): string {
         return this._textHighlightColor;
     }
@@ -135,6 +142,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets control margin */
+    @serialize()
     public get margin(): string {
         return this._margin.toString(this._host);
     }
@@ -155,6 +163,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets a boolean indicating if the control can auto stretch its width to adapt to the text */
+    @serialize()
     public get autoStretchWidth(): boolean {
         return this._autoStretchWidth;
     }
@@ -169,6 +178,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets border thickness */
+    @serialize()
     public get thickness(): number {
         return this._thickness;
     }
@@ -183,6 +193,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the background color when focused */
+    @serialize()
     public get focusedBackground(): string {
         return this._focusedBackground;
     }
@@ -197,6 +208,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the background color when focused */
+    @serialize()
     public get focusedColor(): string {
         return this._focusedColor;
     }
@@ -211,6 +223,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -225,6 +238,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the placeholder color */
+    @serialize()
     public get placeholderColor(): string {
         return this._placeholderColor;
     }
@@ -239,6 +253,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the text displayed when the control is empty */
+    @serialize()
     public get placeholderText(): string {
         return this._placeholderText;
     }
@@ -252,6 +267,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the dead key flag */
+    @serialize()
     public get deadKey(): boolean {
         return this._deadKey;
     }
@@ -291,6 +307,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets the text displayed in the control */
+    @serialize()
     public get text(): string {
         return this._textWrapper.text;
     }
@@ -315,6 +332,7 @@ export class InputText extends Control implements IFocusableControl {
     }
 
     /** Gets or sets control width */
+    @serialize()
     public get width(): string | number {
         return this._width.toString(this._host);
     }

+ 7 - 0
gui/src/2D/controls/line.ts

@@ -8,6 +8,7 @@ import { Control } from "./control";
 import { ValueAndUnit } from "../valueAndUnit";
 import { Measure } from "../measure";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /** Class used to render 2D lines */
 export class Line extends Control {
@@ -21,6 +22,7 @@ export class Line extends Control {
     private _connectedControlDirtyObserver: Nullable<Observer<Control>>;
 
     /** Gets or sets the dash pattern */
+    @serialize()
     public get dash(): Array<number> {
         return this._dash;
     }
@@ -58,6 +60,7 @@ export class Line extends Control {
     }
 
     /** Gets or sets start coordinates on X axis */
+    @serialize()
     public get x1(): string | number {
         return this._x1.toString(this._host);
     }
@@ -73,6 +76,7 @@ export class Line extends Control {
     }
 
     /** Gets or sets start coordinates on Y axis */
+    @serialize()
     public get y1(): string | number {
         return this._y1.toString(this._host);
     }
@@ -88,6 +92,7 @@ export class Line extends Control {
     }
 
     /** Gets or sets end coordinates on X axis */
+    @serialize()
     public get x2(): string | number {
         return this._x2.toString(this._host);
     }
@@ -103,6 +108,7 @@ export class Line extends Control {
     }
 
     /** Gets or sets end coordinates on Y axis */
+    @serialize()
     public get y2(): string | number {
         return this._y2.toString(this._host);
     }
@@ -118,6 +124,7 @@ export class Line extends Control {
     }
 
     /** Gets or sets line width */
+    @serialize()
     public get lineWidth(): number {
         return this._lineWidth;
     }

+ 2 - 0
gui/src/2D/controls/multiLine.ts

@@ -6,6 +6,7 @@ import { MultiLinePoint } from "../multiLinePoint";
 import { Measure } from "../measure";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Vector3 } from "babylonjs/Maths/math.vector";
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Class used to create multi line control
@@ -38,6 +39,7 @@ export class MultiLine extends Control {
     }
 
     /** Gets or sets dash pattern */
+    @serialize()
     public get dash(): Array<number> {
         return this._dash;
     }

+ 6 - 0
gui/src/2D/controls/radioButton.ts

@@ -6,6 +6,7 @@ import { StackPanel } from "./stackPanel";
 import { TextBlock } from "./textBlock";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Class used to create radio button controls
@@ -17,6 +18,7 @@ export class RadioButton extends Control {
     private _thickness = 1;
 
     /** Gets or sets border thickness */
+    @serialize()
     public get thickness(): number {
         return this._thickness;
     }
@@ -31,12 +33,14 @@ export class RadioButton extends Control {
     }
 
     /** Gets or sets group name */
+    @serialize()
     public group = "";
 
     /** Observable raised when isChecked is changed */
     public onIsCheckedChangedObservable = new Observable<boolean>();
 
     /** Gets or sets a value indicating the ratio between overall size and check size */
+    @serialize()
     public get checkSizeRatio(): number {
         return this._checkSizeRatio;
     }
@@ -53,6 +57,7 @@ export class RadioButton extends Control {
     }
 
     /** Gets or sets background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -67,6 +72,7 @@ export class RadioButton extends Control {
     }
 
     /** Gets or sets a boolean indicating if the checkbox is checked or not */
+    @serialize()
     public get isChecked(): boolean {
         return this._isChecked;
     }

+ 3 - 0
gui/src/2D/controls/rectangle.ts

@@ -1,6 +1,7 @@
 import { Container } from "./container";
 import { Measure } from "../measure";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /** Class used to create rectangle container */
 export class Rectangle extends Container {
@@ -8,6 +9,7 @@ export class Rectangle extends Container {
     private _cornerRadius = 0;
 
     /** Gets or sets border thickness */
+    @serialize()
     public get thickness(): number {
         return this._thickness;
     }
@@ -22,6 +24,7 @@ export class Rectangle extends Container {
     }
 
     /** Gets or sets the corner radius angle */
+    @serialize()
     public get cornerRadius(): number {
         return this._cornerRadius;
     }

+ 10 - 0
gui/src/2D/controls/sliders/baseSlider.ts

@@ -4,6 +4,7 @@ import { Vector2 } from "babylonjs/Maths/math.vector";
 import { Control } from "../control";
 import { ValueAndUnit } from "../../valueAndUnit";
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from "babylonjs/Misc/decorators";
 
 /**
  * Class used to create slider controls
@@ -35,6 +36,7 @@ export class BaseSlider extends Control {
     public onValueChangedObservable = new Observable<number>();
 
     /** Gets or sets a boolean indicating if the thumb must be rendered */
+    @serialize()
     public get displayThumb(): boolean {
         return this._displayThumb;
     }
@@ -49,6 +51,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets a step to apply to values (0 by default) */
+    @serialize()
     public get step(): number {
         return this._step;
     }
@@ -63,6 +66,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
+    @serialize()
     public get barOffset(): string | number {
         return this._barOffset.toString(this._host);
     }
@@ -83,6 +87,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets thumb width */
+    @serialize()
     public get thumbWidth(): string | number {
         return this._thumbWidth.toString(this._host);
     }
@@ -103,6 +108,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets minimum value */
+    @serialize()
     public get minimum(): number {
         return this._minimum;
     }
@@ -119,6 +125,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets maximum value */
+    @serialize()
     public get maximum(): number {
         return this._maximum;
     }
@@ -135,6 +142,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets current value */
+    @serialize()
     public get value(): number {
         return this._value;
     }
@@ -152,6 +160,7 @@ export class BaseSlider extends Control {
     }
 
     /**Gets or sets a boolean indicating if the slider should be vertical or horizontal */
+    @serialize()
     public get isVertical(): boolean {
         return this._isVertical;
     }
@@ -166,6 +175,7 @@ export class BaseSlider extends Control {
     }
 
     /** Gets or sets a value indicating if the thumb can go over main bar extends */
+    @serialize()
     public get isThumbClamped(): boolean {
         return this._isThumbClamped;
     }

+ 2 - 0
gui/src/2D/controls/sliders/imageBasedSlider.ts

@@ -3,6 +3,7 @@ import { Measure } from "../../measure";
 import { Image } from "../image";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Nullable } from 'babylonjs/types';
+import { serialize } from "babylonjs/Misc/decorators";
 
 /**
  * Class used to create slider controls based on images
@@ -14,6 +15,7 @@ export class ImageBasedSlider extends BaseSlider {
 
     private _tempMeasure = new Measure(0, 0, 0, 0);
 
+    @serialize()
     public get displayThumb(): boolean {
         return this._displayThumb && this.thumbImage != null;
     }

+ 2 - 0
gui/src/2D/controls/sliders/imageScrollBar.ts

@@ -4,6 +4,7 @@ import { Control } from "../control";
 import { Image } from "../image";
 import { Measure } from "../../measure";
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from "babylonjs/Misc/decorators";
 
 /**
  * Class used to create slider controls
@@ -19,6 +20,7 @@ export class ImageScrollBar extends BaseSlider {
     private _tempMeasure = new Measure(0, 0, 0, 0);
 
     /** Number of 90° rotation to apply on the images when in vertical mode */
+    @serialize()
     public num90RotationInVerticalMode = 1;
 
     /**

+ 3 - 0
gui/src/2D/controls/sliders/scrollBar.ts

@@ -3,6 +3,7 @@ import { BaseSlider } from "./baseSlider";
 import { Control } from "../control";
 import { Measure } from "../../measure";
 import { PointerInfoBase } from 'babylonjs/Events/pointerEvents';
+import { serialize } from "babylonjs/Misc/decorators";
 
 /**
  * Class used to create slider controls
@@ -13,6 +14,7 @@ export class ScrollBar extends BaseSlider {
     private _tempMeasure = new Measure(0, 0, 0, 0);
 
     /** Gets or sets border color */
+    @serialize()
     public get borderColor(): string {
         return this._borderColor;
     }
@@ -27,6 +29,7 @@ export class ScrollBar extends BaseSlider {
     }
 
     /** Gets or sets background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }

+ 6 - 0
gui/src/2D/controls/sliders/slider.ts

@@ -2,6 +2,7 @@ import { BaseSlider } from "./baseSlider";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
 import { Nullable } from 'babylonjs/types';
 import { Measure } from '../../measure';
+import { serialize } from "babylonjs/Misc/decorators";
 
 /**
  * Class used to create slider controls
@@ -14,6 +15,7 @@ export class Slider extends BaseSlider {
     protected _displayValueBar = true;
 
     /** Gets or sets a boolean indicating if the value bar must be rendered */
+    @serialize()
     public get displayValueBar(): boolean {
         return this._displayValueBar;
     }
@@ -28,6 +30,7 @@ export class Slider extends BaseSlider {
     }
 
     /** Gets or sets border color */
+    @serialize()
     public get borderColor(): string {
         return this._borderColor;
     }
@@ -42,6 +45,7 @@ export class Slider extends BaseSlider {
     }
 
     /** Gets or sets background color */
+    @serialize()
     public get background(): string {
         return this._background;
     }
@@ -56,6 +60,7 @@ export class Slider extends BaseSlider {
     }
 
     /** Gets or sets thumb's color */
+    @serialize()
     public get thumbColor(): string {
         return this._thumbColor;
     }
@@ -70,6 +75,7 @@ export class Slider extends BaseSlider {
     }
 
     /** Gets or sets a boolean indicating if the thumb should be round or square */
+    @serialize()
     public get isThumbCircle(): boolean {
         return this._isThumbCircle;
     }

+ 25 - 0
gui/src/2D/controls/stackPanel.ts

@@ -4,6 +4,8 @@ import { Container } from "./container";
 import { Measure } from "../measure";
 import { Control } from "./control";
 import { _TypeStore } from 'babylonjs/Misc/typeStore';
+import { serialize } from 'babylonjs/Misc/decorators';
+import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
 
 /**
  * Class used to create a 2D stack panel container
@@ -17,9 +19,11 @@ export class StackPanel extends Container {
     /**
      * Gets or sets a boolean indicating that layou warnings should be ignored
      */
+    @serialize()
     public ignoreLayoutWarnings = false;
 
     /** Gets or sets a boolean indicating if the stack panel is vertical or horizontal*/
+    @serialize()
     public get isVertical(): boolean {
         return this._isVertical;
     }
@@ -37,6 +41,7 @@ export class StackPanel extends Container {
      * Gets or sets panel width.
      * This value should not be set when in horizontal mode as it will be computed automatically
      */
+    @serialize()
     public set width(value: string | number) {
         if (!this._doNotTrackManualChanges) {
             this._manualWidth = true;
@@ -59,6 +64,7 @@ export class StackPanel extends Container {
      * Gets or sets panel height.
      * This value should not be set when in vertical mode as it will be computed automatically
      */
+    @serialize()
     public set height(value: string | number) {
         if (!this._doNotTrackManualChanges) {
             this._manualHeight = true;
@@ -196,5 +202,24 @@ export class StackPanel extends Container {
 
         super._postMeasure();
     }
+
+    /**
+     * Serializes the current control
+     * @param serializationObject defined the JSON serialized object
+     */
+    public serialize(serializationObject: any) {
+        super.serialize(serializationObject);
+        serializationObject.manualWidth = this._manualWidth;
+        serializationObject.manualHeight = this._manualHeight;
+    }
+
+    /** @hidden */
+    public _parseFromContent(serializedObject: any, host: AdvancedDynamicTexture) {
+        this._manualWidth = serializedObject.manualWidth;
+        this._manualHeight = serializedObject.manualHeight;
+
+        super._parseFromContent(serializedObject, host);
+    }
+
 }
 _TypeStore.RegisteredTypes["BABYLON.GUI.StackPanel"] = StackPanel;

+ 12 - 0
gui/src/2D/controls/textBlock.ts

@@ -4,6 +4,7 @@ import { ValueAndUnit } from "../valueAndUnit";
 import { Control } from "./control";
 import { _TypeStore } from "babylonjs/Misc/typeStore";
 import { Nullable } from "babylonjs/types";
+import { serialize } from 'babylonjs/Misc/decorators';
 
 /**
  * Enum that determines the text-wrapping mode to use.
@@ -66,6 +67,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets an boolean indicating that the TextBlock will be resized to fit container
      */
+    @serialize()
     public get resizeToFit(): boolean {
         return this._resizeToFit;
     }
@@ -90,6 +92,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets a boolean indicating if text must be wrapped
      */
+    @serialize()
     public get textWrapping(): TextWrapping | boolean {
         return this._textWrapping;
     }
@@ -108,6 +111,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets text to display
      */
+    @serialize()
     public get text(): string {
         return this._text;
     }
@@ -128,6 +132,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets text horizontal alignment (BABYLON.GUI.Control.HORIZONTAL_ALIGNMENT_CENTER by default)
      */
+    @serialize()
     public get textHorizontalAlignment(): number {
         return this._textHorizontalAlignment;
     }
@@ -147,6 +152,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets text vertical alignment (BABYLON.GUI.Control.VERTICAL_ALIGNMENT_CENTER by default)
      */
+    @serialize()
     public get textVerticalAlignment(): number {
         return this._textVerticalAlignment;
     }
@@ -166,6 +172,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets line spacing value
      */
+    @serialize()
     public set lineSpacing(value: string | number) {
         if (this._lineSpacing.fromString(value)) {
             this._markAsDirty();
@@ -182,6 +189,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets outlineWidth of the text to display
      */
+    @serialize()
     public get outlineWidth(): number {
         return this._outlineWidth;
     }
@@ -200,6 +208,8 @@ export class TextBlock extends Control {
     /**
      * Gets or sets a boolean indicating that text must have underline
      */
+    @serialize()
+
     public get underline(): boolean {
         return this._underline;
     }
@@ -218,6 +228,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets an boolean indicating that text must be crossed out
      */
+    @serialize()
     public get lineThrough(): boolean {
         return this._lineThrough;
     }
@@ -236,6 +247,7 @@ export class TextBlock extends Control {
     /**
      * Gets or sets outlineColor of the text to display
      */
+    @serialize()
     public get outlineColor(): string {
         return this._outlineColor;
     }