Selaa lähdekoodia

Refactored again

Tig 8 kuukautta sitten
vanhempi
commit
e591453694

+ 1 - 3
Terminal.Gui/View/View.ScrollBars.cs

@@ -105,9 +105,7 @@ public partial class View
                                                                                   {
                                                                                       Viewport = Viewport with
                                                                                       {
-                                                                                          Y = Math.Min (
-                                                                                                        args.CurrentValue,
-                                                                                                        GetContentSize ().Height - (Viewport.Height))
+                                                                                          Y = Math.Min (args.CurrentValue, GetContentSize ().Height - (Viewport.Height - 1))
                                                                                       };
                                                                                   };
 

+ 237 - 43
Terminal.Gui/Views/Scroll/Scroll.cs

@@ -1,6 +1,7 @@
 #nullable enable
 
 using System.ComponentModel;
+using System.Drawing;
 
 namespace Terminal.Gui;
 
@@ -16,18 +17,43 @@ namespace Terminal.Gui;
 ///         By default, this view cannot be focused and does not support keyboard.
 ///     </para>
 /// </remarks>
-public class Scroll : View, IOrientation, IDesignable
+public class ScrollBar : View, IOrientation, IDesignable
 {
+    private readonly Button _decreaseButton;
     internal readonly ScrollSlider _slider;
+    private readonly Button _increaseButton;
 
     /// <inheritdoc/>
-    public Scroll ()
+    public ScrollBar ()
     {
-        _slider = new ();
-        base.Add (_slider);
-        _slider.Scroll += SliderOnScroll;
+        _decreaseButton = new ()
+        {
+            CanFocus = false,
+            NoDecorations = true,
+            NoPadding = true,
+            ShadowStyle = ShadowStyle.None,
+            WantContinuousButtonPressed = true
+        };
+        _decreaseButton.Accepting += OnDecreaseButtonOnAccept;
+
+        _slider = new ()
+        {
+            ShrinkBy = 2, // For the buttons
+        };
+        _slider.Scrolled += SliderOnScroll;
         _slider.PositionChanged += SliderOnPositionChanged;
 
+        _increaseButton = new ()
+        {
+            CanFocus = false,
+            NoDecorations = true,
+            NoPadding = true,
+            ShadowStyle = ShadowStyle.None,
+            WantContinuousButtonPressed = true
+        };
+        _increaseButton.Accepting += OnIncreaseButtonOnAccept;
+        base.Add (_decreaseButton, _slider, _increaseButton);
+
         CanFocus = false;
 
         _orientationHelper = new (this); // Do not use object initializer!
@@ -37,22 +63,106 @@ public class Scroll : View, IOrientation, IDesignable
 
         // This sets the width/height etc...
         OnOrientationChanged (Orientation);
+
+        void OnDecreaseButtonOnAccept (object? s, CommandEventArgs e)
+        {
+            ContentPosition -= Increment;
+            e.Cancel = true;
+        }
+
+        void OnIncreaseButtonOnAccept (object? s, CommandEventArgs e)
+        {
+            ContentPosition += Increment;
+            e.Cancel = true;
+        }
+    }
+
+    /// <inheritdoc/>
+    protected override void OnFrameChanged (in Rectangle frame)
+    {
+        ShowHide ();
     }
 
+    private void ShowHide ()
+    {
+        if (!AutoHide || !IsInitialized)
+        {
+            return;
+        }
+
+        if (Orientation == Orientation.Vertical)
+        {
+            Visible = Frame.Height < Size;
+        }
+        else
+        {
+            Visible = Frame.Width < Size;
+        }
+    }
 
     /// <inheritdoc/>
     protected override void OnSubviewLayout (LayoutEventArgs args)
     {
-        if (ViewportDimension < 1)
-        {
-            _slider.Size = 1;
+        _slider.Size = CalculateSliderSize ();
 
-            return;
+        if (Orientation == Orientation.Vertical)
+        {
+            _slider.ViewportDimension = Viewport.Height - _slider.ShrinkBy;
+        }
+        else
+        {
+            _slider.ViewportDimension = Viewport.Width - _slider.ShrinkBy;
         }
+    }
 
-        _slider.Size = (int)Math.Clamp (Math.Floor ((double)ViewportDimension * ViewportDimension / (Size)), 1, ViewportDimension);
+    private int CalculateSliderSize ()
+    {
+        if (Size == 0 || ViewportDimension == 0)
+        {
+            return 1;
+        }
+        return (int)Math.Clamp (Math.Floor ((double)ViewportDimension / Size * (Viewport.Height - 2)), 1, ViewportDimension);
     }
 
+    private void PositionSubviews ()
+    {
+        if (Orientation == Orientation.Vertical)
+        {
+            _decreaseButton.Y = 0;
+            _decreaseButton.X = 0;
+            _decreaseButton.Width = Dim.Fill ();
+            _decreaseButton.Height = 1;
+            _decreaseButton.Title = Glyphs.UpArrow.ToString ();
+
+            _slider.X = 0;
+            _slider.Y = 1;
+            _slider.Width = Dim.Fill ();
+
+            _increaseButton.Y = Pos.AnchorEnd ();
+            _increaseButton.X = 0;
+            _increaseButton.Width = Dim.Fill ();
+            _increaseButton.Height = 1;
+            _increaseButton.Title = Glyphs.DownArrow.ToString ();
+        }
+        else
+        {
+            _decreaseButton.Y = 0;
+            _decreaseButton.X = 0;
+            _decreaseButton.Width = 1;
+            _decreaseButton.Height = Dim.Fill ();
+            _decreaseButton.Title = Glyphs.LeftArrow.ToString ();
+
+            _slider.Y = 0;
+            _slider.X = 1;
+            _slider.Height = Dim.Fill ();
+
+            _increaseButton.Y = 0;
+            _increaseButton.X = Pos.AnchorEnd ();
+            _increaseButton.Width = 1;
+            _increaseButton.Height = Dim.Fill ();
+            _increaseButton.Title = Glyphs.RightArrow.ToString ();
+        }
+    }
     #region IOrientation members
 
     private readonly OrientationHelper _orientationHelper;
@@ -92,10 +202,45 @@ public class Scroll : View, IOrientation, IDesignable
         }
 
         _slider.Orientation = newOrientation;
+        PositionSubviews ();
     }
 
     #endregion
 
+
+    private bool _autoHide = true;
+
+    /// <summary>
+    ///     Gets or sets whether <see cref="View.Visible"/> will be set to <see langword="false"/> if the dimension of the
+    ///     scroll bar is greater than or equal to <see cref="Size"/>.
+    /// </summary>
+    public bool AutoHide
+    {
+        get => _autoHide;
+        set
+        {
+            if (_autoHide != value)
+            {
+                _autoHide = value;
+
+                if (!AutoHide)
+                {
+                    Visible = true;
+                }
+
+                SetNeedsLayout ();
+            }
+        }
+    }
+
+    public bool KeepContentInAllViewport
+    {
+        //get => _scroll.KeepContentInAllViewport;
+        //set => _scroll.KeepContentInAllViewport = value;
+        get;
+        set;
+    }
+
     /// <summary>
     ///     Gets or sets whether the Scroll will show the percentage the slider
     ///     takes up within the <see cref="Size"/>.
@@ -165,11 +310,9 @@ public class Scroll : View, IOrientation, IDesignable
             return;
         }
 
-        int calculatedSliderPos = CalculateSliderPosition (_contentPosition);
+        int pos = e.CurrentValue;
 
-        ContentPosition = (int)Math.Round ((double)e.CurrentValue / (ViewportDimension - _slider.Size) * (Size - ViewportDimension));
-
-        RaiseSliderPositionChangeEvents (calculatedSliderPos, e.CurrentValue);
+        RaiseSliderPositionChangeEvents (_slider.Position, pos);
     }
 
     private void SliderOnScroll (object? sender, EventArgs<int> e)
@@ -178,6 +321,14 @@ public class Scroll : View, IOrientation, IDesignable
         {
             return;
         }
+
+        int calculatedSliderPos = CalculateSliderPosition (_contentPosition, e.CurrentValue >= 0 ? NavigationDirection.Forward : NavigationDirection.Backward);
+        int sliderScrolledAmount = e.CurrentValue;
+        int scrolledAmount = CalculateContentPosition (sliderScrolledAmount);
+
+        RaiseSliderPositionChangeEvents (calculatedSliderPos, _slider.Position);
+
+        ContentPosition = _contentPosition + scrolledAmount;
     }
 
     /// <summary>
@@ -187,7 +338,7 @@ public class Scroll : View, IOrientation, IDesignable
 
     private void RaiseSliderPositionChangeEvents (int calculatedSliderPosition, int newSliderPosition)
     {
-        if (/*newSliderPosition > Size - ViewportDimension ||*/ calculatedSliderPosition == newSliderPosition)
+        if (calculatedSliderPosition == newSliderPosition)
         {
             return;
         }
@@ -205,16 +356,26 @@ public class Scroll : View, IOrientation, IDesignable
     /// <summary>Raised when the slider position has changed.</summary>
     public event EventHandler<EventArgs<int>>? SliderPositionChanged;
 
-    private int CalculateSliderPosition (int contentPosition)
+    private int CalculateSliderPosition (int contentPosition, NavigationDirection direction = NavigationDirection.Forward)
     {
         if (Size - ViewportDimension == 0)
         {
             return 0;
         }
 
-        return (int)Math.Round ((double)contentPosition / (Size - ViewportDimension) * (ViewportDimension - _slider.Size));
+        int scrollBarSize = Orientation == Orientation.Vertical ? Viewport.Height : Viewport.Width;
+        double newSliderPosition = (double)contentPosition / (Size - ViewportDimension) * (scrollBarSize - _slider.Size - _slider.ShrinkBy);
+
+        return direction == NavigationDirection.Forward ? (int)Math.Floor (newSliderPosition) : (int)Math.Ceiling (newSliderPosition);
     }
 
+    private int CalculateContentPosition (int sliderPosition)
+    {
+        int scrollBarSize = Orientation == Orientation.Vertical ? Viewport.Height : Viewport.Width;
+        return (int)Math.Round ((double)(sliderPosition) / (scrollBarSize - _slider.Size - _slider.ShrinkBy) * (Size - ViewportDimension));
+    }
+
+
     #endregion SliderPosition
 
     #region ContentPosition
@@ -245,33 +406,39 @@ public class Scroll : View, IOrientation, IDesignable
 
             // Clamp the value between 0 and Size - ViewportDimension
             int newContentPosition = (int)Math.Clamp (value, 0, Math.Max (0, Size - ViewportDimension));
+            NavigationDirection direction = newContentPosition >= _contentPosition ? NavigationDirection.Forward : NavigationDirection.Backward;
 
-            RaiseContentPositionChangeEvents (newContentPosition);
+            if (OnContentPositionChanging (_contentPosition, newContentPosition))
+            {
+                return;
+            }
 
-            _slider.SetPosition (CalculateSliderPosition (_contentPosition));
-        }
-    }
+            CancelEventArgs<int> args = new (ref _contentPosition, ref newContentPosition);
+            ContentPositionChanging?.Invoke (this, args);
 
-    private void RaiseContentPositionChangeEvents (int newContentPosition)
-    {
+            if (args.Cancel)
+            {
+                return;
+            }
 
-        if (OnContentPositionChanging (_contentPosition, newContentPosition))
-        {
-            return;
-        }
+            int distance = newContentPosition - _contentPosition;
 
-        CancelEventArgs<int> args = new (ref _contentPosition, ref newContentPosition);
-        ContentPositionChanging?.Invoke (this, args);
+            _contentPosition = newContentPosition;
 
-        if (args.Cancel)
-        {
-            return;
-        }
+            OnContentPositionChanged (_contentPosition);
+            ContentPositionChanged?.Invoke (this, new (in _contentPosition));
+
+            OnScrolled (distance);
+            Scrolled?.Invoke (this, new (in distance));
+
+            int currentSliderPosition = _slider.Position;
+            int calculatedSliderPosition = CalculateSliderPosition (_contentPosition, direction);
 
-        _contentPosition = newContentPosition;
+            _slider.MoveToPosition (calculatedSliderPosition);
 
-        OnContentPositionChanged (_contentPosition);
-        ContentPositionChanged?.Invoke (this, new (in _contentPosition));
+            RaiseSliderPositionChangeEvents (currentSliderPosition, _slider.Position);
+
+        }
     }
 
     /// <summary>
@@ -291,16 +458,30 @@ public class Scroll : View, IOrientation, IDesignable
     /// <summary>Raised when the <see cref="ContentPosition"/> has changed.</summary>
     public event EventHandler<EventArgs<int>>? ContentPositionChanged;
 
+    /// <summary>Called when <see cref="ContentPosition"/> has changed. Indicates how much to scroll.</summary>
+    protected virtual void OnScrolled (int distance) { }
+
+    /// <summary>Raised when the <see cref="ContentPosition"/> has changed. Indicates how much to scroll.</summary>
+    public event EventHandler<EventArgs<int>>? Scrolled;
+
     #endregion ContentPosition
 
     /// <inheritdoc/>
     protected override bool OnClearingViewport ()
     {
-        FillRect (Viewport, Glyphs.Stipple);
+        if (Orientation == Orientation.Vertical)
+        {
+            FillRect (Viewport with { Y = Viewport.Y + 1, Height = Viewport.Height - 2 }, Glyphs.Stipple);
+        }
+        else
+        {
+            FillRect (Viewport with { X = Viewport.X + 1, Width = Viewport.Width - 2 }, Glyphs.Stipple);
+        }
 
         return true;
     }
 
+    // TODO: Change this to work OnMouseEvent with continuouse press and grab so it's continous.
     /// <inheritdoc/>
     protected override bool OnMouseClick (MouseEventArgs args)
     {
@@ -315,22 +496,35 @@ public class Scroll : View, IOrientation, IDesignable
 
         if (Orientation == Orientation.Vertical)
         {
-            sliderCenter = _slider.Frame.Y + _slider.Frame.Height / 2;
+            sliderCenter = 1 + _slider.Frame.Y + _slider.Frame.Height / 2;
             distanceFromCenter = args.Position.Y - sliderCenter;
         }
         else
         {
-            sliderCenter = _slider.Frame.X + _slider.Frame.Width / 2;
+            sliderCenter = 1 + _slider.Frame.X + _slider.Frame.Width / 2;
             distanceFromCenter = args.Position.X - sliderCenter;
         }
 
+#if PROPORTIONAL_SCROLL_JUMP
+        // BUGBUG: This logic mostly works to provide a proportional jump. However, the math
+        // BUGBUG: falls apart in edge cases. Most other scroll bars (e.g. Windows) do not do prooportional
+        // BUGBUG: Thus, this is disabled and we just jump a page each click.
         // Ratio of the distance to the viewport dimension
-        double ratio = (double)Math.Abs (distanceFromCenter) / ViewportDimension;
+        double ratio = (double)Math.Abs (distanceFromCenter) / (ViewportDimension);
         // Jump size based on the ratio and the total content size
-        int jump = (int)Math.Ceiling (ratio * Size);
-
+        int jump = (int)(ratio * (Size - ViewportDimension));
+#else
+        int jump = (ViewportDimension);
+#endif
         // Adjust the content position based on the distance
-        ContentPosition += distanceFromCenter < 0 ? -jump : jump;
+        if (distanceFromCenter < 0)
+        {
+            ContentPosition = Math.Max (0, ContentPosition - jump);
+        }
+        else
+        {
+            ContentPosition = Math.Min (Size - _slider.ViewportDimension, ContentPosition + jump);
+        }
 
         return true;
     }

+ 344 - 315
Terminal.Gui/Views/Scroll/ScrollBar.cs

@@ -1,315 +1,344 @@
-#nullable enable
-
-using System.ComponentModel;
-
-namespace Terminal.Gui;
-
-/// <summary>
-///     Provides a visual indicator that content can be scrolled. ScrollBars consist of two buttons, one each for scrolling
-///     forward or backwards, a <see cref="Scroll"/> 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>
-/// </remarks>
-public class ScrollBar : View, IOrientation, IDesignable
-{
-    private readonly Scroll _scroll;
-    private readonly Button _decreaseButton;
-    private readonly Button _increaseButton;
-
-    /// <inheritdoc/>
-    public ScrollBar ()
-    {
-        CanFocus = false;
-
-        _scroll = new ();
-        _scroll.SliderPositionChanged += OnScrollOnSliderPositionChanged;
-        _scroll.ContentPositionChanging += OnScrollOnContentPositionChanging;
-        _scroll.ContentPositionChanged += OnScrollOnContentPositionChanged;
-        _scroll.SizeChanged += OnScrollOnSizeChanged;
-
-        _decreaseButton = new ()
-        {
-            CanFocus = false,
-            NoDecorations = true,
-            NoPadding = true,
-            ShadowStyle = ShadowStyle.None,
-            WantContinuousButtonPressed = true
-        };
-        _decreaseButton.Accepting += OnDecreaseButtonOnAccept;
-
-        _increaseButton = new ()
-        {
-            CanFocus = false,
-            NoDecorations = true,
-            NoPadding = true,
-            ShadowStyle = ShadowStyle.None,
-            WantContinuousButtonPressed = true
-        };
-        _increaseButton.Accepting += OnIncreaseButtonOnAccept;
-        Add (_decreaseButton, _scroll, _increaseButton);
-
-        _orientationHelper = new (this); // Do not use object initializer!
-        _orientationHelper.Orientation = Orientation.Vertical;
-        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
-        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
-
-        // This sets the width/height etc...
-        OnOrientationChanged (Orientation);
-
-        return;
-
-        void OnDecreaseButtonOnAccept (object? s, CommandEventArgs e)
-        {
-            ContentPosition -= Increment;
-            e.Cancel = true;
-        }
-
-        void OnIncreaseButtonOnAccept (object? s, CommandEventArgs e)
-        {
-            ContentPosition += Increment;
-            e.Cancel = true;
-        }
-    }
-
-    #region IOrientation members
-
-    private readonly OrientationHelper _orientationHelper;
-
-    /// <inheritdoc/>
-    public Orientation Orientation
-    {
-        get => _orientationHelper.Orientation;
-        set => _orientationHelper.Orientation = value;
-    }
-
-    /// <inheritdoc/>
-    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
-
-    /// <inheritdoc/>
-    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
-
-    /// <inheritdoc/>
-    public void OnOrientationChanged (Orientation newOrientation)
-    {
-        TextDirection = Orientation == Orientation.Vertical ? TextDirection.TopBottom_LeftRight : TextDirection.LeftRight_TopBottom;
-        TextAlignment = Alignment.Center;
-        VerticalTextAlignment = Alignment.Center;
-
-        if (Orientation == Orientation.Vertical)
-        {
-            Width = 1;
-            Height = Dim.Fill ();
-        }
-        else
-        {
-            Width = Dim.Fill ();
-            Height = 1;
-        }
-
-        // Force a layout to ensure _scroll 
-        Layout ();
-        _scroll.Orientation = newOrientation;
-    }
-
-    #endregion
-
-    private bool _autoHide = true;
-
-    /// <summary>
-    ///     Gets or sets whether <see cref="View.Visible"/> will be set to <see langword="false"/> if the dimension of the
-    ///     scroll bar is greater than or equal to <see cref="Size"/>.
-    /// </summary>
-    public bool AutoHide
-    {
-        get => _autoHide;
-        set
-        {
-            if (_autoHide != value)
-            {
-                _autoHide = value;
-
-                if (!AutoHide)
-                {
-                    Visible = true;
-                }
-
-                SetNeedsLayout ();
-            }
-        }
-    }
-
-    /// <inheritdoc/>
-    protected override void OnFrameChanged (in Rectangle frame) { ShowHide (); }
-
-    private void ShowHide ()
-    {
-        if (!AutoHide || !IsInitialized)
-        {
-            return;
-        }
-
-        if (Orientation == Orientation.Vertical)
-        {
-            Visible = Frame.Height < Size;
-        }
-        else
-        {
-            Visible = Frame.Width < Size;
-        }
-    }
-
-    /// <summary>
-    ///     Gets or sets whether the Scroll will show the percentage the slider
-    ///     takes up within the <see cref="Size"/>.
-    /// </summary>
-    public bool ShowPercent
-    {
-        get => _scroll.ShowPercent;
-        set => _scroll.ShowPercent = value;
-    }
-
-
-    /// <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;
-        //set => _scroll.KeepContentInAllViewport = value;
-        get;
-        set;
-    }
-
-    /// <summary>Gets or sets the position of the slider within the ScrollBar's Viewport.</summary>
-    /// <returns>The position.</returns>
-    public int GetSliderPosition () => _scroll.GetSliderPosition ();
-
-    private void OnScrollOnSliderPositionChanged (object? sender, EventArgs<int> e) { SliderPositionChanged?.Invoke (this, e); }
-
-    /// <summary>Raised when the position of the slider has changed.</summary>
-    public event EventHandler<EventArgs<int>>? SliderPositionChanged;
-
-    /// <summary>
-    ///     Gets or sets the size of the Scroll. This is the total size of the content that can be scrolled through.
-    /// </summary>
-    public int Size
-    {
-        get => _scroll.Size;
-        set => _scroll.Size = value;
-    }
-
-    /// <summary>
-    ///     Gets or sets the size of the viewport into the content being scrolled, bounded by <see cref="Size"/>.
-    /// </summary>
-    /// <remarks>
-    ///     If not explicitly set, will be the appropriate dimension of the Scroll's Frame.
-    /// </remarks>
-    public int ViewportDimension
-    {
-        get => _scroll.ViewportDimension;
-        set => _scroll.ViewportDimension = value;
-    }
-    /// <summary>
-    ///     Gets or sets the position of the ScrollSlider within the range of 0...<see cref="Size"/>.
-    /// </summary>
-    public int ContentPosition
-    {
-        get => _scroll.ContentPosition;
-        set => _scroll.ContentPosition = value;
-    }
-
-    private void OnScrollOnContentPositionChanging (object? sender, CancelEventArgs<int> e) { ContentPositionChanging?.Invoke (this, e); }
-    private void OnScrollOnContentPositionChanged (object? sender, EventArgs<int> e) { ContentPositionChanged?.Invoke (this, e); }
-
-    /// <summary>
-    ///     Raised when the <see cref="ContentPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
-    ///     <see langword="true"/> to prevent the position from being changed.
-    /// </summary>
-    public event EventHandler<CancelEventArgs<int>>? ContentPositionChanging;
-
-    /// <summary>Raised when the <see cref="ContentPosition"/> has changed.</summary>
-    public event EventHandler<EventArgs<int>>? ContentPositionChanged;
-
-    /// <summary>Raised when <see cref="Size"/> has changed.</summary>
-    public event EventHandler<EventArgs<int>>? SizeChanged;
-
-    private void OnScrollOnSizeChanged (object? sender, EventArgs<int> e)
-    {
-        ShowHide ();
-        SizeChanged?.Invoke (this, e);
-    }
-
-    /// <summary>
-    ///     Gets or sets the amount each click of the increment/decrement buttons and each
-    ///     mouse wheel event will incremenet/decrement the <see cref="ContentPosition"/>.
-    /// </summary>
-    /// <remarks>
-    ///     The default is 1.
-    /// </remarks>
-    public int Increment { get => _scroll.Increment; set => _scroll.Increment = value; }
-
-    /// <inheritdoc/>
-    protected override void OnSubviewLayout (LayoutEventArgs args) { PositionSubviews (); }
-
-    private void PositionSubviews ()
-    {
-        if (Orientation == Orientation.Vertical)
-        {
-            _decreaseButton.Y = 0;
-            _decreaseButton.X = 0;
-            _decreaseButton.Width = Dim.Fill ();
-            _decreaseButton.Height = 1;
-            _decreaseButton.Title = Glyphs.UpArrow.ToString ();
-            _increaseButton.Y = Pos.Bottom (_scroll);
-            _increaseButton.X = 0;
-            _increaseButton.Width = Dim.Fill ();
-            _increaseButton.Height = 1;
-            _increaseButton.Title = Glyphs.DownArrow.ToString ();
-            _scroll.X = 0;
-            _scroll.Y = Pos.Bottom (_decreaseButton);
-            _scroll.Height = Dim.Fill (1);
-            _scroll.Width = Dim.Fill ();
-        }
-        else
-        {
-            _decreaseButton.Y = 0;
-            _decreaseButton.X = 0;
-            _decreaseButton.Width = 1;
-            _decreaseButton.Height = Dim.Fill ();
-            _decreaseButton.Title = Glyphs.LeftArrow.ToString ();
-            _increaseButton.Y = 0;
-            _increaseButton.X = Pos.Right (_scroll);
-            _increaseButton.Width = 1;
-            _increaseButton.Height = Dim.Fill ();
-            _increaseButton.Title = Glyphs.RightArrow.ToString ();
-            _scroll.Y = 0;
-            _scroll.X = Pos.Right (_decreaseButton);
-            _scroll.Width = Dim.Fill (1);
-            _scroll.Height = Dim.Fill ();
-        }
-    }
-
-    /// <inheritdoc/>
-    public bool EnableForDesign ()
-    {
-        OrientationChanged += (sender, args) =>
-                              {
-                                  if (args.CurrentValue == Orientation.Vertical)
-                                  {
-                                      Width = 1;
-                                      Height = Dim.Fill ();
-                                  }
-                                  else
-                                  {
-                                      Width = Dim.Fill ();
-                                      Height = 1;
-                                  }
-                              };
-
-        Width = 1;
-        Height = Dim.Fill ();
-        Size = 200;
-        //ShowPercent = true;
-        return true;
-    }
-}
+//#nullable enable
+
+//using System.ComponentModel;
+//using static Unix.Terminal.Delegates;
+
+//namespace Terminal.Gui;
+
+///// <summary>
+/////     Provides a visual indicator that content can be scrolled. ScrollBars consist of two buttons, one each for scrolling
+/////     forward or backwards, a <see cref="Scroll"/> 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>
+///// </remarks>
+//public class ScrollBar : View, IOrientation, IDesignable
+//{
+//    private readonly Scroll _scroll;
+//    private readonly Button _decreaseButton;
+//    private readonly Button _increaseButton;
+
+//    /// <inheritdoc/>
+//    public ScrollBar ()
+//    {
+//        CanFocus = false;
+
+//        _scroll = new ();
+//        _scroll.SliderPositionChanged += ScrollOnSliderPositionChanged;
+//        _scroll.ContentPositionChanging += OnScrollOnContentPositionChanging;
+//        _scroll.ContentPositionChanged += OnScrollOnContentPositionChanged;
+//        _scroll.Scrolled += ScrollOnScrolled;
+//        _scroll.SizeChanged += OnScrollOnSizeChanged;
+
+//        _decreaseButton = new ()
+//        {
+//            CanFocus = false,
+//            NoDecorations = true,
+//            NoPadding = true,
+//            ShadowStyle = ShadowStyle.None,
+//            WantContinuousButtonPressed = true
+//        };
+//        _decreaseButton.Accepting += OnDecreaseButtonOnAccept;
+
+//        _increaseButton = new ()
+//        {
+//            CanFocus = false,
+//            NoDecorations = true,
+//            NoPadding = true,
+//            ShadowStyle = ShadowStyle.None,
+//            WantContinuousButtonPressed = true
+//        };
+//        _increaseButton.Accepting += OnIncreaseButtonOnAccept;
+//        Add (_decreaseButton, _scroll, _increaseButton);
+
+//        _orientationHelper = new (this); // Do not use object initializer!
+//        _orientationHelper.Orientation = Orientation.Vertical;
+//        _orientationHelper.OrientationChanging += (sender, e) => OrientationChanging?.Invoke (this, e);
+//        _orientationHelper.OrientationChanged += (sender, e) => OrientationChanged?.Invoke (this, e);
+
+//        // This sets the width/height etc...
+//        OnOrientationChanged (Orientation);
+
+//        return;
+
+//        void OnDecreaseButtonOnAccept (object? s, CommandEventArgs e)
+//        {
+//            ContentPosition -= Increment;
+//            e.Cancel = true;
+//        }
+
+//        void OnIncreaseButtonOnAccept (object? s, CommandEventArgs e)
+//        {
+//            ContentPosition += Increment;
+//            e.Cancel = true;
+//        }
+//    }
+
+//    #region IOrientation members
+
+//    private readonly OrientationHelper _orientationHelper;
+
+//    /// <inheritdoc/>
+//    public Orientation Orientation
+//    {
+//        get => _orientationHelper.Orientation;
+//        set => _orientationHelper.Orientation = value;
+//    }
+
+//    /// <inheritdoc/>
+//    public event EventHandler<CancelEventArgs<Orientation>>? OrientationChanging;
+
+//    /// <inheritdoc/>
+//    public event EventHandler<EventArgs<Orientation>>? OrientationChanged;
+
+//    /// <inheritdoc/>
+//    public void OnOrientationChanged (Orientation newOrientation)
+//    {
+//        TextDirection = Orientation == Orientation.Vertical ? TextDirection.TopBottom_LeftRight : TextDirection.LeftRight_TopBottom;
+//        TextAlignment = Alignment.Center;
+//        VerticalTextAlignment = Alignment.Center;
+
+//        if (Orientation == Orientation.Vertical)
+//        {
+//            Width = 1;
+//            Height = Dim.Fill ();
+//        }
+//        else
+//        {
+//            Width = Dim.Fill ();
+//            Height = 1;
+//        }
+
+//        // Force a layout to ensure _scroll 
+//        Layout ();
+//        _scroll.Orientation = newOrientation;
+//    }
+
+//    #endregion
+
+//    private bool _autoHide = true;
+
+//    /// <summary>
+//    ///     Gets or sets whether <see cref="View.Visible"/> will be set to <see langword="false"/> if the dimension of the
+//    ///     scroll bar is greater than or equal to <see cref="Size"/>.
+//    /// </summary>
+//    public bool AutoHide
+//    {
+//        get => _autoHide;
+//        set
+//        {
+//            if (_autoHide != value)
+//            {
+//                _autoHide = value;
+
+//                if (!AutoHide)
+//                {
+//                    Visible = true;
+//                }
+
+//                SetNeedsLayout ();
+//            }
+//        }
+//    }
+
+//    /// <inheritdoc/>
+//    protected override void OnFrameChanged (in Rectangle frame)
+//    {
+//        ShowHide ();
+//        if (!_viewportDimension.HasValue)
+//        {
+//            _scroll.ViewportDimension = (Orientation == Orientation.Vertical ? Frame.Height : Frame.Width) - 2;
+//        }
+//    }
+
+//    private void ShowHide ()
+//    {
+//        if (!AutoHide || !IsInitialized)
+//        {
+//            return;
+//        }
+
+//        if (Orientation == Orientation.Vertical)
+//        {
+//            Visible = Frame.Height < Size;
+//        }
+//        else
+//        {
+//            Visible = Frame.Width < Size;
+//        }
+//    }
+
+//    /// <summary>
+//    ///     Gets or sets whether the Scroll will show the percentage the slider
+//    ///     takes up within the <see cref="Size"/>.
+//    /// </summary>
+//    public bool ShowPercent
+//    {
+//        get => _scroll.ShowPercent;
+//        set => _scroll.ShowPercent = value;
+//    }
+
+
+//    /// <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;
+//        //set => _scroll.KeepContentInAllViewport = value;
+//        get;
+//        set;
+//    }
+
+//    /// <summary>Gets or sets the position of the slider within the ScrollBar's Viewport.</summary>
+//    /// <returns>The position.</returns>
+//    public int GetSliderPosition () => _scroll.GetSliderPosition ();
+
+//    private void ScrollOnSliderPositionChanged (object? sender, EventArgs<int> e) { SliderPositionChanged?.Invoke (this, e); }
+
+//    /// <summary>Raised when the position of the slider has changed.</summary>
+//    public event EventHandler<EventArgs<int>>? SliderPositionChanged;
+
+//    private void ScrollOnScrolled (object? sender, EventArgs<int> e) { Scrolled?.Invoke (this, e); }
+
+//    /// <summary>Raised when the position of the slider has changed.</summary>
+//    public event EventHandler<EventArgs<int>>? Scrolled;
+
+//    /// <summary>
+//    ///     Gets or sets the size of the Scroll. This is the total size of the content that can be scrolled through.
+//    /// </summary>
+//    public int Size
+//    {
+//        get => _scroll.Size;
+//        set => _scroll.Size = value;
+//    }
+
+//    private int? _viewportDimension;
+
+//    /// <summary>
+//    ///     Gets or sets the size of the viewport into the content being scrolled, bounded by <see cref="Size"/>.
+//    /// </summary>
+//    /// <remarks>
+//    ///     If not explicitly set, will be the appropriate dimension of the Scroll's Frame.
+//    /// </remarks>
+//    public int ViewportDimension
+//    {
+//        get
+//        {
+//            if (_viewportDimension.HasValue)
+//            {
+//                return _viewportDimension.Value;
+//            }
+//            return Orientation == Orientation.Vertical ? Frame.Height : Frame.Width;
+
+//        }
+//        set
+//        {
+//            _scroll.ViewportDimension = value - 2;
+//            _viewportDimension = value;
+//        }
+//    }
+
+//    /// <summary>
+//    ///     Gets or sets the position of the ScrollSlider within the range of 0...<see cref="Size"/>.
+//    /// </summary>
+//    public int ContentPosition
+//    {
+//        get => _scroll.ContentPosition;
+//        set => _scroll.ContentPosition = value;
+//    }
+
+//    private void OnScrollOnContentPositionChanging (object? sender, CancelEventArgs<int> e) { ContentPositionChanging?.Invoke (this, e); }
+//    private void OnScrollOnContentPositionChanged (object? sender, EventArgs<int> e) { ContentPositionChanged?.Invoke (this, e); }
+
+//    /// <summary>
+//    ///     Raised when the <see cref="ContentPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
+//    ///     <see langword="true"/> to prevent the position from being changed.
+//    /// </summary>
+//    public event EventHandler<CancelEventArgs<int>>? ContentPositionChanging;
+
+//    /// <summary>Raised when the <see cref="ContentPosition"/> has changed.</summary>
+//    public event EventHandler<EventArgs<int>>? ContentPositionChanged;
+
+//    /// <summary>Raised when <see cref="Size"/> has changed.</summary>
+//    public event EventHandler<EventArgs<int>>? SizeChanged;
+
+//    private void OnScrollOnSizeChanged (object? sender, EventArgs<int> e)
+//    {
+//        ShowHide ();
+//        SizeChanged?.Invoke (this, e);
+//    }
+
+//    /// <summary>
+//    ///     Gets or sets the amount each click of the increment/decrement buttons and each
+//    ///     mouse wheel event will incremenet/decrement the <see cref="ContentPosition"/>.
+//    /// </summary>
+//    /// <remarks>
+//    ///     The default is 1.
+//    /// </remarks>
+//    public int Increment { get => _scroll.Increment; set => _scroll.Increment = value; }
+
+//    /// <inheritdoc/>
+//    protected override void OnSubviewLayout (LayoutEventArgs args) { PositionSubviews (); }
+
+//    private void PositionSubviews ()
+//    {
+//        if (Orientation == Orientation.Vertical)
+//        {
+//            _decreaseButton.Y = 0;
+//            _decreaseButton.X = 0;
+//            _decreaseButton.Width = Dim.Fill ();
+//            _decreaseButton.Height = 1;
+//            _decreaseButton.Title = Glyphs.UpArrow.ToString ();
+//            _increaseButton.Y = Pos.Bottom (_scroll);
+//            _increaseButton.X = 0;
+//            _increaseButton.Width = Dim.Fill ();
+//            _increaseButton.Height = 1;
+//            _increaseButton.Title = Glyphs.DownArrow.ToString ();
+//            _scroll.X = 0;
+//            _scroll.Y = Pos.Bottom (_decreaseButton);
+//            _scroll.Height = Dim.Fill (1);
+//            _scroll.Width = Dim.Fill ();
+//        }
+//        else
+//        {
+//            _decreaseButton.Y = 0;
+//            _decreaseButton.X = 0;
+//            _decreaseButton.Width = 1;
+//            _decreaseButton.Height = Dim.Fill ();
+//            _decreaseButton.Title = Glyphs.LeftArrow.ToString ();
+//            _increaseButton.Y = 0;
+//            _increaseButton.X = Pos.Right (_scroll);
+//            _increaseButton.Width = 1;
+//            _increaseButton.Height = Dim.Fill ();
+//            _increaseButton.Title = Glyphs.RightArrow.ToString ();
+//            _scroll.Y = 0;
+//            _scroll.X = Pos.Right (_decreaseButton);
+//            _scroll.Width = Dim.Fill (1);
+//            _scroll.Height = Dim.Fill ();
+//        }
+//    }
+
+//    /// <inheritdoc/>
+//    public bool EnableForDesign ()
+//    {
+//        OrientationChanged += (sender, args) =>
+//                              {
+//                                  if (args.CurrentValue == Orientation.Vertical)
+//                                  {
+//                                      Width = 1;
+//                                      Height = Dim.Fill ();
+//                                  }
+//                                  else
+//                                  {
+//                                      Width = Dim.Fill ();
+//                                      Height = 1;
+//                                  }
+//                              };
+
+//        Width = 1;
+//        Height = Dim.Fill ();
+//        Size = 200;
+//        //ShowPercent = true;
+//        return true;
+//    }
+//}

+ 57 - 37
Terminal.Gui/Views/Scroll/ScrollSlider.cs

@@ -16,7 +16,7 @@ namespace Terminal.Gui;
 ///         be show what percent the slider is to the Superview's Viewport size.
 ///     </para>
 ///     <para>
-///        Used to represent the proportion of the visible content to the Viewport in a <see cref="Scroll"/>.
+///        Used to represent the proportion of the visible content to the Viewport in a <see cref="Scrolled"/>.
 ///     </para>
 /// </remarks>
 public class ScrollSlider : View, IOrientation, IDesignable
@@ -38,9 +38,6 @@ public class ScrollSlider : View, IOrientation, IDesignable
 
         HighlightStyle = HighlightStyle.Hover;
 
-        // Default size is 1
-        Size = 1;
-
         FrameChanged += OnFrameChanged;
     }
 
@@ -70,7 +67,7 @@ public class ScrollSlider : View, IOrientation, IDesignable
         // Reset Position to 0 when changing orientation
         X = 0;
         Y = 0;
-        //Position = 0;
+        Position = 0;
 
         // Reset Size to 1 when changing orientation
         if (Orientation == Orientation.Vertical)
@@ -127,11 +124,11 @@ public class ScrollSlider : View, IOrientation, IDesignable
         {
             if (Orientation == Orientation.Vertical)
             {
-                return Viewport.Height;
+                return Viewport.Height - ShrinkBy / 2;
             }
             else
             {
-                return Viewport.Width;
+                return Viewport.Width - ShrinkBy / 2;
             }
         }
         set
@@ -144,24 +141,37 @@ public class ScrollSlider : View, IOrientation, IDesignable
             }
             else
             {
+                Height = Dim.Fill ();
                 int viewport = Math.Max (1, SuperView?.Viewport.Width ?? 1);
                 Width = Math.Clamp (value, 1, viewport);
-                Height = Dim.Fill ();
             }
         }
     }
 
+    private int? _viewportDimension;
     /// <summary>
     ///     Gets the size of the viewport into the content being scrolled, bounded by <see cref="Size"/>.
     /// </summary>
     /// <remarks>
     ///     This is the SuperView's Viewport demension.
     /// </remarks>
-    public int ViewportDimension => Orientation == Orientation.Vertical ? SuperView?.Viewport.Height ?? 0 : SuperView?.Viewport.Width ?? 0;
+    public int ViewportDimension
+    {
+        get
+        {
+            if (_viewportDimension.HasValue)
+            {
+                return _viewportDimension.Value;
+            }
+            return Orientation == Orientation.Vertical ? SuperView?.Viewport.Height ?? 0 : SuperView?.Viewport.Width ?? 0;
+        }
+        set => _viewportDimension = value;
+    }
 
     private void OnFrameChanged (object? sender, EventArgs<Rectangle> e)
     {
-        Position = Orientation == Orientation.Vertical ? e.CurrentValue.Y : e.CurrentValue.X;
+        
+        Position = (Orientation == Orientation.Vertical ? e.CurrentValue.Y : e.CurrentValue.X) - ShrinkBy / 2;
     }
 
     private int _position;
@@ -187,27 +197,30 @@ public class ScrollSlider : View, IOrientation, IDesignable
         }
     }
 
-    public void SetPosition (int position)
+    /// <summary>
+    ///     Moves the scroll slider to the specified position.
+    ///     The position will be constrained such that the ScrollSlider will not go outside the Viewport of
+    ///     its <see cref="View.SuperView"/>.
+    /// </summary>
+    /// <param name="position"></param>
+    public void MoveToPosition (int position)
     {
         _position = ClampPosition (position);
 
         if (Orientation == Orientation.Vertical)
         {
-            Y = _position;
+            Y = _position + ShrinkBy / 2;
         }
         else
         {
-            X = _position;
+            X = _position + ShrinkBy / 2;
         }
+
+        Layout ();
     }
 
     private int ClampPosition (int newPosittion)
     {
-        if (SuperView is null || !IsInitialized)
-        {
-            return 1;
-        }
-
         if (Orientation == Orientation.Vertical)
         {
             return Math.Clamp (newPosittion, 0, Math.Max (0, ViewportDimension - Viewport.Height));
@@ -233,16 +246,16 @@ public class ScrollSlider : View, IOrientation, IDesignable
             return;
         }
 
-        int scrollAmount = newPosition - _position;
-        _position = newPosition;
+        int distance = newPosition - _position;
+        MoveToPosition (newPosition);
 
         OnPositionChanged (_position);
         PositionChanged?.Invoke (this, new (in _position));
 
-        OnScroll (scrollAmount);
-        Scroll?.Invoke (this, new (in scrollAmount));
+        OnScrolled (distance);
+        Scrolled?.Invoke (this, new (in distance));
 
-        RaiseSelecting (new CommandContext (Command.Select, null, null, scrollAmount));
+        RaiseSelecting (new CommandContext (Command.Select, null, null, distance));
     }
 
     /// <summary>
@@ -264,10 +277,10 @@ public class ScrollSlider : View, IOrientation, IDesignable
 
 
     /// <summary>Called when <see cref="Position"/> has changed. Indicates how much to scroll.</summary>
-    protected virtual void OnScroll (int scrollAmount) { }
+    protected virtual void OnScrolled (int distance) { }
 
     /// <summary>Raised when the <see cref="Position"/> has changed. Indicates how much to scroll.</summary>
-    public event EventHandler<EventArgs<int>>? Scroll;
+    public event EventHandler<EventArgs<int>>? Scrolled;
 
     /// <inheritdoc/>
     protected override bool OnDrawingText ()
@@ -302,6 +315,8 @@ public class ScrollSlider : View, IOrientation, IDesignable
     ///// <inheritdoc/>
     private int _lastLocation = -1;
 
+    public int ShrinkBy { get; set; }
+
     /// <inheritdoc/>
     protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
     {
@@ -310,9 +325,14 @@ public class ScrollSlider : View, IOrientation, IDesignable
             return false;
         }
 
-        int location = Orientation == Orientation.Vertical ? mouseEvent.Position.Y : mouseEvent.Position.X;
+        if (mouseEvent.IsSingleDoubleOrTripleClicked)
+        {
+            return true;
+        }
+
+        int location = (Orientation == Orientation.Vertical ? mouseEvent.Position.Y : mouseEvent.Position.X) + ShrinkBy / 2;
         int offset = _lastLocation > -1 ? location - _lastLocation : 0;
-        int superViewDimension = Orientation == Orientation.Vertical ? SuperView!.Viewport.Height : SuperView!.Viewport.Width;
+        int superViewDimension = ViewportDimension;
 
         if (mouseEvent.IsPressed || mouseEvent.IsReleased)
         {
@@ -328,18 +348,18 @@ public class ScrollSlider : View, IOrientation, IDesignable
             {
                 if (Orientation == Orientation.Vertical)
                 {
-                    Y = Frame.Y + offset < 0
-                                  ? 0
-                            : Frame.Y + offset + Frame.Height > superViewDimension
-                                ? Math.Max (superViewDimension - Frame.Height, 0)
-                                : Frame.Y + offset;
+                    Y = Frame.Y + offset < ShrinkBy / 2
+                             ? ShrinkBy / 2
+                             : Frame.Y + offset + Frame.Height > superViewDimension
+                                 ? Math.Max (superViewDimension - Frame.Height + ShrinkBy, 1)
+                                 : Frame.Y + offset;
                 }
                 else
                 {
-                    X = Frame.X + offset < 0
-                                  ? 0
-                            : Frame.X + offset + Frame.Width > superViewDimension
-                                ? Math.Max (superViewDimension - Frame.Width, 0)
+                    X = Frame.X + offset < ShrinkBy / 2
+                            ? ShrinkBy / 2
+                            : Frame.X + offset + Frame.Height > superViewDimension
+                                ? Math.Max (superViewDimension - Frame.Height + ShrinkBy / 2, 1)
                                 : Frame.X + offset;
                 }
             }
@@ -371,7 +391,7 @@ public class ScrollSlider : View, IOrientation, IDesignable
                                   else
                                   {
                                       Width = 5;
-                                      Height = Dim.Fill();
+                                      Height = Dim.Fill ();
                                   }
                               };
 

+ 4 - 0
UICatalog/Scenarios/CharacterMap/CharMap.cs

@@ -175,6 +175,10 @@ public class CharMap : View, IDesignable
                                                   }
                                               };
 
+        _vScrollBar.Scrolled += (sender, args) =>
+                                              {
+                                                  //ScrollVertical (args.CurrentValue);
+                                              };
         _hScrollBar.ContentPositionChanged += (sender, args) =>
                                               {
                                                   if (Viewport.Width > 0)

+ 129 - 52
UICatalog/Scenarios/ScrollBarDemo.cs

@@ -1,4 +1,5 @@
 using System;
+using System.Collections.ObjectModel;
 using System.Linq;
 using Terminal.Gui;
 
@@ -18,34 +19,44 @@ public class ScrollBarDemo : Scenario
             Arrangement = ViewArrangement.Fixed
         };
 
-        var editor = new AdornmentsEditor ();
-        app.Add (editor);
-
-        var frameView = new FrameView
+        var demoFrame = new FrameView ()
         {
             Title = "Demo View",
-            X = Pos.Right (editor),
-            Width = Dim.Fill (),
-            Height = Dim.Fill (),
+            X = 0,
+            Width = 75,
+            Height = 25 + 4,
             ColorScheme = Colors.ColorSchemes ["Base"],
             Arrangement = ViewArrangement.Resizable
         };
-        frameView!.Padding!.Thickness = new (1);
-        frameView.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
-        app.Add (frameView);
+        demoFrame!.Padding!.Thickness = new (1);
+        demoFrame.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
+        app.Add (demoFrame);
 
         var scrollBar = new ScrollBar
         {
-            X = Pos.AnchorEnd (),
+            X = Pos.AnchorEnd () - 5,
             AutoHide = false,
             Size = 100,
             //ShowPercent = true
         };
-        frameView.Add (scrollBar);
+        demoFrame.Add (scrollBar);
+
+        ListView controlledList = new ()
+        {
+            X = Pos.AnchorEnd (),
+            Width = 5,
+            Height = Dim.Fill (),
+            ColorScheme = Colors.ColorSchemes ["Error"],
+        };
+
+        demoFrame.Add (controlledList);
+
+        // populate the list box with Size items of the form "{n:00000}"
+        controlledList.SetSource (new ObservableCollection<string> (Enumerable.Range (0, scrollBar.Size).Select (n => $"{n:00000}")));
 
         int GetMaxLabelWidth (int groupId)
         {
-            return frameView.Subviews.Max (
+            return demoFrame.Subviews.Max (
                                            v =>
                                            {
                                                if (v.Y.Has<PosAlign> (out var pos) && pos.GroupId == groupId)
@@ -64,7 +75,7 @@ public class ScrollBarDemo : Scenario
             Y = Pos.Align (Alignment.Start, AlignmentModes.StartToEnd, groupId: 1),
             Width = Dim.Func (() => GetMaxLabelWidth (1))
         };
-        frameView.Add (lblWidthHeight);
+        demoFrame.Add (lblWidthHeight);
 
         NumericUpDown<int> scrollWidthHeight = new ()
         {
@@ -72,7 +83,7 @@ public class ScrollBarDemo : Scenario
             X = Pos.Right (lblWidthHeight) + 1,
             Y = Pos.Top (lblWidthHeight)
         };
-        frameView.Add (scrollWidthHeight);
+        demoFrame.Add (scrollWidthHeight);
 
         scrollWidthHeight.ValueChanging += (s, e) =>
                                            {
@@ -106,7 +117,7 @@ public class ScrollBarDemo : Scenario
             Y = Pos.Align (Alignment.Start, groupId: 1),
             Width = Dim.Func (() => GetMaxLabelWidth (1))
         };
-        frameView.Add (lblOrientationabel);
+        demoFrame.Add (lblOrientationabel);
 
         var rgOrientation = new RadioGroup
         {
@@ -115,7 +126,7 @@ public class ScrollBarDemo : Scenario
             RadioLabels = ["Vertical", "Horizontal"],
             Orientation = Orientation.Horizontal
         };
-        frameView.Add (rgOrientation);
+        demoFrame.Add (rgOrientation);
 
         rgOrientation.SelectedItemChanged += (s, e) =>
                                              {
@@ -127,18 +138,21 @@ public class ScrollBarDemo : Scenario
                                                  if (rgOrientation.SelectedItem == 0)
                                                  {
                                                      scrollBar.Orientation = Orientation.Vertical;
-                                                     scrollBar.X = Pos.AnchorEnd ();
+                                                     scrollBar.X = Pos.AnchorEnd () - 5;
                                                      scrollBar.Y = 0;
-                                                     scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView.GetContentSize ().Width);
+                                                     scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView!.GetContentSize ().Width);
                                                      scrollBar.Width = scrollWidthHeight.Value;
+                                                     controlledList.Visible = true;
                                                  }
                                                  else
                                                  {
                                                      scrollBar.Orientation = Orientation.Horizontal;
                                                      scrollBar.X = 0;
                                                      scrollBar.Y = Pos.AnchorEnd ();
-                                                     scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView.GetContentSize ().Height);
+                                                     scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView!.GetContentSize ().Height);
                                                      scrollBar.Height = scrollWidthHeight.Value;
+                                                     controlledList.Visible = false;
+
                                                  }
                                              };
 
@@ -149,7 +163,7 @@ public class ScrollBarDemo : Scenario
             Y = Pos.Align (Alignment.Start, groupId: 1),
             Width = Dim.Func (() => GetMaxLabelWidth (1))
         };
-        frameView.Add (lblSize);
+        demoFrame.Add (lblSize);
 
         NumericUpDown<int> scrollSize = new ()
         {
@@ -157,7 +171,7 @@ public class ScrollBarDemo : Scenario
             X = Pos.Right (lblSize) + 1,
             Y = Pos.Top (lblSize)
         };
-        frameView.Add (scrollSize);
+        demoFrame.Add (scrollSize);
 
         scrollSize.ValueChanging += (s, e) =>
                                     {
@@ -174,6 +188,38 @@ public class ScrollBarDemo : Scenario
                                         }
                                     };
 
+        var lblViewportDimension = new Label
+        {
+            Text = "_ViewportDimension::",
+            TextAlignment = Alignment.End,
+            Y = Pos.Align (Alignment.Start, groupId: 1),
+            Width = Dim.Func (() => GetMaxLabelWidth (1))
+        };
+        demoFrame.Add (lblViewportDimension);
+
+        NumericUpDown<int> viewportDimension = new ()
+        {
+            Value = scrollBar.ViewportDimension,
+            X = Pos.Right (lblViewportDimension) + 1,
+            Y = Pos.Top (lblViewportDimension)
+        };
+        demoFrame.Add (viewportDimension);
+
+        viewportDimension.ValueChanging += (s, e) =>
+                                           {
+                                               if (e.NewValue < 0)
+                                               {
+                                                   e.Cancel = true;
+
+                                                   return;
+                                               }
+
+                                               if (scrollBar.ViewportDimension != e.NewValue)
+                                               {
+                                                   scrollBar.ViewportDimension = e.NewValue;
+                                               }
+                                           };
+
         var lblSliderPosition = new Label
         {
             Text = "_SliderPosition:",
@@ -182,7 +228,7 @@ public class ScrollBarDemo : Scenario
             Width = Dim.Func (() => GetMaxLabelWidth (1))
 
         };
-        frameView.Add (lblSliderPosition);
+        demoFrame.Add (lblSliderPosition);
 
         Label scrollSliderPosition = new ()
         {
@@ -190,7 +236,23 @@ public class ScrollBarDemo : Scenario
             X = Pos.Right (lblSliderPosition) + 1,
             Y = Pos.Top (lblSliderPosition)
         };
-        frameView.Add (scrollSliderPosition);
+        demoFrame.Add (scrollSliderPosition);
+
+        var lblScrolled = new Label
+        {
+            Text = "_Scrolled:",
+            TextAlignment = Alignment.End,
+            Y = Pos.Align (Alignment.Start, groupId: 1),
+            Width = Dim.Func (() => GetMaxLabelWidth (1))
+
+        };
+        demoFrame.Add (lblScrolled);
+        Label scrolled = new ()
+        {
+            X = Pos.Right (lblScrolled) + 1,
+            Y = Pos.Top (lblScrolled)
+        };
+        demoFrame.Add (scrolled);
 
         var lblContentPosition = new Label
         {
@@ -200,7 +262,7 @@ public class ScrollBarDemo : Scenario
             Width = Dim.Func (() => GetMaxLabelWidth (1))
 
         };
-        frameView.Add (lblContentPosition);
+        demoFrame.Add (lblContentPosition);
 
         NumericUpDown<int> scrollContentPosition = new ()
         {
@@ -208,7 +270,7 @@ public class ScrollBarDemo : Scenario
             X = Pos.Right (lblContentPosition) + 1,
             Y = Pos.Top (lblContentPosition)
         };
-        frameView.Add (scrollContentPosition);
+        demoFrame.Add (scrollContentPosition);
 
         scrollContentPosition.ValueChanging += (s, e) =>
                                                {
@@ -237,7 +299,7 @@ public class ScrollBarDemo : Scenario
             Y = Pos.Align (Alignment.Start, groupId: 1),
             Width = Dim.Func (() => GetMaxLabelWidth (1))
         };
-        frameView.Add (lblOptions);
+        demoFrame.Add (lblOptions);
         var ckbAutoHide = new CheckBox
         {
             Y = Pos.Top (lblOptions),
@@ -246,7 +308,7 @@ public class ScrollBarDemo : Scenario
             CheckedState = scrollBar.AutoHide ? CheckState.Checked : CheckState.UnChecked
         };
         ckbAutoHide.CheckedStateChanging += (s, e) => scrollBar.AutoHide = e.NewValue == CheckState.Checked;
-        frameView.Add (ckbAutoHide);
+        demoFrame.Add (ckbAutoHide);
 
         var ckbShowPercent = new CheckBox
         {
@@ -256,7 +318,7 @@ public class ScrollBarDemo : Scenario
             CheckedState = scrollBar.ShowPercent ? CheckState.Checked : CheckState.UnChecked
         };
         ckbShowPercent.CheckedStateChanging += (s, e) => scrollBar.ShowPercent = e.NewValue == CheckState.Checked;
-        frameView.Add (ckbShowPercent);
+        demoFrame.Add (ckbShowPercent);
 
         //var ckbKeepContentInAllViewport = new CheckBox
         //{
@@ -270,64 +332,79 @@ public class ScrollBarDemo : Scenario
         {
             Y = Pos.Bottom (lblOptions) + 1
         };
-        frameView.Add (lblScrollFrame);
+        demoFrame.Add (lblScrollFrame);
 
         var lblScrollViewport = new Label
         {
             Y = Pos.Bottom (lblScrollFrame)
         };
-        frameView.Add (lblScrollViewport);
+        demoFrame.Add (lblScrollViewport);
 
         var lblScrollContentSize = new Label
         {
             Y = Pos.Bottom (lblScrollViewport)
         };
-        frameView.Add (lblScrollContentSize);
+        demoFrame.Add (lblScrollContentSize);
 
         scrollBar.SubviewsLaidOut += (s, e) =>
                                      {
                                          lblScrollFrame.Text = $"Scroll Frame: {scrollBar.Frame.ToString ()}";
                                          lblScrollViewport.Text = $"Scroll Viewport: {scrollBar.Viewport.ToString ()}";
                                          lblScrollContentSize.Text = $"Scroll ContentSize: {scrollBar.GetContentSize ().ToString ()}";
+                                         viewportDimension.Value = scrollBar.ViewportDimension;
                                      };
 
         EventLog eventLog = new ()
         {
-            X = Pos.AnchorEnd () - 1,
+            X = Pos.AnchorEnd (),
             Y = 0,
-            Height = Dim.Height (frameView),
+            Height = Dim.Fill (),
             BorderStyle = LineStyle.Single,
             ViewToLog = scrollBar
         };
         app.Add (eventLog);
-        frameView.Width = Dim.Fill (Dim.Func (() => Math.Max (28, eventLog.Frame.Width + 1)));
 
         app.Initialized += AppOnInitialized;
 
-
         void AppOnInitialized (object sender, EventArgs e)
         {
             scrollBar.SizeChanged += (s, e) =>
-                                     {
-                                         eventLog.Log ($"SizeChanged: {e.CurrentValue}");
+                                  {
+                                      eventLog.Log ($"SizeChanged: {e.CurrentValue}");
 
-                                         if (scrollSize.Value != e.CurrentValue)
-                                         {
-                                             scrollSize.Value = e.CurrentValue;
-                                         }
-                                     };
+                                      if (scrollSize.Value != e.CurrentValue)
+                                      {
+                                          scrollSize.Value = e.CurrentValue;
+                                      }
+                                  };
 
             scrollBar.SliderPositionChanged += (s, e) =>
-                                         {
-                                             eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
-                                             eventLog.Log ($"  ContentPosition: {scrollBar.ContentPosition}");
-                                             scrollSliderPosition.Text = e.CurrentValue.ToString ();
-                                         };
+                                            {
+                                                eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
+                                                eventLog.Log ($"  ContentPosition: {scrollBar.ContentPosition}");
+                                                scrollSliderPosition.Text = e.CurrentValue.ToString ();
+                                            };
+
+            scrollBar.Scrolled += (s, e) =>
+                               {
+                                   eventLog.Log ($"Scrolled: {e.CurrentValue}");
+                                   eventLog.Log ($"  SliderPosition: {scrollBar.GetSliderPosition ()}");
+                                   scrolled.Text = e.CurrentValue.ToString ();
+                               };
+
+            scrollBar.ContentPositionChanged += (s, e) =>
+                                             {
+                                                 eventLog.Log ($"ContentPositionChanged: {e.CurrentValue}");
+                                                 scrollContentPosition.Value = e.CurrentValue;
+                                                 controlledList.Viewport = controlledList.Viewport with { Y = e.CurrentValue };
+                                             };
 
-            editor.Initialized += (s, e) =>
-                                  {
-                                      editor.ViewToEdit = scrollBar;
-                                  };
+
+            controlledList.ViewportChanged += (s, e) =>
+                                              {
+                                                  eventLog.Log ($"ViewportChanged: {e.NewViewport.Y}");
+                                                  scrollBar.ContentPosition = e.NewViewport.Y;
+                                              };
 
         }
 

+ 369 - 334
UICatalog/Scenarios/ScrollDemo.cs

@@ -1,334 +1,369 @@
-using System;
-using System.Linq;
-using Terminal.Gui;
-
-namespace UICatalog.Scenarios;
-
-[ScenarioMetadata ("Scroll Demo", "Demonstrates Scroll.")]
-[ScenarioCategory ("Scrolling")]
-public class ScrollDemo : Scenario
-{
-    public override void Main ()
-    {
-        Application.Init ();
-
-        Window app = new ()
-        {
-            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
-            Arrangement = ViewArrangement.Fixed
-        };
-
-        var editor = new AdornmentsEditor ();
-        app.Add (editor);
-
-        var frameView = new FrameView
-        {
-            Title = "Demo View",
-            X = Pos.Right (editor),
-            Width = Dim.Fill (),
-            Height = Dim.Fill (),
-            ColorScheme = Colors.ColorSchemes ["Base"],
-            Arrangement = ViewArrangement.Resizable
-        };
-        frameView.Padding.Thickness = new (1);
-        frameView.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
-        app.Add (frameView);
-
-        var scroll = new Scroll
-        {
-            X = Pos.AnchorEnd (),
-            Size = 1000,
-        };
-        frameView.Add (scroll);
-
-        int GetMaxLabelWidth (int groupId)
-        {
-            return frameView.Subviews.Max (
-                                           v =>
-                                           {
-                                               if (v.Y.Has<PosAlign> (out var pos) && pos.GroupId == groupId)
-                                               {
-                                                   return v.Text.GetColumns ();
-                                               }
-
-                                               return 0;
-                                           });
-        }
-
-        var lblWidthHeight = new Label
-        {
-            Text = "_Width/Height:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, AlignmentModes.StartToEnd, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-        };
-        frameView.Add (lblWidthHeight);
-
-        NumericUpDown<int> scrollWidthHeight = new ()
-        {
-            Value = scroll.Frame.Width,
-            X = Pos.Right (lblWidthHeight) + 1,
-            Y = Pos.Top (lblWidthHeight)
-        };
-        frameView.Add (scrollWidthHeight);
-
-        scrollWidthHeight.ValueChanging += (s, e) =>
-        {
-            if (e.NewValue < 1
-                || (e.NewValue
-                    > (scroll.Orientation == Orientation.Vertical
-                           ? scroll.SuperView?.GetContentSize ().Width
-                           : scroll.SuperView?.GetContentSize ().Height)))
-            {
-                // TODO: This must be handled in the ScrollSlider if Width and Height being virtual
-                e.Cancel = true;
-
-                return;
-            }
-
-            if (scroll.Orientation == Orientation.Vertical)
-            {
-                scroll.Width = e.NewValue;
-            }
-            else
-            {
-                scroll.Height = e.NewValue;
-            }
-        };
-
-
-        var lblOrientationabel = new Label
-        {
-            Text = "_Orientation:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-        };
-        frameView.Add (lblOrientationabel);
-
-        var rgOrientation = new RadioGroup
-        {
-            X = Pos.Right (lblOrientationabel) + 1,
-            Y = Pos.Top (lblOrientationabel),
-            RadioLabels = ["Vertical", "Horizontal"],
-            Orientation = Orientation.Horizontal
-        };
-        frameView.Add (rgOrientation);
-
-        rgOrientation.SelectedItemChanged += (s, e) =>
-        {
-            if (e.SelectedItem == e.PreviousSelectedItem)
-            {
-                return;
-            }
-
-            if (rgOrientation.SelectedItem == 0)
-            {
-                scroll.Orientation = Orientation.Vertical;
-                scroll.X = Pos.AnchorEnd ();
-                scroll.Y = 0;
-                scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scroll.SuperView.GetContentSize ().Width);
-                scroll.Width = scrollWidthHeight.Value;
-            }
-            else
-            {
-                scroll.Orientation = Orientation.Horizontal;
-                scroll.X = 0;
-                scroll.Y = Pos.AnchorEnd ();
-                scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scroll.SuperView.GetContentSize ().Height);
-                scroll.Height = scrollWidthHeight.Value;
-            }
-        };
-
-        var lblSize = new Label
-        {
-            Text = "_Size:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-        };
-        frameView.Add (lblSize);
-
-        NumericUpDown<int> scrollSize = new ()
-        {
-            Value = scroll.Size,
-            X = Pos.Right (lblSize) + 1,
-            Y = Pos.Top (lblSize)
-        };
-        frameView.Add (scrollSize);
-
-        scrollSize.ValueChanging += (s, e) =>
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            if (scroll.Size != e.NewValue)
-            {
-                scroll.Size = e.NewValue;
-            }
-        };
-
-        var lblSliderPosition = new Label
-        {
-            Text = "_SliderPosition:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-
-        };
-        frameView.Add (lblSliderPosition);
-
-        Label scrollSliderPosition = new ()
-        {
-            Text = scroll.GetSliderPosition ().ToString (),
-            X = Pos.Right (lblSliderPosition) + 1,
-            Y = Pos.Top (lblSliderPosition)
-        };
-        frameView.Add (scrollSliderPosition);
-
-        var lblContentPosition = new Label
-        {
-            Text = "_ContentPosition:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-
-        };
-        frameView.Add (lblContentPosition);
-
-        NumericUpDown<int> scrollContentPosition = new ()
-        {
-            Value = scroll.GetSliderPosition (),
-            X = Pos.Right (lblContentPosition) + 1,
-            Y = Pos.Top (lblContentPosition)
-        };
-        frameView.Add (scrollContentPosition);
-
-        scrollContentPosition.ValueChanging += (s, e) =>
-        {
-            if (e.NewValue < 0)
-            {
-                e.Cancel = true;
-
-                return;
-            }
-
-            if (scroll.ContentPosition != e.NewValue)
-            {
-                scroll.ContentPosition = e.NewValue;
-            }
-
-            if (scroll.ContentPosition != e.NewValue)
-            {
-                e.Cancel = true;
-            }
-        };
-
-        var lblOptions = new Label
-        {
-            Text = "_Options:",
-            TextAlignment = Alignment.End,
-            Y = Pos.Align (Alignment.Start, groupId: 1),
-            Width = Dim.Func (() => GetMaxLabelWidth (1))
-        };
-        frameView.Add (lblOptions);
-
-        var ckbShowPercent = new CheckBox
-        {
-            Y = Pos.Top (lblOptions),
-            X = Pos.Right (lblOptions) + 1,
-            Text = "Sho_wPercent",
-            CheckedState = scroll.ShowPercent ? CheckState.Checked : CheckState.UnChecked
-        };
-        ckbShowPercent.CheckedStateChanging += (s, e) => scroll.ShowPercent = e.NewValue == CheckState.Checked;
-        frameView.Add (ckbShowPercent);
-
-        //var ckbKeepContentInAllViewport = new CheckBox
-        //{
-        //    X = Pos.Right (ckbShowScrollIndicator) + 1, Y = Pos.Bottom (scrollPosition), Text = "KeepContentInAllViewport",
-        //    CheckedState = Scroll.KeepContentInAllViewport ? CheckState.Checked : CheckState.UnChecked
-        //};
-        //ckbKeepContentInAllViewport.CheckedStateChanging += (s, e) => Scroll.KeepContentInAllViewport = e.NewValue == CheckState.Checked;
-        //view.Add (ckbKeepContentInAllViewport);
-
-        var lblScrollFrame = new Label
-        {
-            Y = Pos.Bottom (lblOptions) + 1
-        };
-        frameView.Add (lblScrollFrame);
-
-        var lblScrollViewport = new Label
-        {
-            Y = Pos.Bottom (lblScrollFrame)
-        };
-        frameView.Add (lblScrollViewport);
-
-        var lblScrollContentSize = new Label
-        {
-            Y = Pos.Bottom (lblScrollViewport)
-        };
-        frameView.Add (lblScrollContentSize);
-
-        scroll.SubviewsLaidOut += (s, e) =>
-        {
-            lblScrollFrame.Text = $"Scroll Frame: {scroll.Frame.ToString ()}";
-            lblScrollViewport.Text = $"Scroll Viewport: {scroll.Viewport.ToString ()}";
-            lblScrollContentSize.Text = $"Scroll ContentSize: {scroll.GetContentSize ().ToString ()}";
-        };
-
-        EventLog eventLog = new ()
-        {
-            X = Pos.AnchorEnd () - 1,
-            Y = 0,
-            Height = Dim.Height (frameView),
-            BorderStyle = LineStyle.Single,
-            ViewToLog = scroll
-        };
-        app.Add (eventLog);
-        frameView.Width = Dim.Fill (Dim.Func (() => Math.Max (28, eventLog.Frame.Width + 1)));
-
-        app.Initialized += AppOnInitialized;
-
-
-        void AppOnInitialized (object sender, EventArgs e)
-        {
-            scroll.SizeChanged += (s, e) =>
-            {
-                eventLog.Log ($"SizeChanged: {e.CurrentValue}");
-
-                if (scrollSize.Value != e.CurrentValue)
-                {
-                    scrollSize.Value = e.CurrentValue;
-                }
-            };
-
-            scroll.SliderPositionChanged += (s, e) =>
-            {
-                eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
-                eventLog.Log ($"  ContentPosition: {scroll.ContentPosition}");
-                scrollSliderPosition.Text = e.CurrentValue.ToString ();
-            };
-
-            scroll.ContentPositionChanged += (s, e) =>
-                                            {
-                                                eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
-                                                eventLog.Log ($"  ContentPosition: {scroll.ContentPosition}");
-                                                scrollContentPosition.Value = e.CurrentValue;
-                                            };
-
-            editor.Initialized += (s, e) =>
-            {
-                editor.ViewToEdit = scroll;
-            };
-
-        }
-
-        Application.Run (app);
-        app.Dispose ();
-        Application.Shutdown ();
-    }
-}
+//using System;
+//using System.Collections.ObjectModel;
+//using System.Linq;
+//using Terminal.Gui;
+
+//namespace UICatalog.Scenarios;
+
+//[ScenarioMetadata ("Scroll Demo", "Demonstrates Scroll.")]
+//[ScenarioCategory ("Scrolling")]
+//public class ScrollDemo : Scenario
+//{
+//    public override void Main ()
+//    {
+//        Application.Init ();
+
+//        Window app = new ()
+//        {
+//            Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}",
+//            Arrangement = ViewArrangement.Fixed
+//        };
+
+//        var demoFrame = new FrameView ()
+//        {
+//            Title = "Demo View",
+//            X = 0,
+//            Width = 75,
+//            Height = 25 + 4,
+//            ColorScheme = Colors.ColorSchemes ["Base"],
+//            Arrangement = ViewArrangement.Resizable
+//        };
+//        demoFrame.Padding.Thickness = new (1);
+//        demoFrame.Padding.Diagnostics = ViewDiagnosticFlags.Ruler;
+
+//        app.Add (demoFrame);
+
+//        var scroll = new Scroll
+//        {
+//            X = Pos.AnchorEnd () - 5,
+//            Size = 1000,
+//        };
+//        demoFrame.Add (scroll);
+
+//        ListView controlledList = new ()
+//        {
+//            X = Pos.AnchorEnd (),
+//            Width = 5,
+//            Height = Dim.Fill (),
+//            ColorScheme = Colors.ColorSchemes ["Error"],
+//        };
+//        demoFrame.Add (controlledList);
+
+//        // populate the list box with Size items of the form "{n:00000}"
+//        controlledList.SetSource (new ObservableCollection<string> (Enumerable.Range (0, scroll.Size).Select (n => $"{n:00000}")));
+
+//        int GetMaxLabelWidth (int groupId)
+//        {
+//            return demoFrame.Subviews.Max (
+//                                           v =>
+//                                           {
+//                                               if (v.Y.Has<PosAlign> (out var pos) && pos.GroupId == groupId)
+//                                               {
+//                                                   return v.Text.GetColumns ();
+//                                               }
+
+//                                               return 0;
+//                                           });
+//        }
+
+//        var lblWidthHeight = new Label
+//        {
+//            Text = "_Width/Height:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, AlignmentModes.StartToEnd, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+//        };
+//        demoFrame.Add (lblWidthHeight);
+
+//        NumericUpDown<int> scrollWidthHeight = new ()
+//        {
+//            Value = scroll.Frame.Width,
+//            X = Pos.Right (lblWidthHeight) + 1,
+//            Y = Pos.Top (lblWidthHeight)
+//        };
+//        demoFrame.Add (scrollWidthHeight);
+
+//        scrollWidthHeight.ValueChanging += (s, e) =>
+//        {
+//            if (e.NewValue < 1
+//                || (e.NewValue
+//                    > (scroll.Orientation == Orientation.Vertical
+//                           ? scroll.SuperView?.GetContentSize ().Width
+//                           : scroll.SuperView?.GetContentSize ().Height)))
+//            {
+//                // TODO: This must be handled in the ScrollSlider if Width and Height being virtual
+//                e.Cancel = true;
+
+//                return;
+//            }
+
+//            if (scroll.Orientation == Orientation.Vertical)
+//            {
+//                scroll.Width = e.NewValue;
+//            }
+//            else
+//            {
+//                scroll.Height = e.NewValue;
+//            }
+//        };
+
+
+//        var lblOrientationabel = new Label
+//        {
+//            Text = "_Orientation:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+//        };
+//        demoFrame.Add (lblOrientationabel);
+
+//        var rgOrientation = new RadioGroup
+//        {
+//            X = Pos.Right (lblOrientationabel) + 1,
+//            Y = Pos.Top (lblOrientationabel),
+//            RadioLabels = ["Vertical", "Horizontal"],
+//            Orientation = Orientation.Horizontal
+//        };
+//        demoFrame.Add (rgOrientation);
+
+//        rgOrientation.SelectedItemChanged += (s, e) =>
+//        {
+//            if (e.SelectedItem == e.PreviousSelectedItem)
+//            {
+//                return;
+//            }
+
+//            if (rgOrientation.SelectedItem == 0)
+//            {
+//                scroll.Orientation = Orientation.Vertical;
+//                scroll.X = Pos.AnchorEnd ();
+//                scroll.Y = 0;
+//                scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scroll.SuperView.GetContentSize ().Width);
+//                scroll.Width = scrollWidthHeight.Value;
+//                controlledList.Visible = true;
+//            }
+//            else
+//            {
+//                scroll.Orientation = Orientation.Horizontal;
+//                scroll.X = 0;
+//                scroll.Y = Pos.AnchorEnd ();
+//                scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scroll.SuperView.GetContentSize ().Height);
+//                scroll.Height = scrollWidthHeight.Value;
+//                controlledList.Visible = false;
+//            }
+//        };
+
+//        var lblSize = new Label
+//        {
+//            Text = "_Size:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+//        };
+//        demoFrame.Add (lblSize);
+
+//        NumericUpDown<int> scrollSize = new ()
+//        {
+//            Value = scroll.Size,
+//            X = Pos.Right (lblSize) + 1,
+//            Y = Pos.Top (lblSize)
+//        };
+//        demoFrame.Add (scrollSize);
+
+//        scrollSize.ValueChanging += (s, e) =>
+//        {
+//            if (e.NewValue < 0)
+//            {
+//                e.Cancel = true;
+
+//                return;
+//            }
+
+//            if (scroll.Size != e.NewValue)
+//            {
+//                scroll.Size = e.NewValue;
+//            }
+//        };
+
+//        var lblSliderPosition = new Label
+//        {
+//            Text = "_SliderPosition:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+
+//        };
+//        demoFrame.Add (lblSliderPosition);
+//        Label scrollSliderPosition = new ()
+//        {
+//            X = Pos.Right (lblSliderPosition) + 1,
+//            Y = Pos.Top (lblSliderPosition)
+//        };
+//        demoFrame.Add (scrollSliderPosition);
+
+//        var lblScrolled = new Label
+//        {
+//            Text = "_Scrolled:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+
+//        };
+//        demoFrame.Add (lblScrolled);
+//        Label scrolled = new ()
+//        {
+//            X = Pos.Right (lblScrolled) + 1,
+//            Y = Pos.Top (lblScrolled)
+//        };
+//        demoFrame.Add (scrolled);
+
+//        var lblContentPosition = new Label
+//        {
+//            Text = "_ContentPosition:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+
+//        };
+//        demoFrame.Add (lblContentPosition);
+
+//        NumericUpDown<int> scrollContentPosition = new ()
+//        {
+//            Value = scroll.GetSliderPosition (),
+//            X = Pos.Right (lblContentPosition) + 1,
+//            Y = Pos.Top (lblContentPosition)
+//        };
+//        demoFrame.Add (scrollContentPosition);
+
+//        scrollContentPosition.ValueChanging += (s, e) =>
+//        {
+//            if (e.NewValue < 0)
+//            {
+//                e.Cancel = true;
+
+//                return;
+//            }
+
+//            if (scroll.ContentPosition != e.NewValue)
+//            {
+//                scroll.ContentPosition = e.NewValue;
+//            }
+
+//            if (scroll.ContentPosition != e.NewValue)
+//            {
+//                e.Cancel = true;
+//            }
+//        };
+
+//        var lblOptions = new Label
+//        {
+//            Text = "_Options:",
+//            TextAlignment = Alignment.End,
+//            Y = Pos.Align (Alignment.Start, groupId: 1),
+//            Width = Dim.Func (() => GetMaxLabelWidth (1))
+//        };
+//        demoFrame.Add (lblOptions);
+
+//        var ckbShowPercent = new CheckBox
+//        {
+//            Y = Pos.Top (lblOptions),
+//            X = Pos.Right (lblOptions) + 1,
+//            Text = "Sho_wPercent",
+//            CheckedState = scroll.ShowPercent ? CheckState.Checked : CheckState.UnChecked
+//        };
+//        ckbShowPercent.CheckedStateChanging += (s, e) => scroll.ShowPercent = e.NewValue == CheckState.Checked;
+//        demoFrame.Add (ckbShowPercent);
+
+//        //var ckbKeepContentInAllViewport = new CheckBox
+//        //{
+//        //    X = Pos.Right (ckbShowScrollIndicator) + 1, Y = Pos.Bottom (scrollPosition), Text = "KeepContentInAllViewport",
+//        //    CheckedState = Scroll.KeepContentInAllViewport ? CheckState.Checked : CheckState.UnChecked
+//        //};
+//        //ckbKeepContentInAllViewport.CheckedStateChanging += (s, e) => Scroll.KeepContentInAllViewport = e.NewValue == CheckState.Checked;
+//        //view.Add (ckbKeepContentInAllViewport);
+
+//        var lblScrollFrame = new Label
+//        {
+//            Y = Pos.Bottom (lblOptions) + 1
+//        };
+//        demoFrame.Add (lblScrollFrame);
+
+//        var lblScrollViewport = new Label
+//        {
+//            Y = Pos.Bottom (lblScrollFrame)
+//        };
+//        demoFrame.Add (lblScrollViewport);
+
+//        var lblScrollContentSize = new Label
+//        {
+//            Y = Pos.Bottom (lblScrollViewport)
+//        };
+//        demoFrame.Add (lblScrollContentSize);
+
+//        scroll.SubviewsLaidOut += (s, e) =>
+//        {
+//            lblScrollFrame.Text = $"Scroll Frame: {scroll.Frame.ToString ()}";
+//            lblScrollViewport.Text = $"Scroll Viewport: {scroll.Viewport.ToString ()}";
+//            lblScrollContentSize.Text = $"Scroll ContentSize: {scroll.GetContentSize ().ToString ()}";
+//        };
+
+//        EventLog eventLog = new ()
+//        {
+//            X = Pos.AnchorEnd (),
+//            Y = 0,
+//            Height = Dim.Fill (),
+//            BorderStyle = LineStyle.Single,
+//            ViewToLog = scroll
+//        };
+//        app.Add (eventLog);
+
+//        app.Initialized += AppOnInitialized;
+
+
+//        void AppOnInitialized (object sender, EventArgs e)
+//        {
+//            scroll.SizeChanged += (s, e) =>
+//            {
+//                eventLog.Log ($"SizeChanged: {e.CurrentValue}");
+
+//                if (scrollSize.Value != e.CurrentValue)
+//                {
+//                    scrollSize.Value = e.CurrentValue;
+//                }
+//            };
+
+//            scroll.SliderPositionChanged += (s, e) =>
+//            {
+//                eventLog.Log ($"SliderPositionChanged: {e.CurrentValue}");
+//                eventLog.Log ($"  ContentPosition: {scroll.ContentPosition}");
+//                scrollSliderPosition.Text = e.CurrentValue.ToString ();
+//            };
+
+//            scroll.Scrolled += (s, e) =>
+//                                            {
+//                                                eventLog.Log ($"Scrolled: {e.CurrentValue}");
+//                                                eventLog.Log ($"  SliderPosition: {scroll.GetSliderPosition ()}");
+//                                                scrolled.Text = e.CurrentValue.ToString ();
+//                                            };
+
+//            scroll.ContentPositionChanged += (s, e) =>
+//                                            {
+//                                                eventLog.Log ($"ContentPositionChanged: {e.CurrentValue}");
+//                                                scrollContentPosition.Value = e.CurrentValue;
+//                                                controlledList.Viewport = controlledList.Viewport with { Y = e.CurrentValue };
+//                                            };
+
+
+//            controlledList.ViewportChanged += (s, e) =>
+//                                              {
+//                                                  eventLog.Log ($"ViewportChanged: {e.NewViewport.Y}");
+//                                                  scroll.ContentPosition = e.NewViewport.Y;
+//                                              };
+
+//        }
+
+//        Application.Run (app);
+//        app.Dispose ();
+//        Application.Shutdown ();
+//    }
+//}

+ 1 - 45
UnitTests/Views/ScrollBarTests.cs

@@ -60,7 +60,7 @@ public class ScrollBarTests
     [Fact]
     public void OnOrientationChanged_Keeps_Size ()
     {
-        var scroll = new Scroll ();
+        var scroll = new ScrollBar ();
         scroll.Layout ();
         scroll.Size = 1;
 
@@ -156,50 +156,6 @@ public class ScrollBarTests
     }
 
 
-    [Theory (Skip = "Disabled - Will put this feature in View")]
-    [AutoInitShutdown]
-    [InlineData (Orientation.Vertical)]
-    [InlineData (Orientation.Horizontal)]
-    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,
-            ContentPosition = 5, Orientation = orientation, KeepContentInAllViewport = true
-        };
-        var top = new Toplevel ();
-        top.Add (scrollBar);
-        RunState rs = Application.Begin (top);
-
-        var scroll = (Scroll)scrollBar.Subviews.FirstOrDefault (x => x is Scroll);
-        Rectangle scrollSliderFrame = scroll!.Subviews.FirstOrDefault (x => x is ScrollSlider)!.Frame;
-        Assert.Equal (scrollSliderFrame, orientation == Orientation.Vertical ? new (0, 2, 1, 4) : new (2, 0, 4, 1));
-
-        Application.RaiseMouseEvent (new () { ScreenPosition = orientation == Orientation.Vertical ? new (10, 14) : new (14, 10), Flags = MouseFlags.Button1Pressed });
-        Application.RunIteration (ref rs);
-
-        Application.RaiseMouseEvent (
-                                     new ()
-                                     {
-                                         ScreenPosition = orientation == Orientation.Vertical ? new (10, 0) : new (0, 10),
-                                         Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-                                     });
-        Application.RunIteration (ref rs);
-        Assert.Equal (new (0, 0), scroll.Subviews.FirstOrDefault (x => x is ScrollSlider)!.Frame.Location);
-
-        Application.RaiseMouseEvent (
-                                     new ()
-                                     {
-                                         ScreenPosition = orientation == Orientation.Vertical ? new (0, 25) : new (80, 0),
-                                         Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-                                     });
-
-        Application.RunIteration (ref rs);
-        Assert.Equal (
-                      orientation == Orientation.Vertical ? new (0, 4) : new (4, 0),
-                      scroll.Subviews.FirstOrDefault (x => x is ScrollSlider)!.Frame.Location);
-    }
-
     [Fact]
     public void Size_Cannot_Be_Negative ()
     {

+ 5 - 5
UnitTests/Views/ScrollSliderTests.cs

@@ -1,4 +1,5 @@
-using Microsoft.VisualStudio.TestPlatform.Utilities;
+using System.Diagnostics;
+using Microsoft.VisualStudio.TestPlatform.Utilities;
 using Xunit.Abstractions;
 using static Unix.Terminal.Delegates;
 
@@ -88,7 +89,7 @@ public class ScrollSliderTests (ITestOutputHelper output)
 
     [Theory]
     [CombinatorialData]
-    public void Position_Clamps_To_SuperView_Viewport ([CombinatorialRange (1, 6, 1)] int sliderSize, [CombinatorialRange (-1, 6, 1)] int sliderPosition, Orientation orientation)
+    public void Position_Clamps_To_SuperView_Viewport ([CombinatorialRange (0, 5, 1)] int sliderSize, [CombinatorialRange (-2, 6, 2)] int sliderPosition, Orientation orientation)
     {
         var super = new View
         {
@@ -106,7 +107,6 @@ public class ScrollSliderTests (ITestOutputHelper output)
         scrollSlider.Layout ();
 
         scrollSlider.Position = sliderPosition;
-        scrollSlider.Layout ();
 
         Assert.True (scrollSlider.Position <= 5);
     }
@@ -419,7 +419,7 @@ public class ScrollSliderTests (ITestOutputHelper output)
 │██████████│
 │██████████│
 └──────────┘")]
-    public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int sliderPosition, Orientation orientation, string expected)
+    public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int position, Orientation orientation, string expected)
     {
         var super = new Window
         {
@@ -436,7 +436,7 @@ public class ScrollSliderTests (ITestOutputHelper output)
 
         scrollSlider.Size = sliderSize;
         scrollSlider.Layout ();
-        scrollSlider.Position = sliderPosition;
+        scrollSlider.Position = position;
 
         super.BeginInit ();
         super.EndInit ();

+ 597 - 597
UnitTests/Views/ScrollTests.cs

@@ -1,597 +1,597 @@
-using Microsoft.VisualStudio.TestPlatform.Utilities;
-using Xunit.Abstractions;
-
-namespace Terminal.Gui.ViewsTests;
-
-public class ScrollTests
-{
-    public ScrollTests (ITestOutputHelper output) { _output = output; }
-    private readonly ITestOutputHelper _output;
-
-
-    [Fact]
-    public void OnOrientationChanged_Keeps_Size ()
-    {
-        var scroll = new Scroll ();
-        scroll.Layout ();
-        scroll.Size = 1;
-
-        scroll.Orientation = Orientation.Horizontal;
-        Assert.Equal (1, scroll.Size);
-    }
-
-    [Fact]
-    public void OnOrientationChanged_Sets_ContentPosition_To_0 ()
-    {
-        View super = new View ()
-        {
-            Id = "super",
-            Width = 10,
-            Height = 10
-        };
-        var scroll = new Scroll ()
-        {
-        };
-        super.Add (scroll);
-        scroll.Layout ();
-        scroll.ContentPosition = 1;
-        scroll.Orientation = Orientation.Horizontal;
-
-        Assert.Equal (0, scroll.ContentPosition);
-    }
-
-
-    //    [Theory]
-    //    [AutoInitShutdown]
-    //    [InlineData (
-    //                    20,
-    //                    @"
-    //█
-    //█
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //░
-    //░
-    //█
-    //█
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░",
-    //                    @"
-    //░
-    //░
-    //░
-    //░
-    //░
-    //█
-    //█
-    //█
-    //█
-    //█",
-    //                    @"
-    //░
-    //░
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //█████░░░░░",
-    //                    @"
-    //░░█████░░░",
-    //                    @"
-    //░░░░░█████",
-    //                    @"
-    //░░███░░░░░")]
-    //    [InlineData (
-    //                    40,
-    //                    @"
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //░
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //░
-    //░
-    //█
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //░
-    //█
-    //█
-    //░
-    //░
-    //░
-    //░
-    //░
-    //░
-    //░",
-    //                    @"
-    //███░░░░░░░",
-    //                    @"
-    //░███░░░░░░",
-    //                    @"
-    //░░███░░░░░",
-    //                    @"
-    //░██░░░░░░░")]
-    //    public void Changing_Position_Size_Orientation_Draws_Correctly_KeepContentInAllViewport_True (
-    //        int size,
-    //        string firstVertExpected,
-    //        string middleVertExpected,
-    //        string endVertExpected,
-    //        string sizeVertExpected,
-    //        string firstHoriExpected,
-    //        string middleHoriExpected,
-    //        string endHoriExpected,
-    //        string sizeHoriExpected
-    //    )
-    //    {
-    //        var scroll = new Scroll
-    //        {
-    //            Orientation = Orientation.Vertical,
-    //            Size = size,
-    //            Height = 10,
-    //            KeepContentInAllViewport = true
-    //        };
-    //        var top = new Toplevel ();
-    //        top.Add (scroll);
-    //        RunState rs = Application.Begin (top);
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstVertExpected, _output);
-
-    //        scroll.Position = 4;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleVertExpected, _output);
-
-    //        scroll.Position = 10;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (endVertExpected, _output);
-
-    //        scroll.Size = size * 2;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeVertExpected, _output);
-
-    //        scroll.Orientation = Orientation.Horizontal;
-    //        scroll.Width = 10;
-    //        scroll.Height = 1;
-    //        scroll.Position = 0;
-    //        scroll.Size = size;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstHoriExpected, _output);
-
-    //        scroll.Position = 4;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleHoriExpected, _output);
-
-    //        scroll.Position = 10;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (endHoriExpected, _output);
-
-    //        scroll.Size = size * 2;
-    //        Application.RunIteration (ref rs);
-
-    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeHoriExpected, _output);
-    //    }
-
-    [Fact]
-    public void Constructor_Defaults ()
-    {
-        var scroll = new Scroll ();
-        Assert.False (scroll.CanFocus);
-        Assert.Equal (Orientation.Vertical, scroll.Orientation);
-        Assert.Equal (0, scroll.Size);
-        Assert.Equal (0, scroll.GetSliderPosition ());
-    }
-
-    //[Fact]
-    //[AutoInitShutdown]
-    //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, KeepContentInAllViewport = true };
-    //    scroll.PositionChanged += (_, e) => view.Viewport = view.Viewport with { Y = e.CurrentValue };
-    //    view.Padding.Add (scroll);
-    //    var top = new Toplevel ();
-    //    top.Add (view);
-    //    Application.Begin (top);
-
-    //    Assert.True (scroll.KeepContentInAllViewport);
-    //    Assert.Equal (80, view.Padding.Viewport.Width);
-    //    Assert.Equal (25, view.Padding.Viewport.Height);
-    //    Assert.Equal (2, scroll.Viewport.Width);
-    //    Assert.Equal (25, scroll.Viewport.Height);
-    //    Assert.Equal (30, scroll.Size);
-
-    //    scroll.KeepContentInAllViewport = false;
-    //    scroll.Position = 50;
-    //    Assert.Equal (scroll.Position, scroll.Size - 1);
-    //    Assert.Equal (scroll.Position, view.Viewport.Y);
-    //    Assert.Equal (29, scroll.Position);
-    //    Assert.Equal (29, view.Viewport.Y);
-
-    //    top.Dispose ();
-    //}
-
-
-    //[Theory]
-    //[AutoInitShutdown]
-    //[InlineData (Orientation.Vertical)]
-    //[InlineData (Orientation.Horizontal)]
-    //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,
-    //        SliderPosition = 5, Orientation = orientation
-    //    };
-    //    var top = new Toplevel ();
-    //    top.Add (scroll);
-    //    RunState rs = Application.Begin (top);
-
-    //    Rectangle scrollSliderFrame = scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame;
-    //    Assert.Equal (scrollSliderFrame, orientation == Orientation.Vertical ? new (0, 2, 1, 5) : new (2, 0, 5, 1));
-
-    //    Application.RaiseMouseEvent (new () { ScreenPosition = orientation == Orientation.Vertical ? new (10, 12) : new (12, 10), Flags = MouseFlags.Button1Pressed });
-    //    Application.RunIteration (ref rs);
-
-    //    Application.RaiseMouseEvent (
-    //                              new ()
-    //                              {
-    //                                  ScreenPosition = orientation == Orientation.Vertical ? new (10, 0) : new (0, 10),
-    //                                  Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-    //                              });
-    //    Application.RunIteration (ref rs);
-    //    Assert.Equal (new (0, 0), scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
-
-    //    Application.RaiseMouseEvent (
-    //                              new ()
-    //                              {
-    //                                  ScreenPosition = orientation == Orientation.Vertical ? new (0, 25) : new (80, 0),
-    //                                  Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
-    //                              });
-    //    Application.RunIteration (ref rs);
-
-    //    Assert.Equal (
-    //                  orientation == Orientation.Vertical ? new (0, 5) : new (5, 0),
-    //                  scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
-    //}
-
-    [Theory]
-    [CombinatorialData]
-    [AutoInitShutdown]
-    public void Mouse_Wheel_Increments_Position ([CombinatorialRange (1, 3, 1)] int increment, Orientation orientation)
-    {
-        var top = new Toplevel ()
-        {
-            Id = "top",
-            Width = 10,
-            Height = 10
-        };
-        var scroll = new Scroll
-        {
-            Id = "scroll",
-            Orientation = orientation,
-            Size = 20,
-            Increment = increment
-        };
-
-        top.Add (scroll);
-        RunState rs = Application.Begin (top);
-
-        Assert.Equal (0, scroll.GetSliderPosition ());
-        Assert.Equal (0, scroll.ContentPosition);
-
-        Application.RaiseMouseEvent (new ()
-        {
-            ScreenPosition = new (0, 0),
-            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledDown : MouseFlags.WheeledRight
-        });
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (increment, scroll.ContentPosition);
-
-        Application.RaiseMouseEvent (new ()
-        {
-            ScreenPosition = new (0, 0),
-            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledDown : MouseFlags.WheeledRight
-        });
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (increment * 2, scroll.ContentPosition);
-
-        Application.RaiseMouseEvent (new ()
-        {
-            ScreenPosition = new (0, 0),
-            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledUp : MouseFlags.WheeledLeft
-        });
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (increment, scroll.ContentPosition);
-
-        Application.ResetState (true);
-    }
-
-    [Theory]
-    [CombinatorialData]
-    [AutoInitShutdown]
-    public void Mouse_Click_Outside_Slider_Moves (Orientation orientation)
-    {
-        var top = new Toplevel ()
-        {
-            Id = "top",
-            Width = 10,
-            Height = 10
-        };
-        var scroll = new Scroll
-        {
-            Id = "scroll",
-            Orientation = orientation,
-            Size = 20,
-        };
-
-        top.Add (scroll);
-        RunState rs = Application.Begin (top);
-        scroll.ContentPosition = 5;
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (5, scroll.GetSliderPosition ());
-        Assert.Equal (10, scroll.ContentPosition);
-
-        Application.RaiseMouseEvent (new ()
-        {
-            ScreenPosition = new (0, 0),
-            Flags = MouseFlags.Button1Clicked
-        });
-        Application.RunIteration (ref rs);
-
-        Assert.Equal (0, scroll.GetSliderPosition ());
-        Assert.Equal (0, scroll.ContentPosition);
-
-        Application.ResetState (true);
-    }
-
-    [Theory]
-    [CombinatorialData]
-    public void Position_Clamps_To_Size (
-        [CombinatorialRange (1, 6, 1)] int scrollSize,
-        [CombinatorialRange (-1, 6, 1)] int scrollPosition,
-        Orientation orientation
-    )
-    {
-        var super = new View
-        {
-            Id = "super",
-            Width = 5,
-            Height = 5
-        };
-
-        var scroll = new Scroll
-        {
-            Orientation = orientation,
-            Width = Dim.Fill (),
-            Height = Dim.Fill ()
-        };
-        super.Add (scroll);
-        scroll.Size = scrollSize;
-        super.Layout ();
-
-        scroll.ContentPosition = scrollPosition;
-        super.Layout ();
-
-        Assert.True (scroll.GetSliderPosition () <= scrollSize);
-    }
-
-    [Fact]
-    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
-    {
-        var changing = 0;
-        var cancel = false;
-        var changed = 0;
-        var scroll = new Scroll { Height = 10, Size = 20 };
-        scroll.SliderPositionChanged += Scroll_PositionChanged;
-
-        Assert.Equal (Orientation.Vertical, scroll.Orientation);
-        scroll.Layout ();
-        Assert.Equal (new (0, 0, 1, 10), scroll.Viewport);
-        Assert.Equal (0, scroll.GetSliderPosition ());
-        Assert.Equal (0, changing);
-        Assert.Equal (0, changed);
-
-        scroll.ContentPosition = 0;
-        Assert.Equal (0, scroll.GetSliderPosition ());
-        Assert.Equal (0, changing);
-        Assert.Equal (0, changed);
-
-        scroll.ContentPosition = 1;
-        Assert.Equal (1, scroll.GetSliderPosition ());
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
-
-        Reset ();
-        cancel = true;
-        scroll.ContentPosition = 2;
-        Assert.Equal (1, scroll.GetSliderPosition ());
-        Assert.Equal (1, changing);
-        Assert.Equal (0, changed);
-
-        Reset ();
-        scroll.ContentPosition = 10;
-        Assert.Equal (5, scroll.GetSliderPosition ());
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
-
-        Reset ();
-        scroll.ContentPosition = 11;
-        Assert.Equal (5, scroll.GetSliderPosition ());
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
-
-        Reset ();
-        scroll.ContentPosition = 0;
-        Assert.Equal (0, scroll.GetSliderPosition ());
-        Assert.Equal (1, changing);
-        Assert.Equal (1, changed);
-
-        scroll.SliderPositionChanged -= Scroll_PositionChanged;
-
-        void Scroll_PositionChanged (object sender, EventArgs<int> e) { changed++; }
-
-        void Reset ()
-        {
-            changing = 0;
-            cancel = false;
-            changed = 0;
-        }
-    }
-
-    [Fact]
-    public void Size_Cannot_Be_Negative ()
-    {
-        var scroll = new Scroll { Height = 10, Size = -1 };
-        Assert.Equal (0, scroll.Size);
-        scroll.Size = -10;
-        Assert.Equal (0, scroll.Size);
-    }
-
-    [Fact]
-    public void SizeChanged_Event ()
-    {
-        var count = 0;
-        var scroll = new Scroll ();
-        scroll.Layout ();
-        scroll.SizeChanged += (s, e) => count++;
-
-        scroll.Size = 10;
-        Assert.Equal (10, scroll.Size);
-        Assert.Equal (1, count);
-    }
-
-    [Theory]
-    [SetupFakeDriver]
-    [InlineData (
-                    10,
-                    1,
-                    20,
-                    0,
-                    Orientation.Horizontal,
-                    @"
-┌──────────┐
-│█████░░░░░│
-└──────────┘")]
-
-    [InlineData (
-                    10,
-                    3,
-                    20,
-                    1,
-                    Orientation.Horizontal,
-                    @"
-┌──────────┐
-│░█████░░░░│
-│░█████░░░░│
-│░█████░░░░│
-└──────────┘")]
-
-    [InlineData (
-                    3,
-                    10,
-                    20,
-                    0,
-                    Orientation.Vertical,
-                    @"
-┌───┐
-│███│
-│███│
-│███│
-│███│
-│███│
-│░░░│
-│░░░│
-│░░░│
-│░░░│
-│░░░│
-└───┘")]
-
-
-    public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int sliderPosition, Orientation orientation, string expected)
-    {
-        var super = new Window
-        {
-            Id = "super",
-            Width = superViewportWidth + 2,
-            Height = superViewportHeight + 2
-        };
-
-        var scroll = new Scroll
-        {
-            Orientation = orientation,
-        };
-
-        if (orientation == Orientation.Vertical)
-        {
-            scroll.Width = Dim.Fill ();
-        }
-        else
-        {
-            scroll.Height = Dim.Fill ();
-        }
-        super.Add (scroll);
-
-        scroll.Size = sliderSize;
-        scroll.Layout ();
-        scroll.ContentPosition = sliderPosition;
-
-        super.BeginInit ();
-        super.EndInit ();
-        super.Layout ();
-        super.Draw ();
-
-        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
-    }
-}
+//using Microsoft.VisualStudio.TestPlatform.Utilities;
+//using Xunit.Abstractions;
+
+//namespace Terminal.Gui.ViewsTests;
+
+//public class ScrollTests
+//{
+//    public ScrollTests (ITestOutputHelper output) { _output = output; }
+//    private readonly ITestOutputHelper _output;
+
+
+//    [Fact]
+//    public void OnOrientationChanged_Keeps_Size ()
+//    {
+//        var scroll = new Scroll ();
+//        scroll.Layout ();
+//        scroll.Size = 1;
+
+//        scroll.Orientation = Orientation.Horizontal;
+//        Assert.Equal (1, scroll.Size);
+//    }
+
+//    [Fact]
+//    public void OnOrientationChanged_Sets_ContentPosition_To_0 ()
+//    {
+//        View super = new View ()
+//        {
+//            Id = "super",
+//            Width = 10,
+//            Height = 10
+//        };
+//        var scroll = new Scroll ()
+//        {
+//        };
+//        super.Add (scroll);
+//        scroll.Layout ();
+//        scroll.ContentPosition = 1;
+//        scroll.Orientation = Orientation.Horizontal;
+
+//        Assert.Equal (0, scroll.ContentPosition);
+//    }
+
+
+//    //    [Theory]
+//    //    [AutoInitShutdown]
+//    //    [InlineData (
+//    //                    20,
+//    //                    @"
+//    //█
+//    //█
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //░
+//    //░
+//    //█
+//    //█
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //█
+//    //█
+//    //█
+//    //█
+//    //█",
+//    //                    @"
+//    //░
+//    //░
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //█████░░░░░",
+//    //                    @"
+//    //░░█████░░░",
+//    //                    @"
+//    //░░░░░█████",
+//    //                    @"
+//    //░░███░░░░░")]
+//    //    [InlineData (
+//    //                    40,
+//    //                    @"
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //░
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //░
+//    //░
+//    //█
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //░
+//    //█
+//    //█
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░
+//    //░",
+//    //                    @"
+//    //███░░░░░░░",
+//    //                    @"
+//    //░███░░░░░░",
+//    //                    @"
+//    //░░███░░░░░",
+//    //                    @"
+//    //░██░░░░░░░")]
+//    //    public void Changing_Position_Size_Orientation_Draws_Correctly_KeepContentInAllViewport_True (
+//    //        int size,
+//    //        string firstVertExpected,
+//    //        string middleVertExpected,
+//    //        string endVertExpected,
+//    //        string sizeVertExpected,
+//    //        string firstHoriExpected,
+//    //        string middleHoriExpected,
+//    //        string endHoriExpected,
+//    //        string sizeHoriExpected
+//    //    )
+//    //    {
+//    //        var scroll = new Scroll
+//    //        {
+//    //            Orientation = Orientation.Vertical,
+//    //            Size = size,
+//    //            Height = 10,
+//    //            KeepContentInAllViewport = true
+//    //        };
+//    //        var top = new Toplevel ();
+//    //        top.Add (scroll);
+//    //        RunState rs = Application.Begin (top);
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstVertExpected, _output);
+
+//    //        scroll.Position = 4;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleVertExpected, _output);
+
+//    //        scroll.Position = 10;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (endVertExpected, _output);
+
+//    //        scroll.Size = size * 2;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeVertExpected, _output);
+
+//    //        scroll.Orientation = Orientation.Horizontal;
+//    //        scroll.Width = 10;
+//    //        scroll.Height = 1;
+//    //        scroll.Position = 0;
+//    //        scroll.Size = size;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (firstHoriExpected, _output);
+
+//    //        scroll.Position = 4;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (middleHoriExpected, _output);
+
+//    //        scroll.Position = 10;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (endHoriExpected, _output);
+
+//    //        scroll.Size = size * 2;
+//    //        Application.RunIteration (ref rs);
+
+//    //        _ = TestHelpers.AssertDriverContentsWithFrameAre (sizeHoriExpected, _output);
+//    //    }
+
+//    [Fact]
+//    public void Constructor_Defaults ()
+//    {
+//        var scroll = new Scroll ();
+//        Assert.False (scroll.CanFocus);
+//        Assert.Equal (Orientation.Vertical, scroll.Orientation);
+//        Assert.Equal (0, scroll.Size);
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//    }
+
+//    //[Fact]
+//    //[AutoInitShutdown]
+//    //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, KeepContentInAllViewport = true };
+//    //    scroll.PositionChanged += (_, e) => view.Viewport = view.Viewport with { Y = e.CurrentValue };
+//    //    view.Padding.Add (scroll);
+//    //    var top = new Toplevel ();
+//    //    top.Add (view);
+//    //    Application.Begin (top);
+
+//    //    Assert.True (scroll.KeepContentInAllViewport);
+//    //    Assert.Equal (80, view.Padding.Viewport.Width);
+//    //    Assert.Equal (25, view.Padding.Viewport.Height);
+//    //    Assert.Equal (2, scroll.Viewport.Width);
+//    //    Assert.Equal (25, scroll.Viewport.Height);
+//    //    Assert.Equal (30, scroll.Size);
+
+//    //    scroll.KeepContentInAllViewport = false;
+//    //    scroll.Position = 50;
+//    //    Assert.Equal (scroll.Position, scroll.Size - 1);
+//    //    Assert.Equal (scroll.Position, view.Viewport.Y);
+//    //    Assert.Equal (29, scroll.Position);
+//    //    Assert.Equal (29, view.Viewport.Y);
+
+//    //    top.Dispose ();
+//    //}
+
+
+//    //[Theory]
+//    //[AutoInitShutdown]
+//    //[InlineData (Orientation.Vertical)]
+//    //[InlineData (Orientation.Horizontal)]
+//    //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,
+//    //        SliderPosition = 5, Orientation = orientation
+//    //    };
+//    //    var top = new Toplevel ();
+//    //    top.Add (scroll);
+//    //    RunState rs = Application.Begin (top);
+
+//    //    Rectangle scrollSliderFrame = scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame;
+//    //    Assert.Equal (scrollSliderFrame, orientation == Orientation.Vertical ? new (0, 2, 1, 5) : new (2, 0, 5, 1));
+
+//    //    Application.RaiseMouseEvent (new () { ScreenPosition = orientation == Orientation.Vertical ? new (10, 12) : new (12, 10), Flags = MouseFlags.Button1Pressed });
+//    //    Application.RunIteration (ref rs);
+
+//    //    Application.RaiseMouseEvent (
+//    //                              new ()
+//    //                              {
+//    //                                  ScreenPosition = orientation == Orientation.Vertical ? new (10, 0) : new (0, 10),
+//    //                                  Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+//    //                              });
+//    //    Application.RunIteration (ref rs);
+//    //    Assert.Equal (new (0, 0), scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
+
+//    //    Application.RaiseMouseEvent (
+//    //                              new ()
+//    //                              {
+//    //                                  ScreenPosition = orientation == Orientation.Vertical ? new (0, 25) : new (80, 0),
+//    //                                  Flags = MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition
+//    //                              });
+//    //    Application.RunIteration (ref rs);
+
+//    //    Assert.Equal (
+//    //                  orientation == Orientation.Vertical ? new (0, 5) : new (5, 0),
+//    //                  scroll.Subviews.FirstOrDefault (x => x.Id == "scrollSlider")!.Frame.Location);
+//    //}
+
+//    [Theory]
+//    [CombinatorialData]
+//    [AutoInitShutdown]
+//    public void Mouse_Wheel_Increments_Position ([CombinatorialRange (1, 3, 1)] int increment, Orientation orientation)
+//    {
+//        var top = new Toplevel ()
+//        {
+//            Id = "top",
+//            Width = 10,
+//            Height = 10
+//        };
+//        var scroll = new Scroll
+//        {
+//            Id = "scroll",
+//            Orientation = orientation,
+//            Size = 20,
+//            Increment = increment
+//        };
+
+//        top.Add (scroll);
+//        RunState rs = Application.Begin (top);
+
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//        Assert.Equal (0, scroll.ContentPosition);
+
+//        Application.RaiseMouseEvent (new ()
+//        {
+//            ScreenPosition = new (0, 0),
+//            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledDown : MouseFlags.WheeledRight
+//        });
+//        Application.RunIteration (ref rs);
+
+//        Assert.Equal (increment, scroll.ContentPosition);
+
+//        Application.RaiseMouseEvent (new ()
+//        {
+//            ScreenPosition = new (0, 0),
+//            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledDown : MouseFlags.WheeledRight
+//        });
+//        Application.RunIteration (ref rs);
+
+//        Assert.Equal (increment * 2, scroll.ContentPosition);
+
+//        Application.RaiseMouseEvent (new ()
+//        {
+//            ScreenPosition = new (0, 0),
+//            Flags = orientation == Orientation.Vertical ? MouseFlags.WheeledUp : MouseFlags.WheeledLeft
+//        });
+//        Application.RunIteration (ref rs);
+
+//        Assert.Equal (increment, scroll.ContentPosition);
+
+//        Application.ResetState (true);
+//    }
+
+//    [Theory]
+//    [CombinatorialData]
+//    [AutoInitShutdown]
+//    public void Mouse_Click_Outside_Slider_Moves (Orientation orientation)
+//    {
+//        var top = new Toplevel ()
+//        {
+//            Id = "top",
+//            Width = 10,
+//            Height = 10
+//        };
+//        var scroll = new Scroll
+//        {
+//            Id = "scroll",
+//            Orientation = orientation,
+//            Size = 20,
+//        };
+
+//        top.Add (scroll);
+//        RunState rs = Application.Begin (top);
+//        scroll.ContentPosition = 5;
+//        Application.RunIteration (ref rs);
+
+//        Assert.Equal (5, scroll.GetSliderPosition ());
+//        Assert.Equal (10, scroll.ContentPosition);
+
+//        Application.RaiseMouseEvent (new ()
+//        {
+//            ScreenPosition = new (0, 0),
+//            Flags = MouseFlags.Button1Clicked
+//        });
+//        Application.RunIteration (ref rs);
+
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//        Assert.Equal (0, scroll.ContentPosition);
+
+//        Application.ResetState (true);
+//    }
+
+//    [Theory]
+//    [CombinatorialData]
+//    public void Position_Clamps_To_Size (
+//        [CombinatorialRange (1, 6, 1)] int scrollSize,
+//        [CombinatorialRange (-1, 6, 1)] int scrollPosition,
+//        Orientation orientation
+//    )
+//    {
+//        var super = new View
+//        {
+//            Id = "super",
+//            Width = 5,
+//            Height = 5
+//        };
+
+//        var scroll = new Scroll
+//        {
+//            Orientation = orientation,
+//            Width = Dim.Fill (),
+//            Height = Dim.Fill ()
+//        };
+//        super.Add (scroll);
+//        scroll.Size = scrollSize;
+//        super.Layout ();
+
+//        scroll.ContentPosition = scrollPosition;
+//        super.Layout ();
+
+//        Assert.True (scroll.GetSliderPosition () <= scrollSize);
+//    }
+
+//    [Fact]
+//    public void PositionChanging_PositionChanged_Events_Only_Raises_Once_If_Position_Was_Really_Changed ()
+//    {
+//        var changing = 0;
+//        var cancel = false;
+//        var changed = 0;
+//        var scroll = new Scroll { Height = 10, Size = 20 };
+//        scroll.SliderPositionChanged += Scroll_PositionChanged;
+
+//        Assert.Equal (Orientation.Vertical, scroll.Orientation);
+//        scroll.Layout ();
+//        Assert.Equal (new (0, 0, 1, 10), scroll.Viewport);
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//        Assert.Equal (0, changing);
+//        Assert.Equal (0, changed);
+
+//        scroll.ContentPosition = 0;
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//        Assert.Equal (0, changing);
+//        Assert.Equal (0, changed);
+
+//        scroll.ContentPosition = 1;
+//        Assert.Equal (1, scroll.GetSliderPosition ());
+//        Assert.Equal (1, changing);
+//        Assert.Equal (1, changed);
+
+//        Reset ();
+//        cancel = true;
+//        scroll.ContentPosition = 2;
+//        Assert.Equal (1, scroll.GetSliderPosition ());
+//        Assert.Equal (1, changing);
+//        Assert.Equal (0, changed);
+
+//        Reset ();
+//        scroll.ContentPosition = 10;
+//        Assert.Equal (5, scroll.GetSliderPosition ());
+//        Assert.Equal (1, changing);
+//        Assert.Equal (1, changed);
+
+//        Reset ();
+//        scroll.ContentPosition = 11;
+//        Assert.Equal (5, scroll.GetSliderPosition ());
+//        Assert.Equal (1, changing);
+//        Assert.Equal (1, changed);
+
+//        Reset ();
+//        scroll.ContentPosition = 0;
+//        Assert.Equal (0, scroll.GetSliderPosition ());
+//        Assert.Equal (1, changing);
+//        Assert.Equal (1, changed);
+
+//        scroll.SliderPositionChanged -= Scroll_PositionChanged;
+
+//        void Scroll_PositionChanged (object sender, EventArgs<int> e) { changed++; }
+
+//        void Reset ()
+//        {
+//            changing = 0;
+//            cancel = false;
+//            changed = 0;
+//        }
+//    }
+
+//    [Fact]
+//    public void Size_Cannot_Be_Negative ()
+//    {
+//        var scroll = new Scroll { Height = 10, Size = -1 };
+//        Assert.Equal (0, scroll.Size);
+//        scroll.Size = -10;
+//        Assert.Equal (0, scroll.Size);
+//    }
+
+//    [Fact]
+//    public void SizeChanged_Event ()
+//    {
+//        var count = 0;
+//        var scroll = new Scroll ();
+//        scroll.Layout ();
+//        scroll.SizeChanged += (s, e) => count++;
+
+//        scroll.Size = 10;
+//        Assert.Equal (10, scroll.Size);
+//        Assert.Equal (1, count);
+//    }
+
+//    [Theory]
+//    [SetupFakeDriver]
+//    [InlineData (
+//                    10,
+//                    1,
+//                    20,
+//                    0,
+//                    Orientation.Horizontal,
+//                    @"
+//┌──────────┐
+//│█████░░░░░│
+//└──────────┘")]
+
+//    [InlineData (
+//                    10,
+//                    3,
+//                    20,
+//                    1,
+//                    Orientation.Horizontal,
+//                    @"
+//┌──────────┐
+//│░█████░░░░│
+//│░█████░░░░│
+//│░█████░░░░│
+//└──────────┘")]
+
+//    [InlineData (
+//                    3,
+//                    10,
+//                    20,
+//                    0,
+//                    Orientation.Vertical,
+//                    @"
+//┌───┐
+//│███│
+//│███│
+//│███│
+//│███│
+//│███│
+//│░░░│
+//│░░░│
+//│░░░│
+//│░░░│
+//│░░░│
+//└───┘")]
+
+
+//    public void Draws_Correctly (int superViewportWidth, int superViewportHeight, int sliderSize, int sliderPosition, Orientation orientation, string expected)
+//    {
+//        var super = new Window
+//        {
+//            Id = "super",
+//            Width = superViewportWidth + 2,
+//            Height = superViewportHeight + 2
+//        };
+
+//        var scroll = new Scroll
+//        {
+//            Orientation = orientation,
+//        };
+
+//        if (orientation == Orientation.Vertical)
+//        {
+//            scroll.Width = Dim.Fill ();
+//        }
+//        else
+//        {
+//            scroll.Height = Dim.Fill ();
+//        }
+//        super.Add (scroll);
+
+//        scroll.Size = sliderSize;
+//        scroll.Layout ();
+//        scroll.ContentPosition = sliderPosition;
+
+//        super.BeginInit ();
+//        super.EndInit ();
+//        super.Layout ();
+//        super.Draw ();
+
+//        _ = TestHelpers.AssertDriverContentsWithFrameAre (expected, _output);
+//    }
+//}