#nullable enable
namespace Terminal.Gui;
internal class ScrollSlider : View
{
public ScrollSlider (Scroll host)
{
_host = host;
Id = "slider";
Width = Dim.Auto (DimAutoStyle.Content);
Height = Dim.Auto (DimAutoStyle.Content);
WantMousePositionReports = true;
}
internal bool _wasSliderMouse;
private readonly Scroll _host;
private int _lastLocation = -1;
private ColorScheme? _savedColorScheme;
///
public override Attribute GetNormalColor ()
{
if (_savedColorScheme is null)
{
ColorScheme = new () { Normal = new (_host.ColorScheme.HotNormal.Foreground, _host.ColorScheme.HotNormal.Foreground) };
}
else
{
ColorScheme = new () { Normal = new (_host.ColorScheme.Normal.Foreground, _host.ColorScheme.Normal.Foreground) };
}
return base.GetNormalColor ();
}
///
protected internal override bool? OnMouseEnter (MouseEvent mouseEvent)
{
_savedColorScheme ??= _host.ColorScheme;
ColorScheme = new ()
{
Normal = new (_savedColorScheme.HotNormal.Foreground, _savedColorScheme.HotNormal.Foreground),
Focus = new (_savedColorScheme.Focus.Foreground, _savedColorScheme.Focus.Foreground),
HotNormal = new (_savedColorScheme.Normal.Foreground, _savedColorScheme.Normal.Foreground),
HotFocus = new (_savedColorScheme.HotFocus.Foreground, _savedColorScheme.HotFocus.Foreground),
Disabled = new (_savedColorScheme.Disabled.Foreground, _savedColorScheme.Disabled.Foreground)
};
return base.OnMouseEnter (mouseEvent);
}
///
protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
int location = _host.Orientation == Orientation.Vertical ? mouseEvent.Position.Y : mouseEvent.Position.X;
int offset = _lastLocation > -1 ? location - _lastLocation : 0;
int barSize = _host.Orientation == Orientation.Vertical ? _host.GetContentSize ().Height : _host.GetContentSize ().Width;
if (mouseEvent.Flags == MouseFlags.Button1Pressed)
{
if (Application.MouseGrabView != this)
{
Application.GrabMouse (this);
_lastLocation = location;
}
}
else if (mouseEvent.Flags == (MouseFlags.Button1Pressed | MouseFlags.ReportMousePosition))
{
if (_host.Orientation == Orientation.Vertical)
{
if (Frame.Y + offset >= 0 && Frame.Y + offset + Frame.Height <= barSize)
{
_wasSliderMouse = true;
Y = Frame.Y + offset;
_host.Position = GetPositionFromSliderLocation (Frame.Y);
}
}
else
{
if (Frame.X + offset >= 0 && Frame.X + offset + Frame.Width <= barSize)
{
_wasSliderMouse = true;
X = Frame.X + offset;
_host.Position = GetPositionFromSliderLocation (Frame.X);
}
}
}
else if (mouseEvent.Flags == MouseFlags.Button1Released)
{
_lastLocation = -1;
if (Application.MouseGrabView == this)
{
Application.UngrabMouse ();
}
}
else if ((mouseEvent.Flags == MouseFlags.WheeledDown && _host.Orientation == Orientation.Vertical)
|| (mouseEvent.Flags == MouseFlags.WheeledRight && _host.Orientation == Orientation.Horizontal))
{
_host.Position = Math.Min (_host.Position + 1, _host.Size - barSize);
}
else if ((mouseEvent.Flags == MouseFlags.WheeledUp && _host.Orientation == Orientation.Vertical)
|| (mouseEvent.Flags == MouseFlags.WheeledLeft && _host.Orientation == Orientation.Horizontal))
{
_host.Position = Math.Max (_host.Position - 1, 0);
}
else if (mouseEvent.Flags != MouseFlags.ReportMousePosition)
{
return base.OnMouseEvent (mouseEvent);
}
return true;
}
///
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
if (_savedColorScheme is { } && !mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
{
ColorScheme = _savedColorScheme;
_savedColorScheme = null;
}
return base.OnMouseLeave (mouseEvent);
}
private int GetPositionFromSliderLocation (int location)
{
if (_host.GetContentSize ().Height == 0 || _host.GetContentSize ().Width == 0)
{
return 0;
}
int scrollSize = _host.Orientation == Orientation.Vertical ? _host.GetContentSize ().Height : _host.GetContentSize ().Width;
// Ensure the Position is valid if the slider is at end
// We use Frame here instead of ContentSize because even if the slider has a margin or border, Frame indicates the actual size
if ((_host.Orientation == Orientation.Vertical && location + Frame.Height == scrollSize)
|| (_host.Orientation == Orientation.Horizontal && location + Frame.Width == scrollSize))
{
return _host.Size - scrollSize;
}
return Math.Min ((location * _host.Size + location) / scrollSize, _host.Size - scrollSize);
}
}