Explorar o código

Merge pull request #6854 from Null0924/master

added xml loader
David Catuhe %!s(int64=6) %!d(string=hai) anos
pai
achega
76b359dee7

+ 2 - 0
dist/preview release/what's new.md

@@ -104,6 +104,7 @@
 - Added `Ray.intersectsAxis` to translate screen to axis coordinates without checking collisions ([horusscope](https://github.com/horusscope))
 - Added `Ray.intersectsAxis` to translate screen to axis coordinates without checking collisions ([horusscope](https://github.com/horusscope))
 
 
 ### GUI
 ### GUI
+- Added `xmlLoader` to load GUI layouts from XML ([null0924](https://github.com/null0924))
 - Added `disableMobilePrompt` option to InputText for OculusQuest(and other android base VR devices) ([shinyoshiaki](https://github.com/shinyoshiaki))
 - Added `disableMobilePrompt` option to InputText for OculusQuest(and other android base VR devices) ([shinyoshiaki](https://github.com/shinyoshiaki))
 - Added `Button.delegatePickingToChildren` to let buttons delegate hit testing to embedded controls ([Deltakosh](https://github.com/deltakosh/))
 - Added `Button.delegatePickingToChildren` to let buttons delegate hit testing to embedded controls ([Deltakosh](https://github.com/deltakosh/))
 - Added `Container.maxLayoutCycle` and `Container.logLayoutCycleErrors` to get more control over layout cycles ([Deltakosh](https://github.com/deltakosh/))
 - Added `Container.maxLayoutCycle` and `Container.logLayoutCycleErrors` to get more control over layout cycles ([Deltakosh](https://github.com/deltakosh/))
@@ -111,6 +112,7 @@
 - Added `_getSVGAttribs` functionality for loading multiple svg icons from an external svg file via icon id.([lockphase](https://github.com/lockphase/))
 - Added `_getSVGAttribs` functionality for loading multiple svg icons from an external svg file via icon id.([lockphase](https://github.com/lockphase/))
 
 
 
 
+
 ### Navigation Mesh
 ### Navigation Mesh
 - Added moveAlong function to cast a segment on mavmesh ([CedricGuillemet](https://github.com/CedricGuillemet/))
 - Added moveAlong function to cast a segment on mavmesh ([CedricGuillemet](https://github.com/CedricGuillemet/))
 
 

+ 3 - 1
gui/src/2D/controls/button.ts

@@ -5,6 +5,7 @@ import { Rectangle } from "./rectangle";
 import { Control } from "./control";
 import { Control } from "./control";
 import { TextBlock } from "./textBlock";
 import { TextBlock } from "./textBlock";
 import { Image } from "./image";
 import { Image } from "./image";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create 2D buttons
  * Class used to create 2D buttons
@@ -262,4 +263,5 @@ export class Button extends Rectangle {
 
 
         return result;
         return result;
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Button"] = Button;

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

@@ -4,6 +4,7 @@ import { Vector2 } from "babylonjs/Maths/math";
 import { Control } from "./control";
 import { Control } from "./control";
 import { StackPanel } from "./stackPanel";
 import { StackPanel } from "./stackPanel";
 import { TextBlock } from "./textBlock";
 import { TextBlock } from "./textBlock";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to represent a 2D checkbox
  * Class used to represent a 2D checkbox
@@ -175,3 +176,4 @@ export class Checkbox extends Control {
         return panel;
         return panel;
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.Checkbox"] = Checkbox;

+ 3 - 1
gui/src/2D/controls/colorpicker.ts

@@ -9,6 +9,7 @@ import { Button } from "./button";
 import { Grid } from "./grid";
 import { Grid } from "./grid";
 import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
 import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
 import { TextBlock } from "../controls/textBlock";
 import { TextBlock } from "../controls/textBlock";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /** Class used to create color pickers */
 /** Class used to create color pickers */
 export class ColorPicker extends Control {
 export class ColorPicker extends Control {
@@ -1512,4 +1513,5 @@ export class ColorPicker extends Control {
             }
             }
         });
         });
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.ColorPicker"] = ColorPicker;

+ 3 - 1
gui/src/2D/controls/container.ts

@@ -4,6 +4,7 @@ import { Logger } from "babylonjs/Misc/logger";
 import { Control } from "./control";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
 import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
 import { AdvancedDynamicTexture } from "../advancedDynamicTexture";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Root class for 2D containers
  * Root class for 2D containers
@@ -456,4 +457,5 @@ export class Container extends Control {
             this.children[index].dispose();
             this.children[index].dispose();
         }
         }
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Container"] = Container;

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

@@ -13,6 +13,7 @@ import { ValueAndUnit } from "../valueAndUnit";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
 import { Style } from "../style";
 import { Style } from "../style";
 import { Matrix2D, Vector2WithInfo } from "../math2D";
 import { Matrix2D, Vector2WithInfo } from "../math2D";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Root class used for all 2D controls
  * Root class used for all 2D controls
@@ -1967,3 +1968,4 @@ export class Control {
         context.translate(-x, -y);
         context.translate(-x, -y);
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.Control"] = Control;

+ 3 - 1
gui/src/2D/controls/displayGrid.ts

@@ -1,4 +1,5 @@
 import { Control } from "./control";
 import { Control } from "./control";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /** Class used to render a grid  */
 /** Class used to render a grid  */
 export class DisplayGrid extends Control {
 export class DisplayGrid extends Control {
@@ -218,4 +219,5 @@ export class DisplayGrid extends Control {
     protected _getTypeName(): string {
     protected _getTypeName(): string {
         return "DisplayGrid";
         return "DisplayGrid";
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.DisplayGrid"] = DisplayGrid;

+ 3 - 1
gui/src/2D/controls/ellipse.ts

@@ -1,6 +1,7 @@
 import { Container } from "./container";
 import { Container } from "./container";
 import { Control } from "./control";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /** Class used to create 2D ellipse containers */
 /** Class used to create 2D ellipse containers */
 export class Ellipse extends Container {
 export class Ellipse extends Container {
@@ -84,4 +85,5 @@ export class Ellipse extends Container {
 
 
         context.clip();
         context.clip();
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Ellipse"] = Ellipse;

+ 3 - 1
gui/src/2D/controls/grid.ts

@@ -5,6 +5,7 @@ import { ValueAndUnit } from "../valueAndUnit";
 import { Control } from "./control";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
 import { Tools } from 'babylonjs/Misc/tools';
 import { Tools } from 'babylonjs/Misc/tools';
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create a 2D grid container
  * Class used to create a 2D grid container
@@ -488,4 +489,5 @@ export class Grid extends Container {
 
 
         this._childControls = [];
         this._childControls = [];
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Grid"] = Grid;

+ 3 - 1
gui/src/2D/controls/image.ts

@@ -4,6 +4,7 @@ import { Tools } from "babylonjs/Misc/tools";
 
 
 import { Control } from "./control";
 import { Control } from "./control";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create 2D images
  * Class used to create 2D images
@@ -749,4 +750,5 @@ export class Image extends Control {
     public static readonly STRETCH_EXTEND = 3;
     public static readonly STRETCH_EXTEND = 3;
     /** NINE_PATCH */
     /** NINE_PATCH */
     public static readonly STRETCH_NINE_PATCH = 4;
     public static readonly STRETCH_NINE_PATCH = 4;
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Image"] = Image;

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

@@ -1,4 +1,5 @@
 import { InputText } from "./inputText";
 import { InputText } from "./inputText";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create a password control
  * Class used to create a password control
@@ -12,3 +13,4 @@ export class InputPassword extends InputText {
         return txt;
         return txt;
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.InputPassword"] = InputPassword;

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

@@ -8,6 +8,7 @@ import { Control } from "./control";
 import { IFocusableControl } from "../advancedDynamicTexture";
 import { IFocusableControl } from "../advancedDynamicTexture";
 import { ValueAndUnit } from "../valueAndUnit";
 import { ValueAndUnit } from "../valueAndUnit";
 import { VirtualKeyboard } from "./virtualKeyboard";
 import { VirtualKeyboard } from "./virtualKeyboard";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create input text control
  * Class used to create input text control
@@ -1028,3 +1029,4 @@ export class InputText extends Control implements IFocusableControl {
         this.onKeyboardEventProcessedObservable.clear();
         this.onKeyboardEventProcessedObservable.clear();
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.InputText"] = InputText;

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

@@ -7,6 +7,7 @@ import { Scene } from "babylonjs/scene";
 import { Control } from "./control";
 import { Control } from "./control";
 import { ValueAndUnit } from "../valueAndUnit";
 import { ValueAndUnit } from "../valueAndUnit";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /** Class used to render 2D lines */
 /** Class used to render 2D lines */
 export class Line extends Control {
 export class Line extends Control {
@@ -245,3 +246,4 @@ export class Line extends Control {
         }
         }
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.Line"] = Line;

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

@@ -4,6 +4,7 @@ import { AbstractMesh } from "babylonjs/Meshes/abstractMesh";
 import { Control } from "./control";
 import { Control } from "./control";
 import { MultiLinePoint } from "../multiLinePoint";
 import { MultiLinePoint } from "../multiLinePoint";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create multi line control
  * Class used to create multi line control
@@ -260,5 +261,5 @@ export class MultiLine extends Control {
 
 
         super.dispose();
         super.dispose();
     }
     }
-
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.MultiLine"] = MultiLine;

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

@@ -4,6 +4,7 @@ import { Vector2 } from "babylonjs/Maths/math";
 import { Control } from "./control";
 import { Control } from "./control";
 import { StackPanel } from "./stackPanel";
 import { StackPanel } from "./stackPanel";
 import { TextBlock } from "./textBlock";
 import { TextBlock } from "./textBlock";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create radio button controls
  * Class used to create radio button controls
@@ -203,3 +204,4 @@ export class RadioButton extends Control {
         return panel;
         return panel;
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.RadioButton"] = RadioButton;

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

@@ -1,5 +1,6 @@
 import { Container } from "./container";
 import { Container } from "./container";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /** Class used to create rectangle container */
 /** Class used to create rectangle container */
 export class Rectangle extends Container {
 export class Rectangle extends Container {
@@ -132,4 +133,5 @@ export class Rectangle extends Container {
             context.clip();
             context.clip();
         }
         }
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.Rectangle"] = Rectangle;

+ 3 - 1
gui/src/2D/controls/scrollViewers/scrollViewer.ts

@@ -10,6 +10,7 @@ import { Measure } from "../../measure";
 import { AdvancedDynamicTexture } from "../../advancedDynamicTexture";
 import { AdvancedDynamicTexture } from "../../advancedDynamicTexture";
 import { _ScrollViewerWindow } from "./scrollViewerWindow";
 import { _ScrollViewerWindow } from "./scrollViewerWindow";
 import { ScrollBar } from "../sliders/scrollBar";
 import { ScrollBar } from "../sliders/scrollBar";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to hold a viewer window and sliders in a grid
  * Class used to hold a viewer window and sliders in a grid
@@ -387,4 +388,5 @@ export class ScrollViewer extends Rectangle {
         }
         }
         super.dispose();
         super.dispose();
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.ScrollViewer"] = ScrollViewer;

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

@@ -1,6 +1,7 @@
 import { BaseSlider } from "./baseSlider";
 import { BaseSlider } from "./baseSlider";
 import { Measure } from "../../measure";
 import { Measure } from "../../measure";
 import { Image } from "../image";
 import { Image } from "../image";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create slider controls based on images
  * Class used to create slider controls based on images
@@ -160,4 +161,5 @@ export class ImageBasedSlider extends BaseSlider {
 
 
         context.restore();
         context.restore();
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.ImageBasedSlider"] = ImageBasedSlider;

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

@@ -1,4 +1,5 @@
 import { BaseSlider } from "./baseSlider";
 import { BaseSlider } from "./baseSlider";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create slider controls
  * Class used to create slider controls
@@ -238,3 +239,4 @@ export class Slider extends BaseSlider {
         context.restore();
         context.restore();
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.Slider"] = Slider;

+ 3 - 1
gui/src/2D/controls/stackPanel.ts

@@ -3,6 +3,7 @@ import { Tools } from "babylonjs/Misc/tools";
 import { Container } from "./container";
 import { Container } from "./container";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
 import { Control } from "./control";
 import { Control } from "./control";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to create a 2D stack panel container
  * Class used to create a 2D stack panel container
@@ -192,4 +193,5 @@ export class StackPanel extends Container {
 
 
         super._postMeasure();
         super._postMeasure();
     }
     }
-}
+}
+_TypeStore.RegisteredTypes["BABYLON.GUI.StackPanel"] = StackPanel;

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

@@ -2,6 +2,7 @@ import { Observable } from "babylonjs/Misc/observable";
 import { Measure } from "../measure";
 import { Measure } from "../measure";
 import { ValueAndUnit } from "../valueAndUnit";
 import { ValueAndUnit } from "../valueAndUnit";
 import { Control } from "./control";
 import { Control } from "./control";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Enum that determines the text-wrapping mode to use.
  * Enum that determines the text-wrapping mode to use.
@@ -462,3 +463,4 @@ export class TextBlock extends Control {
         this.onTextChangedObservable.clear();
         this.onTextChangedObservable.clear();
     }
     }
 }
 }
+_TypeStore.RegisteredTypes["BABYLON.GUI.TextBlock"] = TextBlock;

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

@@ -6,6 +6,7 @@ import { Button } from "./button";
 import { Container } from "./container";
 import { Container } from "./container";
 import { TextBlock } from "./textBlock";
 import { TextBlock } from "./textBlock";
 import { InputText } from "./inputText";
 import { InputText } from "./inputText";
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
 
 
 /**
 /**
  * Class used to store key control properties
  * Class used to store key control properties
@@ -308,3 +309,5 @@ export class VirtualKeyboard extends StackPanel {
         return returnValue;
         return returnValue;
     }
     }
 }
 }
+
+_TypeStore.RegisteredTypes["BABYLON.GUI.VirtualKeyboard"] = VirtualKeyboard;

+ 2 - 1
gui/src/2D/index.ts

@@ -6,4 +6,5 @@ export * from "./math2D";
 export * from "./measure";
 export * from "./measure";
 export * from "./multiLinePoint";
 export * from "./multiLinePoint";
 export * from "./style";
 export * from "./style";
-export * from "./valueAndUnit";
+export * from "./valueAndUnit";
+export * from "./xmlLoader";

+ 330 - 0
gui/src/2D/xmlLoader.ts

@@ -0,0 +1,330 @@
+import { _TypeStore } from 'babylonjs/Misc/typeStore';
+
+/**
+* Class used to load GUI via XML.
+*/
+export class XmlLoader {
+    private _nodes: any = {};
+
+    private _nodeTypes: any = {
+        element: 1,
+        attribute: 2,
+        text: 3
+    };
+
+    private _isLoaded: boolean = false;
+
+    private _objectAttributes: any = {
+        "textHorizontalAlignment": 1,
+        "textVerticalAlignment": 2,
+        "horizontalAlignment": 3,
+        "verticalAlignment": 4,
+        "stretch": 5,
+    };
+
+    private _parentClass: any;
+
+     /**
+     * Create a new xml loader
+     * @param parentClass Sets the class context. Used when the loader is instanced inside a class and not in a global context
+     */
+    constructor(parentClass = null) {
+        if (parentClass) {
+            this._parentClass = parentClass;
+        }
+    }
+
+    private _getChainElement(attributeValue: any): any {
+        let element = window;
+
+        if (this._parentClass) {
+            element = this._parentClass;
+        }
+        let value = attributeValue;
+        value = value.split(".");
+
+        for (let i = 0; i < value.length; i++) {
+            element = element[value[i]];
+        }
+        return element;
+
+    }
+
+    private _getClassAttribute(attributeName : string) : any {
+        const attribute = attributeName.split(".");
+        const className = _TypeStore.GetClass("BABYLON.GUI." + attribute[0]);
+        return className[attribute[1]];
+    }
+
+    private _createGuiElement(node: any, parent: any, linkParent: boolean = true): void {
+        try {
+            let className = _TypeStore.GetClass("BABYLON.GUI." + node.nodeName);
+            let guiNode = new className();
+
+            if (parent && linkParent) {
+                parent.addControl(guiNode);
+            }
+
+            for (let i = 0; i < node.attributes.length; i++) {
+
+                if (node.attributes[i].name.toLowerCase().includes("datasource")) {
+                    continue;
+                }
+
+                if (node.attributes[i].name.toLowerCase().includes("observable")) {
+
+                    let element = this._getChainElement(node.attributes[i].value);
+                    guiNode[node.attributes[i].name].add(element);
+
+                    continue;
+                } else if (node.attributes[i].name == "linkWithMesh") {
+                    if (this._parentClass) {
+                        guiNode.linkWithMesh(this._parentClass[node.attributes[i].value]);
+                    } else {
+                        guiNode.linkWithMesh(window[node.attributes[i].value]);
+                    }
+                } else if (node.attributes[i].value.startsWith("{{") && node.attributes[i].value.endsWith("}}")) {
+                    let element = this._getChainElement(node.attributes[i].value.substring(2, node.attributes[i].value.length - 2));
+                    guiNode[node.attributes[i].name] = element;
+                } else if (!this._objectAttributes[node.attributes[i].name]) {
+                    if (node.attributes[i].value == "true" || node.attributes[i].value == "false") {
+                        guiNode[node.attributes[i].name] = (node.attributes[i].value == 'true');
+                    } else {
+                        guiNode[node.attributes[i].name] = !isNaN(Number(node.attributes[i].value)) ? Number(node.attributes[i].value) : node.attributes[i].value;
+                    }
+                } else {
+                    guiNode[node.attributes[i].name] = this._getClassAttribute(node.attributes[i].value);
+                }
+            }
+
+            if (!node.attributes.getNamedItem("id")) {
+                this._nodes[node.nodeName + Object.keys(this._nodes).length + "_gen"] = guiNode;
+                return guiNode;
+            }
+
+            if (!this._nodes[node.attributes.getNamedItem("id").nodeValue]) {
+                this._nodes[node.attributes.getNamedItem("id").nodeValue] = guiNode;
+            } else {
+                throw "XmlLoader Exception : Duplicate ID, every element should have an unique ID attribute";
+            }
+            return guiNode;
+
+        } catch (e) {
+            throw "XmlLoader Exception : Error parsing Control " + node.nodeName + "," + e + ".";
+        }
+    }
+
+    private _parseGrid(node: any, guiNode: any, parent: any): void {
+        let width;
+        let height;
+        let columns;
+        let rows = node.children;
+        let cells;
+        let isPixel = false;
+        let cellNode;
+        let rowNumber = -1;
+        let columnNumber = -1;
+        let totalColumnsNumber = 0;
+
+        for (let i = 0; i < rows.length; i++) {
+            if (rows[i].nodeType != this._nodeTypes.element) {
+                continue;
+            }
+            if (rows[i].nodeName != "Row") {
+                throw "XmlLoader Exception : Expecting Row node, received " + rows[i].nodeName;
+            }
+            rowNumber += 1;
+            columns = rows[i].children;
+
+            if (!rows[i].attributes.getNamedItem("height")) {
+                throw "XmlLoader Exception : Height must be defined for grid rows";
+            }
+            height = Number(rows[i].attributes.getNamedItem("height").nodeValue);
+            isPixel = rows[i].attributes.getNamedItem("isPixel") ? JSON.parse(rows[i].attributes.getNamedItem("isPixel").nodeValue) : false;
+            guiNode.addRowDefinition(height, isPixel);
+
+            for (let j = 0; j < columns.length; j++) {
+                if (columns[j].nodeType != this._nodeTypes.element) {
+                    continue;
+                }
+                if (columns[j].nodeName != "Column") {
+                    throw "XmlLoader Exception : Expecting Column node, received " + columns[j].nodeName;
+                }
+                columnNumber += 1;
+                if (rowNumber > 0 && columnNumber > totalColumnsNumber) {
+                    throw "XmlLoader Exception : In the Grid element, the number of columns is defined in the first row, do not add more columns in the subsequent rows.";
+                }
+
+                if (rowNumber == 0) {
+                    if (!columns[j].attributes.getNamedItem("width")) {
+                        throw "XmlLoader Exception : Width must be defined for all the grid columns in the first row";
+                    }
+                    width = Number(columns[j].attributes.getNamedItem("width").nodeValue);
+                    isPixel = columns[j].attributes.getNamedItem("isPixel") ? JSON.parse(columns[j].attributes.getNamedItem("isPixel").nodeValue) : false;
+                    guiNode.addColumnDefinition(width, isPixel);
+                }
+
+                cells = columns[j].children;
+
+                for (let k = 0; k < cells.length; k++) {
+                    if (cells[k].nodeType != this._nodeTypes.element) {
+                        continue;
+                    }
+                    cellNode = this._createGuiElement(cells[k], guiNode, false);
+                    guiNode.addControl(cellNode, rowNumber, columnNumber);
+                    if (cells[k].firstChild) {
+                        this._parseXml(cells[k].firstChild, cellNode);
+                    }
+                }
+            }
+            if (rowNumber == 0) {
+                totalColumnsNumber = columnNumber;
+            }
+            columnNumber = -1;
+        }
+
+        if (node.nextSibling) {
+            this._parseXml(node.nextSibling, parent);
+        }
+    }
+
+    private _parseElement(node: any, guiNode: any, parent: any): void {
+
+        if (node.firstChild) {
+            this._parseXml(node.firstChild, guiNode);
+        }
+
+        if (node.nextSibling) {
+            this._parseXml(node.nextSibling, parent);
+        }
+    }
+
+    private _prepareSourceElement(node: any, guiNode: any, variable: any, source: any, iterator: any): void {
+        if (this._parentClass) {
+            this._parentClass[variable] = source[iterator];
+        } else {
+            window[variable] = source[iterator];
+        }
+
+        if (node.firstChild) {
+            this._parseXml(node.firstChild, guiNode, true);
+        }
+    }
+
+    private _parseElementsFromSource(node: any, guiNode: any, parent: any): void {
+        let dataSource = node.attributes.getNamedItem("dataSource").value;
+        if (!dataSource.includes(" in ")) {
+            throw "XmlLoader Exception : Malformed XML, Data Source must include an in";
+        } else {
+            let isArray = true;
+            let splittedSource = dataSource.split(" in ");
+            if (splittedSource.length < 2) {
+                throw "XmlLoader Exception : Malformed XML, Data Source must an iterator and a source";
+            }
+            let source = splittedSource[1];
+            if (source.startsWith("{") && source.endsWith("}")) {
+                isArray = false;
+            }
+
+            if (!isArray || (source.startsWith("[") && source.endsWith("]"))) {
+                source = source.substring(1, source.length - 1);
+            }
+
+            if (this._parentClass) {
+                source = this._parentClass[source];
+            } else {
+                source = window[source];
+            }
+
+            if (isArray) {
+                for (let i = 0; i < source.length; i++) {
+                    this._prepareSourceElement(node, guiNode, splittedSource[0], source, i);
+                }
+            } else {
+                for (let i in source) {
+                    this._prepareSourceElement(node, guiNode, splittedSource[0], source, i);
+                }
+            }
+
+            if (node.nextSibling) {
+                this._parseXml(node.nextSibling, parent);
+            }
+        }
+    }
+
+    private _parseXml(node: any, parent: any, generated: boolean = false): void {
+
+        if (node.nodeType != this._nodeTypes.element) {
+            if (node.nextSibling) {
+                this._parseXml(node.nextSibling, parent, generated);
+            }
+            return;
+        }
+
+        if (generated) {
+            node.setAttribute("id", parent.id + parent._children.length + 1);
+        }
+
+        let guiNode = this._createGuiElement(node, parent);
+
+        if (node.nodeName == "Grid") {
+            this._parseGrid(node, guiNode, parent);
+        } else if (!node.attributes.getNamedItem("dataSource")) {
+            this._parseElement(node, guiNode, parent);
+        } else {
+            this._parseElementsFromSource(node, guiNode, parent);
+        }
+    }
+
+    /**
+     * Gets if the loading has finished.
+     * @returns whether the loading has finished or not
+    */
+    public isLoaded(): boolean {
+        return this._isLoaded;
+    }
+
+    /**
+     * Gets a loaded node / control by id.
+     * @param id the Controls id set in the xml
+     * @returns element of type Control
+    */
+    public getNodeById(id: string): any {
+        return this._nodes[id];
+    }
+
+    /**
+     * Gets all loaded nodes / controls
+     * @returns Array of controls
+    */
+    public getNodes(): any {
+        return this._nodes;
+    }
+
+    /**
+     * Initiates the xml layout loading
+     * @param xmlFile defines the xml layout to load
+     * @param rootNode defines the node / control to use as a parent for the loaded layout controls.
+     * @param callback defines the callback called on layout load.
+     */
+    public loadLayout(xmlFile: any, rootNode: any, callback: any): void {
+        let xhttp = new XMLHttpRequest();
+        xhttp.onreadystatechange = function(this: XmlLoader) {
+            if (xhttp.readyState == 4 && xhttp.status == 200) {
+                if (!xhttp.responseXML) {
+                    throw "XmlLoader Exception : XML file is malformed or corrupted.";
+                }
+
+                let xmlDoc = xhttp.responseXML.documentElement;
+                this._parseXml(xmlDoc.firstChild, rootNode);
+                this._isLoaded = true;
+                if (callback) {
+                    callback();
+                }
+            }
+        }.bind(this);
+
+        xhttp.open("GET", xmlFile, true);
+        xhttp.send();
+    }
+}