Refactoring... WIP 3

This commit is contained in:
Tig
2024-11-09 19:25:12 -07:00
parent 427f5b1e3d
commit b2eae4c903
3 changed files with 205 additions and 140 deletions

View File

@@ -1,7 +1,6 @@
#nullable enable
using System.ComponentModel;
using System.Drawing;
namespace Terminal.Gui;
@@ -39,8 +38,7 @@ public class Scroll : View, IOrientation, IDesignable
OnOrientationChanged (Orientation);
}
/// <inheritdoc />
/// <inheritdoc/>
protected override void OnSubviewLayout (LayoutEventArgs args)
{
if (ViewportDimension < 1)
@@ -49,10 +47,12 @@ public class Scroll : View, IOrientation, IDesignable
return;
}
_slider.Size = (int)Math.Clamp (Math.Floor ((double)ViewportDimension * ViewportDimension / (Size)), 1, ViewportDimension);
_slider.Size = (int)Math.Clamp (Math.Floor ((double)ViewportDimension * ViewportDimension / (Size - 2)), 1, ViewportDimension);
}
#region IOrientation members
private readonly OrientationHelper _orientationHelper;
/// <inheritdoc/>
@@ -133,16 +133,16 @@ public class Scroll : View, IOrientation, IDesignable
/// <summary>Raised when <see cref="Size"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? SizeChanged;
private int _sliderPosition;
#region SliderPosition
private void OnSliderOnFrameChanged (object? sender, EventArgs<Rectangle> args)
{
if (ViewportDimension == 0)
{
return;
}
int framePos = Orientation == Orientation.Vertical ? args.CurrentValue.Y : args.CurrentValue.X;
RaisePositionChangeEvents (_sliderPosition, framePos);
SliderPosition = framePos;
}
/// <summary>
@@ -150,57 +150,23 @@ public class Scroll : View, IOrientation, IDesignable
/// </summary>
public int SliderPosition
{
get => _sliderPosition;
set => RaisePositionChangeEvents (_sliderPosition, value);
get => CalculateSliderPosition (_contentPosition);
set => RaiseSliderPositionChangeEvents (value);
}
/// <summary>
/// Gets or sets the position of the ScrollSlider within the range of 0...<see cref="Size"/>.
/// </summary>
public int ContentPosition
private void RaiseSliderPositionChangeEvents (int newSliderPosition)
{
get
int currentSliderPosition = CalculateSliderPosition (_contentPosition);
if (newSliderPosition > Size - ViewportDimension)
{
if (ViewportDimension - _slider.Size == 0)
{
return 0;
}
return (int)Math.Round (GetCurrentContentPosition ());
return;
}
set
{
if (Size - ViewportDimension == 0)
{
SliderPosition = 0;
return;
}
double newContentPos = (double)value / (ViewportDimension - _slider.Size) * (Size - ViewportDimension);
double newSliderPos = (double)value / (Size - ViewportDimension) * (ViewportDimension - _slider.Size);
int newSliderPosition = (int)Math.Ceiling (newSliderPos);
if (newContentPos >= GetCurrentContentPosition ())
{
newSliderPosition = (int)Math.Floor (newSliderPos);
}
if (SliderPosition != newSliderPosition)
{
SliderPosition = newSliderPosition;
}
}
}
private double GetCurrentContentPosition ()
{
return (double)SliderPosition / (ViewportDimension - _slider.Size) * (Size - ViewportDimension);
}
private void RaisePositionChangeEvents (int currentSliderPosition, int newSliderPosition)
{
if (OnPositionChanging (currentSliderPosition, newSliderPosition))
if (OnSliderPositionChanging (currentSliderPosition, newSliderPosition))
{
_slider.Position = currentSliderPosition;
return;
}
@@ -210,30 +176,28 @@ public class Scroll : View, IOrientation, IDesignable
if (args.Cancel)
{
_slider.Position = currentSliderPosition;
return;
}
// This sets the slider position and clamps the value
_slider.Position = newSliderPosition;
if (_slider.Position == _sliderPosition)
if (_slider.Position == currentSliderPosition)
{
return;
}
_sliderPosition = newSliderPosition;
ContentPosition = (int)Math.Round ((double)newSliderPosition / (ViewportDimension - _slider.Size) * (Size - ViewportDimension));
OnPositionChanged (_sliderPosition);
OnSliderPositionChanged (newSliderPosition);
SliderPositionChanged?.Invoke (this, new (in newSliderPosition));
}
/// <summary>
/// Called when <see cref="SliderPosition"/> is changing. Return true to cancel the change.
/// </summary>
protected virtual bool OnPositionChanging (int currentPos, int newPos)
{
return false;
}
protected virtual bool OnSliderPositionChanging (int currentSliderPosition, int newSliderPosition) { return false; }
/// <summary>
/// Raised when the <see cref="SliderPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
@@ -242,11 +206,88 @@ public class Scroll : View, IOrientation, IDesignable
public event EventHandler<CancelEventArgs<int>>? SliderPositionChanging;
/// <summary>Called when <see cref="SliderPosition"/> has changed.</summary>
protected virtual void OnPositionChanged (int position) { }
protected virtual void OnSliderPositionChanged (int position) { }
/// <summary>Raised when the <see cref="SliderPosition"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? SliderPositionChanged;
private int CalculateSliderPosition (int contentPosition)
{
if (Size - ViewportDimension == 0)
{
return 0;
}
return (int)Math.Round ((double)contentPosition / (Size - ViewportDimension) * (ViewportDimension - _slider.Size));
}
#endregion SliderPosition
#region ContentPosition
private int _contentPosition;
/// <summary>
/// Gets or sets the position of the ScrollSlider within the range of 0...<see cref="Size"/>.
/// </summary>
public int ContentPosition
{
get => _contentPosition;
set
{
if (value == _contentPosition)
{
return;
}
RaiseContentPositionChangeEvents (value);
}
}
private void RaiseContentPositionChangeEvents (int newContentPosition)
{
// Clamp the value between 0 and Size - ViewportDimension
newContentPosition = (int)Math.Clamp (newContentPosition, 0, Size - ViewportDimension);
if (OnContentPositionChanging (_contentPosition, newContentPosition))
{
return;
}
CancelEventArgs<int> args = new (ref _contentPosition, ref newContentPosition);
ContentPositionChanging?.Invoke (this, args);
if (args.Cancel)
{
return;
}
_contentPosition = newContentPosition;
SliderPosition = CalculateSliderPosition (_contentPosition);
OnContentPositionChanged (_contentPosition);
ContentPositionChanged?.Invoke (this, new (in _contentPosition));
}
/// <summary>
/// Called when <see cref="ContentPosition"/> is changing. Return true to cancel the change.
/// </summary>
protected virtual bool OnContentPositionChanging (int currentPos, int newPos) { return false; }
/// <summary>
/// Raised when the <see cref="ContentPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
/// <see langword="true"/> to prevent the position from being changed.
/// </summary>
public event EventHandler<CancelEventArgs<int>>? ContentPositionChanging;
/// <summary>Called when <see cref="ContentPosition"/> has changed.</summary>
protected virtual void OnContentPositionChanged (int position) { }
/// <summary>Raised when the <see cref="ContentPosition"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? ContentPositionChanged;
#endregion ContentPosition
/// <inheritdoc/>
protected override bool OnClearingViewport ()
@@ -256,7 +297,7 @@ public class Scroll : View, IOrientation, IDesignable
return true;
}
/// <inheritdoc />
/// <inheritdoc/>
protected override bool OnMouseClick (MouseEventArgs args)
{
if (!args.IsSingleClicked)
@@ -285,7 +326,6 @@ public class Scroll : View, IOrientation, IDesignable
SliderPosition = args.Position.X;
}
return true;
}
@@ -306,29 +346,31 @@ public class Scroll : View, IOrientation, IDesignable
{
if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledDown))
{
SliderPosition++;
ContentPosition++;
}
if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledUp))
{
SliderPosition--;
ContentPosition--;
}
}
else
{
if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledRight))
{
SliderPosition++;
ContentPosition++;
}
if (mouseEvent.Flags.HasFlag (MouseFlags.WheeledLeft))
{
SliderPosition--;
ContentPosition--;
}
}
return true;
}
/// <inheritdoc />
/// <inheritdoc/>
public bool EnableForDesign ()
{
OrientationChanged += (sender, args) =>
@@ -348,9 +390,7 @@ public class Scroll : View, IOrientation, IDesignable
Width = 1;
Height = Dim.Fill ();
Size = 1000;
SliderPosition = 10;
ContentPosition = 10;
return true;
}

View File

@@ -29,6 +29,8 @@ public class ScrollBar : View, IOrientation, IDesignable
_scroll = new ();
_scroll.SliderPositionChanging += OnScrollOnSliderPositionChanging;
_scroll.SliderPositionChanged += OnScrollOnSliderPositionChanged;
_scroll.ContentPositionChanging += OnScrollOnContentPositionChanging;
_scroll.ContentPositionChanged += OnScrollOnContentPositionChanged;
_scroll.SizeChanged += OnScrollOnSizeChanged;
_decreaseButton = new ()
@@ -64,13 +66,13 @@ public class ScrollBar : View, IOrientation, IDesignable
void OnDecreaseButtonOnAccept (object? s, CommandEventArgs e)
{
_scroll.ContentPosition--;
ContentPosition -= Increment;
e.Cancel = true;
}
void OnIncreaseButtonOnAccept (object? s, CommandEventArgs e)
{
_scroll.ContentPosition++;
ContentPosition += Increment;
e.Cancel = true;
}
}
@@ -188,8 +190,8 @@ public class ScrollBar : View, IOrientation, IDesignable
set => _scroll.SliderPosition = value;
}
private void OnScrollOnSliderPositionChanged (object? sender, EventArgs<int> e) { SliderPositionChanged?.Invoke (this, e); }
private void OnScrollOnSliderPositionChanging (object? sender, CancelEventArgs<int> e) { SliderPositionChanging?.Invoke (this, e); }
private void OnScrollOnSliderPositionChanged (object? sender, EventArgs<int> e) { SliderPositionChanged?.Invoke (this, e); }
/// <summary>
/// Raised when the <see cref="SliderPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
@@ -219,6 +221,18 @@ public class ScrollBar : View, IOrientation, IDesignable
set => _scroll.ContentPosition = value;
}
private void OnScrollOnContentPositionChanging (object? sender, CancelEventArgs<int> e) { ContentPositionChanging?.Invoke (this, e); }
private void OnScrollOnContentPositionChanged (object? sender, EventArgs<int> e) { ContentPositionChanged?.Invoke (this, e); }
/// <summary>
/// Raised when the <see cref="SliderPosition"/> is changing. Set <see cref="CancelEventArgs.Cancel"/> to
/// <see langword="true"/> to prevent the position from being changed.
/// </summary>
public event EventHandler<CancelEventArgs<int>>? ContentPositionChanging;
/// <summary>Raised when the <see cref="SliderPosition"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? ContentPositionChanged;
/// <summary>Raised when <see cref="Size"/> has changed.</summary>
public event EventHandler<EventArgs<int>>? SizeChanged;
@@ -228,6 +242,14 @@ public class ScrollBar : View, IOrientation, IDesignable
SizeChanged?.Invoke (this, e);
}
/// <summary>
/// Gets or sets the amount each click of the increment/decrement buttons will incremenet/decrement the <see cref="ContentPosition"/>.
/// </summary>
/// <remarks>
/// The default is 1.
/// </remarks>
public int Increment { get; set; } = 1;
/// <inheritdoc/>
protected override void OnSubviewLayout (LayoutEventArgs args) { PositionSubviews (); }

View File

@@ -10,9 +10,7 @@ using System.Text;
using System.Text.Json;
using System.Text.Unicode;
using System.Threading.Tasks;
using JetBrains.Annotations;
using Terminal.Gui;
using static System.Runtime.InteropServices.JavaScript.JSType;
using static Terminal.Gui.SpinnerStyle;
namespace UICatalog.Scenarios;
@@ -303,7 +301,7 @@ public class CharacterMap : Scenario
public override List<Key> GetDemoKeyStrokes ()
{
List<Key> keys = new List<Key> ();
List<Key> keys = new ();
for (var i = 0; i < 200; i++)
{
@@ -345,15 +343,17 @@ internal class CharMap : View, IDesignable
ColorScheme = Colors.ColorSchemes ["Dialog"];
CanFocus = true;
CursorVisibility = CursorVisibility.Default;
//ViewportSettings = ViewportSettings.AllowLocationGreaterThanContentSize;
SetContentSize (new (COLUMN_WIDTH * 16, (_maxCodePoint / 16) * _rowHeight)); // +1 for Header
SetContentSize (new (COLUMN_WIDTH * 16 + RowLabelWidth, _maxCodePoint / 16 * _rowHeight)); // +1 for Header
AddCommand (
Command.Up,
() =>
{
SelectedCodePoint -= 16;
return true;
}
);
@@ -363,6 +363,7 @@ internal class CharMap : View, IDesignable
() =>
{
SelectedCodePoint += 16;
return true;
}
);
@@ -372,6 +373,7 @@ internal class CharMap : View, IDesignable
() =>
{
SelectedCodePoint--;
return true;
}
);
@@ -380,7 +382,8 @@ internal class CharMap : View, IDesignable
Command.Right,
() =>
{
SelectedCodePoint++;
SelectedCodePoint++;
return true;
}
);
@@ -455,11 +458,12 @@ internal class CharMap : View, IDesignable
_hScrollBar = new ()
{
AutoHide = false,
X = RowLabelWidth + 1,
X = RowLabelWidth,
Y = Pos.AnchorEnd (),
Orientation = Orientation.Horizontal,
Width = Dim.Fill (1),
Size = GetContentSize ().Width
Size = GetContentSize ().Width - RowLabelWidth,
Increment = COLUMN_WIDTH
};
_vScrollBar = new ()
@@ -473,26 +477,31 @@ internal class CharMap : View, IDesignable
Padding.Add (_vScrollBar, _hScrollBar);
_vScrollBar.SliderPositionChanged += (sender, args) =>
{
if (Viewport.Height > 0)
{
Viewport = Viewport with { Y = _vScrollBar.ContentPosition };
}
};
_vScrollBar.ContentPositionChanged += (sender, args) =>
{
if (Viewport.Height > 0)
{
Viewport = Viewport with
{
Y = Math.Min (args.CurrentValue, GetContentSize ().Height - (Viewport.Height - 2))
};
}
};
_hScrollBar.SliderPositionChanged += (sender, args) =>
{
if (Viewport.Width > 0)
{
Viewport = Viewport with { X = _hScrollBar.ContentPosition };
}
};
_hScrollBar.ContentPositionChanged += (sender, args) =>
{
if (Viewport.Width > 0)
{
Viewport = Viewport with
{
X = Math.Min (args.CurrentValue, GetContentSize ().Width - Viewport.Width)
};
}
};
FrameChanged += (sender, args) =>
{
int width = Viewport.Width / COLUMN_WIDTH * COLUMN_WIDTH - RowLabelWidth;
if (width < GetContentSize ().Width)
if (Viewport.Width < GetContentSize ().Width)
{
Padding.Thickness = Padding.Thickness with { Bottom = 1 };
}
@@ -500,6 +509,7 @@ internal class CharMap : View, IDesignable
{
Padding.Thickness = Padding.Thickness with { Bottom = 0 };
}
_hScrollBar.ContentPosition = Viewport.X;
_vScrollBar.ContentPosition = Viewport.Y;
};
@@ -527,7 +537,8 @@ internal class CharMap : View, IDesignable
if (e.Flags == MouseFlags.WheeledRight)
{
_hScrollBar.SliderPosition++;
ScrollHorizontal (1);
_hScrollBar.ContentPosition = Viewport.X;
e.Handled = true;
return;
@@ -535,7 +546,8 @@ internal class CharMap : View, IDesignable
if (e.Flags == MouseFlags.WheeledLeft)
{
_hScrollBar.SliderPosition--;
ScrollHorizontal (-1);
_hScrollBar.ContentPosition = Viewport.X;
e.Handled = true;
}
}
@@ -545,8 +557,8 @@ internal class CharMap : View, IDesignable
{
get
{
int row = SelectedCodePoint / 16 * _rowHeight - Viewport.Y + 1; // + 1 for header
int col = SelectedCodePoint % 16 * COLUMN_WIDTH - Viewport.X + RowLabelWidth + 1; // + 1 for padding between label and first column
int row = SelectedCodePoint / 16 * _rowHeight + 1 - Viewport.Y; // + 1 for header
int col = SelectedCodePoint % 16 * COLUMN_WIDTH + RowLabelWidth + 1 - Viewport.X; // + 1 for padding between label and first column
return new (col, row);
}
@@ -556,7 +568,8 @@ internal class CharMap : View, IDesignable
public static int _maxCodePoint = UnicodeRange.Ranges.Max (r => r.End);
/// <summary>
/// Gets or sets the currently selected codepoint. Causes the Viewport to scroll to make the selected code point visible.
/// Gets or sets the currently selected codepoint. Causes the Viewport to scroll to make the selected code point
/// visible.
/// </summary>
public int SelectedCodePoint
{
@@ -571,58 +584,48 @@ internal class CharMap : View, IDesignable
Point prevCursor = Cursor;
int newSelectedCodePoint = Math.Clamp (value, 0, _maxCodePoint);
ScrollToMakeRowVisible (newSelectedCodePoint / 16 * _rowHeight, prevCursor.Y);
ScrollToMakeColumnVisible (newSelectedCodePoint % 16 * COLUMN_WIDTH, prevCursor.X);
if (_selected != newSelectedCodePoint)
Point newCursor = new ()
{
_selected = newSelectedCodePoint;
SetNeedsDraw ();
SelectedCodePointChanged?.Invoke (this, new (SelectedCodePoint, null));
}
X = newSelectedCodePoint % 16 * COLUMN_WIDTH + RowLabelWidth + 1 - Viewport.X,
Y = newSelectedCodePoint / 16 * _rowHeight + 1 - Viewport.Y
};
// Ensure the new cursor position is visible
EnsureCursorIsVisible (newCursor);
_selected = newSelectedCodePoint;
SetNeedsDraw ();
SelectedCodePointChanged?.Invoke (this, new (SelectedCodePoint, null));
}
}
private void ScrollToMakeRowVisible (int row, int prevCursorY)
private void EnsureCursorIsVisible (Point newCursor)
{
int height = Viewport.Height - 1; // Header
int delta = row - (Viewport.Y + height);
int scroll = Viewport.Height - (prevCursorY - delta);
// Adjust vertical scrolling
if (newCursor.Y < 1) // Header is at Y = 0
{
ScrollVertical (newCursor.Y - 1);
}
else if (newCursor.Y >= Viewport.Height)
{
ScrollVertical (newCursor.Y - Viewport.Height + 1);
}
if (row - Viewport.Y < 0)
_vScrollBar.ContentPosition = Viewport.Y;
// Adjust horizontal scrolling
if (newCursor.X < RowLabelWidth + 1)
{
// Moving up.
Viewport = Viewport with { Y = Viewport.Y + (row - Viewport.Y) };
ScrollHorizontal (newCursor.X - (RowLabelWidth + 1));
}
else if (row - Viewport.Y >= height)
else if (newCursor.X >= Viewport.Width)
{
// Moving down.
Viewport = Viewport with { Y = Math.Min (Viewport.Y + scroll, GetContentSize ().Height - height ) };
ScrollHorizontal (newCursor.X - Viewport.Width + 1);
}
_vScrollBar.ContentPosition = row;
_hScrollBar.ContentPosition = Viewport.X;
}
private void ScrollToMakeColumnVisible (int col, int prevCursorX)
{
int width = Viewport.Width - RowLabelWidth;
int delta = col - (Viewport.X + width);
int scroll = Viewport.Width - (prevCursorX - delta);
if (col - Viewport.X < 0)
{
// Moving left.
Viewport = Viewport with { X = Viewport.X + (col - Viewport.X) };
_hScrollBar.ContentPosition = col;
}
else if (col - Viewport.X >= width)
{
// Moving right.
Viewport = Viewport with { X = Math.Min (Viewport.X + scroll, GetContentSize ().Width - width) };
_hScrollBar.ContentPosition = col;
}
}
public bool ShowGlyphWidths
{
get => _rowHeight == 2;
@@ -997,7 +1000,7 @@ internal class CharMap : View, IDesignable
document.RootElement,
new
JsonSerializerOptions
{ WriteIndented = true }
{ WriteIndented = true }
);
}