Browse Source

Merge branch 'v2_2489_scroll-scrollbar-new' of tig:BDisp/Terminal.Gui into BDisp-v2_2489_scroll-scrollbar-new

Tig 8 months ago
parent
commit
b5979259f2

+ 1 - 0
Terminal.Gui/View/View.Content.cs

@@ -213,6 +213,7 @@ public partial class View
             {
                 // Force set Viewport to cause settings to be applied as needed
                 SetViewport (Viewport);
+                SetScrollBarsKeepContentInAllViewport (_viewportSettings);
             }
         }
     }

+ 209 - 0
Terminal.Gui/View/View.ScrollBars.cs

@@ -0,0 +1,209 @@
+#nullable enable
+namespace Terminal.Gui;
+
+public partial class View
+{
+    private Lazy<ScrollBar> _horizontalScrollBar;
+    private Lazy<ScrollBar> _verticalScrollBar;
+
+    /// <summary>
+    ///     Initializes the ScrollBars of the View. Called by the constructor.
+    /// </summary>
+    private void SetupScrollBars ()
+    {
+        _horizontalScrollBar = new (
+                                    () =>
+                                    {
+                                        var scrollBar = new ScrollBar
+                                        {
+                                            Orientation = Orientation.Horizontal,
+                                            X = 0,
+                                            Y = Pos.AnchorEnd (),
+                                            Width = Dim.Fill (
+                                                              Dim.Func (
+                                                                        () =>
+                                                                        {
+                                                                            if (_verticalScrollBar.IsValueCreated)
+                                                                            {
+                                                                                return _verticalScrollBar.Value.Visible ? 1 : 0;
+                                                                            }
+
+                                                                            return 0;
+                                                                        })),
+                                            Size = GetContentSize ().Width,
+                                            Visible = false
+                                        };
+
+                                        Padding?.Add (scrollBar);
+
+                                        scrollBar.Initialized += (_, _) =>
+                                        {
+                                            Padding!.Thickness = Padding.Thickness with
+                                            {
+                                                Bottom = scrollBar.Visible ? Padding.Thickness.Bottom + 1 : 0
+                                            };
+
+                                            scrollBar.PositionChanged += (_, args) =>
+                                            {
+                                                Viewport = Viewport with { X = args.CurrentValue };
+                                            };
+
+                                            scrollBar.VisibleChanged += (_, _) =>
+                                            {
+                                                Padding.Thickness = Padding.Thickness with
+                                                {
+                                                    Bottom = scrollBar.Visible
+                                                        ? Padding.Thickness.Bottom + 1
+                                                        : Padding.Thickness.Bottom - 1
+                                                };
+                                            };
+                                        };
+
+                                        return scrollBar;
+                                    });
+
+        _verticalScrollBar = new (
+                                  () =>
+                                  {
+                                      var scrollBar = new ScrollBar
+                                      {
+                                          Orientation = Orientation.Vertical,
+                                          X = Pos.AnchorEnd (),
+                                          Y = Pos.Func (() => Padding.Thickness.Top),
+                                          Height = Dim.Fill (
+                                                             Dim.Func (
+                                                                       () =>
+                                                                       {
+                                                                           if (_horizontalScrollBar.IsValueCreated)
+                                                                           {
+                                                                               return _horizontalScrollBar.Value.Visible ? 1 : 0;
+                                                                           }
+
+                                                                           return 0;
+                                                                       })),
+                                          Size = GetContentSize ().Height,
+                                          Visible = false
+                                      };
+
+                                      Padding?.Add (scrollBar);
+
+                                      scrollBar.Initialized += (_, _) =>
+                                      {
+                                          if (Padding is { })
+                                          {
+                                              Padding.Thickness = Padding.Thickness with
+                                              {
+                                                  Right = scrollBar.Visible ? Padding.Thickness.Right + 1 : 0
+                                              };
+
+                                              scrollBar.PositionChanged += (_, args) =>
+                                                                           {
+                                                                               Viewport = Viewport with { Y = args.CurrentValue };
+                                                                           };
+
+                                              scrollBar.VisibleChanged += (_, _) =>
+                                                                          {
+                                                                              Padding.Thickness = Padding.Thickness with
+                                                                              {
+                                                                                  Right = scrollBar.Visible
+                                                                                      ? Padding.Thickness.Right + 1
+                                                                                      : Padding.Thickness.Right - 1
+                                                                              };
+                                                                          };
+                                          }
+                                      };
+
+                                      return scrollBar;
+                                  });
+
+        ViewportChanged += (_, _) =>
+        {
+            if (_verticalScrollBar.IsValueCreated)
+            {
+                _verticalScrollBar.Value.Position = Viewport.Y;
+            }
+
+            if (_horizontalScrollBar.IsValueCreated)
+            {
+                _horizontalScrollBar.Value.Position = Viewport.X;
+            }
+        };
+
+        ContentSizeChanged += (_, _) =>
+        {
+            if (_verticalScrollBar.IsValueCreated)
+            {
+                _verticalScrollBar.Value.Size = GetContentSize ().Height;
+            }
+            if (_horizontalScrollBar.IsValueCreated)
+            {
+                _horizontalScrollBar.Value.Size = GetContentSize ().Width;
+            }
+        };
+    }
+
+    /// <summary>
+    /// </summary>
+    public ScrollBar HorizontalScrollBar => _horizontalScrollBar.Value;
+
+    /// <summary>
+    /// </summary>
+    public ScrollBar VerticalScrollBar => _verticalScrollBar.Value;
+
+    /// <summary>
+    ///     Clean up the ScrollBars of the View. Called by View.Dispose.
+    /// </summary>
+    private void DisposeScrollBars ()
+    {
+        if (_horizontalScrollBar.IsValueCreated)
+        {
+            Padding?.Remove (_horizontalScrollBar.Value);
+            _horizontalScrollBar.Value.Dispose ();
+        }
+
+        if (_verticalScrollBar.IsValueCreated)
+        {
+            Padding?.Remove (_verticalScrollBar.Value);
+            _verticalScrollBar.Value.Dispose ();
+        }
+    }
+
+    private void SetScrollBarsKeepContentInAllViewport (ViewportSettings viewportSettings)
+    {
+        if (viewportSettings == ViewportSettings.None)
+        {
+            _horizontalScrollBar.Value.KeepContentInAllViewport = true;
+            _verticalScrollBar.Value.KeepContentInAllViewport = true;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowNegativeX))
+        {
+            _horizontalScrollBar.Value.AutoHide = false;
+            _horizontalScrollBar.Value.ShowScrollIndicator = false;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowNegativeY))
+        {
+            _verticalScrollBar.Value.AutoHide = false;
+            _verticalScrollBar.Value.ShowScrollIndicator = false;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowNegativeLocation))
+        {
+            _horizontalScrollBar.Value.AutoHide = false;
+            _horizontalScrollBar.Value.ShowScrollIndicator = false;
+            _verticalScrollBar.Value.AutoHide = false;
+            _verticalScrollBar.Value.ShowScrollIndicator = false;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowXGreaterThanContentWidth))
+        {
+            _horizontalScrollBar.Value.KeepContentInAllViewport = false;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowYGreaterThanContentHeight))
+        {
+            _verticalScrollBar.Value.KeepContentInAllViewport = false;
+        }
+        else if (viewportSettings.HasFlag (ViewportSettings.AllowLocationGreaterThanContentSize))
+        {
+            _horizontalScrollBar.Value.KeepContentInAllViewport = false;
+            _verticalScrollBar.Value.KeepContentInAllViewport = false;
+        }
+    }
+}

+ 3 - 0
Terminal.Gui/View/View.cs

@@ -135,6 +135,7 @@ public partial class View : Responder, ISupportInitializeNotification
 
         DisposeKeyboard ();
         DisposeAdornments ();
+        DisposeScrollBars ();
 
         for (int i = InternalSubviews.Count - 1; i >= 0; i--)
         {
@@ -185,6 +186,8 @@ public partial class View : Responder, ISupportInitializeNotification
 
         //SetupMouse ();
         SetupText ();
+
+        SetupScrollBars ();
     }
 
     /// <summary>

+ 27 - 22
Terminal.Gui/View/ViewportSettings.cs

@@ -15,43 +15,35 @@ public enum ViewportSettings
     ///     If set, <see cref="View.Viewport"/><c>.X</c> can be set to negative values enabling scrolling beyond the left of
     ///     the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.X</c> is constrained to positive values.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeX = 1,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Y</c> can be set to negative values enabling scrolling beyond the top of the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Y</c> is constrained to positive values.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeY = 2,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Size</c> can be set to negative coordinates enabling scrolling beyond the
     ///     top-left of the
     ///     content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Size</c> is constrained to positive coordinates.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowNegativeLocation = AllowNegativeX | AllowNegativeY,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.X</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     <c>.Width</c> enabling scrolling beyond the right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.X</c> is constrained to <see cref="View.GetContentSize ()"/>
     ///         <c>.Width - 1</c>.
@@ -61,15 +53,13 @@ public enum ViewportSettings
     ///     <para>
     ///         The practical effect of this is that the last column of the content will always be visible.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowXGreaterThanContentWidth = 4,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Y</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     <c>.Height</c> enabling scrolling beyond the right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/><c>.Y</c> is constrained to <see cref="View.GetContentSize ()"/>
     ///         <c>.Height - 1</c>.
@@ -79,21 +69,19 @@ public enum ViewportSettings
     ///     <para>
     ///         The practical effect of this is that the last row of the content will always be visible.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowYGreaterThanContentHeight = 8,
 
     /// <summary>
     ///     If set, <see cref="View.Viewport"/><c>.Size</c> can be set values greater than <see cref="View.GetContentSize ()"/>
     ///     enabling scrolling beyond the bottom-right
     ///     of the content area.
-    /// </summary>
-    /// <remarks>
     ///     <para>
     ///         When not set, <see cref="View.Viewport"/> is constrained to <see cref="View.GetContentSize ()"/><c> -1</c>.
     ///         This means the last column and row of the content will remain visible even if there is an attempt to
     ///         scroll the Viewport past the last column or row.
     ///     </para>
-    /// </remarks>
+    /// </summary>
     AllowLocationGreaterThanContentSize = AllowXGreaterThanContentWidth | AllowYGreaterThanContentHeight,
 
     /// <summary>
@@ -106,10 +94,27 @@ public enum ViewportSettings
     ///     If set <see cref="View.Clear()"/> will clear only the portion of the content
     ///     area that is visible within the <see cref="View.Viewport"/>. This is useful for views that have a
     ///     content area larger than the Viewport and want the area outside the content to be visually distinct.
-    /// </summary>
-    /// <remarks>
     ///     <see cref="ClipContentOnly"/> must be set for this setting to work (clipping beyond the visible area must be
     ///     disabled).
-    /// </remarks>
-    ClearContentOnly = 32
+    /// </summary>
+    ClearContentOnly = 32,
+
+    /// <summary>
+    ///     If set, the vertical scroll bar (see <see cref="View.HorizontalScrollBar"/>) will be enabled and automatically made visible
+    ///     when the dimension of the <see cref="View.Viewport"/> is smaller than the dimension of <see cref="View.GetContentSize()"/>.
+    /// </summary>
+    EnableHorizontalScrollBar = 64,
+
+    /// <summary>
+    ///     If set, the vertical scroll bar (see <see cref="View.VerticalScrollBar"/>) will be enabled and automatically made visible
+    ///     when the dimension of the <see cref="View.Viewport"/> is smaller than the dimension of <see cref="View.GetContentSize()"/>.
+    /// </summary>
+    EnableVerticalScrollBar = 128,
+
+    /// <summary>
+    ///     If set, the horizontal and vertical scroll bars (see cref="View.HorizontalScrollBar"/> and <see cref="View.VerticalScrollBar"/>)
+    ///     will be enabled and automatically made visible when the dimension of the <see cref="View.Viewport"/> is smaller than the
+    ///     dimension of <see cref="View.GetContentSize()"/>.
+    /// </summary>
+    EnableScrollBars = EnableHorizontalScrollBar | EnableVerticalScrollBar
 }

+ 8 - 5
Terminal.Gui/Views/Scroll/Scroll.cs

@@ -5,8 +5,11 @@ using System.ComponentModel;
 namespace Terminal.Gui;
 
 /// <summary>
-///     Indicates the position and size of scrollable content. The indicator can be dragged with the mouse. Can be
-///     oriented either vertically or horizontally. Used within a <see cref="ScrollBar"/>.
+///     Indicates the size of scrollable content and provides a visible element, referred to as the "ScrollSlider" that
+///     that is sized to
+///     show the proportion of the scrollable content to the size of the <see cref="View.Viewport"/>. The ScrollSlider
+///     can be dragged with the mouse. A Scroll can be oriented either vertically or horizontally and is used within a
+///     <see cref="ScrollBar"/>.
 /// </summary>
 /// <remarks>
 ///     <para>
@@ -33,7 +36,7 @@ public class Scroll : View
     private Orientation _orientation;
     private int _position;
     private int _size;
-    private bool _keepContentInAllViewport = true;
+    private bool _keepContentInAllViewport;
 
     /// <inheritdoc/>
     public override void EndInit ()
@@ -253,9 +256,9 @@ public class Scroll : View
     {
         int barSize = BarSize;
 
-        if (position + barSize > Size)
+        if (position + barSize > Size + (KeepContentInAllViewport ? 0 : barSize) - (SuperViewAsScrollBar is { } ? 2 : 0))
         {
-            return KeepContentInAllViewport ? Math.Max (Size - barSize, 0) : Math.Max (Size - 1, 0);
+            return KeepContentInAllViewport ? Math.Max (Size - barSize - (SuperViewAsScrollBar is { } ? 2 : 0), 0) : Math.Max (Size - 1, 0);
         }
 
         return position;

+ 7 - 2
Terminal.Gui/Views/Scroll/ScrollBar.cs

@@ -4,7 +4,12 @@ using System.ComponentModel;
 
 namespace Terminal.Gui;
 
-/// <summary>A proportional scroll bar that can be oriented either horizontally or vertically.</summary>
+/// <summary>
+///     Provides a visual indicator that content can be scrolled. ScrollBars consist of two buttons, one each for scrolling
+///     forward or backwards, a Scroll that can be clicked to scroll large amounts, and a ScrollSlider that can be dragged
+///     to scroll continuously. ScrollBars can be oriented either horizontally or vertically and support the user dragging
+///     and clicking with the mouse to scroll.
+/// </summary>
 /// <remarks>
 ///     <para>
 ///         <see cref="Position"/> indicates the current location between zero and <see cref="Size"/>.
@@ -55,7 +60,7 @@ public class ScrollBar : View
         }
     }
 
-    /// <summary>Get or sets if the view-port is kept in all visible area of this <see cref="ScrollBar"/></summary>
+    /// <summary>Get or sets if the view-port is kept in all visible area of this <see cref="ScrollBar"/>.</summary>
     public bool KeepContentInAllViewport
     {
         get => _scroll.KeepContentInAllViewport;

+ 33 - 12
Terminal.Gui/Views/Scroll/ScrollSlider.cs

@@ -91,18 +91,19 @@ internal class ScrollSlider : View
             {
                 Y = Frame.Y + offset < 0
                         ? 0
-                        :
-                        Frame.Y + offset + Frame.Height > barSize + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : barSize)
-                            ?
-                            Math.Max (barSize - Frame.Height, 0)
+                        : Frame.Y + offset + Frame.Height > barSize
+                            ? Math.Max (barSize - Frame.Height, 0)
                             : Frame.Y + offset;
 
                 SuperViewAsScroll.Position = GetPositionFromSliderLocation (Frame.Y);
             }
             else
             {
-                X = Frame.X + offset < 0 ? 0 :
-                    Frame.X + offset + Frame.Width > barSize ? Math.Max (barSize - Frame.Width, 0) : Frame.X + offset;
+                X = Frame.X + offset < 0
+                        ? 0
+                        : Frame.X + offset + Frame.Width > barSize
+                            ? Math.Max (barSize - Frame.Width, 0)
+                            : Frame.X + offset;
 
                 SuperViewAsScroll.Position = GetPositionFromSliderLocation (Frame.X);
             }
@@ -119,7 +120,9 @@ internal class ScrollSlider : View
         else if ((mouseEvent.Flags == MouseFlags.WheeledDown && SuperViewAsScroll.Orientation == Orientation.Vertical)
                  || (mouseEvent.Flags == MouseFlags.WheeledRight && SuperViewAsScroll.Orientation == Orientation.Horizontal))
         {
-            SuperViewAsScroll.Position = Math.Min (SuperViewAsScroll.Position + 1, SuperViewAsScroll.Size - barSize);
+            SuperViewAsScroll.Position = Math.Min (
+                                                   SuperViewAsScroll.Position + 1,
+                                                   SuperViewAsScroll.KeepContentInAllViewport ? SuperViewAsScroll.Size - barSize : SuperViewAsScroll.Size - 1);
         }
         else if ((mouseEvent.Flags == MouseFlags.WheeledUp && SuperViewAsScroll.Orientation == Orientation.Vertical)
                  || (mouseEvent.Flags == MouseFlags.WheeledLeft && SuperViewAsScroll.Orientation == Orientation.Horizontal))
@@ -165,7 +168,12 @@ internal class ScrollSlider : View
             return SuperViewAsScroll.Size - scrollSize + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize);
         }
 
-        return (int)Math.Min (Math.Round ((double)(location * SuperViewAsScroll.Size + location) / scrollSize), SuperViewAsScroll.Size - scrollSize);
+        return (int)Math.Min (
+                              Math.Round (
+                                          (double)(location * (SuperViewAsScroll.Size + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize))
+                                                   + location)
+                                          / scrollSize),
+                              SuperViewAsScroll.Size - scrollSize + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize));
     }
 
     internal (int Location, int Dimension) GetSliderLocationDimensionFromPosition ()
@@ -183,18 +191,31 @@ internal class ScrollSlider : View
 
         if (SuperViewAsScroll.Size > 0)
         {
-            dimension = (int)Math.Min (Math.Max (Math.Ceiling ((double)scrollSize * scrollSize / SuperViewAsScroll.Size), 1), scrollSize);
+            dimension = (int)Math.Min (
+                                       Math.Max (
+                                                 Math.Ceiling (
+                                                               (double)scrollSize
+                                                               * scrollSize
+                                                               / (SuperViewAsScroll.Size + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize))),
+                                                 1),
+                                       scrollSize);
 
             // Ensure the Position is valid
             if (SuperViewAsScroll.Position > 0
                 && SuperViewAsScroll.Position + scrollSize > SuperViewAsScroll.Size + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize))
             {
-                SuperViewAsScroll.Position = SuperViewAsScroll.Size - scrollSize;
+                SuperViewAsScroll.Position = SuperViewAsScroll.KeepContentInAllViewport ? SuperViewAsScroll.Size - scrollSize : SuperViewAsScroll.Size - 1;
             }
 
-            location = (int)Math.Min (Math.Round ((double)SuperViewAsScroll.Position * scrollSize / (SuperViewAsScroll.Size + 1)), scrollSize - dimension);
+            location = (int)Math.Min (
+                                      Math.Round (
+                                                  (double)SuperViewAsScroll.Position
+                                                  * scrollSize
+                                                  / (SuperViewAsScroll.Size + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize))),
+                                      scrollSize - dimension);
 
-            if (SuperViewAsScroll.Position == SuperViewAsScroll.Size - scrollSize && location + dimension < scrollSize)
+            if (SuperViewAsScroll.Position == SuperViewAsScroll.Size - scrollSize + (SuperViewAsScroll.KeepContentInAllViewport ? 0 : scrollSize)
+                && location + dimension < scrollSize)
             {
                 location = scrollSize - dimension;
             }

+ 26 - 9
UICatalog/Scenarios/CharacterMap.cs

@@ -320,7 +320,7 @@ internal class CharMap : View
         CanFocus = true;
         CursorVisibility = CursorVisibility.Default;
 
-        SetContentSize (new (RowWidth, (_maxCodePoint / 16 + 2) * _rowHeight));
+        SetContentSize (new (RowWidth, (_maxCodePoint / 16 + 1) * _rowHeight));
 
         AddCommand (
                     Command.ScrollUp,
@@ -369,6 +369,11 @@ internal class CharMap : View
                             ScrollHorizontal (-COLUMN_WIDTH);
                         }
 
+                        if (Cursor.X >= Viewport.Width)
+                        {
+                            ScrollHorizontal (Cursor.X - Viewport.Width + 1);
+                        }
+
                         return true;
                     }
                    );
@@ -464,22 +469,22 @@ internal class CharMap : View
 
         ScrollBar hScrollBar = new ()
         {
-            X = 0,
+            AutoHide = false,
+            X = RowLabelWidth + 1,
             Y = Pos.AnchorEnd (),
             Width = Dim.Fill (1),
-            Size = GetContentSize ().Width,
+            Size = COLUMN_WIDTH * 15,
             Orientation = Orientation.Horizontal
         };
 
-        hScrollBar.VisibleChanged += (sender, args) => { Padding.Thickness = Padding.Thickness with { Bottom = hScrollBar.Visible ? 1 : 0 }; };
-
         ScrollBar vScrollBar = new ()
         {
+            AutoHide = false,
             X = Pos.AnchorEnd (),
-            Y = 0,
-            Height = Dim.Fill (Dim.Func (() => hScrollBar.Visible ? 1 : 0)),
+            Y = 1, // Header
+            Height = Dim.Fill (Dim.Func (() => Padding.Thickness.Bottom)),
             Orientation = Orientation.Vertical,
-            Size = GetContentSize ().Height
+            Size = GetContentSize ().Height,
         };
         vScrollBar.PositionChanged += (sender, args) => { Viewport = Viewport with { Y = args.CurrentValue }; };
 
@@ -488,6 +493,18 @@ internal class CharMap : View
 
         ViewportChanged += (sender, args) =>
                            {
+                               if (Viewport.Width < GetContentSize ().Width)
+                               {
+                                   Padding.Thickness = Padding.Thickness with { Bottom = 1 };
+                               }
+                               else
+                               {
+                                   Padding.Thickness = Padding.Thickness with { Bottom = 0 };
+                               }
+
+                               hScrollBar.Size = COLUMN_WIDTH * 15;
+                               hScrollBar.Position = Viewport.X;
+
                                vScrollBar.Size = GetContentSize ().Height;
                                vScrollBar.Position = Viewport.Y;
                            };
@@ -970,7 +987,7 @@ internal class CharMap : View
                                                         document.RootElement,
                                                         new
                                                             JsonSerializerOptions
-                                                            { WriteIndented = true }
+                                                        { WriteIndented = true }
                                                        );
             }
 

+ 151 - 40
UICatalog/Scenarios/ContentScrolling.cs

@@ -123,11 +123,12 @@ public class ContentScrolling : Scenario
             Width = Dim.Fill (),
             Height = Dim.Fill ()
         };
+
         app.Add (view);
 
         // Add Scroll Setting UI to Padding
-        view.Padding.Thickness = new (0, 3, 0, 0);
-        view.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
+        view.Padding.Thickness = view.Padding.Thickness with { Top = view.Padding.Thickness.Top + 4 };
+        //view.Padding.ColorScheme = Colors.ColorSchemes ["Error"];
 
         var cbAllowNegativeX = new CheckBox
         {
@@ -135,20 +136,7 @@ public class ContentScrolling : Scenario
             Y = 0,
             CanFocus = false
         };
-        cbAllowNegativeX.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowNegativeX.CheckedStateChanging += AllowNegativeX_Toggle;
-
-        void AllowNegativeX_Toggle (object sender, CancelEventArgs<CheckState> e)
-        {
-            if (e.NewValue == CheckState.Checked)
-            {
-                view.ViewportSettings |= ViewportSettings.AllowNegativeX;
-            }
-            else
-            {
-                view.ViewportSettings &= ~ViewportSettings.AllowNegativeX;
-            }
-        }
+        cbAllowNegativeX.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowNegativeX) ? CheckState.Checked : CheckState.UnChecked;
 
         view.Padding.Add (cbAllowNegativeX);
 
@@ -159,20 +147,7 @@ public class ContentScrolling : Scenario
             Y = 0,
             CanFocus = false
         };
-        cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowNegativeY.CheckedStateChanging += AllowNegativeY_Toggle;
-
-        void AllowNegativeY_Toggle (object sender, CancelEventArgs<CheckState> e)
-        {
-            if (e.NewValue == CheckState.Checked)
-            {
-                view.ViewportSettings |= ViewportSettings.AllowNegativeY;
-            }
-            else
-            {
-                view.ViewportSettings &= ~ViewportSettings.AllowNegativeY;
-            }
-        }
+        cbAllowNegativeY.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowNegativeY) ? CheckState.Checked : CheckState.UnChecked;
 
         view.Padding.Add (cbAllowNegativeY);
 
@@ -182,8 +157,23 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = false
         };
-        cbAllowXGreaterThanContentWidth.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowXGreaterThanContentWidth.CheckedStateChanging += AllowXGreaterThanContentWidth_Toggle;
+        cbAllowXGreaterThanContentWidth.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowXGreaterThanContentWidth) ? CheckState.Checked : CheckState.UnChecked;
+
+        view.Padding.Add (cbAllowXGreaterThanContentWidth);
+
+        void AllowNegativeX_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            if (e.NewValue == CheckState.Checked)
+            {
+                view.ViewportSettings |= ViewportSettings.AllowNegativeX;
+            }
+            else
+            {
+                view.ViewportSettings &= ~ViewportSettings.AllowNegativeX;
+            }
+
+            SetHorizontalScrollBar (e.NewValue, cbAllowXGreaterThanContentWidth.CheckedState);
+        }
 
         void AllowXGreaterThanContentWidth_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
@@ -195,9 +185,9 @@ public class ContentScrolling : Scenario
             {
                 view.ViewportSettings &= ~ViewportSettings.AllowXGreaterThanContentWidth;
             }
-        }
 
-        view.Padding.Add (cbAllowXGreaterThanContentWidth);
+            SetHorizontalScrollBar (e.NewValue, cbAllowNegativeX.CheckedState);
+        }
 
         var cbAllowYGreaterThanContentHeight = new CheckBox
         {
@@ -206,8 +196,23 @@ public class ContentScrolling : Scenario
             Y = Pos.Bottom (cbAllowNegativeX),
             CanFocus = false
         };
-        cbAllowYGreaterThanContentHeight.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
-        cbAllowYGreaterThanContentHeight.CheckedStateChanging += AllowYGreaterThanContentHeight_Toggle;
+        cbAllowYGreaterThanContentHeight.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.AllowYGreaterThanContentHeight) ? CheckState.Checked : CheckState.UnChecked;
+
+        view.Padding.Add (cbAllowYGreaterThanContentHeight);
+
+        void AllowNegativeY_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            if (e.NewValue == CheckState.Checked)
+            {
+                view.ViewportSettings |= ViewportSettings.AllowNegativeY;
+            }
+            else
+            {
+                view.ViewportSettings &= ~ViewportSettings.AllowNegativeY;
+            }
+
+            SetVerticalScrollBar (e.NewValue, cbAllowYGreaterThanContentHeight.CheckedState);
+        }
 
         void AllowYGreaterThanContentHeight_Toggle (object sender, CancelEventArgs<CheckState> e)
         {
@@ -219,9 +224,9 @@ public class ContentScrolling : Scenario
             {
                 view.ViewportSettings &= ~ViewportSettings.AllowYGreaterThanContentHeight;
             }
-        }
 
-        view.Padding.Add (cbAllowYGreaterThanContentHeight);
+            SetVerticalScrollBar (e.NewValue, cbAllowNegativeY.CheckedState);
+        }
 
         var labelContentSize = new Label
         {
@@ -284,7 +289,7 @@ public class ContentScrolling : Scenario
             Y = Pos.Top (labelContentSize),
             CanFocus = false
         };
-        cbClearOnlyVisible.CheckedState = view.ViewportSettings.HasFlag(ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
+        cbClearOnlyVisible.CheckedState = view.ViewportSettings.HasFlag (ViewportSettings.ClearContentOnly) ? CheckState.Checked : CheckState.UnChecked;
         cbClearOnlyVisible.CheckedStateChanging += ClearVisibleContentOnly_Toggle;
 
         void ClearVisibleContentOnly_Toggle (object sender, CancelEventArgs<CheckState> e)
@@ -321,7 +326,113 @@ public class ContentScrolling : Scenario
             }
         }
 
-        view.Padding.Add (labelContentSize, contentSizeWidth, labelComma, contentSizeHeight, cbClearOnlyVisible, cbDoNotClipContent);
+        var cbVerticalScrollBar = new CheckBox
+        {
+            Title = "Vertical ScrollBar",
+            X = 0,
+            Y = Pos.Bottom (labelContentSize),
+            CanFocus = false
+        };
+        view.VerticalScrollBar.ShowScrollIndicator = false;
+        cbVerticalScrollBar.CheckedState = view.VerticalScrollBar.Visible ? CheckState.Checked : CheckState.UnChecked;
+        cbVerticalScrollBar.CheckedStateChanging += VerticalScrollBar_Toggle;
+
+        void VerticalScrollBar_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            view.VerticalScrollBar.ShowScrollIndicator = e.NewValue == CheckState.Checked;
+        }
+
+        var cbHorizontalScrollBar = new CheckBox
+        {
+            Title = "Horizontal ScrollBar",
+            X = Pos.Right (cbVerticalScrollBar) + 1,
+            Y = Pos.Bottom (labelContentSize),
+            CanFocus = false,
+            CheckedState = view.HorizontalScrollBar.ShowScrollIndicator ? CheckState.Checked : CheckState.UnChecked
+        };
+        view.HorizontalScrollBar.ShowScrollIndicator = false;
+        cbHorizontalScrollBar.CheckedStateChanging += HorizontalScrollBar_Toggle;
+
+        void HorizontalScrollBar_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            view.HorizontalScrollBar.ShowScrollIndicator = e.NewValue == CheckState.Checked;
+        }
+
+        var cbAutoHideVerticalScrollBar = new CheckBox
+        {
+            Title = "Auto-hide Vertical ScrollBar",
+            X = Pos.Right (cbHorizontalScrollBar) + 1,
+            Y = Pos.Bottom (labelContentSize),
+            CanFocus = false,
+            CheckedState = view.HorizontalScrollBar.AutoHide ? CheckState.Checked : CheckState.UnChecked
+        };
+        view.VerticalScrollBar.AutoHide = true;
+        cbAutoHideVerticalScrollBar.CheckedStateChanging += AutoHideVerticalScrollBar_Toggle;
+
+        void AutoHideVerticalScrollBar_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            view.VerticalScrollBar.AutoHide = e.NewValue == CheckState.Checked;
+        }
+
+        var cbAutoHideHorizontalScrollBar = new CheckBox
+        {
+            Title = "Auto-hide Horizontal ScrollBar",
+            X = Pos.Right (cbAutoHideVerticalScrollBar) + 1,
+            Y = Pos.Bottom (labelContentSize),
+            CanFocus = false,
+            CheckedState = view.HorizontalScrollBar.AutoHide ? CheckState.Checked : CheckState.UnChecked
+        };
+        view.HorizontalScrollBar.AutoHide = true;
+        cbAutoHideHorizontalScrollBar.CheckedStateChanging += AutoHideHorizontalScrollBar_Toggle;
+
+        void AutoHideHorizontalScrollBar_Toggle (object sender, CancelEventArgs<CheckState> e)
+        {
+            view.HorizontalScrollBar.AutoHide = e.NewValue == CheckState.Checked;
+        }
+
+        cbAllowNegativeX.CheckedStateChanging += AllowNegativeX_Toggle;
+        cbAllowNegativeY.CheckedStateChanging += AllowNegativeY_Toggle;
+
+        cbAllowXGreaterThanContentWidth.CheckedStateChanging += AllowXGreaterThanContentWidth_Toggle;
+        cbAllowYGreaterThanContentHeight.CheckedStateChanging += AllowYGreaterThanContentHeight_Toggle;
+
+        void SetHorizontalScrollBar (CheckState newValue, CheckState value)
+        {
+            if (newValue == CheckState.Checked)
+            {
+                cbAutoHideHorizontalScrollBar.CheckedState = CheckState.UnChecked;
+                view.HorizontalScrollBar.AutoHide = view.HorizontalScrollBar.ShowScrollIndicator = false;
+                cbHorizontalScrollBar.CheckedState = CheckState.UnChecked;
+            }
+            else
+            {
+                cbAutoHideHorizontalScrollBar.CheckedState = CheckState.Checked;
+
+                view.HorizontalScrollBar.AutoHide = view.HorizontalScrollBar.ShowScrollIndicator = newValue == CheckState.UnChecked
+                                                    && value == CheckState.UnChecked;
+                cbHorizontalScrollBar.CheckedState = CheckState.Checked;
+            }
+        }
+
+        void SetVerticalScrollBar (CheckState newValue, CheckState value)
+        {
+            if (newValue == CheckState.Checked)
+            {
+                cbAutoHideVerticalScrollBar.CheckedState = CheckState.UnChecked;
+                view.VerticalScrollBar.AutoHide = view.VerticalScrollBar.ShowScrollIndicator = false;
+                cbVerticalScrollBar.CheckedState = CheckState.UnChecked;
+            }
+            else
+            {
+                cbAutoHideVerticalScrollBar.CheckedState = CheckState.Checked;
+
+                view.VerticalScrollBar.AutoHide = view.VerticalScrollBar.ShowScrollIndicator = newValue == CheckState.UnChecked
+                                                  && value == CheckState.UnChecked;
+                cbVerticalScrollBar.CheckedState = CheckState.Checked;
+            }
+        }
+
+        view.Padding.Add (labelContentSize, contentSizeWidth, labelComma, contentSizeHeight, cbClearOnlyVisible, cbDoNotClipContent, cbVerticalScrollBar, cbHorizontalScrollBar, cbAutoHideVerticalScrollBar, cbAutoHideHorizontalScrollBar);
 
         // Add demo views to show that things work correctly
         var textField = new TextField { X = 20, Y = 7, Width = 15, Text = "Test TextField" };

+ 34 - 30
UnitTests/Views/ScrollBarTests.cs

@@ -153,7 +153,7 @@ public class ScrollBarTests
 ◄░░██░░░░►",
                     @"
 ◄░█░░░░░░►")]
-    public void Changing_Position_Size_Orientation_Draws_Correctly (
+    public void Changing_Position_Size_Orientation_Draws_Correctly_KeepContentInAllViewport_True (
         int size,
         string firstVertExpected,
         string middleVertExpected,
@@ -169,7 +169,8 @@ public class ScrollBarTests
         {
             Orientation = Orientation.Vertical,
             Size = size,
-            Height = 10
+            Height = 10,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scrollBar);
@@ -245,7 +246,8 @@ public class ScrollBarTests
         top.Add (view);
         Application.Begin (top);
 
-        Assert.True (scrollBar.KeepContentInAllViewport);
+        Assert.False (scrollBar.KeepContentInAllViewport);
+        scrollBar.KeepContentInAllViewport = true;
         Assert.Equal (80, view.Padding.Viewport.Width);
         Assert.Equal (25, view.Padding.Viewport.Height);
         Assert.Equal (2, scrollBar.Viewport.Width);
@@ -340,14 +342,15 @@ public class ScrollBarTests
                     18,
                     @"
 ◄░░░░██░░►")]
-    public void Mouse_On_The_Container (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
+    public void Mouse_On_The_Container_KeepContentInAllViewport_True (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
     {
         var scrollBar = new ScrollBar
         {
             Width = orientation == Orientation.Vertical ? 1 : 10,
             Height = orientation == Orientation.Vertical ? 10 : 1,
             Orientation = orientation, Size = size,
-            Position = position
+            Position = position,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scrollBar);
@@ -471,7 +474,7 @@ public class ScrollBarTests
 ▼",
                     MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
-                    12,
+                    10,
                     @"
@@ -513,7 +516,7 @@ public class ScrollBarTests
 ▼",
                     MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
-                    12,
+                    10,
                     @"
@@ -534,7 +537,7 @@ public class ScrollBarTests
                     @"
 ◄░░░░████►",
                     MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition,
-                    12,
+                    10,
                     @"
 ◄░░░░████►")]
     [InlineData (
@@ -705,7 +708,7 @@ public class ScrollBarTests
                     20,
                     @"
 ◄░░░░██░░►")]
-    public void Mouse_On_The_Slider (
+    public void Mouse_On_The_Slider_KeepContentInAllViewport_True (
         Orientation orientation,
         int size,
         int position,
@@ -722,7 +725,8 @@ public class ScrollBarTests
             Width = orientation == Orientation.Vertical ? 1 : 10,
             Height = orientation == Orientation.Vertical ? 10 : 1,
             Orientation = orientation,
-            Size = size, Position = position
+            Size = size, Position = position,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scrollBar);
@@ -782,12 +786,12 @@ public class ScrollBarTests
     [AutoInitShutdown]
     [InlineData (Orientation.Vertical)]
     [InlineData (Orientation.Horizontal)]
-    public void Mouse_Pressed_On_ScrollButton_Changes_Position (Orientation orientation)
+    public void Mouse_Pressed_On_ScrollButton_Changes_Position_KeepContentInAllViewport_True (Orientation orientation)
     {
         var scrollBar = new ScrollBar
         {
             X = 10, Y = 10, Width = orientation == Orientation.Vertical ? 1 : 10, Height = orientation == Orientation.Vertical ? 10 : 1, Size = 20,
-            Orientation = orientation
+            Orientation = orientation, KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scrollBar);
@@ -799,7 +803,7 @@ public class ScrollBarTests
         Assert.Equal (0, scrollBar.Position);
 
         // ScrollButton increase
-        for (var i = 0; i < 13; i++)
+        for (var i = 0; i < 11; i++)
         {
             Application.OnMouseEvent (
                                       new ()
@@ -807,7 +811,7 @@ public class ScrollBarTests
                                           Position = orientation == Orientation.Vertical ? new (10, 19) : new (19, 10), Flags = MouseFlags.Button1Pressed
                                       });
 
-            if (i < 12)
+            if (i < 10)
             {
                 Assert.Equal (i + 1, scrollBar.Position);
             }
@@ -821,7 +825,7 @@ public class ScrollBarTests
             }
         }
 
-        for (var i = 12; i > -1; i--)
+        for (var i = 10; i > -1; i--)
         {
             Application.OnMouseEvent (new () { Position = new (10, 10), Flags = MouseFlags.Button1Pressed });
 
@@ -841,12 +845,12 @@ public class ScrollBarTests
     [AutoInitShutdown]
     [InlineData (Orientation.Vertical)]
     [InlineData (Orientation.Horizontal)]
-    public void Moving_Mouse_Outside_Host_Ensures_Correct_Location (Orientation orientation)
+    public void Moving_Mouse_Outside_Host_Ensures_Correct_Location_KeepContentInAllViewport_True (Orientation orientation)
     {
         var scrollBar = new ScrollBar
         {
             X = 10, Y = 10, Width = orientation == Orientation.Vertical ? 1 : 10, Height = orientation == Orientation.Vertical ? 10 : 1, Size = 20,
-            Position = 5, Orientation = orientation
+            Position = 5, Orientation = orientation, KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scrollBar);
@@ -879,11 +883,11 @@ public class ScrollBarTests
     }
 
     [Theory]
-    [InlineData (Orientation.Vertical, 20, 12, 10)]
-    [InlineData (Orientation.Vertical, 40, 32, 30)]
-    public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length (Orientation orientation, int size, int expectedPos1, int expectedPos2)
+    [InlineData (Orientation.Vertical, 20, 10, 10)]
+    [InlineData (Orientation.Vertical, 40, 30, 30)]
+    public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length_KeepContentInAllViewport_True (Orientation orientation, int size, int expectedPos1, int expectedPos2)
     {
-        var scrollBar = new ScrollBar { Orientation = orientation, Height = 10, Size = size };
+        var scrollBar = new ScrollBar { Orientation = orientation, Height = 10, Size = size, KeepContentInAllViewport = true };
         Assert.Equal (0, scrollBar.Position);
 
         scrollBar.Position = -1;
@@ -926,12 +930,12 @@ public class ScrollBarTests
     }
 
     [Fact]
-    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
+    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed_KeepContentInAllViewport_True ()
     {
         var changing = 0;
         var cancel = false;
         var changed = 0;
-        var scrollBar = new ScrollBar { Height = 10, Size = 20 };
+        var scrollBar = new ScrollBar { Height = 10, Size = 20, KeepContentInAllViewport = true };
         scrollBar.PositionChanging += Scroll_PositionChanging;
         scrollBar.PositionChanged += Scroll_PositionChanged;
 
@@ -966,19 +970,19 @@ public class ScrollBarTests
 
         Reset ();
         scrollBar.Position = 11;
-        Assert.Equal (11, scrollBar.Position);
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
+        Assert.Equal (10, scrollBar.Position);
+        Assert.Equal (0, changing);
+        Assert.Equal (0, changed);
 
         Reset ();
         scrollBar.Position = 12;
-        Assert.Equal (12, scrollBar.Position);
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
+        Assert.Equal (10, scrollBar.Position);
+        Assert.Equal (0, changing);
+        Assert.Equal (0, changed);
 
         Reset ();
         scrollBar.Position = 13;
-        Assert.Equal (12, scrollBar.Position);
+        Assert.Equal (10, scrollBar.Position);
         Assert.Equal (0, changing);
         Assert.Equal (0, changed);
 

+ 8 - 5
UnitTests/Views/ScrollSliderTests.cs

@@ -14,7 +14,7 @@ public class ScrollSliderTests
     [InlineData (Orientation.Vertical, 26, 236, 27, 210)]
     [InlineData (Orientation.Vertical, 37, 236, 2, 13)]
     [InlineData (Orientation.Vertical, 42, 236, 29, 164)]
-    public void Test_Position_Location_Consistency (Orientation orientation, int scrollLength, int size, int location, int expectedPosition)
+    public void Test_Position_Location_Consistency_KeepContentInAllViewport_True (Orientation orientation, int scrollLength, int size, int location, int expectedPosition)
     {
         // Arrange
         Scroll scroll = new ()
@@ -22,7 +22,8 @@ public class ScrollSliderTests
             Orientation = orientation,
             Width = orientation == Orientation.Vertical ? 1 : scrollLength,
             Height = orientation == Orientation.Vertical ? scrollLength : 1,
-            Size = size
+            Size = size,
+            KeepContentInAllViewport = true
         };
 
         scroll.BeginInit ();
@@ -42,8 +43,9 @@ public class ScrollSliderTests
 
     // Randomized Test for more extensive testing
     [Theory]
-    [InlineData (Orientation.Vertical, 26, 236, 5)]
-    public void Test_Position_Location_Consistency_Random (Orientation orientation, int scrollLength, int size, int testCount)
+    [InlineData (Orientation.Vertical, true, 26, 236, 5)]
+    [InlineData (Orientation.Vertical, false, 26, 236, 5)]
+    public void Test_Position_Location_Consistency_Random (Orientation orientation, bool keepContentInAllViewport, int scrollLength, int size, int testCount)
     {
         var random = new Random ();
 
@@ -52,7 +54,8 @@ public class ScrollSliderTests
             Orientation = orientation,
             Width = orientation == Orientation.Vertical ? 1 : scrollLength,
             Height = orientation == Orientation.Vertical ? scrollLength : 1,
-            Size = size
+            Size = size,
+            KeepContentInAllViewport = keepContentInAllViewport
         };
 
         scroll.BeginInit ();

+ 21 - 17
UnitTests/Views/ScrollTests.cs

@@ -117,7 +117,7 @@ public class ScrollTests
 ░░███░░░░░",
                     @"
 ░██░░░░░░░")]
-    public void Changing_Position_Size_Orientation_Draws_Correctly (
+    public void Changing_Position_Size_Orientation_Draws_Correctly_KeepContentInAllViewport_True (
         int size,
         string firstVertExpected,
         string middleVertExpected,
@@ -133,7 +133,8 @@ public class ScrollTests
         {
             Orientation = Orientation.Vertical,
             Size = size,
-            Height = 10
+            Height = 10,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scroll);
@@ -190,17 +191,17 @@ public class ScrollTests
         Assert.Equal (Orientation.Vertical, scroll.Orientation);
         Assert.Equal (0, scroll.Size);
         Assert.Equal (0, scroll.Position);
-        Assert.True (scroll.KeepContentInAllViewport);
+        Assert.False (scroll.KeepContentInAllViewport);
     }
 
     [Fact]
     [AutoInitShutdown]
-    public void KeepContentInAllViewport_True_False ()
+    public void KeepContentInAllViewport_True_False_KeepContentInAllViewport_True ()
     {
         var view = new View { Width = Dim.Fill (), Height = Dim.Fill () };
         view.Padding.Thickness = new (0, 0, 2, 0);
         view.SetContentSize (new (view.Viewport.Width, 30));
-        var scroll = new Scroll { Width = 2, Height = Dim.Fill (), Size = view.GetContentSize ().Height };
+        var scroll = new Scroll { Width = 2, Height = Dim.Fill (), Size = view.GetContentSize ().Height, KeepContentInAllViewport = true };
         scroll.PositionChanged += (_, e) => view.Viewport = view.Viewport with { Y = e.CurrentValue };
         view.Padding.Add (scroll);
         var top = new Toplevel ();
@@ -302,14 +303,15 @@ public class ScrollTests
                     20,
                     @"
 ░░░░░███░░")]
-    public void Mouse_On_The_Container (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
+    public void Mouse_On_The_Container_KeepContentInAllViewport_True (Orientation orientation, int size, int position, int location, string output, int expectedPos, string expectedOut)
     {
         var scroll = new Scroll
         {
             Width = orientation == Orientation.Vertical ? 1 : 10,
             Height = orientation == Orientation.Vertical ? 10 : 1,
             Orientation = orientation, Size = size,
-            Position = position
+            Position = position,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scroll);
@@ -667,7 +669,7 @@ public class ScrollTests
                     16,
                     @"
 ░░░░███░░░")]
-    public void Mouse_On_The_Slider (
+    public void Mouse_On_The_Slider_KeepContentInAllViewport_True (
         Orientation orientation,
         int size,
         int position,
@@ -684,7 +686,8 @@ public class ScrollTests
             Width = orientation == Orientation.Vertical ? 1 : 10,
             Height = orientation == Orientation.Vertical ? 10 : 1,
             Orientation = orientation,
-            Size = size, Position = position
+            Size = size, Position = position,
+            KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scroll);
@@ -743,12 +746,12 @@ public class ScrollTests
     [AutoInitShutdown]
     [InlineData (Orientation.Vertical)]
     [InlineData (Orientation.Horizontal)]
-    public void Moving_Mouse_Outside_Host_Ensures_Correct_Location (Orientation orientation)
+    public void Moving_Mouse_Outside_Host_Ensures_Correct_Location_KeepContentInAllViewport_True (Orientation orientation)
     {
         var scroll = new Scroll
         {
             X = 10, Y = 10, Width = orientation == Orientation.Vertical ? 1 : 10, Height = orientation == Orientation.Vertical ? 10 : 1, Size = 20,
-            Position = 5, Orientation = orientation
+            Position = 5, Orientation = orientation, KeepContentInAllViewport = true
         };
         var top = new Toplevel ();
         top.Add (scroll);
@@ -782,9 +785,9 @@ public class ScrollTests
     [Theory]
     [InlineData (Orientation.Vertical, 20, 10)]
     [InlineData (Orientation.Vertical, 40, 30)]
-    public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length (Orientation orientation, int size, int expectedPos)
+    public void Position_Cannot_Be_Negative_Nor_Greater_Than_Size_Minus_Frame_Length_KeepContentInAllViewport_True (Orientation orientation, int size, int expectedPos)
     {
-        var scroll = new Scroll { Orientation = orientation, Height = 10, Size = size };
+        var scroll = new Scroll { Orientation = orientation, Height = 10, Size = size, KeepContentInAllViewport = true };
         Assert.Equal (0, scroll.Position);
 
         scroll.Position = -1;
@@ -827,12 +830,12 @@ public class ScrollTests
     }
 
     [Fact]
-    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
+    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed_KeepContentInAllViewport_True ()
     {
         var changing = 0;
         var cancel = false;
         var changed = 0;
-        var scroll = new Scroll { Height = 10, Size = 20 };
+        var scroll = new Scroll { Height = 10, Size = 20, KeepContentInAllViewport = true };
         scroll.PositionChanging += Scroll_PositionChanging;
         scroll.PositionChanged += Scroll_PositionChanged;
 
@@ -971,7 +974,7 @@ public class ScrollTests
 │████░░░░│
 │████░░░░│
 └────────┘")]
-    public void Vertical_Horizontal_Draws_Correctly (int sizeWidth, int sizeHeight, int widthHeight, Orientation orientation, string expected)
+    public void Vertical_Horizontal_Draws_Correctly_KeepContentInAllViewport_True (int sizeWidth, int sizeHeight, int widthHeight, Orientation orientation, string expected)
     {
         var super = new Window { Id = "super", Width = Dim.Fill (), Height = Dim.Fill () };
         var top = new Toplevel ();
@@ -982,7 +985,8 @@ public class ScrollTests
             Orientation = orientation,
             Size = orientation == Orientation.Vertical ? sizeHeight * 2 : sizeWidth * 2,
             Width = orientation == Orientation.Vertical ? widthHeight : Dim.Fill (),
-            Height = orientation == Orientation.Vertical ? Dim.Fill () : widthHeight
+            Height = orientation == Orientation.Vertical ? Dim.Fill () : widthHeight,
+            KeepContentInAllViewport = true
         };
         super.Add (scroll);
 

+ 26 - 179
docfx/docs/View.md

@@ -1,192 +1,39 @@
-# V2 Spec for View Refactor - WORK IN PROGRESS
+# View Deep Dive
 
-IMPORTANT: I am critical of the existing codebase below. Do not take any of this personally. It is about the code, not the amazing people who wrote the code.
+## View Lexicon & Taxonomy
 
-ALSO IMPORTANT: I've written this to encourage and drive DEBATE. My style is to "Have strong opinions, weakly held." If you read something here you don't understand or don't agree with, SAY SO. Tell me why. Take a stand. 
+### Hierarchy
 
-This covers my thinking on how we will refactor `View` and the classes in the `View` hierarchy (including `Responder`). It does not cover Text formatting which will be covered in another spec. 
-  * TrueColor support will be covered separately.
-  * ConsoleDriver refactor.
+  * *[View](~/api/Terminal.Gui.View.yml)* - The base class for implementing higher-level visual/interactive Terminal.Gui elements. Implemented in the [View](~/api/Terminal.Gui.View.yml) base class.
+  
+  * *SubView* - A View that is contained in another view and will be rendered as part of the containing view's *ContentArea*. SubViews are added to another view via the [View.Add](~/api/Terminal.Gui.View.Add.yml) method. A View may only be a SubView of a single View. Each View has a [Subviews](~/api/Terminal.Gui.View.Subviews.yml) property that is a list of all Subviews that have been added.
+  
+  * *[SuperView](~/api/Terminal.Gui.View.SuperView.yml)* - The View that is a container for SubViews. Each View has a [SuperView](~/api/Terminal.Gui.View.SuperView.yml) property that references it's SuperView after it has been added.
+  
+  * *Child View* - A view that holds a reference to another view in a parent/child relationship. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally Subview/SuperView is preferred.
+  
+  * *Parent View* - A view that holds a reference to another view in a parent/child relationship, but is NOT a SuperView of the child. Terminal.Gui uses the terms "Child" and "Parent" sparingly. Generally Subview/SuperView is preferred.
+  
+### Layout
 
-## Goals
+See the [Layout Deep Dive](layout.md).
 
-1. Refactor View to have "real" Bounds where the Location part can be non-zero
-2. Enable a real "margin", "border", and "padding" thickness can be implemented that matches how these concepts work in HTML
-3. Leverage LineCanvas to draw borders and auto-join borders. Remove the need for `TileVeiw` and `SplitView` classes.
-4. Reduce 20/30% of the existing View, Toplevel, Window, and FrameView can code.
-5. Make porting apps to use the new architecture relatively easy, but result in less code in apps.
-6. Make it easier to add new Views and View-like classes.
+### Drawing
 
-## Terminal.Gui v2 View-related Lexicon & Taxonomy
+See the [Drawing Deep Dive](drawing.md).
 
-  * *Responder* - A class that can handle user input. Implemented in the `Responder` base class. 
-    * In v2 we will move more mouse/keyboard base-logic out of `View` and `Window` and into `Responder`.
-  * *View* - A base class for implementing higher-level visual/interactive Terminal.Gui elements. Implemented in the `View` base class, which is a `Responder` and hosts several `Frame`s. 
-    * In v2 we will move all logic for rendering out of `Toplevel`, `FrameView`, and `Window` into `View`.
-  * *SubView* - A View that is contained in another view and will be rendered as part of the containing view's *ContentArea*. SubViews are added to another view via the `View.Add` method. A View may only be a SubView of a single View. 
-  * *SuperView* - The View that is a container for SubViews. Referring to the View another View was added to as *SubView*. 
-  * *Child View* - A view that is held by another view in a parent/child relationship, but is NOT a SubView. Examples of this are the submenus of `MenuBar`. 
-  * *Parent View* - A view that holds a reference to another view in a parent/child relationship, but is NOT a SuperView of the child. 
-  * *Thickness* - A class describing a rectangle where each of the four sides can have a width. Valid width values are >= 0. The inner area of a Thickness is the sum of the widths of the four sides minus the size of the rectangle. The `Thickness` class has a `Draw` method that clears the rectangle. 
-  * *Frame Class* - A `Frame` is a special form of `View` that appears outside of a normal `View`'s content area. Examples of `Frame`s are `Margin`, `Border`, and `Padding`. The `Frame` class is derived from `View` and uses a `Thickness` to hold the rectangle. 
-  * *Frame* - The `Rect` that defines the location and size of the `View` including all of the margin, border, adornments, padding, and content area. The coordinates are relative to the SuperView of the View (or, in the case of `Application.Top`, `ConsoleDriver.Row == 0; ConsoleDriver.Col == 0`). The Frame's location and size are controlled by the `.X`, `.Y`, `.Height`, and `.Width` properties of the View. 
-     * In v2, `View.Frame.Size` is the size of the `View`'s `ContentArea` plus the `Thickness` of the `View`'s `Margin`, `Border`, and `Padding`.
-  * *Margin* - The `Frame` that separates a View from other SubViews of the same SuperView. The Margin is not part of the View's content and is not clipped by the View's `ClipArea`. By default `Margin` is `{0,0,0,0}`. `Margin` can be used instead of (or with) `Dim.Pos` to position a View relative to another View. 
-      Eg. 
-      ```cs
-      view.X = Pos.Right (otherView) + 1;
-      view.Y = Pos.Bottom (otherView) + 1;
-      ```
-      is equivalent to 
-      ```cs
-      otherView.Margin.Thickness = new Thickness (0, 0, 1, 1);
-      view.X = Pos.Right (otherView);
-      view.Y = Pos.Bottom (otherView);
-      ```
-    * QUESTION: Will it be possible to have a negative Margin? If so, will that allow us to have "magic borderframe connections" as I've demonstrated in my TileViewExperiment? Or, should the magic happen when a View's dimensions overlap with another, independent of the Margin?
-  * *Title* - Text that is displayed for the View that describes the View to users. Typically the Title is displayed at the top-left, overlaying the Border. The title is not part of the View's content and is not clipped by the View's `ClipArea`. 
-  * *Text* - Text that is rendered by the view within the view's content area, using `TextFormatter`. `Text` is part of the View's content and is clipped by the View's `ClipArea`. 
-  * *Border* (currently `BorderFrame` until the old `Border` can be removed) - The `Frame` where a visual border (drawn using line-drawing glyphs) and the Title are drawn. The Border expands inward; in other words if `Border.Thickness.Top == 2` the border & title will take up the first row and the second row will be filled with spaces. The Border is not part of the View's content and is not clipped by the View's `ClipArea`.
-  * *Adornments* (NOT IMPLEMENTED YET; May replace `BorderFrame`)- The `Frame` between the `Border` and `Padding`. Adornments are not part of the View's content and are not clipped by the View's `ClipArea`. Examples of Adornments:
-    * A `TitleBar` renders the View's `Title` and a horizontal line defining the top of the View. Adds thickness to the top of Adornments. 
-    * One or more `LineView`s that render the View's border (NOTE: The magic of `LineCanvas` lets us automatically have the right joins for these and `TitleBar`!).
-    * A `Vertical Scrollbar` adds thickness to `Adornments.Right` (or `.Left` when right-to-left language support is added). 
-    * A `Horizontal Scrollbar` adds thickness to `Adornments.Bottom` when enabled.
-    * A `MenuBar` adds thickness to `Adornments.Top` (NOTE: This is a change from v1 where `subview.Y = 1` is required).
-    * A `StatusBar` adds thickness ot `Adornments.Bottom` and is rendered at the bottom of `Padding`.
-    * NOTE: The use of `View.Add` in v1 to add adornments to Views is the cause of much code complexity. Changing the API such that `View.Add` is ONLY for subviews and adding a `View.Adornments.Add` API for menu, StatusBar, scroll bar... will enable us to significantly simplify the codebase.
-  * *Padding* - The `Frame` inside of an element that offsets the `Content` from the Border. (NOTE: in v1 `Padding` is OUTSIDE of the `Border`). Padding is `{0, 0, 0, 0}` by default. Padding is not part of the View's content and is not clipped by the View's `ClipArea`.
-  * *VisibleArea* - (NOT IMPLEMENTED YET) Means the area inside of the Margin + Border (Title) + Padding. `VisibleArea.Location` is always `{0, 0}`. `VisibleArea.Size` is the `View.Frame.Size` shrunk by Margin + Border + Padding. 
-  * *ContentArea* - (NOT IMPLEMENTED YET; currently `Bounds`) The `Rect` that describes the location and size of the View's content, relative to `VisibleArea`. If `ContentArea.Location` is negative, anything drawn there will be clipped and any subview positioned in the negative area will cause (optional) scrollbars to appear (making the Thickness of Padding thicker on the appropriate sides). If `ContentArea.Size` is changed such that the dimensions fall outside of `Frame.Size shrunk by Margin + Border + `Padding`, drawing will be clipped and (optional) scrollbars will appear.
-    * QUESTION: Can we just have one `ContentArea` property that is the `Rect` that describes the location and size of the View's content, relative to `Frame`? If so, we can remove `VisibleArea` and `Bounds` and just have `ContentArea` and `Frame`? The key to answering this is all wrapped up in scrolling and clipping.
-  * *Bounds* - Synomous with *VisibleArea*. (Debate: Do we rename `Bounds` to `VisbleArea` in v2?)
-  * *ClipArea* - The currently visible portion of the *Content*. This is defined as a`Rect` in coordinates relative to *ContentArea* (NOT *VisibleArea*) (e.g. `ClipArea {X = 0, Y = 0} == ContentArea {X = 0, Y = 0}`). In v2 we will NOT pass this `Rect` is passed `View.Redraw` and instead just have `Redraw` use `Bounds`. 
-    * QUESTION: Do we need `ClipArea` at all? Can we just have `Redraw` use `Bounds`?
+### Navigation
 
-  * *Modal* - *Modal* - The term used when describing a `View` that was created using the `Application.Run(view)` or `Application.Run<T>` APIs. When a View is running as a modal, user input is restricted to just that View until `Application.Run` exits. A `Modal` View has its own `RunState`. 
-    * In v1, classes derived from `Dialog` were originally thought to only work modally. However, `Wizard` proved that a `Dialog`-based class can also work non-modally. 
-    * In v2, we will simplify the `Dialog` class, and let any class be run via `Applicaiton.Run`. The `Modal` property will be set by `Application.Run` so the class can detect it is running modally if it needs to. 
-
-  * *TopLevel* - The v1 term used to describe a view that can have a MenuBar and/or StatusBar. In v2, we will delete the `TopLevel` class and ensure ANY View can have a menu bar and/or status bar (via `Adornments`).
-    * NOTE: There will still be an `Application.Top` which is the `View` that is the root of the `Application`'s view hierarchy.
-
-  * *Window* - A View that, by default, has a `Border` and a `Title`. 
-    * QUESTION: Why can't this just be a property on `View` (e.g. `View.Border = true`)? Why do we need a `Window` class at all in v2?
-    
-  * *Tile*, *Tiled*, *Tiling* (NOT IMPLEMENTED YET) - Refer to a form of `ComputedLayout` where SubViews of a `View` are visually arranged such that they abut each other and do not overlap. In a Tiled view arrangement, Z-ordering only comes into play when a developer intentionally causes views to be aligned such that they overlap. Borders that are drawn between the SubViews can optionally support resizing the SubViews (negating the need for `TileView`).
-
-  * *Overlap*, *Overlapped*, *Overlapping* (NOT IMPLEMENTED YET) - Refers to a form of `ComputedLayout` where SubViews of a View are visually arranged such that their Frames overlap. In Overlap view arrangements there is a Z-axis (Z-order) in addition to the X and Y dimension. The Z-order indicates which Views are shown above other views.
-
-## Focus
-
-* Focus is a concept that is used to describe which Responder is currently receiving user input.
-* QUESTION: Since `Frame`s are `Views` in v2, the `Frame` is a `Responder` that receives user input. This raises the question of how a user can use the keyboard to navigate between `Frame`s and `View`s within a `Frame` (and the `Frame`'s `Parent`'s subviews).
-
-
-## View classes to be nuked
-* PanelView (done)
-* FrameView (almost done)
-* TileVeiw 
-* TopLevel?
-* Window?
-* `LineView` can be reimplemented using `LineCanvas`?
-* `Button` and `Label` can be merged. 
-* `StatusBar` and `MenuBar` could be combined. If not, then at least made consistent (e.g. in how hotkeys are specified).
-* `ComboBox` can be replaced by `MenuBar`
+See the [Navigation Deep Dive](navigation.md).
 
-## What's wrong with the View and the View-class hierarchy in v1?
+### Application Concepts 
 
-* `Frame`, `Bounds`, and `ClipRect` are confusing and not consistently applied...
-  * `Bounds` is `Rect` but is used to describe a `Size` (e.g. `Bounds.Size` is the size of the `View`'s content area). It literally is implemented as a property that returns `new Rect(0, 0, Width, Height)`. Throughtout the codebase `bounds` is used for things that have non-zero `Size` (and actually descibe either the cliprect or the Frame).
-  * The restrictive nature of how `Bounds` is defined led to the hacky `FrameView` and `Window` classes with an embedded `ContentView` in order to draw a border around the content. 
-    * The only reason FrameView exists is because the original architecture didn't support offsetting `View.Bounds` such that a border could be drawn and the interior content would clip correctly. Thus Miguel (or someone) built
-  FrameView with nested `ContentView` that was at `new Rect(+1, +1, -2, -2)`. 
-    * `Border` was added later, but couldn't be retrofitted into `View` such that if `View.Border ~= null` just worked like `FrameView`.
-    * Thus devs are forced to use the clunky `FrameView` instead of just setting `View.Border`.
-  * `Border` has a bunch of confusing concepts that don't match other systems (esp the Web/HTML)
-    * `Margin` on the web means the space between elements - `Border` doesn't have a margin property, but does has the confusing `DrawMarginFrame` property.
-    * `Border` on the web means the space where a border is drawn. The current implementaiton confuses the term `Frame` and `Border`. `BorderThickness` is provided. 
-    * `Padding` on the web means the padding inside of an element between the `Border` and `Content`. In the current implementation `Padding` is actually OUTSIDE of the `Border`. This means it's not possible for a view to offset internally by simply changing `Bounds`. 
-    * `Content` on the web means the area inside of the Margin + Border + Padding. `View` does not currently have a concept of this (but `FrameView` and `Window` do via the embedded `ContentView`s.
-    * `Border` has a `Title` property. So does `Window` and `FrameView`. This is duplicate code.
-    * It is not possible for a class derived from View to override the drawing of the "Border" (frame, title, padding, etc...). Multiple devs have asked to be able to have the border frame to be drawn with a different color than `View.ColorScheme`. The API should explicitly enable devs to override the drawing of `Border` independently of the `View.Draw` method. See how `WM_NCDRAW` works in Windows (Draw non-client). It should be easy to do this from within a `View` sub-class (e.g. override `OnDrawBorder`) and externally (e.g. `DrawBorder += () => ...`. 
-
-* `AutoSize` mostly works, but only because of heroic special-casing logic all over the place by @bdisp. This should be massively simplified.`FrameView` is superfluous and should be removed from the hierarchy (instead devs should just be able to manipulate `View.Border` (or similar) to achieve what `FrameView` provides). The internal `FrameView.ContentView` is a bug-farm and un-needed if `View.Border` worked correctly. 
-* `TopLevel` is currently built around several concepts that are muddled:
-  * Views that host a Menu and StatusBar. It is not clear why this is and if it's needed as a concept. 
-  * Views that can be run via `Application.Run<TopLevel>` (need a separate `RunState`). It is not clear why ANY VIEW can't be run this way, but it seems to be a limitation of the current implementation.
-  * Views that can be used as a pop-up (modal) (e.g. `Dialog`). As proven by `Wizard`, it is possible to build a View that works well both ways. But it's way too hard to do this today.
-  * Views that can be moved by the user must inherit from `Window` today. It should be possilbe to enable moving of any View (e.g. `View.CanMove = true`).
-* The `MdiContainer` stuff is complex, perhaps overly so, and is not actually used by anyone outside of the project. It's also mis-named because Terminal.Gui doesn't actually support "documents" nor does it have a full "MDI" system like Windows (did). It seems to represent features useful in overlapping Views, but it is super confusing on how this works, and the naming doesn't help. This all can be refactored to support specific scenarios and thus be simplified.
-* There is no facility for users' resizing of Views. @tznind's awesome work on `LineCanvas` and `TileView` combined with @tig's experiments show it could be done in a great way for both modal (overlapping) and tiled Views. 
-* `DrawFrame` and `DrawTitle` are implemented in `ConsoleDriver` and can be replaced by a combination of `LineCanvas` and `Border`.
-* Colors - 
-  * As noted above each of Margin, Border, Padding, and Content should support independent colors.
-  * Many View sub-classes bastardize the existing ColorSchemes to get look/feel that works (e.g. `TextView` and `Wizard`). Separately we should revamp ColorSchemes to enable more scenarios. 
-  * TrueColor support is needed and should be the default.
-* `Responder` is supposed to be where all common, non-visual-related, code goes. We should ensure this is the case.
-* `View` should have default support for scroll bars. e.g. assume in the new world `View.ContentBounds` is the clip area (defined by `VIew.Frame` minus `Margin` + `Border` + `Padding`) then if any view is added with `View.Add` that has Frame coordinates outside of `ContentBounds` the appropriate scroll bars show up automatgically (optionally of course). Without any code, scrolling just works. 
-* We have many requests to support non-full-screen apps. We need to ensure the `View` class hierarchy supports this in a simple, understandable way. In a world with non-full-screen (where screen is defined as the visible terminal view) apps, the idea that `Frame` is "screen relative" is broken. Although we COULD just define "screen" as "the area that bounds the Terminal.GUI app.".  
-
-
-## Design
-
-* `Responder`("Responder base class implemented by objects that want to participate on keyboard and mouse input.") remains mostly unchanged, with minor changes:
-   * Methods that take `View` parameters change to take `Responder` (bad OO design).
-   * Nuke `IsOverriden` (bad OO design)
-   * Move `View.Data` to `Responder` (primitive)
-   * Move `Command` and `KeyBinding` stuff from `View`.
-   * Move the generic mouse and keyboard stuff from `View` (e.g. `WantMousePositionReports`)
-
-
-## Example of creating Adornments
-```cs
-// ends up looking just like the v1 default Window with a menu & status bar
-// and a vertical scrollbar. In v2 the Window class would do all of this automatically.
-var top = new TitleBar() {
-    X = 0, Y = 0,
-    Width = Dim.Fill(),
-    Height = 1
-    LineStyle = LineStyle.Single
-};
-var left = new LineView() {
-    X = 0, Y = 0,
-    Width = 1,
-    Height = Dim.Fill(),
-    LineStyle = LineStyle.Single
-};
-var right = new LineView() {
-    X = Pos.AnchorEnd(), Y = 0,
-    Width = 1,
-    Height = Dim.Fill(),
-    LineStyle = LineStyle.Single
-};
-var bottom = new LineView() {
-    X = 0, Y = Pos.AnchorEnd(),
-    Width = Dim.Fill(),
-    Height = 1,
-    LineStyle = LineStyle.Single
-};
+  * *TopLevel* - The v1 term used to describe a view that can have a MenuBar and/or StatusBar. In v2, we will delete the `TopLevel` class and ensure ANY View can have a menu bar and/or status bar (via `Adornments`).
+    * NOTE: There will still be an `Application.Top` which is the [View](~/api/Terminal.Gui.View.yml) that is the root of the `Application`'s view hierarchy.
 
-var menu = new MenuBar() { 
-    X = Pos.Right(left), Y = Pos.Bottom(top)
-};
-var status = new StatusBar () {
-    X = Pos.Right(left), Y = Pos.Top(bottom)
-};
-var vscroll = new ScrollBarView () {
-    X = Pos.Left(right),
-    Y = Dim.Fill(2) // for menu & status bar
-};
+  * *Runnable* - TBD
 
-Adornments.Add(titleBar);
-Adornments.Add(left);
-Adornments.Add(right);
-Adornments.Add(bottom);
-Adornments.Add(vscroll);
+  * *Modal* - *Modal* - The term used when describing a [View](~/api/Terminal.Gui.View.yml) that was created using the `Application.Run(view)` or `Application.Run<T>` APIs. When a View is running as a modal, user input is restricted to just that View until `Application.Run` exits. A `Modal` View has its own `RunState`. 
+    * In v1, classes derived from `Dialog` were originally thought to only work modally. However, `Wizard` proved that a `Dialog`-based class can also work non-modally. 
+    * In v2, we will simplify the `Dialog` class, and let any class be run via `Applicaiton.Run`. The `Modal` property will be set by `Application.Run` so the class can detect it is running modally if it needs to. 
 
-var treeView = new TreeView () {
-    X = 0, Y = 0, Width = Dim.Fill(), Height = Dim.Fill()
-};
-Add (treeView);
-```

+ 1 - 1
docfx/docs/index.md

@@ -110,7 +110,7 @@ All visible elements in a Terminal.Gui application are implemented as
 See the full list of [Views provided by the Terminal.Gui library here](views.md).
 
 Every view can contain an arbitrary number of child views, called `SubViews`. Call the
-[View.Add](~/api/Terminal.Gui.View.yml#Terminal_Gui_View_Add_Terminal_Gui_View_) method to add a couple of buttons to a UI:
+[View.Add](~/api/Terminal.Gui.View.Add.yml) method to add a couple of buttons to a UI:
 
 ```csharp
 void SetupMyView (View myView)

+ 56 - 11
docfx/docs/layout.md

@@ -1,28 +1,67 @@
 # Layout
 
-Terminal.Gui provides a rich system for how `View` objects are laid out relative to each other. The layout system also defines how coordinates are specified.
+Terminal.Gui provides a rich system for how [View](View.md) objects are laid out relative to each other. The layout system also defines how coordinates are specified.
 
-## Coordinates
+See [View Deep Dive](View.md) for more.
 
-* **Screen-Relative** - Describes the dimensions and characteristics of the underlying terminal. Currently Terminal.Gui only supports applications that run "full-screen", meaning they fill the entire terminal when running. As the user resizes their terminal, the `Screen` changes size and the applicaiton will be resized to fit. *Screen-Relative* means an origin (`0, 0`) at the top-left corner of the terminal. `ConsoleDriver`s operate exclusively on *Screen-Relative* coordinates.
-* **Application-Relative** - The dimensions and characteristics of the application. Because only full-screen apps are currently supported, `Application` is effectively the same as `Screen` from a layout perspective. *Application-Relative* currently means an origin (`0, 0`) at the top-left corner of the terminal. `Applicaiton.Top` is a `View` with a top-left corner fixed at the *Application.Relative* coordinate of (`0, 0`) and is the size of `Screen`.
-* **Frame-Relative**  - The `Frame` property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. *Frame-Relative* means a coordinate is relative to the top-left corner of the View in question. `View.FrameToScreen ()` and `View.ScreenToFrame ()` are helper methods for translating a *Frame-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
-* **Content-Relative** - A rectangle, with an origin of (`0, 0`) and size (defined by `View.GetContentSize()`) where the View's content exists. *Content-Relative* means a coordinate is relative to the top-left corner of the content, which is always (`0,0`). `View.ContentToScreen ()` and `View.ScreenToContent ()` are helper methods for translating a *Content-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
-* **Viewport-Relative** - A *Content-Relative* rectangle representing the subset of the View's content that is visible to the user. If `View.GetContentSize()` is larger than the Viewport, scrolling is enabled. *Viewport-Relative* means a coordinate that is bound by (`0,0`) and the size of the inner-rectangle of the View's `Padding`. The View drawing primitives (e.g. `View.Move`) take *Viewport-Relative* coordinates; `Move (0, 0)` means the `Cell` in the top-left corner of the inner rectangle of `Padding`. `View.ViewportToScreen ()` and `View.ScreenToViewport ()` are helper methods for translating a *Viewport-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa. To convert a *Viewport-Relative* coordinate to a *Content-Relative* coordinate, simply subtract `Viewport.X` and/or `Viewport.Y` from the *Content-Relative* coordinate. To convert a *Viewport-Relative* coordinate to a *Frame-Relative* coordinate, subtract the point returned by `View.GetViewportOffsetFromFrame`.
+## Lexicon & Taxonomy
+
+### Coordinates
+
+* **Screen-Relative** - Describes the dimensions and characteristics of the underlying terminal. Currently Terminal.Gui only supports applications that run "full-screen", meaning they fill the entire terminal when running. As the user resizes their terminal, the [Screen](~/api/Terminal.Gui.Application.Screen.yml) changes size and the application will be resized to fit. *Screen-Relative* means an origin (`0, 0`) at the top-left corner of the terminal. [ConsoleDrivers](~/api/Terminal.Gui.ConsoleDriver.yml) s operate exclusively on *Screen-Relative* coordinates.
+
+* **Application-Relative** - The dimensions and characteristics of the application. Because only full-screen apps are currently supported, [Application](~/api/Terminal.Gui.Application.yml) is effectively the same as `Screen` from a layout perspective. *Application-Relative* currently means an origin (`0, 0`) at the top-left corner of the terminal. [Application.Top](~/api/Terminal.Gui.Application.Top.yml)  is a `View` with a top-left corner fixed at the *Application.Relative* coordinate of (`0, 0`) and is the size of `Screen`.
+
+* **Frame-Relative**  - The [Frame](~/api/Terminal.Gui.View.Frame.yml) property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. *Frame-Relative* means a coordinate is relative to the top-left corner of the View in question. [View.FrameToScreen()](~/api/Terminal.Gui.View.FrameToScreen.yml) and [View.ScreenToFrame()](~/api/Terminal.Gui.View.ScreenToFrame.yml) are helper methods for translating a *Frame-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
+
+* **Content-Relative** - A rectangle, with an origin of (`0, 0`) and size (defined by [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml)) where the View's content exists. *Content-Relative* means a coordinate is relative to the top-left corner of the content, which is always (`0,0`). [View.ContentToScreen()](~/api/Terminal.Gui.View.ContentToScreen.yml) and [View.ScreenToContent()](~/api/Terminal.Gui.View.ScreenToContent.yml) are helper methods for translating a *Content-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa.
+
+* **Viewport-Relative** - A *Content-Relative* rectangle representing the subset of the View's content that is visible to the user: [Viewport](~/api/Terminal.Gui.View.Viewport.yml). If [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml) is larger than the [Viewport](~/api/Terminal.Gui.View.Viewport.yml), scrolling is enabled. *Viewport-Relative* means a coordinate that is bound by (`0,0`) and the size of the inner-rectangle of the View's `Padding`. The View drawing primitives (e.g. `View.Move`) take *Viewport-Relative* coordinates; `Move (0, 0)` means the `Cell` in the top-left corner of the inner rectangle of `Padding`. `View.ViewportToScreen ()` and `View.ScreenToViewport ()` are helper methods for translating a *Viewport-Relative* coordinate to a *Screen-Relative* coordinate and vice-versa. To convert a *Viewport-Relative* coordinate to a *Content-Relative* coordinate, simply subtract `Viewport.X` and/or `Viewport.Y` from the *Content-Relative* coordinate. To convert a *Viewport-Relative* coordinate to a *Frame-Relative* coordinate, subtract the point returned by [View.GetViewportOffsetFromFrame()](~/api/Terminal.Gui.View.GetViewportOffsetFromFrame.yml).
+
+### View Composition
+
+* *[Thickness](~/api/Terminal.Gui.Thickness.yml)* - A `record struct` describing a rectangle where each of the four sides can have a width. Valid width values are >= 0. The inner area of a Thickness is the sum of the widths of the four sides minus the size of the rectangle.
+
+* *[Frame](~/api/Terminal.Gui.View.Frame.yml)* - The `Rectangle` that defines the location and size of the [View](~/api/Terminal.Gui.View.yml) including all of the margin, border, padding, and content area. The coordinates are relative to the SuperView of the View (or, in the case of `Application.Top`, `ConsoleDriver.Row == 0; ConsoleDriver.Col == 0`). The Frame's location and size are controlled by the `.X`, `.Y`, `.Height`, and `.Width` properties of the View. 
+
+* *Adornments* - The `Thickness`es that separate the `Frame` from the `ContentArea`. There are three Adornments, `Margin`, `Padding`, and `Border`. Adornments are not part of the View's content and are not clipped by the View's `ClipArea`. Examples of Adornments:
+
+* *[Margin](~/api/Terminal.Gui.View.Margin.yml)* - The `Adornment` that separates a View from other SubViews of the same SuperView. The Margin is not part of the View's content and is not clipped by the View's `ClipArea`. By default `Margin` is `{0,0,0,0}`. `Margin` can be used instead of (or with) `Dim.Pos` to position a View relative to another View. 
+    Eg. 
+    ```cs
+    view.X = Pos.Right (otherView) + 1;
+    view.Y = Pos.Bottom (otherView) + 1;
+    ```
+    is equivalent to 
+    ```cs
+    otherView.Margin.Thickness = new Thickness (0, 0, 1, 1);
+    view.X = Pos.Right (otherView);
+    view.Y = Pos.Bottom (otherView);
+    ```
+
+* *[Border](~/api/Terminal.Gui.View.Border.yml)* - The `Adornment` where a visual border (drawn using line-drawing glyphs) and the [Title](~/api/Terminal.Gui.View.Title.yml) are drawn. The Border expands inward; in other words if `Border.Thickness.Top == 2` the border & title will take up the first row and the second row will be filled with spaces. The Border is not part of the View's content and is not clipped by the View's `Clip`.
+
+* *[Padding](~/api/Terminal.Gui.View.Padding.yml)*  - The `Adornment` that offsets the `ContentArea` from the `Border`. `Padding` is `{0, 0, 0, 0}` by default. Padding is not part of the View's content and is not clipped by the View's `Clip`. When, enabled, scroll bars reside within `Padding`. 
+
+## Layout Modes
+
+* *Tile*, *Tiled*, *Tiling* - Refer to a form of [Layout](layout.md) where SubViews of a [View](~/api/Terminal.Gui.View.yml) are visually arranged such that they abut each other and do not overlap. In a Tiled view arrangement, Z-ordering only comes into play when a developer intentionally causes views to be aligned such that they overlap. Borders that are drawn between the SubViews can optionally support resizing the SubViews (negating the need for `TileView`).
+
+* *Overlap*, *Overlapped*, *Overlapping* - Refers to a form [Layout](layout.md) where SubViews of a View are visually arranged such that their Frames overlap. In Overlap view arrangements there is a Z-axis (Z-order) in addition to the X and Y dimension. The Z-order indicates which Views are shown above other views.
 
 ## The Frame
 
-The `Frame` property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. The `Frame`  has a `Location` and `Size`. The `Location` describes the top-left corner of the view relative to the `Superview`'s content area. The `Size` describes the width and height of the view. The `Frame` is used to determine where the view is drawn on the screen and is used to calculate the Viewport and content size.
+The [Frame](~/api/Terminal.Gui.View.Frame.yml) property of a `View` is a rectangle that describes the current location and size of the view relative to the `Superview`'s content area. The `Frame`  has a `Location` and `Size`. The `Location` describes the top-left corner of the view relative to the `SuperView`'s content area. The `Size` describes the width and height of the view. The `Frame` is used to determine where the view is drawn on the screen and is used to calculate the Viewport and content size.
 
 ## The Content Area
 
- The content area is the area where the view's content is drawn. Content can be any combination of the `View.Text` property, `Subviews`, and other content drawn by the View. The `View.GetContentSize()` property gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with a size of `ContentSize`.
+ The content area is the area where the view's content is drawn. Content can be any combination of the [View.Text](~/api/Terminal.Gui.View.Text.yml) property, `Subviews`, and other content drawn by the View. The [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml) method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml).
 
- The Content size tracks the size of the `Viewport` by default. If the content size is set via `SetContentSize()`, the content area is the provided size. If the content size is larger than the `Viewport`, scrolling is enabled. 
+ The Content Area size tracks the size of the [Viewport](~/api/Terminal.Gui.View.Viewport.yml) by default. If the content size is set via [View.SetContentSize()](~/api/Terminal.Gui.View.SetContentSize.yml), the content area is the provided size. If the content size is larger than the [Viewport](~/api/Terminal.Gui.View.Viewport.yml), scrolling is enabled. 
 
 ## The Viewport
 
-The Viewport (`View.Viewport`) is a rectangle describing the portion of the *Content Area* that is currently visible to the user. It is a "portal" into the content. The `Viewport.Location` is relative to the top-left corner of the inner rectangle of `View.Padding`. If `Viewport.Size` is the same as `View.GetContentSize()`, `Viewport.Location` will be `0,0`. 
+The Viewport ([Viewport](~/api/Terminal.Gui.View.Viewport.yml)) is a rectangle describing the portion of the *Content Area* that is currently visible to the user. It is a "portal" into the content. The `Viewport.Location` is relative to the top-left corner of the inner rectangle of `View.Padding`. If `Viewport.Size` is the same as `View.GetContentSize()`, `Viewport.Location` will be `0,0`. 
 
 To enable scrolling call `View.SetContentSize()` and then set `Viewport.Location` to positive values. Making `Viewport.Location` positive moves the Viewport down and to the right in the content. 
 
@@ -30,6 +69,12 @@ The `View.ViewportSettings` property controls how the Viewport is constrained. B
 
 The default `ViewportSettings` also constrains the Viewport to the size of the content, ensuring the right-most column or bottom-most row of the content will always be visible (in v1 the equivalent concept was `ScrollBarView.AlwaysKeepContentInViewport`). To allow the Viewport to be smaller than the content, set `ViewportSettings.AllowXGreaterThanContentWidth` and/or `ViewportSettings.AllowXGreaterThanContentHeight`.
 
+
+
+* *[Content Area](~/api/Terminal.Gui.View.GetContentSize().yml)* - The content area is the area where the view's content is drawn. Content can be any combination of the [View.Text](~/api/Terminal.Gui.View.Text.yml) property, `Subviews`, and other content drawn by the View. The [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml) method gets the size of the content area of the view. *Content Area* refers to the rectangle with a location of `0,0` with the size returned by [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml). The [Layout Deep Dive](layout.md) has more details on the Content Area.
+
+* *[Viewport](~/api/Terminal.Gui.View.Viewport.yml)* A rectangle describing the portion of the *Content Area* that is currently visible to the user. It is a "portal" into the content. The `Viewport.Location` is relative to the top-left corner of the inner rectangle of `View.Padding`. If `Viewport.Size` is the same as `View.GetContentSize()`, `Viewport.Location` will be `0,0`. 
+  
 ## Layout
 
 Terminal.Gui provides a rich system for how views are laid out relative to each other. The position of a view is set by setting the `X` and `Y` properties, which are of time [Pos](~/api/Terminal.Gui.Pos.yml). The size is set via `Width` and `Height`, which are of type [Dim](~/api/Terminal.Gui.Dim.yml).

+ 61 - 0
docfx/docs/scrolling.md

@@ -0,0 +1,61 @@
+# Scrolling
+
+Terminal.Gui provides a rich system for how [View](View.md) users can scroll content with the keyboard and/or mouse.
+
+## Lexicon & Taxonomy
+
+See [View Deep Dive](View.md) for broader definitions.
+
+* *Scroll* (Verb) - The act of causing content to move either horizontally or vertically within the [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml). Also referred to as "Content Scrolling".
+* *[Scroll](~/api/Terminal.Gui.Scroll.yml)* (Noun) - Indicates the size of scrollable content and provides a visible element, referred to as the "ScrollSlider" that that is sized to show the proportion of the scrollable content to the size of the [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml) and can be dragged with the mouse. A Scroll can be oriented either vertically or horizontally and is used within a [ScrollBar](~/api/Terminal.Gui.ScrollBar.yml).
+* *ScrollSlider* - The visual indicator that shows the proportion of the scrollable content to the size of the [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml) and allows the user to use the mouse to scroll. The Scroll Slider is not exposed publicly. 
+* *[ScrollBar](~/api/Terminal.Gui.ScrollBar.yml)* - Provides a visual indicator that content can be scrolled. ScrollBars consist of two buttons, one each for scrolling forward or backwards, a Scroll that can be clicked to scroll large amounts, and a ScrollSlider that can be dragged to scroll continuously. ScrollBars can be oriented either horizontally or vertically and support the user dragging and clicking with the mouse to scroll.
+
+
+## Overview
+
+The ability to scroll content is built into View. The [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml) represents the scrollable "viewport" into the View's Content Area (which is defined by the return value of [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml)). 
+
+By default, [View](~/api/Terminal.Gui.View.yml), includes no bindings for the typical directional keyboard and mouse input and cause the Content Area.
+
+Terminal.Gui also provides the ability show a visual scroll bar that responds to mouse input. This ability is not enabled by default given how precious TUI screen real estate is.
+
+Scrolling with the mouse and keyboard are enabled by:
+
+1) Making the [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml) size smaller than the size returned by [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml). 
+2) Creating key bindings for the appropriate directional keys (e.g. [Key.CursorDown](~/api/Terminal.Gui.Key)), and calling [View.ScrollHorizontal()](~/api/Terminal.Gui.View.ScrollHorizontal.yml)/[ScrollVertical()](~/api/Terminal.Gui.View.ScrollVertical.yml) as needed.
+3) Subscribing to [View.MouseEvent](~/api/Terminal.Gui.View.MouseEvent.yml) and calling calling [View.ScrollHorizontal()](~/api/Terminal.Gui.View.ScrollHorizontal.yml)/[ScrollVertical()](~/api/Terminal.Gui.View.ScrollVertical.yml) as needed.
+4) Enabling the [ScrollBar](~/api/Terminal.Gui.ScrollBar.yml)s built into View ([View.HorizontalScrollBar/VerticalScrollBar](~/api/Terminal.Gui.View.HorizontalScrollBar.yml)) by setting the flag [ViewportSettings.EnableScrollBars](~/api/Terminal.Gui.ViewportSettings.EnableScrollBars.yml) on [View.ViewportSettings](~/api/Terminal.Gui.View.ViewportSettings.yml). 
+
+## Examples
+
+These Scenarios illustrate Terminal.Gui scrolling:
+
+* *Content Scrolling* - Demonstrates the various [Viewport Settings](~/api/Terminal.Gui.ViewportSettings.yml) (see below) in an interactive manner. Used by the development team to visually verify that convoluted View layout and arrangement scenarios scroll properly.
+* *Character Map* - Demonstrates a sophisticated scrolling use-case. The entire set of Unicode code-points can be scrolled and searched. From a scrolling perspective, this Scenario illustrates how to manually configure `Viewport`, `SetContentArea()`, and `ViewportSettings` to enable horizontal and vertical headers (as might appear in a spreadsheet), full keyboard and mouse support, and more. 
+* *Scroll Demo* - Designed to demonstrate using the `Scroll` view in a standalone manner.
+* *ScrollBar Demo* - Designed to demonstrate using the `ScrollBar` view in a standalone manner.
+* *Scrolling* - A legacy Scenario from v1 that is used to visually test that scrolling is working properly.
+* *ListView* and *TableView* - The source code to these built-in Views are good references for how to support scrolling and ScrollBars in a re-usable View sub-class. 
+
+## [Viewport Settings](~/api/Terminal.Gui.ViewportSettings.yml)
+
+Use [View.ViewportSettings](~/api/Terminal.Gui.View.ViewportSettings.yml) to adjust the behavior of scrolling. 
+
+* [AllowNegativeX/Y](~/api/Terminal.Gui.ViewportSettings.AllowNegativeXyml) - If set, Viewport.Size can be set to negative coordinates enabling scrolling beyond the top-left of the content area.
+
+* [AllowX/YGreaterThanContentWidth](~/api/Terminal.Gui.ViewportSettings.AllowXGreaterThanContentWidth) - If set, Viewport.Size can be set values greater than GetContentSize() enabling scrolling beyond the bottom-right of the Content Area. When not set, `Viewport.X/Y` are constrained to the dimension of the content area - 1. This means the last column of the content will remain visible even if there is an attempt to scroll the Viewport past the last column. The practical effect of this is that the last column/row of the content will always be visible.
+
+* [ClipContentOnly](~/api/Terminal.Gui.ViewportSettings.ClipContentOnly) - By default, clipping is applied to [Viewport](~/api/Terminal.Gui.View.Viewport.yml). Setting this flag will cause clipping to be applied to the visible content area.
+
+* [ClearContentOnly](~/api/Terminal.Gui.ViewportSettings.ClearContentOnly) - If set [View.Clear()](~/api/Terminal.Gui.View.Clear.yml) will clear only the portion of the content area that is visible within the Viewport. This is useful for views that have a content area larger than the Viewport and want the area outside the content to be visually distinct.
+
+* [EnableHorizontal/VerticalScrollBar](~/api/Terminal.Gui.ViewportSettings.EnableHorizontalScrollBar) - If set, the scroll bar will be enabled and automatically made visible when the corresponding dimension of [View.Viewport](~/api/Terminal.Gui.View.Viewport.yml) is smaller than the dimension of [View.GetContentSize()](~/api/Terminal.Gui.View.GetContentSize.yml).
+
+
+## [ScrollBar](~/api/Terminal.Gui.ScrollBar.yml)
+
+Provides a visual indicator that content can be scrolled. ScrollBars consist of two buttons, one each for scrolling forward or backwards, a Scroll that can be clicked to scroll large amounts, and a ScrollSlider that can be dragged to scroll continuously. ScrollBars can be oriented either horizontally or vertically and support the user dragging and clicking with the mouse to scroll.
+
+While the *[Scroll](~/api/Terminal.Gui.Scroll.yml)* *[ScrollBar](~/api/Terminal.Gui.ScrollBar.yml)* Views can be used in a standalone manner to provide proportional scrolling, they are typically enabled automatically via the [View.HorizontalScrollBar](~/api/Terminal.Gui.View.HorizontalScrollBar.yml) and  [View.VerticalScrollBar](~/api/Terminal.Gui.View.VerticalScrollBar.yml) properties.
+