#nullable enable
using System.Diagnostics;
namespace Terminal.Gui;
///
/// The ScrollSlider can be dragged with the mouse, constrained by the size of the Viewport of it's superview. The ScrollSlider can be
/// oriented either vertically or horizontally.
///
///
///
/// If is set, it will be displayed centered within the slider. Set
/// to automatically have the Text
/// be show what percent the slider is to the Superview's Viewport size.
///
///
/// Used to represent the proportion of the visible content to the Viewport in a .
///
///
public class ScrollSlider : View, IOrientation, IDesignable
{
///
/// Initializes a new instance.
///
public ScrollSlider ()
{
Id = "scrollSlider";
WantMousePositionReports = true;
_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);
OnOrientationChanged (Orientation);
HighlightStyle = HighlightStyle.Hover;
// Default size is 1
Size = 1;
}
#region IOrientation members
private readonly OrientationHelper _orientationHelper;
///
public Orientation Orientation
{
get => _orientationHelper.Orientation;
set => _orientationHelper.Orientation = value;
}
///
public event EventHandler>? OrientationChanging;
///
public event EventHandler>? OrientationChanged;
///
public void OnOrientationChanged (Orientation newOrientation)
{
TextDirection = Orientation == Orientation.Vertical ? TextDirection.TopBottom_LeftRight : TextDirection.LeftRight_TopBottom;
TextAlignment = Alignment.Center;
VerticalTextAlignment = Alignment.Center;
// Reset Position to 0 when changing orientation
X = 0;
Y = 0;
// Reset Size to 1 when changing orientation
if (Orientation == Orientation.Vertical)
{
Width = Dim.Fill ();
Height = 1;
}
else
{
Width = 1;
Height = Dim.Fill ();
}
}
#endregion
///
protected override bool OnClearingViewport ()
{
FillRect (Viewport, Glyphs.ContinuousMeterSegment);
return true;
}
private bool _showPercent;
///
/// Gets or sets whether the ScrollSlider will set to show the percentage the slider
/// takes up within the 's Viewport.
///
public bool ShowPercent
{
get => _showPercent;
set
{
_showPercent = value;
SetNeedsDraw();
}
}
///
/// Gets or sets the size of the ScrollSlider. This is a helper that simply gets or sets the Width or Height depending on the
/// . The size will be constrained such that the ScrollSlider will not go outside the Viewport of
/// the . The size will never be less than 1.
///
///
///
/// The dimension of the ScrollSlider that is perpendicular to the will be set to
///
///
public int Size
{
get
{
if (Orientation == Orientation.Vertical)
{
return Frame.Height;
}
else
{
return Frame.Width;
}
}
set
{
if (Orientation == Orientation.Vertical)
{
Width = Dim.Fill ();
int viewport = Math.Max (1, SuperView?.Viewport.Height ?? 1);
Height = Math.Clamp (value, 1, viewport);
}
else
{
int viewport = Math.Max (1, SuperView?.Viewport.Width ?? 1);
Width = Math.Clamp (value, 1, viewport);
Height = Dim.Fill ();
}
}
}
///
/// Gets or sets the position of the ScrollSlider relative to the size of the ScrollSlider's Frame. This is a helper that simply gets or sets the X or Y depending on the
/// . The position will be constrained such that the ScrollSlider will not go outside the Viewport of
/// the .
///
public int Position
{
get
{
if (Orientation == Orientation.Vertical)
{
return Frame.Y;
}
else
{
return Frame.X;
}
}
set
{
if (Orientation == Orientation.Vertical)
{
int viewport = Math.Max (1, SuperView?.Viewport.Height ?? 1);
Y = Math.Clamp (value, 0, viewport - Frame.Height);
}
else
{
int viewport = Math.Max (1, SuperView?.Viewport.Width ?? 1);
X = Math.Clamp (value, 0, viewport - Frame.Width);
}
}
}
///
protected override bool OnDrawingText ()
{
if (!ShowPercent)
{
Text = string.Empty;
return false;
}
if (SuperView is null)
{
return false;
}
if (Orientation == Orientation.Vertical)
{
Text = $"{(int)Math.Round ((double)Viewport.Height / SuperView!.GetContentSize ().Height * 100)}%";
}
else
{
Text = $"{(int)Math.Round ((double)Viewport.Width / SuperView!.GetContentSize ().Width * 100)}%";
}
return false;
}
///
public override Attribute GetNormalColor () { return base.GetHotNormalColor (); }
/////
private int _lastLocation = -1;
///
protected override bool OnMouseEvent (MouseEventArgs mouseEvent)
{
if (SuperView is null)
{
return false;
}
int location = Orientation == Orientation.Vertical ? mouseEvent.Position.Y : mouseEvent.Position.X;
int offset = _lastLocation > -1 ? location - _lastLocation : 0;
int superViewDimension = Orientation == Orientation.Vertical ? SuperView!.Viewport.Height : SuperView!.Viewport.Width;
if (mouseEvent.IsPressed || mouseEvent.IsReleased)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed) && _lastLocation == -1)
{
if (Application.MouseGrabView != this)
{
Application.GrabMouse (this);
_lastLocation = location;
}
}
else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
{
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;
}
else
{
X = Frame.X + offset < 0
? 0
: Frame.X + offset + Frame.Width > superViewDimension
? Math.Max (superViewDimension - Frame.Width, 0)
: Frame.X + offset;
}
}
else if (mouseEvent.Flags == MouseFlags.Button1Released)
{
_lastLocation = -1;
if (Application.MouseGrabView == this)
{
Application.UngrabMouse ();
}
}
return true;
}
return false;
}
///
public bool EnableForDesign ()
{
Orientation = Orientation.Vertical;
Width = 1;
Height = 10;
ShowPercent = true;
return true;
}
}