浏览代码

margin done

David Catuhe 8 年之前
父节点
当前提交
cc7cfc7321

+ 14 - 0
gui/readme.md

@@ -0,0 +1,14 @@
+# Babylon.js GUI library
+
+The Babylon.js GUI library is an extension you can use to generate interactive user interface on top of the DynamicTexture.
+
+## How to use it
+
+- unit mode (Default to percentage)
+
+## Features
+* Flixible containers and controls
+* Horizontal and vertical alignment
+* Proportional or pixel constant size modes
+* Text wrapping
+

+ 6 - 13
gui/src/advancedDynamicTexture.js

@@ -17,10 +17,11 @@ var BABYLON;
             __extends(AdvancedDynamicTexture, _super);
             function AdvancedDynamicTexture(name, scene) {
                 var _this = _super.call(this, name, {}, scene, false, BABYLON.Texture.NEAREST_SAMPLINGMODE, BABYLON.Engine.TEXTUREFORMAT_RGBA) || this;
-                _this._dirty = false;
+                _this._isDirty = false;
                 _this._rootContainer = new GUI.Container("root");
                 _this._resizeObserver = _this.getScene().getEngine().onResizeObservable.add(function () { return _this._onResize(); });
                 _this._renderObserver = _this.getScene().onBeforeRenderObservable.add(function () { return _this._checkUpdate(); });
+                _this._rootContainer._link(null, _this);
                 _this._onResize();
                 return _this;
             }
@@ -33,13 +34,12 @@ var BABYLON;
                         return;
                     }
                     this._background = value;
-                    this._markAsDirty();
+                    this._isDirty = true;
                 },
                 enumerable: true,
                 configurable: true
             });
             AdvancedDynamicTexture.prototype.addControl = function (control) {
-                control._setRoot(this._rootContainer);
                 this._rootContainer.addControl(control);
                 return this;
             };
@@ -60,21 +60,14 @@ var BABYLON;
                 var renderHeight = engine.getRenderHeight();
                 if (textureSize.width !== renderWidth || textureSize.height !== renderHeight) {
                     this.scaleTo(renderWidth, renderHeight);
+                    this._isDirty = true;
                 }
-                // Update constant pixel resources            
-                var scaleX = renderWidth / 1000.0;
-                var scaleY = renderHeight / 1000.0;
-                this._rootContainer._rescale(scaleX, scaleY);
-                this._markAsDirty();
-            };
-            AdvancedDynamicTexture.prototype._markAsDirty = function () {
-                this._dirty = true;
             };
             AdvancedDynamicTexture.prototype._checkUpdate = function () {
-                if (!this._dirty) {
+                if (!this._isDirty && !this._rootContainer.isDirty) {
                     return;
                 }
-                this._dirty = false;
+                this._isDirty = false;
                 this._render();
                 this.update();
             };

+ 7 - 17
gui/src/advancedDynamicTexture.ts

@@ -2,7 +2,7 @@
 
 module BABYLON.GUI {
     export class AdvancedDynamicTexture extends DynamicTexture {
-        private _dirty = false;
+        private _isDirty = false;
         private _renderObserver: Observer<Scene>;
         private _resizeObserver: Observer<Engine>;
         private _background: string;
@@ -18,7 +18,7 @@ module BABYLON.GUI {
             }
 
             this._background = value;
-            this._markAsDirty();
+            this._isDirty = true;
         }
         
         constructor(name: string, scene: Scene) {
@@ -27,11 +27,12 @@ module BABYLON.GUI {
             this._resizeObserver = this.getScene().getEngine().onResizeObservable.add(() => this._onResize());
             this._renderObserver = this.getScene().onBeforeRenderObservable.add(() => this._checkUpdate());
 
+            this._rootContainer._link(null, this);
+
             this._onResize();
         }
 
         public addControl(control: Control): AdvancedDynamicTexture {
-            control._setRoot(this._rootContainer);
             this._rootContainer.addControl(control);
 
             return this;
@@ -58,26 +59,15 @@ module BABYLON.GUI {
 
             if (textureSize.width !== renderWidth || textureSize.height !== renderHeight) {
                 this.scaleTo(renderWidth, renderHeight);
+                this._isDirty = true;
             }
-
-            // Update constant pixel resources            
-            var scaleX = renderWidth / 1000.0;
-            var scaleY = renderHeight / 1000.0;
-
-            this._rootContainer._rescale(scaleX, scaleY);
-
-            this._markAsDirty();
-        }
-
-        public _markAsDirty() {
-            this._dirty = true;
         }
 
         private _checkUpdate(): void {
-            if (!this._dirty) {
+            if (!this._isDirty && !this._rootContainer.isDirty) {
                 return;
             }
-            this._dirty = false;
+            this._isDirty = false;
 
             this._render();
             this.update();

+ 1 - 8
gui/src/controls/container.js

@@ -26,7 +26,7 @@ var BABYLON;
                 if (index !== -1) {
                     return this;
                 }
-                control._setRoot(this);
+                control._link(this, this._host);
                 this._reOrderControl(control);
                 this._markAsDirty();
                 return this;
@@ -60,13 +60,6 @@ var BABYLON;
                 }
                 context.restore();
             };
-            Container.prototype._rescale = function (scaleX, scaleY) {
-                _super.prototype._rescale.call(this, scaleX, scaleY);
-                for (var _i = 0, _a = this._children; _i < _a.length; _i++) {
-                    var child = _a[_i];
-                    child._rescale(scaleX, scaleY);
-                }
-            };
             return Container;
         }(GUI.Control));
         GUI.Container = Container;

+ 1 - 9
gui/src/controls/container.ts

@@ -14,7 +14,7 @@ module BABYLON.GUI {
             if (index !== -1) {
                 return this;
             }
-            control._setRoot(this);
+            control._link(this, this._host);
 
             this._reOrderControl(control);
 
@@ -59,13 +59,5 @@ module BABYLON.GUI {
             }
             context.restore();
         }
-
-        public _rescale(scaleX: number, scaleY: number) {
-            super._rescale(scaleX, scaleY);
-
-            for (var child of this._children) {
-                child._rescale(scaleX, scaleY);
-            }
-        }
     }    
 }

+ 6 - 7
gui/src/controls/contentControl.js

@@ -18,6 +18,7 @@ var BABYLON;
             function ContentControl(name) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
+                _this._measureForChild = GUI.Measure.Empty();
                 return _this;
             }
             Object.defineProperty(ContentControl.prototype, "child", {
@@ -29,7 +30,7 @@ var BABYLON;
                         return;
                     }
                     this._child = control;
-                    control._setRoot(this._root);
+                    control._link(this._root, this._host);
                     this._markAsDirty();
                 },
                 enumerable: true,
@@ -44,15 +45,13 @@ var BABYLON;
                 this.applyStates(context);
                 this._localDraw(context);
                 if (this._child) {
-                    this._child._draw(this._currentMeasure, context);
+                    this._child._draw(this._measureForChild, context);
                 }
                 context.restore();
             };
-            ContentControl.prototype._rescale = function (scaleX, scaleY) {
-                _super.prototype._rescale.call(this, scaleX, scaleY);
-                if (this._child) {
-                    this._child._rescale(scaleX, scaleY);
-                }
+            ContentControl.prototype._additionalProcessing = function (parentMeasure, context) {
+                _super.prototype._additionalProcessing.call(this, parentMeasure, context);
+                this._measureForChild.copyFrom(this._currentMeasure);
             };
             return ContentControl;
         }(GUI.Control));

+ 7 - 8
gui/src/controls/contentControl.ts

@@ -2,7 +2,8 @@
 
 module BABYLON.GUI {
     export class ContentControl extends Control {
-        private _child: Control;       
+        private _child: Control;  
+        protected _measureForChild = Measure.Empty();     
 
         public get child(): Control {
             return this._child;
@@ -14,7 +15,7 @@ module BABYLON.GUI {
             }
 
             this._child = control;
-            control._setRoot(this._root);
+            control._link(this._root, this._host);
 
             this._markAsDirty();
         }
@@ -37,17 +38,15 @@ module BABYLON.GUI {
             this._localDraw(context);
 
             if (this._child) {
-                this._child._draw(this._currentMeasure, context);
+                this._child._draw(this._measureForChild, context);
             }
             context.restore();
         }
 
-        public _rescale(scaleX: number, scaleY: number) {
-            super._rescale(scaleX, scaleY);
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {  
+            super._additionalProcessing(parentMeasure, context);
 
-            if (this._child) {
-                this._child._rescale(scaleX, scaleY);
-            }
+            this._measureForChild.copyFrom(this._currentMeasure);
         }
     }    
 }

+ 128 - 62
gui/src/controls/control.js

@@ -4,16 +4,40 @@ var BABYLON;
     var GUI;
     (function (GUI) {
         var Control = (function () {
+            // Functions
             function Control(name) {
                 this.name = name;
                 this._zIndex = 0;
+                this._currentMeasure = GUI.Measure.Empty();
                 this._fontSize = 18;
                 this._width = 1;
                 this._height = 1;
                 this._horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
                 this._verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
+                this._isDirty = true;
+                this._cachedParentMeasure = GUI.Measure.Empty();
+                this._marginLeft = 0;
+                this._marginRight = 0;
+                this._marginTop = 0;
+                this._marginBottom = 0;
+                this._unitMode = Control.UNITMODE_PERCENTAGE;
                 this.fontFamily = "Arial";
             }
+            Object.defineProperty(Control.prototype, "unitMode", {
+                // Properties
+                get: function () {
+                    return this._unitMode;
+                },
+                set: function (value) {
+                    if (this._unitMode === value) {
+                        return;
+                    }
+                    this._unitMode = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Control.prototype, "horizontalAlignment", {
                 get: function () {
                     return this._horizontalAlignment;
@@ -50,9 +74,6 @@ var BABYLON;
                     if (value < 0) {
                         value = 0;
                     }
-                    if (value > 1) {
-                        value = 1;
-                    }
                     if (this._width === value) {
                         return;
                     }
@@ -70,9 +91,6 @@ var BABYLON;
                     if (value < 0) {
                         value = 0;
                     }
-                    if (value > 1) {
-                        value = 1;
-                    }
                     if (this._height === value) {
                         return;
                     }
@@ -82,114 +100,135 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "widthConstantPixel", {
+            Object.defineProperty(Control.prototype, "fontFamily", {
                 get: function () {
-                    return this._widthConstantPixel;
+                    return this._fontFamily;
                 },
                 set: function (value) {
-                    if (this._widthConstantPixel === value) {
+                    if (this._fontFamily === value) {
                         return;
                     }
-                    this._widthConstantPixel = value;
-                    this._markAsDirty();
+                    this._fontFamily = value;
+                    this._prepareFont();
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "heightConstantPixel", {
+            Object.defineProperty(Control.prototype, "fontSize", {
                 get: function () {
-                    return this._heightConstantPixel;
+                    return this._fontSize;
                 },
                 set: function (value) {
-                    if (this._heightConstantPixel === value) {
+                    if (this._fontSize === value) {
                         return;
                     }
-                    this._heightConstantPixel = value;
+                    this._fontSize = value;
+                    this._prepareFont();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "color", {
+                get: function () {
+                    return this._color;
+                },
+                set: function (value) {
+                    if (this._color === value) {
+                        return;
+                    }
+                    this._color = value;
                     this._markAsDirty();
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "fontFamily", {
+            Object.defineProperty(Control.prototype, "zIndex", {
                 get: function () {
-                    return this._fontFamily;
+                    return this._zIndex;
                 },
                 set: function (value) {
-                    if (this._fontFamily === value) {
+                    if (this.zIndex === value) {
                         return;
                     }
-                    this._fontFamily = value;
-                    this._prepareFont();
+                    this._zIndex = value;
+                    this._root._reOrderControl(this);
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "fontSize", {
+            Object.defineProperty(Control.prototype, "isDirty", {
                 get: function () {
-                    return this._fontSize;
+                    return this._isDirty;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control.prototype, "marginLeft", {
+                get: function () {
+                    return this._marginLeft;
                 },
                 set: function (value) {
-                    if (this._fontSize === value) {
+                    if (this._marginLeft === value) {
                         return;
                     }
-                    this._fontSize = value;
-                    this._fontSizeConstantPixel = null;
-                    this._prepareFont();
+                    this._marginLeft = value;
+                    this._markAsDirty();
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "fontSizeConstantPixel", {
+            Object.defineProperty(Control.prototype, "marginRight", {
                 get: function () {
-                    return this._fontSizeConstantPixel;
+                    return this._marginRight;
                 },
                 set: function (value) {
-                    if (this._fontSizeConstantPixel === value) {
+                    if (this._marginRight === value) {
                         return;
                     }
-                    this._fontSize = null;
-                    this._fontSizeConstantPixel = value;
-                    this._prepareFont();
+                    this._marginRight = value;
+                    this._markAsDirty();
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "color", {
+            Object.defineProperty(Control.prototype, "marginTop", {
                 get: function () {
-                    return this._color;
+                    return this._marginTop;
                 },
                 set: function (value) {
-                    if (this._color === value) {
+                    if (this._marginTop === value) {
                         return;
                     }
-                    this._color = value;
+                    this._marginTop = value;
                     this._markAsDirty();
                 },
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Control.prototype, "zIndex", {
+            Object.defineProperty(Control.prototype, "marginBottom", {
                 get: function () {
-                    return this._zIndex;
+                    return this._marginBottom;
                 },
                 set: function (value) {
-                    if (this.zIndex === value) {
+                    if (this._marginBottom === value) {
                         return;
                     }
-                    this._zIndex = value;
-                    this._root._reOrderControl(this);
+                    this._marginBottom = value;
+                    this._markAsDirty();
                 },
                 enumerable: true,
                 configurable: true
             });
             Control.prototype._markAsDirty = function () {
+                this._isDirty = true;
                 if (!this._root) {
                     return; // Not yet connected
                 }
                 this._root._markAsDirty();
             };
-            Control.prototype._setRoot = function (root) {
+            Control.prototype._link = function (root, host) {
                 this._root = root;
+                this._host = host;
             };
             Control.prototype.applyStates = function (context) {
                 if (this._font) {
@@ -200,24 +239,29 @@ var BABYLON;
                 }
             };
             Control.prototype._processMeasures = function (parentMeasure, context) {
-                this._measure(parentMeasure, context);
-                this._computeAlignment(parentMeasure, context);
+                if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
+                    this._currentMeasure.copyFrom(parentMeasure);
+                    this._measure(parentMeasure, context);
+                    this._computeAlignment(parentMeasure, context);
+                    this._additionalProcessing(parentMeasure, context);
+                }
                 // Clip
                 context.beginPath();
                 context.rect(this._currentMeasure.left, this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
                 context.clip();
+                this._isDirty = false;
+                this._cachedParentMeasure.copyFrom(parentMeasure);
             };
             Control.prototype._measure = function (parentMeasure, context) {
-                this._currentMeasure = parentMeasure.copy();
                 // Width / Height
-                if (this._widthConstantPixel) {
-                    this._currentMeasure.width = this._widthConstantPixel * this._scaleX;
+                if (this._unitMode === Control.UNITMODE_PIXEL) {
+                    this._currentMeasure.width = this._width;
                 }
                 else {
                     this._currentMeasure.width *= this._width;
                 }
-                if (this._heightConstantPixel) {
-                    this._currentMeasure.height = this._heightConstantPixel * this._scaleY;
+                if (this._unitMode === Control.UNITMODE_PIXEL) {
+                    this._currentMeasure.height = this._height;
                 }
                 else {
                     this._currentMeasure.height *= this._height;
@@ -228,6 +272,7 @@ var BABYLON;
                 var height = this._currentMeasure.height;
                 var parentWidth = parentMeasure.width;
                 var parentHeight = parentMeasure.height;
+                // Left / top
                 var x = 0;
                 var y = 0;
                 switch (this.horizontalAlignment) {
@@ -252,30 +297,49 @@ var BABYLON;
                         y = (parentHeight - height) / 2;
                         break;
                 }
-                this._currentMeasure.left = this._currentMeasure.left + x;
-                this._currentMeasure.top = this._currentMeasure.top + y;
+                if (this._unitMode === Control.UNITMODE_PIXEL) {
+                    this._currentMeasure.left += this._marginLeft;
+                    this._currentMeasure.left -= this._marginRight;
+                    this._currentMeasure.top += this._marginTop;
+                    this._currentMeasure.top -= this._marginBottom;
+                }
+                else {
+                    this._currentMeasure.left += parentWidth * this._marginLeft;
+                    this._currentMeasure.left -= parentWidth * this._marginRight;
+                    this._currentMeasure.top += parentHeight * this._marginTop;
+                    this._currentMeasure.top -= parentHeight * this._marginBottom;
+                }
+                this._currentMeasure.left += x;
+                this._currentMeasure.top += y;
             };
-            Control.prototype._draw = function (parentMeasure, context) {
+            Control.prototype._additionalProcessing = function (parentMeasure, context) {
                 // Do nothing
             };
-            Control.prototype._rescale = function (scaleX, scaleY) {
-                this._scaleX = scaleX;
-                this._scaleY = scaleY;
-                this._prepareFont();
+            Control.prototype._draw = function (parentMeasure, context) {
+                // Do nothing
             };
             Control.prototype._prepareFont = function () {
                 if (!this._fontFamily) {
                     return;
                 }
-                if (this._fontSizeConstantPixel) {
-                    this._font = (this._fontSizeConstantPixel * this._scaleX) + "px " + this._fontFamily;
-                }
-                else {
-                    this._font = this._fontSize + "px " + this._fontFamily;
-                }
+                this._font = this._fontSize + "px " + this._fontFamily;
                 this._fontOffset = Control._GetFontOffset(this._font);
                 this._markAsDirty();
             };
+            Object.defineProperty(Control, "UNITMODE_PERCENTAGE", {
+                get: function () {
+                    return Control._UNITMODE_PERCENTAGE;
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Control, "UNITMODE_PIXEL", {
+                get: function () {
+                    return Control._UNITMODE_PIXEL;
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(Control, "HORIZONTAL_ALIGNMENT_LEFT", {
                 get: function () {
                     return Control._HORIZONTAL_ALIGNMENT_LEFT;
@@ -358,6 +422,8 @@ var BABYLON;
         Control._VERTICAL_ALIGNMENT_TOP = 0;
         Control._VERTICAL_ALIGNMENT_BOTTOM = 1;
         Control._VERTICAL_ALIGNMENT_CENTER = 2;
+        Control._UNITMODE_PERCENTAGE = 0;
+        Control._UNITMODE_PIXEL = 1;
         Control._FontHeightSizes = {};
         GUI.Control = Control;
     })(GUI = BABYLON.GUI || (BABYLON.GUI = {}));

+ 132 - 80
gui/src/controls/control.ts

@@ -4,23 +4,40 @@ module BABYLON.GUI {
     export class Control {        
         private _zIndex = 0;
         public _root: Container;
-        public _currentMeasure: Measure;
-        private _scaleX: number;
-        private _scaleY: number;
+        public _host: AdvancedDynamicTexture;
+        public _currentMeasure = Measure.Empty();
         private _fontFamily: string;
         private _fontSize = 18;
-        private _fontSizeConstantPixel: number;
         private _font: string;
         private _width = 1;
-        private _widthConstantPixel: number;
         private _height = 1;
-        private _heightConstantPixel: number;
         private _lastMeasuredFont: string;
         protected _fontOffset: {ascent: number, height: number, descent: number};
         private _color: string;
         private _horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         private _verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
+        private _isDirty = true;
+        private _cachedParentMeasure = Measure.Empty();
+        private _marginLeft = 0;
+        private _marginRight = 0;
+        private _marginTop = 0;
+        private _marginBottom = 0;        
+        private _unitMode = Control.UNITMODE_PERCENTAGE;
         
+        // Properties
+        public get unitMode(): number {
+            return this._unitMode;
+        }
+
+        public set unitMode(value: number) {
+            if (this._unitMode === value) {
+                return;
+            }
+
+            this._unitMode = value;
+            this._markAsDirty();
+        } 
+
         public get horizontalAlignment(): number {
             return this._horizontalAlignment;
         }
@@ -55,10 +72,6 @@ module BABYLON.GUI {
             if (value < 0) {
                 value = 0;
             }
-
-            if (value > 1) {
-                value = 1;
-            }
             if (this._width === value) {
                 return;
             }
@@ -75,10 +88,6 @@ module BABYLON.GUI {
             if (value < 0) {
                 value = 0;
             }
-
-            if (value > 1) {
-                value = 1;
-            }            
             if (this._height === value) {
                 return;
             }
@@ -87,32 +96,6 @@ module BABYLON.GUI {
             this._markAsDirty();
         }   
 
-        public get widthConstantPixel(): number {
-            return this._widthConstantPixel;
-        }
-
-        public set widthConstantPixel(value: number) {
-            if (this._widthConstantPixel === value) {
-                return;
-            }
-
-            this._widthConstantPixel = value;
-            this._markAsDirty();
-        }
-
-        public get heightConstantPixel(): number {
-            return this._heightConstantPixel;
-        }
-
-        public set heightConstantPixel(value: number) {
-            if (this._heightConstantPixel === value) {
-                return;
-            }
-
-            this._heightConstantPixel = value;
-            this._markAsDirty();
-        }      
-
         public get fontFamily(): string {
             return this._fontFamily;
         }
@@ -136,21 +119,6 @@ module BABYLON.GUI {
             }
 
             this._fontSize = value;
-            this._fontSizeConstantPixel = null;
-            this._prepareFont();
-        }
-
-        public get fontSizeConstantPixel(): number {
-            return this._fontSizeConstantPixel;
-        }
-
-        public set fontSizeConstantPixel(value: number) {
-            if (this._fontSizeConstantPixel === value) {
-                return;
-            }
-            
-            this._fontSize = null;
-            this._fontSizeConstantPixel = value;
             this._prepareFont();
         }
 
@@ -180,19 +148,79 @@ module BABYLON.GUI {
             this._root._reOrderControl(this);
         }
 
+        public get isDirty(): boolean {
+            return this._isDirty;
+        }
+        
+        public get marginLeft(): number {
+            return this._marginLeft;
+        }
+
+        public set marginLeft(value: number) {
+            if (this._marginLeft === value) {
+                return;
+            }
+
+            this._marginLeft = value;
+            this._markAsDirty();
+        }    
+
+        public get marginRight(): number {
+            return this._marginRight;
+        }
+
+        public set marginRight(value: number) {
+            if (this._marginRight === value) {
+                return;
+            }
+
+            this._marginRight = value;
+            this._markAsDirty();
+        }
+
+        public get marginTop(): number {
+            return this._marginTop;
+        }
+
+        public set marginTop(value: number) {
+            if (this._marginTop === value) {
+                return;
+            }
+
+            this._marginTop = value;
+            this._markAsDirty();
+        }
+
+        public get marginBottom(): number {
+            return this._marginBottom;
+        }
+
+        public set marginBottom(value: number) {
+            if (this._marginBottom === value) {
+                return;
+            }
+
+            this._marginBottom = value;
+            this._markAsDirty();
+        }                
+
+        // Functions
         constructor(public name: string) {
             this.fontFamily = "Arial";
         }
 
-        protected _markAsDirty(): void {
+        protected _markAsDirty(): void {            
+            this._isDirty = true;
+
             if (!this._root) {
                 return; // Not yet connected
             }
             this._root._markAsDirty();
         }
 
-        public _setRoot(root: Container): void {
+        public _link(root: Container, host: AdvancedDynamicTexture): void {
             this._root = root;
+            this._host = host;
         }
 
         protected applyStates(context: CanvasRenderingContext2D): void {
@@ -205,28 +233,34 @@ module BABYLON.GUI {
             }
         }
 
-        protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D) {
-            this._measure(parentMeasure, context);
-            this._computeAlignment(parentMeasure, context);
+        protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D) {     
+            if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
+                this._currentMeasure.copyFrom(parentMeasure);
+
+                this._measure(parentMeasure, context);
+                this._computeAlignment(parentMeasure, context);
+                this._additionalProcessing(parentMeasure, context);
+            }      
                         
             // Clip
             context.beginPath();
             context.rect(this._currentMeasure.left ,this._currentMeasure.top, this._currentMeasure.width, this._currentMeasure.height);
             context.clip();
+
+            this._isDirty = false;
+            this._cachedParentMeasure.copyFrom(parentMeasure);
         }
 
-        protected _measure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
-            this._currentMeasure = parentMeasure.copy();
-            
+        protected _measure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {  
             // Width / Height
-            if (this._widthConstantPixel) {
-                this._currentMeasure.width = this._widthConstantPixel * this._scaleX;
+            if (this._unitMode === Control.UNITMODE_PIXEL) {
+                this._currentMeasure.width = this._width;
             } else {
                 this._currentMeasure.width *= this._width; 
             }
 
-            if (this._heightConstantPixel) {
-                this._currentMeasure.height = this._heightConstantPixel * this._scaleY;
+            if (this._unitMode === Control.UNITMODE_PIXEL) {
+                this._currentMeasure.height = this._height;
             } else {
                 this._currentMeasure.height *= this._height; 
             }
@@ -239,6 +273,7 @@ module BABYLON.GUI {
             var parentWidth = parentMeasure.width;
             var parentHeight = parentMeasure.height;
 
+            // Left / top
             var x = 0;
             var y = 0;
 
@@ -266,19 +301,28 @@ module BABYLON.GUI {
                     break;
             }
             
-            this._currentMeasure.left = this._currentMeasure.left + x;
-            this._currentMeasure.top =  this._currentMeasure.top + y;            
+            if (this._unitMode === Control.UNITMODE_PIXEL) {
+                this._currentMeasure.left += this._marginLeft;
+                this._currentMeasure.left -= this._marginRight;
+                this._currentMeasure.top += this._marginTop;
+                this._currentMeasure.top -= this._marginBottom;
+            } else {
+                this._currentMeasure.left += parentWidth * this._marginLeft;
+                this._currentMeasure.left -= parentWidth * this._marginRight;
+                this._currentMeasure.top += parentHeight * this._marginTop;
+                this._currentMeasure.top -= parentHeight * this._marginBottom;
+            }
+
+            this._currentMeasure.left += x;
+            this._currentMeasure.top += y;            
         }
 
-        public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             // Do nothing
         }
 
-        public _rescale(scaleX: number, scaleY: number) {
-            this._scaleX = scaleX;
-            this._scaleY = scaleY;
-
-            this._prepareFont();
+        public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+            // Do nothing
         }
 
         private _prepareFont() {
@@ -286,11 +330,7 @@ module BABYLON.GUI {
                 return;
             }
 
-            if (this._fontSizeConstantPixel) {
-                this._font = (this._fontSizeConstantPixel * this._scaleX) + "px " + this._fontFamily;
-            } else {
-                this._font = this._fontSize + "px " + this._fontFamily;
-            }
+            this._font = this._fontSize + "px " + this._fontFamily;
         
             this._fontOffset = Control._GetFontOffset(this._font);
             this._markAsDirty();
@@ -300,10 +340,22 @@ module BABYLON.GUI {
         private static _HORIZONTAL_ALIGNMENT_LEFT = 0;
         private static _HORIZONTAL_ALIGNMENT_RIGHT = 1;
         private static _HORIZONTAL_ALIGNMENT_CENTER = 2;
+        
         private static _VERTICAL_ALIGNMENT_TOP = 0;
         private static _VERTICAL_ALIGNMENT_BOTTOM = 1;
         private static _VERTICAL_ALIGNMENT_CENTER = 2;
 
+        private static _UNITMODE_PERCENTAGE = 0;
+        private static _UNITMODE_PIXEL = 1;
+
+        public static get UNITMODE_PERCENTAGE(): number {
+            return Control._UNITMODE_PERCENTAGE;
+        }
+
+        public static get UNITMODE_PIXEL(): number {
+            return Control._UNITMODE_PIXEL;
+        }
+
         public static get HORIZONTAL_ALIGNMENT_LEFT(): number {
             return Control._HORIZONTAL_ALIGNMENT_LEFT;
         }

+ 7 - 0
gui/src/controls/rectangle.js

@@ -64,6 +64,13 @@ var BABYLON;
                 }
                 context.restore();
             };
+            Rectangle.prototype._additionalProcessing = function (parentMeasure, context) {
+                _super.prototype._additionalProcessing.call(this, parentMeasure, context);
+                this._measureForChild.width -= 2 * this._thickness;
+                this._measureForChild.height -= 2 * this._thickness;
+                this._measureForChild.left += this._thickness;
+                this._measureForChild.top += this._thickness;
+            };
             return Rectangle;
         }(GUI.ContentControl));
         GUI.Rectangle = Rectangle;

+ 9 - 0
gui/src/controls/rectangle.ts

@@ -54,5 +54,14 @@ module BABYLON.GUI {
         
             context.restore();
         }
+
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {  
+            super._additionalProcessing(parentMeasure, context);
+
+            this._measureForChild.width -= 2 * this._thickness;
+            this._measureForChild.height -= 2 * this._thickness;
+            this._measureForChild.left += this._thickness;
+            this._measureForChild.top += this._thickness;            
+        }
     }    
 }

+ 58 - 29
gui/src/controls/textBlock.js

@@ -18,11 +18,26 @@ var BABYLON;
             function TextBlock(name, text) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
+                _this._textWrapping = false;
                 _this._textHorizontalAlignment = GUI.Control.HORIZONTAL_ALIGNMENT_CENTER;
                 _this._textVerticalAlignment = GUI.Control.VERTICAL_ALIGNMENT_CENTER;
                 _this.text = text;
                 return _this;
             }
+            Object.defineProperty(TextBlock.prototype, "textWrapping", {
+                get: function () {
+                    return this._textWrapping;
+                },
+                set: function (value) {
+                    if (this._textWrapping === value) {
+                        return;
+                    }
+                    this._textWrapping = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
             Object.defineProperty(TextBlock.prototype, "text", {
                 get: function () {
                     return this._text;
@@ -65,6 +80,9 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            TextBlock.prototype._measure = function (parentMeasure, context) {
+                _super.prototype._measure;
+            };
             TextBlock.prototype._drawText = function (text, textWidth, y, context) {
                 var width = this._currentMeasure.width;
                 var x = 0;
@@ -83,52 +101,63 @@ var BABYLON;
             };
             TextBlock.prototype._draw = function (parentMeasure, context) {
                 context.save();
-                _super.prototype._processMeasures.call(this, parentMeasure, context);
                 this.applyStates(context);
-                this._computeTextAlignment(context);
-                var words = this.text.split(' ');
-                var line = '';
-                var width = this._currentMeasure.width;
-                var y = this._textY;
-                var lineWidth = 0;
-                for (var n = 0; n < words.length; n++) {
-                    var testLine = line + words[n] + ' ';
-                    var metrics = context.measureText(testLine);
-                    var testWidth = metrics.width;
-                    if (testWidth > width && n > 0) {
-                        this._drawText(line, lineWidth, y, context);
-                        line = words[n] + ' ';
-                        lineWidth = context.measureText(line).width;
-                        y += this._lineHeight;
-                    }
-                    else {
-                        lineWidth = testWidth;
-                        line = testLine;
+                _super.prototype._processMeasures.call(this, parentMeasure, context);
+                // Render lines
+                this._renderLines(context);
+                context.restore();
+            };
+            TextBlock.prototype._additionalProcessing = function (parentMeasure, context) {
+                this._lines = [];
+                if (this._textWrapping) {
+                    var words = this.text.split(' ');
+                    var line = '';
+                    var width = this._currentMeasure.width;
+                    var lineWidth = 0;
+                    for (var n = 0; n < words.length; n++) {
+                        var testLine = line + words[n] + ' ';
+                        var metrics = context.measureText(testLine);
+                        var testWidth = metrics.width;
+                        if (testWidth > width && n > 0) {
+                            this._lines.push({ text: line, width: lineWidth });
+                            line = words[n] + ' ';
+                            lineWidth = context.measureText(line).width;
+                        }
+                        else {
+                            lineWidth = testWidth;
+                            line = testLine;
+                        }
                     }
+                    this._lines.push({ text: line, width: lineWidth });
+                }
+                else {
+                    this._lines.push({ text: this.text, width: context.measureText(this.text).width });
                 }
-                this._drawText(line, lineWidth, y, context);
-                context.restore();
             };
-            TextBlock.prototype._computeTextAlignment = function (context) {
+            TextBlock.prototype._renderLines = function (context) {
                 var width = this._currentMeasure.width;
                 var height = this._currentMeasure.height;
-                var y = 0;
                 if (!this._fontOffset) {
                     this._fontOffset = GUI.Control._GetFontOffset(context.font);
                 }
+                var rootY = 0;
                 switch (this._textVerticalAlignment) {
                     case GUI.Control.VERTICAL_ALIGNMENT_TOP:
-                        y = this._fontOffset.ascent;
+                        rootY = this._fontOffset.ascent;
                         break;
                     case GUI.Control.VERTICAL_ALIGNMENT_BOTTOM:
-                        y = height - this._fontOffset.descent;
+                        rootY = height - this._fontOffset.height * (this._lines.length - 1) - this._fontOffset.descent;
                         break;
                     case GUI.Control.VERTICAL_ALIGNMENT_CENTER:
-                        y = (height / 2) + (this._fontOffset.ascent - this._fontOffset.height / 2);
+                        rootY = this._fontOffset.ascent + (height - this._fontOffset.height * this._lines.length) / 2;
                         break;
                 }
-                this._lineHeight = this._fontOffset.height;
-                this._textY = this._currentMeasure.top + y;
+                rootY += this._currentMeasure.top;
+                for (var _i = 0, _a = this._lines; _i < _a.length; _i++) {
+                    var line = _a[_i];
+                    this._drawText(line.text, line.width, rootY, context);
+                    rootY += this._fontOffset.height;
+                }
             };
             return TextBlock;
         }(GUI.Control));

+ 63 - 36
gui/src/controls/textBlock.ts

@@ -4,10 +4,25 @@ module BABYLON.GUI {
     export class TextBlock extends Control {
         private _text: string;
         private _textY: number;
-        private _lineHeight: number;
+        private _textWrapping = false;
         private _textHorizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         private _textVerticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
 
+        private _lines: any[];
+        private _totalHeight: number;
+
+        public get textWrapping(): boolean {
+            return this._textWrapping;
+        }
+
+        public set textWrapping(value: boolean) {
+            if (this._textWrapping === value) {
+                return;
+            }
+            this._textWrapping = value;
+            this._markAsDirty();
+        }
+
         public get text(): string {
             return this._text;
         }
@@ -52,8 +67,11 @@ module BABYLON.GUI {
             this.text = text;
         }
 
-        private _drawText(text: string, textWidth: number, y: number, context: CanvasRenderingContext2D): void {
+        protected _measure(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+            super._measure
+        }
 
+        private _drawText(text: string, textWidth: number, y: number, context: CanvasRenderingContext2D): void {
             var width = this._currentMeasure.width;
             var x = 0;
             switch (this._textHorizontalAlignment) {
@@ -69,67 +87,76 @@ module BABYLON.GUI {
             }
 
             context.fillText(text, this._currentMeasure.left + x, y);
-        }
+        }      
 
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
             context.save();
 
+            this.applyStates(context);
             super._processMeasures(parentMeasure, context);
             
-            this.applyStates(context);
-
-            this._computeTextAlignment(context);
+            // Render lines
+            this._renderLines(context);
 
-            var words = this.text.split(' ');
-            var line = '';
+            context.restore();
+        }
 
-            var width = this._currentMeasure.width;
-            var y = this._textY;
-            var lineWidth = 0;
-            for(var n = 0; n < words.length; n++) {
-                var testLine = line + words[n] + ' ';
-                var metrics = context.measureText(testLine);
-                var testWidth = metrics.width;
-                if (testWidth > width && n > 0) {
-                    this._drawText(line, lineWidth, y, context);
-                    line = words[n] + ' ';
-                    lineWidth = context.measureText(line).width;
-                    y += this._lineHeight;
-                }
-                else {
-                    lineWidth = testWidth;
-                    line = testLine;
+        protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+            this._lines = [];
+
+            if (this._textWrapping) {
+                var words = this.text.split(' ');
+                var line = '';
+
+                var width = this._currentMeasure.width;
+                var lineWidth = 0;
+
+                for(var n = 0; n < words.length; n++) {
+                    var testLine = line + words[n] + ' ';
+                    var metrics = context.measureText(testLine);
+                    var testWidth = metrics.width;
+                    if (testWidth > width && n > 0) {
+                        this._lines.push({text: line, width: lineWidth});
+                        line = words[n] + ' ';
+                        lineWidth = context.measureText(line).width;
+                    }
+                    else {
+                        lineWidth = testWidth;
+                        line = testLine;
+                    }
                 }
+                this._lines.push({text: line, width: lineWidth});
+            } else {
+                this._lines.push({text: this.text, width: context.measureText(this.text).width});
             }
-            this._drawText(line, lineWidth, y, context);
-
-            context.restore();
         }
 
-        protected _computeTextAlignment(context: CanvasRenderingContext2D): void {
+        protected _renderLines(context: CanvasRenderingContext2D): void {
             var width = this._currentMeasure.width;
             var height = this._currentMeasure.height;
-
             
-            var y = 0;
             if (!this._fontOffset) {
                 this._fontOffset = Control._GetFontOffset(context.font);
             }
-
+            var rootY = 0;
             switch (this._textVerticalAlignment) {
                 case Control.VERTICAL_ALIGNMENT_TOP:
-                    y = this._fontOffset.ascent;
+                    rootY = this._fontOffset.ascent;
                     break;
                 case Control.VERTICAL_ALIGNMENT_BOTTOM:
-                    y = height - this._fontOffset.descent;
+                    rootY = height - this._fontOffset.height * (this._lines.length - 1) - this._fontOffset.descent;
                     break;
                 case Control.VERTICAL_ALIGNMENT_CENTER:
-                    y = (height /2) + (this._fontOffset.ascent - this._fontOffset.height / 2);
+                    rootY = this._fontOffset.ascent + (height - this._fontOffset.height * this._lines.length) / 2;
                     break;
             }
 
-            this._lineHeight = this._fontOffset.height;            
-            this._textY = this._currentMeasure.top + y;
+            rootY += this._currentMeasure.top;
+
+            for (var line of this._lines) {
+                this._drawText(line.text, line.width, rootY, context);
+                rootY += this._fontOffset.height;
+            }       
         }
     }    
 }

+ 23 - 2
gui/src/measure.js

@@ -10,8 +10,29 @@ var BABYLON;
                 this.width = width;
                 this.height = height;
             }
-            Measure.prototype.copy = function () {
-                return new Measure(this.left, this.top, this.width, this.height);
+            Measure.prototype.copyFrom = function (other) {
+                this.left = other.left;
+                this.top = other.top;
+                this.width = other.width;
+                this.height = other.height;
+            };
+            Measure.prototype.isEqualsTo = function (other) {
+                if (this.left !== other.left) {
+                    return false;
+                }
+                if (this.top !== other.top) {
+                    return false;
+                }
+                if (this.width !== other.width) {
+                    return false;
+                }
+                if (this.height !== other.height) {
+                    return false;
+                }
+                return true;
+            };
+            Measure.Empty = function () {
+                return new Measure(0, 0, 0, 0);
             };
             return Measure;
         }());

+ 30 - 2
gui/src/measure.ts

@@ -6,8 +6,36 @@ module BABYLON.GUI {
 
         }
 
-        public copy(): Measure {
-            return new Measure(this.left, this.top,  this.width, this.height);
+        public copyFrom(other: Measure): void {
+            this.left = other.left;
+            this.top = other.top;
+            this.width = other.width;
+            this.height = other.height;
+        }
+
+        public isEqualsTo(other: Measure): boolean {
+
+            if (this.left !== other.left) {
+                return false;
+            }
+
+            if (this.top !== other.top) {
+                return false;
+            }
+
+            if (this.width !== other.width) {
+                return false;
+            }
+
+            if (this.height !== other.height) {
+                return false;
+            }
+
+            return true;
+        }
+
+        public static Empty(): Measure {
+            return new Measure(0, 0, 0, 0);
         }
     }    
 }