浏览代码

Added slider to GUI

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

文件差异内容过多而无法显示
+ 1036 - 1031
dist/preview release/babylon.d.ts


文件差异内容过多而无法显示
+ 36 - 36
dist/preview release/babylon.js


+ 9 - 0
dist/preview release/babylon.max.js

@@ -7120,6 +7120,10 @@ var BABYLON;
              * Observable event triggered each time the rendering canvas is resized
              */
             this.onResizeObservable = new BABYLON.Observable();
+            /**
+             * Observable event triggered each time the canvas lost focus
+             */
+            this.onCanvasBlurObservable = new BABYLON.Observable();
             this._windowIsBackground = false;
             this._webGLVersion = 1.0;
             this._badOS = false;
@@ -7217,8 +7221,12 @@ var BABYLON;
             this._onFocus = function () {
                 _this._windowIsBackground = false;
             };
+            this._onCanvasBlur = function () {
+                _this.onCanvasBlurObservable.notifyObservers(_this);
+            };
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
+            canvas.addEventListener("pointerout", this._onCanvasBlur);
             // Viewport
             var limitDeviceRatio = options.limitDeviceRatio || window.devicePixelRatio || 1.0;
             this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, window.devicePixelRatio || 1.0) : 1.0;
@@ -10077,6 +10085,7 @@ var BABYLON;
             // Events
             window.removeEventListener("blur", this._onBlur);
             window.removeEventListener("focus", this._onFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
             document.removeEventListener("fullscreenchange", this._onFullscreenChange);
             document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);

文件差异内容过多而无法显示
+ 1036 - 1031
dist/preview release/babylon.module.d.ts


文件差异内容过多而无法显示
+ 36 - 36
dist/preview release/babylon.worker.js


+ 34 - 13
dist/preview release/gui/babylon.gui.d.ts

@@ -6,10 +6,12 @@ declare module BABYLON.GUI {
         private _resizeObserver;
         private _pointerMoveObserver;
         private _pointerObserver;
+        private _canvasBlurObserver;
         private _background;
         _rootContainer: Container;
         _lastControlOver: Control;
         _lastControlDown: Control;
+        _capturingControl: Control;
         _shouldBlockPointer: boolean;
         _layerToDispose: Layer;
         _linkedControls: Control[];
@@ -20,6 +22,7 @@ declare module BABYLON.GUI {
         background: string;
         idealWidth: number;
         idealHeight: number;
+        readonly layer: Layer;
         constructor(name: string, width: number, height: number, scene: Scene, generateMipMaps?: boolean, samplingMode?: number);
         markAsDirty(): void;
         addControl(control: Control): AdvancedDynamicTexture;
@@ -142,6 +145,7 @@ declare module BABYLON.GUI {
         private _isVisible;
         _linkedMesh: AbstractMesh;
         private _fontSet;
+        private _dummyVector2;
         isHitTestVisible: boolean;
         isPointerBlocker: boolean;
         protected _linkOffsetX: ValueAndUnit;
@@ -150,7 +154,7 @@ declare module BABYLON.GUI {
         * An event triggered when the pointer move over the control.
         * @type {BABYLON.Observable}
         */
-        onPointerMoveObservable: Observable<Control>;
+        onPointerMoveObservable: Observable<Vector2>;
         /**
         * An event triggered when the pointer move out of the control.
         * @type {BABYLON.Observable}
@@ -160,12 +164,12 @@ declare module BABYLON.GUI {
         * An event triggered when the pointer taps the control
         * @type {BABYLON.Observable}
         */
-        onPointerDownObservable: Observable<Control>;
+        onPointerDownObservable: Observable<Vector2>;
         /**
         * An event triggered when pointer up
         * @type {BABYLON.Observable}
         */
-        onPointerUpObservable: Observable<Control>;
+        onPointerUpObservable: Observable<Vector2>;
         /**
         * An event triggered when pointer enters the control
         * @type {BABYLON.Observable}
@@ -219,12 +223,13 @@ declare module BABYLON.GUI {
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
         contains(x: number, y: number): boolean;
         _processPicking(x: number, y: number, type: number): boolean;
-        protected _onPointerMove(): void;
+        protected _onPointerMove(coordinates: Vector2): void;
         protected _onPointerEnter(): void;
         protected _onPointerOut(): void;
-        protected _onPointerDown(): void;
-        protected _onPointerUp(): void;
-        protected _processObservables(type: number): boolean;
+        protected _onPointerDown(coordinates: Vector2): void;
+        protected _onPointerUp(coordinates: Vector2): void;
+        forcePointerUp(): void;
+        _processObservables(type: number, x: number, y: number): boolean;
         private _prepareFont();
         private static _HORIZONTAL_ALIGNMENT_LEFT;
         private static _HORIZONTAL_ALIGNMENT_RIGHT;
@@ -291,7 +296,6 @@ declare module BABYLON.GUI {
         private _cornerRadius;
         thickness: number;
         cornerRadius: number;
-        background: string;
         constructor(name?: string);
         protected _localDraw(context: CanvasRenderingContext2D): void;
         protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
@@ -351,9 +355,28 @@ declare var DOMImage: new (width?: number, height?: number) => HTMLImageElement;
 declare module BABYLON.GUI {
     class Slider extends Control {
         name: string;
-        private _barHeight;
+        private _thumbWidth;
+        private _minimum;
+        private _maximum;
+        private _value;
+        private _background;
+        private _foreground;
+        private _borderColor;
+        private _barOffset;
+        onValueChangedObservable: Observable<number>;
+        borderColor: string;
+        foreground: string;
+        background: string;
+        minimum: number;
+        maximum: number;
+        value: number;
         constructor(name?: string);
         _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void;
+        private _pointerIsDown;
+        private _updateValueFromPointer(x);
+        protected _onPointerDown(coordinates: Vector2): void;
+        protected _onPointerMove(coordinates: Vector2): void;
+        protected _onPointerUp(coordinates: Vector2): void;
     }
 }
 
@@ -419,14 +442,12 @@ declare module BABYLON.GUI {
         pointerOutAnimation: () => void;
         pointerDownAnimation: () => void;
         pointerUpAnimation: () => void;
-        private _buttonIsDown;
         constructor(name?: string);
-        private _ensureButtonUp();
         _processPicking(x: number, y: number, type: number): boolean;
         protected _onPointerEnter(): void;
         protected _onPointerOut(): void;
-        protected _onPointerDown(): void;
-        protected _onPointerUp(): void;
+        protected _onPointerDown(coordinates: Vector2): void;
+        protected _onPointerUp(coordinates: Vector2): void;
         static CreateImageButton(name: string, text: string, imageUrl: string): Button;
         static CreateImageOnlyButton(name: string, imageUrl: string): Button;
         static CreateSimpleButton(name: string, text: string): Button;

+ 197 - 51
dist/preview release/gui/babylon.gui.js

@@ -82,6 +82,13 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
+            Object.defineProperty(AdvancedDynamicTexture.prototype, "layer", {
+                get: function () {
+                    return this._layerToDispose;
+                },
+                enumerable: true,
+                configurable: true
+            });
             AdvancedDynamicTexture.prototype.markAsDirty = function () {
                 this._isDirty = true;
             };
@@ -104,6 +111,9 @@ var BABYLON;
                 if (this._pointerObserver) {
                     this.getScene().onPointerObservable.remove(this._pointerObserver);
                 }
+                if (this._canvasBlurObserver) {
+                    this.getScene().getEngine().onCanvasBlurObservable.remove(this._canvasBlurObserver);
+                }
                 if (this._layerToDispose) {
                     this._layerToDispose.texture = null;
                     this._layerToDispose.dispose();
@@ -126,6 +136,11 @@ var BABYLON;
                 }
             };
             AdvancedDynamicTexture.prototype._checkUpdate = function (camera) {
+                if (this._layerToDispose) {
+                    if ((camera.layerMask & this._layerToDispose.layerMask) === 0) {
+                        return;
+                    }
+                }
                 if (this._isFullscreen && this._linkedControls.length) {
                     var scene = this.getScene();
                     var engine = scene.getEngine();
@@ -164,6 +179,10 @@ var BABYLON;
                 this._rootContainer._draw(measure, context);
             };
             AdvancedDynamicTexture.prototype._doPicking = function (x, y, type) {
+                if (this._capturingControl) {
+                    this._capturingControl._processObservables(type, x, y);
+                    return;
+                }
                 if (!this._rootContainer._processPicking(x, y, type)) {
                     if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
                         if (this._lastControlOver && this._lastControlOver.onPointerOutObservable.hasObservers()) {
@@ -186,6 +205,16 @@ var BABYLON;
                     _this._doPicking(scene.pointerX, scene.pointerY, pi.type);
                     pi.skipOnPointerObservable = _this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
                 });
+                this._canvasBlurObserver = scene.getEngine().onCanvasBlurObservable.add(function () {
+                    if (_this._lastControlOver && _this._lastControlOver.onPointerOutObservable.hasObservers()) {
+                        _this._lastControlOver.onPointerOutObservable.notifyObservers(_this._lastControlOver);
+                    }
+                    _this._lastControlOver = null;
+                    if (_this._lastControlDown) {
+                        _this._lastControlDown.forcePointerUp();
+                    }
+                    _this._lastControlDown = null;
+                });
             };
             AdvancedDynamicTexture.prototype.attachToMesh = function (mesh) {
                 var _this = this;
@@ -521,7 +550,7 @@ var BABYLON;
                 this._zIndex = 0;
                 this._currentMeasure = GUI.Measure.Empty();
                 this._fontFamily = "Arial";
-                this._fontSize = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
+                this._fontSize = new GUI.ValueAndUnit(18, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
                 this._width = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
                 this._height = new GUI.ValueAndUnit(1, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
                 this._horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
@@ -545,6 +574,7 @@ var BABYLON;
                 this._isMatrixDirty = true;
                 this._isVisible = true;
                 this._fontSet = false;
+                this._dummyVector2 = BABYLON.Vector2.Zero();
                 this.isHitTestVisible = true;
                 this.isPointerBlocker = false;
                 this._linkOffsetX = new GUI.ValueAndUnit(0);
@@ -1161,12 +1191,12 @@ var BABYLON;
                 if (!this.contains(x, y)) {
                     return false;
                 }
-                this._processObservables(type);
+                this._processObservables(type, x, y);
                 return true;
             };
-            Control.prototype._onPointerMove = function () {
+            Control.prototype._onPointerMove = function (coordinates) {
                 if (this.onPointerMoveObservable.hasObservers()) {
-                    this.onPointerMoveObservable.notifyObservers(this);
+                    this.onPointerMoveObservable.notifyObservers(coordinates);
                 }
             };
             Control.prototype._onPointerEnter = function () {
@@ -1179,19 +1209,23 @@ var BABYLON;
                     this.onPointerOutObservable.notifyObservers(this);
                 }
             };
-            Control.prototype._onPointerDown = function () {
+            Control.prototype._onPointerDown = function (coordinates) {
                 if (this.onPointerDownObservable.hasObservers()) {
-                    this.onPointerDownObservable.notifyObservers(this);
+                    this.onPointerDownObservable.notifyObservers(coordinates);
                 }
             };
-            Control.prototype._onPointerUp = function () {
+            Control.prototype._onPointerUp = function (coordinates) {
                 if (this.onPointerUpObservable.hasObservers()) {
-                    this.onPointerUpObservable.notifyObservers(this);
+                    this.onPointerUpObservable.notifyObservers(coordinates);
                 }
             };
-            Control.prototype._processObservables = function (type) {
+            Control.prototype.forcePointerUp = function () {
+                this._onPointerUp(BABYLON.Vector2.Zero());
+            };
+            Control.prototype._processObservables = function (type, x, y) {
+                this._dummyVector2.copyFromFloats(x, y);
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                    this._onPointerMove();
+                    this._onPointerMove(this._dummyVector2);
                     var previousControlOver = this._host._lastControlOver;
                     if (previousControlOver && previousControlOver !== this) {
                         previousControlOver._onPointerOut();
@@ -1203,13 +1237,13 @@ var BABYLON;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                    this._onPointerDown();
+                    this._onPointerDown(this._dummyVector2);
                     this._host._lastControlDown = this;
                     return true;
                 }
                 if (type === BABYLON.PointerEventTypes.POINTERUP) {
                     if (this._host._lastControlDown) {
-                        this._host._lastControlDown._onPointerUp();
+                        this._host._lastControlDown._onPointerUp(this._dummyVector2);
                     }
                     this._host._lastControlDown = null;
                     return true;
@@ -1440,7 +1474,7 @@ var BABYLON;
                         return true;
                     }
                 }
-                return this._processObservables(type);
+                return this._processObservables(type, x, y);
             };
             Container.prototype._clipForChildren = function (context) {
                 // DO nothing
@@ -1584,20 +1618,6 @@ var BABYLON;
                 enumerable: true,
                 configurable: true
             });
-            Object.defineProperty(Rectangle.prototype, "background", {
-                get: function () {
-                    return this._background;
-                },
-                set: function (value) {
-                    if (this._background === value) {
-                        return;
-                    }
-                    this._background = value;
-                    this._markAsDirty();
-                },
-                enumerable: true,
-                configurable: true
-            });
             Rectangle.prototype._localDraw = function (context) {
                 context.save();
                 if (this._background) {
@@ -1968,25 +1988,161 @@ var BABYLON;
             function Slider(name) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
-                _this._barHeight = new GUI.ValueAndUnit(0.5, GUI.ValueAndUnit.UNITMODE_PERCENTAGE, false);
+                _this._thumbWidth = new GUI.ValueAndUnit(30, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
+                _this._minimum = 0;
+                _this._maximum = 100;
+                _this._value = 50;
+                _this._background = "black";
+                _this._foreground = "green";
+                _this._borderColor = "white";
+                _this._barOffset = new GUI.ValueAndUnit(5, GUI.ValueAndUnit.UNITMODE_PIXEL, false);
+                _this.onValueChangedObservable = new BABYLON.Observable();
+                // Events
+                _this._pointerIsDown = false;
+                _this.isPointerBlocker = true;
                 return _this;
             }
+            Object.defineProperty(Slider.prototype, "borderColor", {
+                get: function () {
+                    return this._borderColor;
+                },
+                set: function (value) {
+                    if (this._borderColor === value) {
+                        return;
+                    }
+                    this._borderColor = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Slider.prototype, "foreground", {
+                get: function () {
+                    return this._foreground;
+                },
+                set: function (value) {
+                    if (this._foreground === value) {
+                        return;
+                    }
+                    this._foreground = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Slider.prototype, "background", {
+                get: function () {
+                    return this._background;
+                },
+                set: function (value) {
+                    if (this._background === value) {
+                        return;
+                    }
+                    this._background = value;
+                    this._markAsDirty();
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Slider.prototype, "minimum", {
+                get: function () {
+                    return this._minimum;
+                },
+                set: function (value) {
+                    if (this._minimum === value) {
+                        return;
+                    }
+                    this._minimum = value;
+                    this._markAsDirty();
+                    this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Slider.prototype, "maximum", {
+                get: function () {
+                    return this._maximum;
+                },
+                set: function (value) {
+                    if (this._maximum === value) {
+                        return;
+                    }
+                    this._maximum = value;
+                    this._markAsDirty();
+                    this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
+                },
+                enumerable: true,
+                configurable: true
+            });
+            Object.defineProperty(Slider.prototype, "value", {
+                get: function () {
+                    return this._value;
+                },
+                set: function (value) {
+                    value = Math.max(Math.min(value, this._maximum), this._minimum);
+                    if (this._value === value) {
+                        return;
+                    }
+                    this._value = value;
+                    this._markAsDirty();
+                    this.onValueChangedObservable.notifyObservers(this._value);
+                },
+                enumerable: true,
+                configurable: true
+            });
             Slider.prototype._draw = function (parentMeasure, context) {
                 context.save();
                 this._applyStates(context);
                 if (this._processMeasures(parentMeasure, context)) {
                     // Main bar
-                    var effectiveBarHeight = 0;
-                    if (this._barHeight.isPixel) {
-                        effectiveBarHeight = Math.min(this._barHeight.getValue(this._host), this._currentMeasure.height);
+                    var effectiveThumbWidth;
+                    var effectiveBarOffset;
+                    if (this._thumbWidth.isPixel) {
+                        effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
                     }
                     else {
-                        effectiveBarHeight = this._currentMeasure.height * this._barHeight.getValue(this._host);
+                        effectiveThumbWidth = this._currentMeasure.height * this._thumbWidth.getValue(this._host);
                     }
-                    context.fillRect(this._currentMeasure.left, this._currentMeasure.top + (this._currentMeasure.height - effectiveBarHeight) / 2, this._currentMeasure.width, effectiveBarHeight);
+                    if (this._barOffset.isPixel) {
+                        effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
+                    }
+                    else {
+                        effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host);
+                    }
+                    var left = this._currentMeasure.left + effectiveThumbWidth / 2;
+                    var width = this._currentMeasure.width - effectiveThumbWidth;
+                    var thumbPosition = (this._value - this._minimum) / (this._maximum - this._minimum) * width;
+                    // Bar
+                    context.fillStyle = this._background;
+                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+                    context.fillStyle = this._foreground;
+                    context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+                    // Thumb
+                    context.fillRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+                    context.strokeStyle = this._borderColor;
+                    context.strokeRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
                 }
                 context.restore();
             };
+            Slider.prototype._updateValueFromPointer = function (x) {
+                this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
+            };
+            Slider.prototype._onPointerDown = function (coordinates) {
+                this._pointerIsDown = true;
+                this._updateValueFromPointer(coordinates.x);
+                this._host._capturingControl = this;
+                _super.prototype._onPointerDown.call(this, coordinates);
+            };
+            Slider.prototype._onPointerMove = function (coordinates) {
+                if (this._pointerIsDown) {
+                    this._updateValueFromPointer(coordinates.x);
+                }
+            };
+            Slider.prototype._onPointerUp = function (coordinates) {
+                this._pointerIsDown = false;
+                this._host._capturingControl = null;
+                _super.prototype._onPointerUp.call(this, coordinates);
+            };
             return Slider;
         }(GUI.Control));
         GUI.Slider = Slider;
@@ -2368,7 +2524,6 @@ var BABYLON;
             function Button(name) {
                 var _this = _super.call(this, name) || this;
                 _this.name = name;
-                _this._buttonIsDown = false;
                 _this.thickness = 1;
                 _this.isPointerBlocker = true;
                 _this.pointerEnterAnimation = function () {
@@ -2387,20 +2542,12 @@ var BABYLON;
                 };
                 return _this;
             }
-            Button.prototype._ensureButtonUp = function () {
-                if (this._buttonIsDown === true) {
-                    if (this.pointerUpAnimation) {
-                        this.pointerUpAnimation();
-                    }
-                    this._buttonIsDown = false;
-                }
-            };
             // While being a container, the button behaves like a control.
             Button.prototype._processPicking = function (x, y, type) {
                 if (!this.contains(x, y)) {
                     return false;
                 }
-                this._processObservables(type);
+                this._processObservables(type, x, y);
                 return true;
             };
             Button.prototype._onPointerEnter = function () {
@@ -2413,20 +2560,19 @@ var BABYLON;
                 if (this.pointerOutAnimation) {
                     this.pointerOutAnimation();
                 }
-                // in case you move the pointer out of view while pointer is "down".
-                this._ensureButtonUp();
                 _super.prototype._onPointerOut.call(this);
             };
-            Button.prototype._onPointerDown = function () {
+            Button.prototype._onPointerDown = function (coordinates) {
                 if (this.pointerDownAnimation) {
                     this.pointerDownAnimation();
                 }
-                this._buttonIsDown = true;
-                _super.prototype._onPointerDown.call(this);
+                _super.prototype._onPointerDown.call(this, coordinates);
             };
-            Button.prototype._onPointerUp = function () {
-                this._ensureButtonUp();
-                _super.prototype._onPointerUp.call(this);
+            Button.prototype._onPointerUp = function (coordinates) {
+                if (this.pointerUpAnimation) {
+                    this.pointerUpAnimation();
+                }
+                _super.prototype._onPointerUp.call(this, coordinates);
             };
             // Statics
             Button.CreateImageButton = function (name, text, imageUrl) {

文件差异内容过多而无法显示
+ 2 - 2
dist/preview release/gui/babylon.gui.min.js


+ 35 - 1
gui/src/advancedDynamicTexture.ts

@@ -7,10 +7,12 @@ module BABYLON.GUI {
         private _resizeObserver: Observer<Engine>;
         private _pointerMoveObserver: Observer<PointerInfoPre>;
         private _pointerObserver: Observer<PointerInfo>;
+        private _canvasBlurObserver: Observer<Engine>;
         private _background: string;
         public _rootContainer = new Container("root");
         public _lastControlOver: Control;
         public _lastControlDown: Control;
+        public _capturingControl: Control;
         public _shouldBlockPointer: boolean;
         public _layerToDispose: Layer;
         public _linkedControls = new Array<Control>();
@@ -58,7 +60,11 @@ module BABYLON.GUI {
             this._idealHeight = value;
             this.markAsDirty();
             this._rootContainer._markAllAsDirty();
-        }        
+        }     
+
+        public get layer(): Layer {
+            return this._layerToDispose;
+        }   
        
         constructor(name: string, width = 0, height = 0, scene: Scene, generateMipMaps = false, samplingMode = Texture.NEAREST_SAMPLINGMODE) {
             super(name, {width: width, height: height}, scene, generateMipMaps, samplingMode, Engine.TEXTUREFORMAT_RGBA);
@@ -107,6 +113,10 @@ module BABYLON.GUI {
                 this.getScene().onPointerObservable.remove(this._pointerObserver);
             }
 
+            if (this._canvasBlurObserver) {
+                this.getScene().getEngine().onCanvasBlurObservable.remove(this._canvasBlurObserver);
+            }
+
             if (this._layerToDispose) {
                 this._layerToDispose.texture = null;
                 this._layerToDispose.dispose();
@@ -135,6 +145,12 @@ module BABYLON.GUI {
         }
 
         private _checkUpdate(camera: Camera): void {
+            if (this._layerToDispose) {
+                if ((camera.layerMask & this._layerToDispose.layerMask) === 0) {
+                    return;
+                }
+            }
+
             if (this._isFullscreen && this._linkedControls.length) {
                 var scene = this.getScene();
                 var engine = scene.getEngine();
@@ -181,6 +197,11 @@ module BABYLON.GUI {
         }
 
         private _doPicking(x: number, y: number, type: number): void {
+            if (this._capturingControl) {
+                this._capturingControl._processObservables(type, x, y);
+                return;
+            }
+
             if (!this._rootContainer._processPicking(x, y, type)) {
 
                 if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
@@ -207,6 +228,19 @@ module BABYLON.GUI {
 
                 pi.skipOnPointerObservable = this._shouldBlockPointer && pi.type !== BABYLON.PointerEventTypes.POINTERUP;
             });
+
+            this._canvasBlurObserver = scene.getEngine().onCanvasBlurObservable.add(() => {
+                if (this._lastControlOver && this._lastControlOver.onPointerOutObservable.hasObservers()) {
+                    this._lastControlOver.onPointerOutObservable.notifyObservers(this._lastControlOver);
+                }
+                
+                this._lastControlOver = null;
+
+                if (this._lastControlDown) {
+                    this._lastControlDown.forcePointerUp();
+                }
+                this._lastControlDown = null;                
+            });
         }
 
         public attachToMesh(mesh: AbstractMesh): void {

+ 9 - 22
gui/src/controls/button.ts

@@ -7,8 +7,6 @@ module BABYLON.GUI {
         public pointerDownAnimation: () => void;
         public pointerUpAnimation: () => void;
 
-        private _buttonIsDown : boolean = false;
-
         constructor(public name?: string) {
             super(name);
             this.thickness = 1;
@@ -33,22 +31,13 @@ module BABYLON.GUI {
             }                      
         }
 
-        private _ensureButtonUp (): void {
-            if (this._buttonIsDown === true) {
-                if (this.pointerUpAnimation) {
-                    this.pointerUpAnimation();
-                }
-                this._buttonIsDown = false;
-            }
-        }
-
         // While being a container, the button behaves like a control.
         public _processPicking(x: number, y: number, type: number): boolean {
             if (!this.contains(x, y)) {
                 return false;
             }
 
-            this._processObservables(type);
+            this._processObservables(type, x, y);
 
             return true;
         }
@@ -65,25 +54,23 @@ module BABYLON.GUI {
                 this.pointerOutAnimation();
             }
 
-            // in case you move the pointer out of view while pointer is "down".
-            this._ensureButtonUp()
-
             super._onPointerOut();
         }
 
-        protected _onPointerDown(): void {
+        protected _onPointerDown(coordinates: Vector2): void {
             if (this.pointerDownAnimation) {
                 this.pointerDownAnimation();
             }
-            this._buttonIsDown = true;
 
-            super._onPointerDown();
+            super._onPointerDown(coordinates);
         }
 
-        protected _onPointerUp (): void {
-            this._ensureButtonUp()
-            
-            super._onPointerUp();
+        protected _onPointerUp(coordinates: Vector2): void {
+            if (this.pointerUpAnimation) {
+                this.pointerUpAnimation();
+            }
+
+            super._onPointerUp(coordinates);
         }        
 
         // Statics

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

@@ -133,7 +133,7 @@ module BABYLON.GUI {
                 }
             }
 
-            return this._processObservables(type);
+            return this._processObservables(type, x, y);
         }
 
         protected _clipForChildren(context: CanvasRenderingContext2D): void {

+ 22 - 16
gui/src/controls/control.ts

@@ -8,7 +8,7 @@ module BABYLON.GUI {
         public _host: AdvancedDynamicTexture;
         public _currentMeasure = Measure.Empty();
         private _fontFamily = "Arial";
-        private _fontSize = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PIXEL, false);
+        private _fontSize = new ValueAndUnit(18, ValueAndUnit.UNITMODE_PIXEL, false);
         private _font: string;
         public _width = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
         public _height = new ValueAndUnit(1, ValueAndUnit.UNITMODE_PERCENTAGE, false);
@@ -39,6 +39,7 @@ module BABYLON.GUI {
         private _isVisible = true;
         public _linkedMesh: AbstractMesh;
         private _fontSet = false;
+        private _dummyVector2 = Vector2.Zero();
 
         public isHitTestVisible = true;
         public isPointerBlocker = false;
@@ -52,7 +53,7 @@ module BABYLON.GUI {
         * An event triggered when the pointer move over the control.
         * @type {BABYLON.Observable}
         */
-        public onPointerMoveObservable = new Observable<Control>();
+        public onPointerMoveObservable = new Observable<Vector2>();
 
         /**
         * An event triggered when the pointer move out of the control.
@@ -64,13 +65,13 @@ module BABYLON.GUI {
         * An event triggered when the pointer taps the control
         * @type {BABYLON.Observable}
         */
-        public onPointerDownObservable = new Observable<Control>();     
+        public onPointerDownObservable = new Observable<Vector2>();     
 
         /**
         * An event triggered when pointer up
         * @type {BABYLON.Observable}
         */
-        public onPointerUpObservable = new Observable<Control>();     
+        public onPointerUpObservable = new Observable<Vector2>();     
 
         /**
         * An event triggered when pointer enters the control
@@ -684,14 +685,14 @@ module BABYLON.GUI {
                 return false;
             }
 
-            this._processObservables(type);
+            this._processObservables(type, x, y);
 
             return true;
         }
 
-        protected _onPointerMove(): void {
+        protected _onPointerMove(coordinates: Vector2): void {
             if (this.onPointerMoveObservable.hasObservers()) {
-                this.onPointerMoveObservable.notifyObservers(this);
+                this.onPointerMoveObservable.notifyObservers(coordinates);
             }
         }
 
@@ -707,25 +708,30 @@ module BABYLON.GUI {
             }
         }
 
-        protected _onPointerDown(): void {
+        protected _onPointerDown(coordinates: Vector2): void {
             if (this.onPointerDownObservable.hasObservers()) {
-                this.onPointerDownObservable.notifyObservers(this);
+                this.onPointerDownObservable.notifyObservers(coordinates);
             }
         }
 
-        protected _onPointerUp(): void {
+        protected _onPointerUp(coordinates: Vector2): void {
             if (this.onPointerUpObservable.hasObservers()) {
-                this.onPointerUpObservable.notifyObservers(this);
+                this.onPointerUpObservable.notifyObservers(coordinates);
             }
         }
 
-        protected _processObservables(type: number): boolean {
+        public forcePointerUp() {
+            this._onPointerUp(Vector2.Zero());
+        }
+
+        public _processObservables(type: number, x: number, y: number): boolean {
+            this._dummyVector2.copyFromFloats(x, y);
             if (type === BABYLON.PointerEventTypes.POINTERMOVE) {
-                this._onPointerMove();
+                this._onPointerMove(this._dummyVector2);
 
                 var previousControlOver = this._host._lastControlOver;
                 if (previousControlOver && previousControlOver !== this) {
-                    previousControlOver._onPointerOut();
+                    previousControlOver._onPointerOut();                
                 }
 
                 if (previousControlOver !== this) {
@@ -737,14 +743,14 @@ module BABYLON.GUI {
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERDOWN) {
-                this._onPointerDown();
+                this._onPointerDown(this._dummyVector2);
                 this._host._lastControlDown = this;
                 return true;
             }
 
             if (type === BABYLON.PointerEventTypes.POINTERUP) {
                 if (this._host._lastControlDown) {
-                    this._host._lastControlDown._onPointerUp();
+                    this._host._lastControlDown._onPointerUp(this._dummyVector2);
                 }
                 this._host._lastControlDown = null;
                 return true;

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

@@ -35,19 +35,6 @@ module BABYLON.GUI {
             this._markAsDirty();
         }   
        
-        public get background(): string {
-            return this._background;
-        }
-
-        public set background(value: string) {
-            if (this._background === value) {
-                return;
-            }
-
-            this._background = value;
-            this._markAsDirty();
-        }           
-     
         constructor(public name?: string) {
             super(name);
         }

+ 154 - 6
gui/src/controls/slider.ts

@@ -4,10 +4,106 @@ var DOMImage = Image;
 
 module BABYLON.GUI {
     export class Slider extends Control {
-        private _barHeight = new ValueAndUnit(0.5, ValueAndUnit.UNITMODE_PERCENTAGE, false);
+        private _thumbWidth = new ValueAndUnit(30, ValueAndUnit.UNITMODE_PIXEL, false);
+        private _minimum = 0; 
+        private _maximum = 100;
+        private _value = 50;
+        private _background = "black";   
+        private _foreground = "green";
+        private _borderColor = "white";
+        private _barOffset = new ValueAndUnit(5, ValueAndUnit.UNITMODE_PIXEL, false);
+
+        public onValueChangedObservable = new Observable<number>();
+
+        public get borderColor(): string {
+            return this._borderColor;
+        }
+
+        public set borderColor(value: string) {
+            if (this._borderColor === value) {
+                return;
+            }
+
+            this._borderColor = value;
+            this._markAsDirty();
+        }  
+
+        public get foreground(): string {
+            return this._foreground;
+        }
+
+        public set foreground(value: string) {
+            if (this._foreground === value) {
+                return;
+            }
+
+            this._foreground = value;
+            this._markAsDirty();
+        }          
+
+        public get background(): string {
+            return this._background;
+        }
+
+        public set background(value: string) {
+            if (this._background === value) {
+                return;
+            }
+
+            this._background = value;
+            this._markAsDirty();
+        }         
+
+        public get minimum(): number {
+            return this._minimum;
+        }
+
+        public set minimum(value: number) {
+            if (this._minimum === value) {
+                return;
+            }
+
+            this._minimum = value;
+            this._markAsDirty();
+
+            this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
+        }         
+
+        public get maximum(): number {
+            return this._maximum;
+        }
+
+        public set maximum(value: number) {
+            if (this._maximum === value) {
+                return;
+            }
+
+            this._maximum = value;
+            this._markAsDirty();
+
+            this.value = Math.max(Math.min(this.value, this._maximum), this._minimum);
+        }     
+
+        public get value(): number {
+            return this._value;
+        }
+
+        public set value(value: number) {
+            value = Math.max(Math.min(value, this._maximum), this._minimum);
+            if (this._value === value) {
+                return;
+            }
+
+            this._value = value;
+            this._markAsDirty();
+
+            this.onValueChangedObservable.notifyObservers(this._value);
+        }                             
 
         constructor(public name?: string) {
             super(name);
+
+            this.isPointerBlocker = true;
         }
 
         public _draw(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
@@ -16,17 +112,69 @@ module BABYLON.GUI {
             this._applyStates(context);
             if (this._processMeasures(parentMeasure, context)) {
                 // Main bar
-                var effectiveBarHeight = 0;
+                var effectiveThumbWidth;
+                var effectiveBarOffset;
 
-                if (this._barHeight.isPixel) {
-                    effectiveBarHeight = Math.min(this._barHeight.getValue(this._host), this._currentMeasure.height);
+                if (this._thumbWidth.isPixel) {
+                    effectiveThumbWidth = Math.min(this._thumbWidth.getValue(this._host), this._currentMeasure.height);
                 } else {
-                    effectiveBarHeight = this._currentMeasure.height * this._barHeight.getValue(this._host); 
+                    effectiveThumbWidth = this._currentMeasure.height * this._thumbWidth.getValue(this._host); 
                 }
 
-                context.fillRect(this._currentMeasure.left, this._currentMeasure.top + (this._currentMeasure.height - effectiveBarHeight) / 2, this._currentMeasure.width, effectiveBarHeight);
+                if (this._barOffset.isPixel) {
+                    effectiveBarOffset = Math.min(this._barOffset.getValue(this._host), this._currentMeasure.height);
+                } else {
+                    effectiveBarOffset = this._currentMeasure.height * this._barOffset.getValue(this._host); 
+                }                
+
+
+                var left = this._currentMeasure.left + effectiveThumbWidth / 2;
+                var width = this._currentMeasure.width - effectiveThumbWidth;
+                var thumbPosition = (this._value - this._minimum) / (this._maximum - this._minimum) * width;
+
+                // Bar
+                context.fillStyle = this._background;
+                context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, width, this._currentMeasure.height - effectiveBarOffset * 2);
+
+                context.fillStyle = this._foreground;
+                context.fillRect(left, this._currentMeasure.top + effectiveBarOffset, thumbPosition, this._currentMeasure.height - effectiveBarOffset * 2);
+
+                // Thumb
+                context.fillRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
+
+                context.strokeStyle = this._borderColor;
+                context.strokeRect(left + thumbPosition - effectiveThumbWidth / 2, this._currentMeasure.top, effectiveThumbWidth, this._currentMeasure.height);
             }
             context.restore();
         }
+
+        // Events
+        private _pointerIsDown = false;
+
+        private _updateValueFromPointer(x: number): void {
+            this.value = this._minimum + ((x - this._currentMeasure.left) / this._currentMeasure.width) * (this._maximum - this._minimum);
+        }
+
+        protected _onPointerDown(coordinates: Vector2): void {
+            this._pointerIsDown = true;
+
+            this._updateValueFromPointer(coordinates.x);
+            this._host._capturingControl = this;
+
+            super._onPointerDown(coordinates);
+        }
+
+        protected _onPointerMove(coordinates: Vector2): void {
+            if (this._pointerIsDown) {
+                this._updateValueFromPointer(coordinates.x);
+            }
+        }
+
+        protected _onPointerUp (coordinates: Vector2): void {
+            this._pointerIsDown = false;
+            
+            this._host._capturingControl = null;
+            super._onPointerUp(coordinates);
+        }         
     }    
 }

+ 0 - 9
localDev/template/index.js

@@ -1,9 +0,0 @@
-// <reference path="../../dist/preview release/babylon.d.ts"/>
-
-var createScene = function() {
-	var scene = new BABYLON.Scene(engine);
-	var camera = new BABYLON.ArcRotateCamera("Camera", 0, Math.PI / 2, 12, BABYLON.Vector3.Zero(), scene);
-	camera.attachControl(canvas, true);
-
-	return scene;
-};

+ 13 - 0
src/babylon.engine.ts

@@ -460,6 +460,11 @@
          */
         public onResizeObservable = new Observable<Engine>();
 
+        /**
+         * Observable event triggered each time the canvas lost focus
+         */
+        public onCanvasBlurObservable = new Observable<Engine>();
+
         //WebVR 
 
         //The new WebVR uses promises.
@@ -482,6 +487,7 @@
 
         public static audioEngine: AudioEngine;
 
+        private _onCanvasBlur: () => void;
         private _onBlur: () => void;
         private _onFocus: () => void;
         private _onFullscreenChange: () => void;
@@ -637,9 +643,15 @@
                 this._windowIsBackground = false;
             };
 
+            this._onCanvasBlur = () => {
+                this.onCanvasBlurObservable.notifyObservers(this);
+            };
+
             window.addEventListener("blur", this._onBlur);
             window.addEventListener("focus", this._onFocus);
 
+            canvas.addEventListener("pointerout", this._onCanvasBlur);
+
             // Viewport
             var limitDeviceRatio = options.limitDeviceRatio || window.devicePixelRatio || 1.0;
             this._hardwareScalingLevel = adaptToDeviceRatio ? 1.0 / Math.min(limitDeviceRatio, window.devicePixelRatio || 1.0) : 1.0;
@@ -3697,6 +3709,7 @@
             // Events
             window.removeEventListener("blur", this._onBlur);
             window.removeEventListener("focus", this._onFocus);
+            this._renderingCanvas.removeEventListener("blur", this._onCanvasBlur);
             document.removeEventListener("fullscreenchange", this._onFullscreenChange);
             document.removeEventListener("mozfullscreenchange", this._onFullscreenChange);
             document.removeEventListener("webkitfullscreenchange", this._onFullscreenChange);