Browse Source

Several improvements for GUI pipeline (including layout phase before drawing)
Reworked scrollviewer
Associated with #5458

David Catuhe 6 năm trước cách đây
mục cha
commit
95bd6df02e

+ 3 - 2
gui/src/2D/advancedDynamicTexture.ts

@@ -826,16 +826,17 @@ export class AdvancedDynamicTexture extends DynamicTexture {
     }
 
     private _attachToOnPointerOut(scene: Scene): void {
+
         this._canvasPointerOutObserver = scene.getEngine().onCanvasPointerOutObservable.add((pointerEvent) => {
             if (this._lastControlOver[pointerEvent.pointerId]) {
                 this._lastControlOver[pointerEvent.pointerId]._onPointerOut(this._lastControlOver[pointerEvent.pointerId]);
             }
             delete this._lastControlOver[pointerEvent.pointerId];
 
-            if (this._lastControlDown[pointerEvent.pointerId]) {
+            if (this._lastControlDown[pointerEvent.pointerId] && this._lastControlDown[pointerEvent.pointerId] !== this._capturingControl[pointerEvent.pointerId]) {
                 this._lastControlDown[pointerEvent.pointerId]._forcePointerUp();
+                delete this._lastControlDown[pointerEvent.pointerId];
             }
-            delete this._lastControlDown[pointerEvent.pointerId];
         });
     }
 

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

@@ -40,7 +40,7 @@ export class BaseSlider extends Control {
 
         this._displayThumb = value;
         this._markAsDirty();
-    }
+    }  
 
     /** Gets or sets main bar offset (ie. the margin applied to the value bar) */
     public get barOffset(): string | number {

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

@@ -257,6 +257,11 @@ export class Container extends Control {
     }
 
     /** @hidden */
+    protected _beforeLayout() {
+        // Do nothing
+    }
+
+    /** @hidden */
     public _layout(parentMeasure: Measure, context: CanvasRenderingContext2D): boolean {
         if (!this.isVisible || this.notRenderable) {
             return false;
@@ -268,6 +273,8 @@ export class Container extends Control {
 
         this._applyStates(context);
 
+        this._beforeLayout();
+
         do
         {
             let computedWidth = -1;

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

@@ -1144,13 +1144,14 @@ export class Control {
 
         context.restore();
 
+        this._isDirty = false;
+
         return true;
     }
 
     /** @hidden */
     protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
         if (this._isDirty || !this._cachedParentMeasure.isEqualsTo(parentMeasure)) {
-            this._isDirty = false;
             this._currentMeasure.copyFrom(parentMeasure);
 
             // Let children take some pre-measurement actions

+ 10 - 4
gui/src/2D/controls/grid.ts

@@ -72,6 +72,11 @@ export class Grid extends Container {
             return this;
         }
 
+        let current = this._rowDefinitions[index];
+        if (current && current.isPixel === isPixel && current.internalValue === height) {
+            return this;
+        }
+
         this._rowDefinitions[index] = new ValueAndUnit(height, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
 
         this._markAsDirty();
@@ -91,6 +96,11 @@ export class Grid extends Container {
             return this;
         }
 
+        let current = this._columnDefinitions[index];
+        if (current && current.isPixel === isPixel && current.internalValue === width) {
+            return this;
+        }
+
         this._columnDefinitions[index] = new ValueAndUnit(width, isPixel ? ValueAndUnit.UNITMODE_PIXEL : ValueAndUnit.UNITMODE_PERCENTAGE);
 
         this._markAsDirty();
@@ -389,10 +399,6 @@ export class Grid extends Container {
     }
 
     public _renderHighlightSpecific(context: CanvasRenderingContext2D): void {
-        if (!this.isHighlighted) {
-            return;
-        }
-
         super._renderHighlightSpecific(context);
 
         this._getGridDefinitions((lefts: number[], tops: number[], widths: number[], heights: number[]) => {

+ 88 - 76
gui/src/2D/controls/scrollViewer.ts

@@ -1,11 +1,11 @@
-import { Measure } from "../measure";
 import { Rectangle } from "./rectangle";
 import { Grid } from "./grid";
 import { Control } from "./control";
 import { Slider } from "./slider";
 import { Container } from "./container";
 import { PointerInfo, Observer, Nullable } from "babylonjs";
-import { AdvancedDynamicTexture } from "2D";
+import { AdvancedDynamicTexture, Measure } from "2D";
+import { _ScrollViewerWindow } from "./scrollViewerWindow";
 
 /**
  * Class used to hold a viewer window and sliders in a grid
@@ -14,20 +14,20 @@ export class ScrollViewer extends Rectangle {
     private _grid: Grid;
     private _horizontalBarSpace: Rectangle;
     private _verticalBarSpace: Rectangle;
-    private _dragSpace: Rectangle;
     private _horizontalBar: Slider;
     private _verticalBar: Slider;
-    private _barColor: string = "grey";
-    private _barBorderColor: string = "#444444";
-    private _barBackground: string = "white";
-    private _scrollGridWidth: number = 30;
-    private _scrollGridHeight: number = 30;
+    private _barColor: string;
+    private _barBorderColor: string;
+    private _barBackground: string ;
+    private _barSize: number = 20;
     private _endLeft: number;
     private _endTop: number;
-    private _window: Container;
+    private _window: _ScrollViewerWindow;
     private _pointerIsOver: Boolean = false;
     private _wheelPrecision: number = 0.05;
     private _onPointerObserver: Nullable<Observer<PointerInfo>>;
+    private _clientWidth: number;
+    private _clientHeight: number;
 
     /**
      * Adds a new control to the current container
@@ -67,15 +67,12 @@ export class ScrollViewer extends Rectangle {
     * Creates a new ScrollViewer
     * @param name of ScrollViewer
     */
-    constructor(
-        /** name of ScrollViewer */
-        public name?: string) {
+    constructor(name?: string) {
         super(name);
 
         this.onDirtyObservable.add(() => {
             this._horizontalBarSpace.color = this.color;
             this._verticalBarSpace.color = this.color;
-            this._dragSpace.color = this.color;
         });
 
         this.onPointerEnterObservable.add(() => {
@@ -90,99 +87,97 @@ export class ScrollViewer extends Rectangle {
         this._horizontalBar = new Slider();
         this._verticalBar = new Slider();
 
-        this._window = new Container();
+        this._window = new _ScrollViewerWindow();
         this._window.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         this._window.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
 
-        this._grid.addColumnDefinition(1, true);
-        this._grid.addColumnDefinition(this._scrollGridWidth, true);
-        this._grid.addRowDefinition(1, true);
-        this._grid.addRowDefinition(this._scrollGridHeight, true);
+        this._grid.addColumnDefinition(1);
+        this._grid.addColumnDefinition(0, true);
+        this._grid.addRowDefinition(1);
+        this._grid.addRowDefinition(0, true);
 
         super.addControl(this._grid);
         this._grid.addControl(this._window, 0, 0);
 
         this._verticalBar.paddingLeft = 0;
-        this._verticalBar.width = "25px";
+        this._verticalBar.width = "100%";
+        this._verticalBar.height = "100%";
+        this._verticalBar.barOffset = 0;
         this._verticalBar.value = 0;
-        this._verticalBar.maximum = 100;
+        this._verticalBar.maximum = 1;
         this._verticalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         this._verticalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
-        this._verticalBar.left = 0.05;
         this._verticalBar.isThumbClamped = true;
-        this._verticalBar.color = "grey";
-        this._verticalBar.borderColor = "#444444";
-        this._verticalBar.background = "white";
         this._verticalBar.isVertical = true;
         this._verticalBar.rotation = Math.PI;
+        this._verticalBar.displayValueBar = false;
+        this._verticalBar.isVisible = false;
 
         this._verticalBarSpace = new Rectangle();
         this._verticalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         this._verticalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
-        this._verticalBarSpace.color = this.color;
         this._verticalBarSpace.thickness = 1;
         this._grid.addControl(this._verticalBarSpace, 0, 1);
         this._verticalBarSpace.addControl(this._verticalBar);
 
         this._verticalBar.onValueChangedObservable.add((value) => {
-            this._window.top = value * this._endTop / 100 + "px";
+            this._window.top = value * this._endTop + "px";
         });
 
         this._horizontalBar.paddingLeft = 0;
-        this._horizontalBar.height = "25px";
+        this._horizontalBar.width = "100%";
+        this._horizontalBar.height = "100%";
+        this._horizontalBar.barOffset = 0;
         this._horizontalBar.value = 0;
-        this._horizontalBar.maximum = 100;
+        this._horizontalBar.maximum = 1;
         this._horizontalBar.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_CENTER;
         this._horizontalBar.verticalAlignment = Control.VERTICAL_ALIGNMENT_CENTER;
-        this._horizontalBar.left = 0.05;
         this._horizontalBar.isThumbClamped = true;
-        this._horizontalBar.color = "grey";
-        this._horizontalBar.borderColor = "#444444";
-        this._horizontalBar.background = "white";
+        this._horizontalBar.displayValueBar = false;
+        this._horizontalBar.isVisible = false;
 
         this._horizontalBarSpace = new Rectangle();
         this._horizontalBarSpace.horizontalAlignment = Control.HORIZONTAL_ALIGNMENT_LEFT;
         this._horizontalBarSpace.verticalAlignment = Control.VERTICAL_ALIGNMENT_TOP;
-        this._horizontalBarSpace.color = this.color;
         this._horizontalBarSpace.thickness = 1;
         this._grid.addControl(this._horizontalBarSpace, 1, 0);
         this._horizontalBarSpace.addControl(this._horizontalBar);
 
         this._horizontalBar.onValueChangedObservable.add((value) => {
-            this._window.left = value * this._endLeft / 100 + "px";
+            this._window.left = value * this._endLeft + "px";
         });
 
-        this._dragSpace = new Rectangle();
-        this._dragSpace.color = this.color;
-        this._dragSpace.thickness = 2;
-        this._dragSpace.background = this._barColor;
-        this._grid.addControl(this._dragSpace, 1, 1);
+        // Colors
+        this.barColor = "grey";
+        this.barBackground = "transparent";
+    }
+
+    /** Reset the scroll viewer window to initial size */
+    public resetWindow() {
+        this._window.width = "100%";
+        this._window.height = "100%";
     }
 
     protected _getTypeName(): string {
         return "ScrollViewer";
     }
 
+    private _buildClientSizes() {
+        this._window.parentClientWidth = this._currentMeasure.width - (this._verticalBar.isVisible ? this._barSize : 0) - 2 * this.thickness;
+        this._window.parentClientHeight = this._currentMeasure.height - (this._horizontalBar.isVisible ? this._barSize : 0) - 2 * this.thickness;
+
+        this._clientWidth = this._window.parentClientWidth;
+        this._clientHeight = this._window.parentClientHeight;
+    }
+    
     protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
         super._additionalProcessing(parentMeasure, context);
 
-        this._measureForChildren.left = 0;
-        this._measureForChildren.top = 0;
-        
-        this._measureForChildren.width = this.widthInPixels - 2 * this.thickness;
-        this._measureForChildren.height = this.heightInPixels - 2 * this.thickness;
+        this._buildClientSizes();
     }
-    
-    protected _processMeasures(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
-        super._processMeasures(parentMeasure, context);
-        
-        let innerWidth = this.widthInPixels - this._scrollGridWidth - 2 * this.thickness;
-        let innerHeight = this.heightInPixels  - this._scrollGridHeight - 2 * this.thickness;
-        this._horizontalBar.width = (innerWidth * 0.8) + "px";
-        this._verticalBar.height = (innerHeight * 0.8) + "px";
 
-        this._grid.setColumnDefinition(0, innerWidth, true);
-        this._grid.setRowDefinition(0, innerHeight, true);
+    protected _postMeasure(): void {
+        super._postMeasure();
 
         this._updateScroller();
     }
@@ -224,7 +219,27 @@ export class ScrollViewer extends Rectangle {
         this._barColor = color;
         this._horizontalBar.color = color;
         this._verticalBar.color = color;
-        this._dragSpace.background = color;
+    }
+
+    /** Gets or sets the size of the bar */
+    public get barSize(): number {
+        return this._barSize;
+    }
+
+    public set barSize(value: number) {
+        if (this._barSize === value) {
+            return;
+        }
+
+        this._barSize = value;
+        this._markAsDirty();
+
+        if (this._horizontalBar.isVisible) {
+            this._grid.setRowDefinition(1, this._barSize, true);
+        }
+        if (this._verticalBar.isVisible) {
+            this._grid.setColumnDefinition(1, this._barSize, true);
+        }
     }
 
     /** Gets or sets the bar color */
@@ -259,39 +274,36 @@ export class ScrollViewer extends Rectangle {
 
     /** @hidden */
     private _updateScroller(): void {
-        let windowContentsWidth = this._window.widthInPixels;
-        let windowContentsHeight = this._window.heightInPixels;
-
-        let viewerWidth = this.widthInPixels;
-        let viewerHeight = this.heightInPixels;
-
-        let innerWidth = viewerWidth - this._scrollGridWidth - 2 * this.thickness;
-        let innerHeight = viewerHeight  - this._scrollGridHeight - 2 * this.thickness;
+        let windowContentsWidth = this._window._currentMeasure.width;
+        let windowContentsHeight = this._window._currentMeasure.height;
 
-        if (windowContentsWidth <= innerWidth) {
-            this._grid.setRowDefinition(0, viewerHeight - 2 * this.thickness , true);
+        if (this._horizontalBar.isVisible && windowContentsWidth <= this._clientWidth) {
             this._grid.setRowDefinition(1, 0, true);
             this._horizontalBar.isVisible = false;
+            this._horizontalBar.value = 0;
+            this._rebuildLayout = true;
         }
-        else {
-            this._grid.setRowDefinition(0, innerHeight, true);
-            this._grid.setRowDefinition(1, this._scrollGridHeight, true);
+        else if (!this._horizontalBar.isVisible && windowContentsWidth > this._clientWidth) {
+            this._grid.setRowDefinition(1, this._barSize, true);
             this._horizontalBar.isVisible = true;
+            this._rebuildLayout = true;
         }
 
-        if (windowContentsHeight < innerHeight) {
-            this._grid.setColumnDefinition(0, viewerWidth - 2 * this.thickness, true);
+        if (this._verticalBar.isVisible && windowContentsHeight <= this._clientHeight) {
             this._grid.setColumnDefinition(1, 0, true);
             this._verticalBar.isVisible = false;
+            this._verticalBar.value = 0;
+            this._rebuildLayout = true;
         }
-        else {
-            this._grid.setColumnDefinition(0, innerWidth, true);
-            this._grid.setColumnDefinition(1, this._scrollGridWidth, true);
+        else if (!this._verticalBar.isVisible && windowContentsHeight > this._clientHeight) {
+            this._grid.setColumnDefinition(1, this._barSize, true);
             this._verticalBar.isVisible = true;
+            this._rebuildLayout = true;
         }
 
-        this._endLeft = innerWidth - windowContentsWidth;
-        this._endTop = innerHeight - windowContentsHeight;
+        this._buildClientSizes();
+        this._endLeft = this._clientWidth - windowContentsWidth;
+        this._endTop = this._clientHeight - windowContentsHeight;
     }
 
     public _link(host: AdvancedDynamicTexture): void {
@@ -335,7 +347,7 @@ export class ScrollViewer extends Rectangle {
 
         super._renderHighlightSpecific(context);
 
-        this._window._renderHighlightSpecific(context);
+        this._grid._renderHighlightSpecific(context);
 
         context.restore();
     }

+ 63 - 0
gui/src/2D/controls/scrollViewerWindow.ts

@@ -0,0 +1,63 @@
+import { Measure } from "../measure";
+import { Container } from "./container";
+import { ValueAndUnit } from "../valueAndUnit";
+
+/**
+ * Class used to hold a the container for ScrollViewer
+ * @hidden
+*/
+export class _ScrollViewerWindow extends Container {
+    public parentClientWidth: number;
+    public parentClientHeight: number;
+
+    /**
+    * Creates a new ScrollViewerWindow
+    * @param name of ScrollViewerWindow
+    */
+    constructor(name?: string) {
+        super(name);
+    }
+
+    protected _getTypeName(): string {
+        return "ScrollViewerWindow";
+    }
+
+    /** @hidden */
+    protected _additionalProcessing(parentMeasure: Measure, context: CanvasRenderingContext2D): void {
+        super._additionalProcessing(parentMeasure, context);
+
+        this._measureForChildren.left = this._currentMeasure.left;
+        this._measureForChildren.top = this._currentMeasure.top;
+        
+        this._measureForChildren.width = this._currentMeasure.width;       
+        this._measureForChildren.height = this._currentMeasure.height;
+    }
+
+    protected _postMeasure(): void {
+        var maxWidth = this.parentClientWidth;
+        var maxHeight = this.parentClientHeight;
+        for (var child of this.children) {
+            if (!child.isVisible || child.notRenderable) {
+                continue;
+            }
+
+            maxWidth = Math.max(maxWidth, child._currentMeasure.width);
+            maxHeight = Math.max(maxHeight, child._currentMeasure.height);
+        }
+
+        if (this._currentMeasure.width !== maxWidth) {
+            this._width = new ValueAndUnit(maxWidth, ValueAndUnit.UNITMODE_PIXEL);
+            this._currentMeasure.width = maxWidth;
+            this._rebuildLayout = true;
+        }
+
+        if (this._currentMeasure.height !== maxHeight) {
+            this._height = new ValueAndUnit(maxHeight, ValueAndUnit.UNITMODE_PIXEL);
+            this._currentMeasure.height = maxHeight;
+            this._rebuildLayout = true;
+        }
+
+        super._postMeasure();
+    }
+
+}

+ 38 - 21
gui/src/2D/controls/slider.ts

@@ -7,6 +7,21 @@ export class Slider extends BaseSlider {
     private _background = "black";
     private _borderColor = "white";
     private _isThumbCircle = false;
+    protected _displayValueBar = true;
+
+    /** Gets or sets a boolean indicating if the value bar must be rendered */
+    public get displayValueBar(): boolean {
+        return this._displayValueBar;
+    }
+
+    public set displayValueBar(value: boolean) {
+        if (this._displayValueBar === value) {
+            return;
+        }
+
+        this._displayValueBar = value;
+        this._markAsDirty();
+    }      
 
     /** Gets or sets border color */
     public get borderColor(): string {
@@ -139,37 +154,39 @@ export class Slider extends BaseSlider {
 
         // Value bar
         context.fillStyle = this.color;
-        if (this.isVertical) {
-            if (this.isThumbClamped) {
-                if (this.isThumbCircle) {
-                    context.beginPath();
-                    context.arc(left + this._backgroundBoxThickness / 2, top + this._backgroundBoxLength, radius, 0, 2 * Math.PI);
-                    context.fill();
-                    context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+        if (this._displayValueBar) {
+            if (this.isVertical) {
+                if (this.isThumbClamped) {
+                    if (this.isThumbCircle) {
+                        context.beginPath();
+                        context.arc(left + this._backgroundBoxThickness / 2, top + this._backgroundBoxLength, radius, 0, 2 * Math.PI);
+                        context.fill();
+                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
+                    }
+                    else {
+                        context.fillRect(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
+                    }
                 }
                 else {
-                    context.fillRect(left, top + thumbPosition, width, height - thumbPosition + this._effectiveThumbThickness);
+                    context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
                 }
             }
             else {
-                context.fillRect(left, top + thumbPosition, width, height - thumbPosition);
-            }
-        }
-        else {
-            if (this.isThumbClamped) {
-                if (this.isThumbCircle) {
-                    context.beginPath();
-                    context.arc(left, top + this._backgroundBoxThickness / 2, radius, 0, 2 * Math.PI);
-                    context.fill();
-                    context.fillRect(left, top, thumbPosition, height);
+                if (this.isThumbClamped) {
+                    if (this.isThumbCircle) {
+                        context.beginPath();
+                        context.arc(left, top + this._backgroundBoxThickness / 2, radius, 0, 2 * Math.PI);
+                        context.fill();
+                        context.fillRect(left, top, thumbPosition, height);
+                    }
+                    else {
+                        context.fillRect(left, top, thumbPosition, height);
+                    }
                 }
                 else {
                     context.fillRect(left, top, thumbPosition, height);
                 }
             }
-            else {
-                context.fillRect(left, top, thumbPosition, height);
-            }
         }
 
         // Thumb

+ 5 - 3
gui/src/2D/controls/textBlock.ts

@@ -246,7 +246,9 @@ export class TextBlock extends Control {
         }
 
         if (this._resizeToFit) {
-            this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
+            if (this._textWrapping === TextWrapping.Clip) {
+                this.width = this.paddingLeftInPixels + this.paddingRightInPixels + maxLineWidth + 'px';
+            }
             this.height = this.paddingTopInPixels + this.paddingBottomInPixels + this._fontOffset.height * this._lines.length + 'px';
         }
 
@@ -305,11 +307,11 @@ export class TextBlock extends Control {
         var lines = [];
         var _lines = this.text.split("\n");
 
-        if (this._textWrapping === TextWrapping.Ellipsis && !this._resizeToFit) {
+        if (this._textWrapping === TextWrapping.Ellipsis) {
             for (var _line of _lines) {
                 lines.push(this._parseLineEllipsis(_line, refWidth, context));
             }
-        } else if (this._textWrapping === TextWrapping.WordWrap && !this._resizeToFit) {
+        } else if (this._textWrapping === TextWrapping.WordWrap) {
             for (var _line of _lines) {
                 lines.push(...this._parseLineWordWrap(_line, refWidth, context));
             }

+ 9 - 1
inspector/src/components/actionTabs/tabs/propertyGridTabComponent.tsx

@@ -19,7 +19,7 @@ import { TextBlockPropertyGridComponent } from "./propertyGrids/gui/textBlockPro
 import { TextBlock } from "babylonjs-gui/2D/controls/textBlock";
 import { InputText } from "babylonjs-gui/2D/controls/inputText";
 import { InputTextPropertyGridComponent } from "./propertyGrids/gui/inputTextPropertyGridComponent";
-import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line } from "babylonjs-gui";
+import { ColorPicker, Image, Slider, ImageBasedSlider, Rectangle, Ellipse, Checkbox, RadioButton, Line, ScrollViewer } from "babylonjs-gui";
 import { ColorPickerPropertyGridComponent } from "./propertyGrids/gui/colorPickerPropertyGridComponent";
 import { AnimationGroupGridComponent } from "./propertyGrids/animationGroupPropertyGridComponent";
 import { LockObject } from "./propertyGrids/lockObject";
@@ -31,6 +31,7 @@ import { EllipsePropertyGridComponent } from "./propertyGrids/gui/ellipsePropert
 import { CheckboxPropertyGridComponent } from "./propertyGrids/gui/checkboxPropertyGridComponent";
 import { RadioButtonPropertyGridComponent } from "./propertyGrids/gui/radioButtonPropertyGridComponent";
 import { LinePropertyGridComponent } from "./propertyGrids/gui/linePropertyGridComponent";
+import { ScrollViewerPropertyGridComponent } from "./propertyGrids/gui/scrollViewerPropertyGridComponent";
 
 export class PropertyGridTabComponent extends PaneComponent {
     private _timerIntervalId: number;
@@ -214,6 +215,13 @@ export class PropertyGridTabComponent extends PaneComponent {
                     onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
             }
 
+            if (className === "ScrollViewer") {
+                const scrollViewer = entity as ScrollViewer;
+                return (<ScrollViewerPropertyGridComponent scrollViewer={scrollViewer}
+                    lockObject={this._lockObject}
+                    onPropertyChangedObservable={this.props.onPropertyChangedObservable} />);
+            }            
+
             if (className === "Ellipse") {
                 const ellipse = entity as Ellipse;
                 return (<EllipsePropertyGridComponent ellipse={ellipse}

+ 42 - 0
inspector/src/components/actionTabs/tabs/propertyGrids/gui/scrollViewerPropertyGridComponent.tsx

@@ -0,0 +1,42 @@
+import * as React from "react";
+import { Observable } from "babylonjs";
+import { PropertyChangedEvent } from "../../../../propertyChangedEvent";
+import { CommonControlPropertyGridComponent } from "./commonControlPropertyGridComponent";
+import { LockObject } from "../lockObject";
+import { ScrollViewer } from "babylonjs-gui";
+import { LineContainerComponent } from "../../../lineContainerComponent";
+import { FloatLineComponent } from "../../../lines/floatLineComponent";
+import { TextInputLineComponent } from "../../../lines/textInputLineComponent";
+
+interface IScrollViewerPropertyGridComponentProps {
+    scrollViewer: ScrollViewer,
+    lockObject: LockObject,
+    onPropertyChangedObservable?: Observable<PropertyChangedEvent>
+}
+
+export class ScrollViewerPropertyGridComponent extends React.Component<IScrollViewerPropertyGridComponentProps> {
+    constructor(props: IScrollViewerPropertyGridComponentProps) {
+        super(props);
+    }
+
+    render() {
+        const scrollViewer = this.props.scrollViewer;
+
+        return (
+            <div className="pane">
+                <CommonControlPropertyGridComponent lockObject={this.props.lockObject} control={scrollViewer} onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                <LineContainerComponent title="RECTANGLE">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Thickness" target={scrollViewer} propertyName="thickness" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Corner radius" target={scrollViewer} propertyName="cornerRadius" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+                <LineContainerComponent title="SCROLLVIEWER">
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Bar size" target={scrollViewer} propertyName="barSize" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar color" target={scrollViewer} propertyName="barColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar border color" target={scrollViewer} propertyName="barBorderColor" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <TextInputLineComponent lockObject={this.props.lockObject} label="Bar background" target={scrollViewer} propertyName="barBackground" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                    <FloatLineComponent lockObject={this.props.lockObject} label="Wheel precision" target={scrollViewer} propertyName="wheelPrecision" onPropertyChangedObservable={this.props.onPropertyChangedObservable} />
+                </LineContainerComponent>
+            </div>
+        );
+    }
+}