mirror of
https://github.com/gui-cs/Terminal.Gui.git
synced 2025-12-31 02:08:03 +01:00
Starting implementing ScrollBar.
This commit is contained in:
@@ -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;
|
||||
|
||||
118
Terminal.Gui/Views/Scroll/ScrollBar.cs
Normal file
118
Terminal.Gui/Views/Scroll/ScrollBar.cs
Normal 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); }
|
||||
}
|
||||
143
Terminal.Gui/Views/Scroll/ScrollButton.cs
Normal file
143
Terminal.Gui/Views/Scroll/ScrollButton.cs
Normal 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 ();
|
||||
}
|
||||
}
|
||||
}
|
||||
259
UICatalog/Scenarios/ScrollBarDemo.cs
Normal file
259
UICatalog/Scenarios/ScrollBarDemo.cs
Normal 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 ();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user