diff --git a/Terminal.Gui/Views/ScrollBar/ScrollBar.cs b/Terminal.Gui/Views/ScrollBar/ScrollBar.cs
index abfd13c75..d4e149e47 100644
--- a/Terminal.Gui/Views/ScrollBar/ScrollBar.cs
+++ b/Terminal.Gui/Views/ScrollBar/ScrollBar.cs
@@ -6,15 +6,15 @@ using System.Drawing;
namespace Terminal.Gui;
///
-/// Indicates the size of scrollable content and provides a visible element, referred to as the "ScrollSlider" that
-/// that is sized to
-/// show the proportion of the scrollable content to the size of the . The ScrollSlider
-/// can be dragged with the mouse. A Scroll can be oriented either vertically or horizontally and is used within a
-/// .
+/// Indicates the size of scrollable content and controls the position of the visible content, either vertically or horizontally.
+/// Two s are provided, one to scroll up or left and one to scroll down or right. Between the buttons is a that can be dragged to
+/// control the position of the visible content. The ScrollSlier is sized to show the proportion of the scrollable content to the size of the .
///
///
///
-/// By default, this view cannot be focused and does not support keyboard.
+///
+///
+/// By default, this view cannot be focused and does not support keyboard input.
///
///
public class ScrollBar : View, IOrientation, IDesignable
@@ -58,8 +58,6 @@ public class ScrollBar : View, IOrientation, IDesignable
_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);
@@ -80,26 +78,13 @@ public class ScrollBar : View, IOrientation, IDesignable
///
protected override void OnFrameChanged (in Rectangle frame)
{
- if (Orientation == Orientation.Vertical)
- {
- _slider.VisibleContentSize = Viewport.Height;
- }
- else
- {
- _slider.VisibleContentSize = Viewport.Width;
- }
- _slider.Size = CalculateSliderSize ();
+
ShowHide ();
}
private void ShowHide ()
{
- if (!AutoHide)
- {
- return;
- }
-
if (Orientation == Orientation.Vertical)
{
Visible = Frame.Height < ScrollableContentSize;
@@ -109,6 +94,20 @@ public class ScrollBar : View, IOrientation, IDesignable
Visible = Frame.Width < ScrollableContentSize;
}
+ if (!AutoHide)
+ {
+ Visible = true;
+ }
+
+ if (Orientation == Orientation.Vertical)
+ {
+ _slider.VisibleContentSize = Viewport.Height;
+ }
+ else
+ {
+ _slider.VisibleContentSize = Viewport.Width;
+ }
+
_slider.Size = CalculateSliderSize ();
_sliderPosition = CalculateSliderPositionFromContentPosition (_position, NavigationDirection.Forward);
_slider.Position = _sliderPosition.Value;
@@ -188,13 +187,17 @@ public class ScrollBar : View, IOrientation, IDesignable
if (Orientation == Orientation.Vertical)
{
+ Height = Dim.Func (() => SuperView?.Viewport.Height ?? 0);
Width = 1;
- Height = Dim.Fill ();
+ X = Pos.AnchorEnd ();
+ Y = 0;
}
else
{
- Width = Dim.Fill ();
+ Width = Dim.Func (() => SuperView?.Viewport.Width ?? 0);
Height = 1;
+ X = 0;
+ Y = Pos.AnchorEnd ();
}
_slider.Orientation = newOrientation;
@@ -279,17 +282,26 @@ public class ScrollBar : View, IOrientation, IDesignable
{
_visibleContentSize = value;
_slider.Size = CalculateSliderSize ();
+ ShowHide ();
}
}
- private int _scrollableContentSize;
+ private int? _scrollableContentSize;
///
/// Gets or sets the size of the content that can be scrolled. This is typically set to .
///
public int ScrollableContentSize
{
- get => _scrollableContentSize;
+ get
+ {
+ if (_scrollableContentSize.HasValue)
+ {
+ return _scrollableContentSize.Value;
+ }
+ return Orientation == Orientation.Vertical ? SuperView?.GetContentSize().Height ?? 0 : SuperView?.GetContentSize ().Width ?? 0;
+
+ }
set
{
if (value == _scrollableContentSize || value < 0)
@@ -299,8 +311,9 @@ public class ScrollBar : View, IOrientation, IDesignable
_scrollableContentSize = value;
_slider.Size = CalculateSliderSize ();
- OnSizeChanged (_scrollableContentSize);
- ScrollableContentSizeChanged?.Invoke (this, new (in _scrollableContentSize));
+ ShowHide();
+ OnSizeChanged (value);
+ ScrollableContentSizeChanged?.Invoke (this, new (in value));
SetNeedsLayout ();
}
}
@@ -370,6 +383,7 @@ public class ScrollBar : View, IOrientation, IDesignable
OnScrolled (distance);
Scrolled?.Invoke (this, new (in distance));
+ SetNeedsLayout ();
}
}
diff --git a/UnitTests/Views/ScrollBarTests.cs b/UnitTests/Views/ScrollBarTests.cs
index 8b16d0ea9..bf3418d8a 100644
--- a/UnitTests/Views/ScrollBarTests.cs
+++ b/UnitTests/Views/ScrollBarTests.cs
@@ -32,7 +32,6 @@ public class ScrollBarTests (ITestOutputHelper output)
var scrollBar = new ScrollBar
{
- ScrollableContentSize = 20,
};
super.Add (scrollBar);
Assert.True (scrollBar.AutoHide);
@@ -213,274 +212,6 @@ public class ScrollBarTests (ITestOutputHelper output)
#endregion Orientation
- #region Slider
-
- [Theory]
- [InlineData (-1, 10, 1)]
- [InlineData (0, 10, 1)]
- [InlineData (10, 15, 5)]
- [InlineData (10, 5, 8)]
- [InlineData (10, 3, 8)]
- [InlineData (10, 2, 8)]
- [InlineData (10, 1, 8)]
- [InlineData (10, 0, 8)]
- [InlineData (10, 10, 8)]
- [InlineData (10, 20, 4)]
- [InlineData (10, 100, 1)]
- [InlineData (15, 0, 13)]
- [InlineData (15, 1, 13)]
- [InlineData (15, 2, 13)]
- [InlineData (15, 3, 13)]
- [InlineData (15, 5, 13)]
- [InlineData (15, 10, 13)]
- [InlineData (15, 14, 13)]
- [InlineData (15, 15, 13)]
- [InlineData (15, 16, 12)]
- [InlineData (20, 10, 18)]
- [InlineData (100, 10, 98)]
- public void CalculateSliderSize_Width_Is_VisibleContentSize_CalculatesCorrectly (int visibleContentSize, int scrollableContentSize, int expectedSliderSize)
- {
- // Arrange
- var scrollBar = new ScrollBar
- {
- VisibleContentSize = visibleContentSize,
- ScrollableContentSize = scrollableContentSize,
- Orientation = Orientation.Horizontal // Assuming horizontal for simplicity
- };
- scrollBar.Width = visibleContentSize;
-
- // Act
- var sliderSize = scrollBar.CalculateSliderSize ();
-
- // Assert
- Assert.Equal (expectedSliderSize, sliderSize);
- }
-
- [Theory]
- // 0123456789
- // -
- // **********
- // ◄███►
- [InlineData (5, 10, 1, 3)]
-
- // 01234567890
- // ----------
- // **********
- // ◄██░►
- [InlineData (5, 10, 11, 2)]
-
-
- [InlineData (20, 10, 1, 18)]
-
- //// ◄█░░░░░░░►
- //[InlineData (1, 10, 1)]
-
- //// ---------
- //// ◄████░░░░►
- //[InlineData (5, 10, 4)]
-
- //// ----------
- //// ◄███░░░░░►
- //[InlineData (5, 11, 3)]
- //[InlineData (5, 12, 3)]
- //[InlineData (5, 13, 3)]
-
- //// 012345678901234
- //// --------------
- //// ◄██░░░░░░►
- //[InlineData (5, 14, 2)]
- //[InlineData (5, 15, 2)]
- //[InlineData (5, 16, 2)]
-
- //// 012345678901234567890
- //// ----------------
- //// ◄██░░░░░░►
- //[InlineData (5, 18, 2)]
- //[InlineData (5, 19, 2)]
- //[InlineData (5, 20, 2)]
-
-
- //// 012345678901234567890
- //// --------------------
- //// ◄█░░░░░░░►
- //[InlineData (5, 21, 1)]
- //[InlineData (5, 22, 1)]
- //[InlineData (5, 23, 1)]
- public void CalculateSliderSize_Width_Is_LT_VisibleContentSize_CalculatesCorrectly (int width, int visibleContentSize, int scrollableContentSize, int expectedSliderSize)
- {
- // Arrange
- var scrollBar = new ScrollBar
- {
- VisibleContentSize = visibleContentSize,
- ScrollableContentSize = scrollableContentSize,
- Orientation = Orientation.Horizontal // Assuming horizontal for simplicity
- };
- scrollBar.Width = width;
-
- // Act
- var sliderSize = scrollBar.CalculateSliderSize ();
-
- // Assert
- Assert.Equal (expectedSliderSize, sliderSize);
- }
-
-
- [Theory]
- // 0123456789
- // ---------
- // ◄█░░░░░░░►
- [InlineData (0, 10, 1)]
- // ◄█░░░░░░░►
- [InlineData (1, 10, 1)]
-
- // ---------
- // ◄████░░░░►
- [InlineData (5, 10, 4)]
-
- // ----------
- // ◄███░░░░░►
- [InlineData (5, 11, 3)]
- [InlineData (5, 12, 3)]
- [InlineData (5, 13, 3)]
-
- // 012345678901234
- // --------------
- // ◄██░░░░░░►
- [InlineData (5, 14, 2)]
- [InlineData (5, 15, 2)]
- [InlineData (5, 16, 2)]
-
- // 012345678901234567890
- // ----------------
- // ◄██░░░░░░►
- [InlineData (5, 18, 2)]
- [InlineData (5, 19, 2)]
- [InlineData (5, 20, 2)]
-
-
- // 012345678901234567890
- // --------------------
- // ◄█░░░░░░░►
- [InlineData (5, 21, 1)]
- [InlineData (5, 22, 1)]
- [InlineData (5, 23, 1)]
-
- public void CalculateSliderSize_Width_Is_GT_VisibleContentSize_CalculatesCorrectly (int visibleContentSize, int scrollableContentSize, int expectedSliderSize)
- {
- // Arrange
- var scrollBar = new ScrollBar
- {
- VisibleContentSize = visibleContentSize,
- ScrollableContentSize = scrollableContentSize,
- Orientation = Orientation.Horizontal // Assuming horizontal for simplicity
- };
- scrollBar.Width = 10;
-
- // Act
- var sliderSize = scrollBar.CalculateSliderSize ();
-
- // Assert
- Assert.Equal (expectedSliderSize, sliderSize);
- }
-
- [Theory]
- // 0123456789
- // ---------
- // ◄█►
- [InlineData (3, 3, 0, 0)]
- [InlineData (3, 3, 1, 0)]
- [InlineData (3, 3, 2, 0)]
-
- // 0123456789
- // ---------
- // ◄██►
- [InlineData (4, 4, 0, 0)]
- [InlineData (4, 4, 1, 0)]
- [InlineData (4, 4, 2, 0)]
- [InlineData (4, 4, 3, 0)]
- [InlineData (4, 4, 4, 0)]
-
-
- // 012345
- // ^----
- // ◄█░►
- [InlineData (4, 5, 0, 0)]
- // -^---
- // ◄█░►
- [InlineData (4, 5, 1, 0)]
- // --^--
- // ◄░█►
- [InlineData (4, 5, 2, 1)]
- // ---^-
- // ◄░█►
- [InlineData (4, 5, 3, 1)]
- // ----^
- // ◄░█►
- [InlineData (4, 5, 4, 1)]
-
- // 01234
- // ^---------
- // ◄█░░►
- [InlineData (5, 10, 0, 0)]
- // -^--------
- // ◄█░░►
- [InlineData (5, 10, 1, 0)]
- // --^-------
- // ◄█░░►
- [InlineData (5, 10, 2, 0)]
- // ---^------
- // ◄█░░►
- [InlineData (5, 10, 3, 0)]
- // ----^----
- // ◄░█░►
- [InlineData (5, 10, 4, 1)]
- // -----^---
- // ◄░█░►
- [InlineData (5, 10, 5, 1)]
- // ------^--
- // ◄░░█►
- [InlineData (5, 10, 6, 2)]
- // ------^--
- // ◄░░█►
- [InlineData (5, 10, 7, 2)]
- // -------^-
- // ◄░░█►
- [InlineData (5, 10, 8, 2)]
- // --------^
- // ◄░░█►
- [InlineData (5, 10, 9, 2)]
-
-
- [InlineData (10, 20, 0, 0)]
- [InlineData (10, 20, 1, 0)]
- [InlineData (10, 20, 2, 0)]
- [InlineData (10, 20, 3, 1)]
- [InlineData (10, 20, 4, 2)]
- [InlineData (10, 20, 5, 2)]
- [InlineData (10, 20, 6, 3)]
- [InlineData (10, 20, 7, 4)]
- [InlineData (10, 20, 8, 4)]
-
- public void CalculateSliderPosition_Calculates_Correctly (int visibleContentSize, int scrollableContentSize, int contentPosition, int expectedSliderPosition)
- {
- // Arrange
- var scrollBar = new ScrollBar
- {
- ScrollableContentSize = scrollableContentSize,
- VisibleContentSize = visibleContentSize,
- Orientation = Orientation.Horizontal // Assuming horizontal for simplicity
- };
- scrollBar.Width = visibleContentSize;
-
- // Act
- var sliderPosition= scrollBar.CalculateSliderPositionFromContentPosition (contentPosition, NavigationDirection.Forward);
-
- // Assert
- Assert.Equal (expectedSliderPosition, sliderPosition);
- }
-
-
- #endregion Slider
#region Size
@@ -489,97 +220,6 @@ public class ScrollBarTests (ITestOutputHelper output)
#endregion Size
#region Position
-
- // 012345678901
- // ◄█░░░░░░░░░►
- [Theory]
- // ◄█►
- [InlineData (3, 3, -1, 0)]
- [InlineData (3, 3, 0, 0)]
- // 012
- // ---
- // ◄█►
- [InlineData (3, 3, 1, 0)]
- [InlineData (3, 3, 2, 0)]
-
- // ◄██►
- [InlineData (4, 2, 1, 0)]
- [InlineData (4, 2, 2, 0)]
-
- // 0123
- // ---
- // ◄██►
- [InlineData (4, 3, 0, 0)] // scrollBarWidth/VisibleContentSize > size - scrolling doesn't make sense. Size should clamp to scrollSlider.Size.
- // ◄██►
- [InlineData (4, 3, 1, 0)]
- // ◄██►
- [InlineData (4, 3, 2, 0)]
-
-
- // 01234
- // ----
- // ◄██►
- [InlineData (4, 4, 0, 0)] // scrollBarWidth/VisibleContentSize == size - scrolling doesn't make sense. Size should clamp to scrollSlider.Size.
- // ◄██►
- [InlineData (4, 4, 1, 0)]
- // ◄██►
- [InlineData (4, 4, 2, 0)]
-
- // 012345
- // ◄███►
- // -----
- [InlineData (5, 5, 3, 0)]
- [InlineData (5, 5, 4, 0)]
-
- // 0123456
- // ◄██░►
- // ^-----
- [InlineData (5, 6, 0, 0)]
- // ◄░██►
- // -^----
- [InlineData (5, 6, 1, 1)]
- [InlineData (5, 6, 2, 1)]
-
- // 012346789
- // ◄█░░►
- // ^--------
- [InlineData (5, 10, -1, 0)]
- [InlineData (5, 10, 0, 0)]
-
- // 0123456789
- // ◄░█░►
- // --^-------
- [InlineData (5, 10, 1, 3)]
-
- // ◄░░█►
- // ----^----
- [InlineData (5, 10, 2, 5)]
-
- // ◄░░█►
- // ------^---
- [InlineData (5, 10, 4, 5)]
-
- // ◄░████░░░►
- // --------------------
- [InlineData (10, 20, 0, 0)]
-
- public void CalculatePosition_Calculates (int visibleContentSize, int scrollableContentSize, int sliderPosition, int expectedContentPosition)
- {
- // Arrange
- var scrollBar = new ScrollBar
- {
- VisibleContentSize = visibleContentSize,
- ScrollableContentSize = scrollableContentSize,
- Orientation = Orientation.Horizontal // Use Horizontal because it's easier to visualize
- };
- scrollBar.Frame = new (0, 0, visibleContentSize, 0);
-
- // Act
- var contentPosition = scrollBar.CalculatePositionFromSliderPosition (sliderPosition);
-
- // Assert
- Assert.Equal (expectedContentPosition, contentPosition);
- }
[Fact]
public void Position_Event_Cancelables ()
{
@@ -694,7 +334,7 @@ public class ScrollBarTests (ITestOutputHelper output)
Orientation.Horizontal,
@"
┌──────────┐
-│◄████░░░░►│
+│◄░████░░░►│
└──────────┘
")]
@@ -741,7 +381,7 @@ public class ScrollBarTests (ITestOutputHelper output)
Orientation.Horizontal,
@"
┌──────────┐
-│◄░░░████░►│
+│◄░░████░░►│
└──────────┘
")]
@@ -766,7 +406,7 @@ public class ScrollBarTests (ITestOutputHelper output)
Orientation.Horizontal,
@"
┌──────────┐
-│◄░░░░████►│
+│◄░░░████░►│
└──────────┘
")]
@@ -885,7 +525,7 @@ public class ScrollBarTests (ITestOutputHelper output)
Orientation.Horizontal,
@"
┌────────────┐
-│◄░██████░░░►│
+│◄░░██████░░►│
└────────────┘
")]
@@ -908,7 +548,7 @@ public class ScrollBarTests (ITestOutputHelper output)
Orientation.Horizontal,
@"
┌────────────┐
-│◄░░░██████░►│
+│◄░░██████░░►│
└────────────┘
")]
@@ -1087,13 +727,13 @@ public class ScrollBarTests (ITestOutputHelper output)
│▲│
│░│
│░│
+│█│
+│█│
+│█│
+│█│
+│█│
+│█│
│░│
-│█│
-│█│
-│█│
-│█│
-│█│
-│█│
│░│
│▼│
└─┘")]
@@ -1121,39 +761,35 @@ public class ScrollBarTests (ITestOutputHelper output)
#endregion Vertical
- public void Draws_Correctly (int width, int height, int contentSize, int contentPosition, Orientation orientation, string expected)
+ public void Draws_Correctly_Default_Settings (int width, int height, int contentSize, int contentPosition, Orientation orientation, string expected)
{
var super = new Window
{
Id = "super",
Width = width + 2,
- Height = height + 2
+ Height = height + 2,
};
var scrollBar = new ScrollBar
{
+ AutoHide = false,
Orientation = orientation,
};
if (orientation == Orientation.Vertical)
{
- scrollBar.Width = 1;
- scrollBar.Height = height;
+ super.SetContentSize (new (width, contentSize));
+ scrollBar.Width = width;
}
else
{
- scrollBar.Width = width;
- scrollBar.Height = 1;
+ super.SetContentSize (new (contentSize, height));
+ scrollBar.Height = height;
}
super.Add (scrollBar);
- scrollBar.ScrollableContentSize = contentSize;
scrollBar.Position = contentPosition;
- int sliderPos = scrollBar.CalculateSliderPositionFromContentPosition (contentPosition, NavigationDirection.Forward);
-
- super.BeginInit ();
- super.EndInit ();
super.Layout ();
super.Draw ();
@@ -1188,7 +824,7 @@ public class ScrollBarTests (ITestOutputHelper output)
RunState rs = Application.Begin (top);
// Scroll to end
- scrollBar.Position = 20;
+ scrollBar.Position = 19;
Assert.Equal (10, scrollBar.Position);
Application.RunIteration (ref rs);
@@ -1196,6 +832,15 @@ public class ScrollBarTests (ITestOutputHelper output)
Assert.Equal (10, scrollBar.Position);
int initialPos = scrollBar.Position;
+ Point btnPoint = orientation == Orientation.Vertical
+ ? new (scrollBar.Frame.X, 0)
+ : new (0, scrollBar.Frame.Y);
+
+ Application.RaiseMouseEvent (new ()
+ {
+ ScreenPosition = btnPoint,
+ Flags = MouseFlags.Button1Clicked
+ });
Application.RaiseMouseEvent (new ()
{
ScreenPosition = new (0, 0),
@@ -1239,9 +884,13 @@ public class ScrollBarTests (ITestOutputHelper output)
Assert.Equal (0, scrollBar.Position);
int initialPos = scrollBar.Position;
+ Point btnPoint = orientation == Orientation.Vertical
+ ? new (scrollBar.Frame.X, scrollBar.Frame.Height - 1)
+ : new (scrollBar.Frame.Width - 1, scrollBar.Frame.Y);
+
Application.RaiseMouseEvent (new ()
{
- ScreenPosition = orientation == Orientation.Vertical ? new (0, scrollBar.Frame.Height - 1) : new (scrollBar.Frame.Width - 1, 0),
+ ScreenPosition = btnPoint,
Flags = MouseFlags.Button1Clicked
});
Application.RunIteration (ref rs);
diff --git a/UnitTests/Views/ScrollSliderTests.cs b/UnitTests/Views/ScrollSliderTests.cs
index 194645add..997c66ffc 100644
--- a/UnitTests/Views/ScrollSliderTests.cs
+++ b/UnitTests/Views/ScrollSliderTests.cs
@@ -303,8 +303,8 @@ public class ScrollSliderTests (ITestOutputHelper output)
// █░
[InlineData (2, 5, 1, 0)]
// --^--
- // █░
- [InlineData (2, 5, 2, 0)]
+ // ░█
+ [InlineData (2, 5, 2, 1)]
// ---^-
// ░█
[InlineData (2, 5, 3, 1)]
@@ -336,7 +336,7 @@ public class ScrollSliderTests (ITestOutputHelper output)
[InlineData (3, 6, 0, 0)]
// -^----
// █░░
- [InlineData (3, 6, 1, 0)]
+ [InlineData (3, 6, 1, 1)]
// --^---
// ░█░
[InlineData (3, 6, 2, 1)]
@@ -442,11 +442,11 @@ public class ScrollSliderTests (ITestOutputHelper output)
// ░███░░░░
// --^---------------
[InlineData (8, 18, 2, 1)]
- [InlineData (8, 18, 3, 1)]
+ [InlineData (8, 18, 3, 2)]
[InlineData (8, 18, 4, 2)]
[InlineData (8, 18, 5, 2)]
[InlineData (8, 18, 6, 3)]
- [InlineData (8, 18, 7, 3)]
+ [InlineData (8, 18, 7, 4)]
[InlineData (8, 18, 8, 4)]
[InlineData (8, 18, 9, 4)]
@@ -457,7 +457,7 @@ public class ScrollSliderTests (ITestOutputHelper output)
[InlineData (8, 18, 11, 5)]
[InlineData (8, 18, 12, 5)]
[InlineData (8, 18, 13, 5)]
- [InlineData (8, 18, 14, 4)]
+ [InlineData (8, 18, 14, 5)]
[InlineData (8, 18, 15, 5)]
[InlineData (8, 18, 16, 5)]
[InlineData (8, 18, 17, 5)]