Starting implementing ScrollBar.

This commit is contained in:
BDisp
2024-08-23 16:45:41 +01:00
parent 92e067e014
commit 5e7bb7bd6c
4 changed files with 539 additions and 7 deletions

View File

@@ -10,8 +10,11 @@ namespace Terminal.Gui;
public class Scroll : View
{
/// <inheritdoc/>
public Scroll ()
public Scroll () : this (null) { }
public Scroll (ScrollBar? host)
{
_host = host;
_slider = new (this);
Add (_slider);
@@ -23,6 +26,7 @@ public class Scroll : View
}
internal readonly ScrollBar? _host;
internal bool _wasSliderLayoutComplete = true;
private readonly ScrollSlider _slider;
@@ -193,6 +197,20 @@ public class Scroll : View
/// <summary>Virtual method called when <see cref="Size"/> has changed. Raises <see cref="SizeChanged"/>.</summary>
protected void OnSizeChanged (int size) { SizeChanged?.Invoke (this, new (in size)); }
internal void AdjustScroll ()
{
if (_host is { })
{
X = Orientation == Orientation.Vertical ? 0 : 1;
Y = Orientation == Orientation.Vertical ? 1 : 0;
Width = Orientation == Orientation.Vertical ? Dim.Fill () : Dim.Fill (1);
Height = Orientation == Orientation.Vertical ? Dim.Fill (1) : Dim.Fill ();
}
_slider.AdjustSlider ();
SetScrollText ();
}
/// <inheritdoc/>
internal override void OnLayoutComplete (LayoutEventArgs args)
{
@@ -201,12 +219,6 @@ public class Scroll : View
AdjustScroll ();
}
private void AdjustScroll ()
{
_slider.AdjustSlider ();
SetScrollText ();
}
private void SetScrollText ()
{
TextDirection = Orientation == Orientation.Vertical ? TextDirection.TopBottom_LeftRight : TextDirection.LeftRight_TopBottom;

View File

@@ -0,0 +1,118 @@
#nullable enable
using System.ComponentModel;
namespace Terminal.Gui;
/// <summary>ScrollBars are views that display a 1-character scrollbar, either horizontal or vertical</summary>
/// <remarks>
/// <para>
/// The scrollbar is drawn to be a representation of the Size, assuming that the scroll position is set at
/// Position.
/// </para>
/// <para>If the region to display the scrollbar is larger than three characters, arrow indicators are drawn.</para>
/// </remarks>
public class ScrollBar : View
{
/// <inheritdoc/>
public ScrollBar ()
{
_scroll = new (this);
_decrease = new (this);
_increase = new (this, VariationMode.Increase);
Add (_scroll, _decrease, _increase);
CanFocus = false;
Orientation = Orientation.Vertical;
Width = Dim.Auto (DimAutoStyle.Content, 1);
Height = Dim.Auto (DimAutoStyle.Content, 1);
_scroll.PositionChanging += Scroll_PositionChanging;
_scroll.PositionChanged += Scroll_PositionChanged;
_scroll.SizeChanged += _scroll_SizeChanged;
}
private readonly Scroll _scroll;
private readonly ScrollButton _decrease;
private readonly ScrollButton _increase;
/// <summary>Defines if a scrollbar is vertical or horizontal.</summary>
public Orientation Orientation
{
get => _scroll.Orientation;
set
{
Resize (value);
_scroll.Orientation = value;
}
}
/// <summary>The position, relative to <see cref="Size"/>, to set the scrollbar at.</summary>
/// <value>The position.</value>
public int Position
{
get => _scroll.Position;
set
{
_scroll.Position = value;
AdjustAll ();
}
}
/// <summary>Raised when the <see cref="Position"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? PositionChanged;
/// <summary>
/// Raised when the <see cref="Position"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
/// <see langword="true"/> to prevent the position from being changed.
/// </summary>
public event EventHandler<CancelEventArgs<int>>? PositionChanging;
public int Size
{
get => _scroll.Size;
set
{
_scroll.Size = value;
AdjustAll ();
}
}
/// <summary>Raised when <see cref="Size"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? SizeChanged;
/// <inheritdoc/>
internal override void OnLayoutComplete (LayoutEventArgs args)
{
base.OnLayoutComplete (args);
AdjustAll ();
}
private void _scroll_SizeChanged (object? sender, EventArgs<int> e) { SizeChanged?.Invoke (this, e); }
private void AdjustAll ()
{
_scroll.AdjustScroll ();
_decrease.AdjustButton ();
_increase.AdjustButton ();
}
private void Resize (Orientation orientation)
{
switch (orientation)
{
case Orientation.Horizontal:
break;
case Orientation.Vertical:
break;
default:
throw new ArgumentOutOfRangeException (nameof (orientation), orientation, null);
}
}
private void Scroll_PositionChanged (object? sender, EventArgs<int> e) { PositionChanged?.Invoke (this, e); }
private void Scroll_PositionChanging (object? sender, CancelEventArgs<int> e) { PositionChanging?.Invoke (this, e); }
}

View File

@@ -0,0 +1,143 @@
#nullable enable
namespace Terminal.Gui;
internal enum VariationMode
{
Decrease,
Increase
}
internal class ScrollButton : View
{
public ScrollButton (ScrollBar host, VariationMode variation = VariationMode.Decrease)
{
_host = host;
VariationMode = variation;
TextAlignment = Alignment.Center;
VerticalTextAlignment = Alignment.Center;
Id = "scrollButton";
//Width = Dim.Auto (DimAutoStyle.Content, 1);
//Height = Dim.Auto (DimAutoStyle.Content, 1);
WantContinuousButtonPressed = true;
}
private readonly ScrollBar _host;
private ColorScheme? _savedColorScheme;
public void AdjustButton ()
{
if (!IsInitialized)
{
return;
}
Width = _host.Orientation == Orientation.Vertical ? Dim.Fill () : 1;
Height = _host.Orientation == Orientation.Vertical ? 1 : Dim.Fill ();
switch (VariationMode)
{
case VariationMode.Decrease:
X = 0;
Y = 0;
break;
case VariationMode.Increase:
X = _host.Orientation == Orientation.Vertical ? 0 : Pos.AnchorEnd (1);
Y = _host.Orientation == Orientation.Vertical ? Pos.AnchorEnd (1) : 0;
break;
default:
throw new ArgumentOutOfRangeException ();
}
SetButtonText ();
}
/// <inheritdoc/>
public override Attribute GetNormalColor ()
{
if (_savedColorScheme is null)
{
ColorScheme = new () { Normal = new (_host.ColorScheme.HotNormal.Foreground, _host.ColorScheme.HotNormal.Background) };
}
else
{
ColorScheme = new () { Normal = new (_host.ColorScheme.Normal.Background, _host.ColorScheme.Normal.Foreground) };
}
return base.GetNormalColor ();
}
public VariationMode VariationMode { get; }
/// <inheritdoc/>
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);
}
/// <inheritdoc/>
protected internal override bool OnMouseEvent (MouseEvent mouseEvent)
{
if (mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
{
switch (VariationMode)
{
case VariationMode.Decrease:
_host.Position--;
return true;
case VariationMode.Increase:
_host.Position++;
return true;
default:
throw new ArgumentOutOfRangeException ();
}
}
return base.OnMouseEvent (mouseEvent);
}
/// <inheritdoc/>
protected internal override bool OnMouseLeave (MouseEvent mouseEvent)
{
if (_savedColorScheme is { } && !mouseEvent.Flags.HasFlag (MouseFlags.Button1Pressed))
{
ColorScheme = _savedColorScheme;
_savedColorScheme = null;
}
return base.OnMouseLeave (mouseEvent);
}
private void SetButtonText ()
{
switch (VariationMode)
{
case VariationMode.Decrease:
Text = _host.Orientation == Orientation.Vertical ? Glyphs.UpArrow.ToString () : Glyphs.LeftArrow.ToString ();
break;
case VariationMode.Increase:
Text = _host.Orientation == Orientation.Vertical ? Glyphs.DownArrow.ToString () : Glyphs.RightArrow.ToString ();
break;
default:
throw new ArgumentOutOfRangeException ();
}
}
}

View File

@@ -0,0 +1,259 @@
using System;
using Terminal.Gui;
namespace UICatalog.Scenarios;
[ScenarioMetadata ("ScrollBar Demo", "Demonstrates using ScrollBar view.")]
[ScenarioCategory ("Drawing")]
[ScenarioCategory ("Scrolling")]
public class ScrollBarDemo : Scenario
{
private ViewDiagnosticFlags _diagnosticFlags;
public override void Main ()
{
Application.Init ();
_diagnosticFlags = View.Diagnostics;
Window app = new ()
{
Title = $"{Application.QuitKey} to Quit - Scenario: {GetName ()}"
};
var editor = new AdornmentsEditor ();
app.Add (editor);
var view = new FrameView
{
Title = "Demo View",
X = Pos.Right (editor),
Width = Dim.Fill (),
Height = Dim.Fill (),
ColorScheme = Colors.ColorSchemes ["Base"]
};
app.Add (view);
var scrollBar = new ScrollBar
{
X = Pos.AnchorEnd (),
Height = Dim.Fill (),
};
view.Add (scrollBar);
var lblWidthHeight = new Label
{
Text = "Width/Height:"
};
view.Add (lblWidthHeight);
NumericUpDown<int> scrollWidthHeight = new ()
{
Value = scrollBar.Frame.Width,
X = Pos.Right (lblWidthHeight) + 1,
Y = Pos.Top (lblWidthHeight)
};
view.Add (scrollWidthHeight);
scrollWidthHeight.ValueChanging += (s, e) =>
{
if (e.NewValue < 1
|| (e.NewValue
> (scrollBar.Orientation == Orientation.Vertical
? scrollBar.SuperView?.GetContentSize ().Width
: scrollBar.SuperView?.GetContentSize ().Height)))
{
// TODO: This must be handled in the ScrollSlider if Width and Height being virtual
e.Cancel = true;
return;
}
if (scrollBar.Orientation == Orientation.Vertical)
{
scrollBar.Width = e.NewValue;
}
else
{
scrollBar.Height = e.NewValue;
}
};
var rgOrientation = new RadioGroup
{
Y = Pos.Bottom (lblWidthHeight),
RadioLabels = ["Vertical", "Horizontal"],
Orientation = Orientation.Horizontal
};
view.Add (rgOrientation);
rgOrientation.SelectedItemChanged += (s, e) =>
{
if (e.SelectedItem == e.PreviousSelectedItem)
{
return;
}
if (rgOrientation.SelectedItem == 0)
{
scrollBar.Orientation = Orientation.Vertical;
scrollBar.X = Pos.AnchorEnd ();
scrollBar.Y = 0;
scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView.GetContentSize ().Width);
scrollBar.Width = scrollWidthHeight.Value;
scrollBar.Height = Dim.Fill ();
scrollBar.Size /= 3;
}
else
{
scrollBar.Orientation = Orientation.Horizontal;
scrollBar.X = 0;
scrollBar.Y = Pos.AnchorEnd ();
scrollBar.Width = Dim.Fill ();
scrollWidthHeight.Value = Math.Min (scrollWidthHeight.Value, scrollBar.SuperView.GetContentSize ().Height);
scrollBar.Height = scrollWidthHeight.Value;
scrollBar.Size *= 3;
}
};
var lblSize = new Label
{
Y = Pos.Bottom (rgOrientation),
Text = "Size:"
};
view.Add (lblSize);
NumericUpDown<int> scrollSize = new ()
{
Value = scrollBar.Size,
X = Pos.Right (lblSize) + 1,
Y = Pos.Top (lblSize)
};
view.Add (scrollSize);
scrollSize.ValueChanging += (s, e) =>
{
if (e.NewValue < 0)
{
e.Cancel = true;
return;
}
if (scrollBar.Size != e.NewValue)
{
scrollBar.Size = e.NewValue;
}
};
var lblPosition = new Label
{
Y = Pos.Bottom (lblSize),
Text = "Position:"
};
view.Add (lblPosition);
NumericUpDown<int> scrollPosition = new ()
{
Value = scrollBar.Position,
X = Pos.Right (lblPosition) + 1,
Y = Pos.Top (lblPosition)
};
view.Add (scrollPosition);
scrollPosition.ValueChanging += (s, e) =>
{
if (e.NewValue < 0)
{
e.Cancel = true;
return;
}
if (scrollBar.Position != e.NewValue)
{
scrollBar.Position = e.NewValue;
}
if (scrollBar.Position != e.NewValue)
{
e.Cancel = true;
}
};
var lblSizeChanged = new Label
{
Y = Pos.Bottom (lblPosition) + 1
};
view.Add (lblSizeChanged);
scrollBar.SizeChanged += (s, e) =>
{
lblSizeChanged.Text = $"SizeChanged event - CurrentValue: {e.CurrentValue}";
if (scrollSize.Value != e.CurrentValue)
{
scrollSize.Value = e.CurrentValue;
}
};
var lblPosChanging = new Label
{
Y = Pos.Bottom (lblSizeChanged)
};
view.Add (lblPosChanging);
scrollBar.PositionChanging += (s, e) => { lblPosChanging.Text = $"PositionChanging event - CurrentValue: {e.CurrentValue}; NewValue: {e.NewValue}"; };
var lblPositionChanged = new Label
{
Y = Pos.Bottom (lblPosChanging)
};
view.Add (lblPositionChanged);
scrollBar.PositionChanged += (s, e) =>
{
lblPositionChanged.Text = $"PositionChanged event - CurrentValue: {e.CurrentValue}";
scrollPosition.Value = e.CurrentValue;
};
var lblScrollFrame = new Label
{
Y = Pos.Bottom (lblPositionChanged) + 1
};
view.Add (lblScrollFrame);
var lblScrollViewport = new Label
{
Y = Pos.Bottom (lblScrollFrame)
};
view.Add (lblScrollViewport);
var lblScrollContentSize = new Label
{
Y = Pos.Bottom (lblScrollViewport)
};
view.Add (lblScrollContentSize);
scrollBar.LayoutComplete += (s, e) =>
{
lblScrollFrame.Text = $"ScrollBar Frame: {scrollBar.Frame.ToString ()}";
lblScrollViewport.Text = $"ScrollBar Viewport: {scrollBar.Viewport.ToString ()}";
lblScrollContentSize.Text = $"ScrollBar ContentSize: {scrollBar.GetContentSize ().ToString ()}";
};
editor.Initialized += (s, e) =>
{
scrollBar.Size = int.Max (app.GetContentSize ().Height * 2, app.GetContentSize ().Width * 2);
editor.ViewToEdit = scrollBar;
};
app.Closed += (s, e) => View.Diagnostics = _diagnosticFlags;
Application.Run (app);
app.Dispose ();
Application.Shutdown ();
}
}