Browse Source

Refactoring... WIP 3

Tig 9 months ago
parent
commit
b2eae4c903

+ 108 - 68
Terminal.Gui/Views/Scroll/Scroll.cs

@@ -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
-        {
-            if (ViewportDimension - _slider.Size == 0)
-            {
-                return 0;
-            }
-            return (int)Math.Round (GetCurrentContentPosition ());
-        }
-        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);
-            }
+        int currentSliderPosition = CalculateSliderPosition (_contentPosition);
 
-            if (SliderPosition != newSliderPosition)
-            {
-                SliderPosition = newSliderPosition;
-            }
+        if (newSliderPosition > Size - ViewportDimension)
+        {
+            return;
         }
-    }
-
-    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;
     }

+ 25 - 3
Terminal.Gui/Views/Scroll/ScrollBar.cs

@@ -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 (); }
 

+ 68 - 65
UICatalog/Scenarios/CharacterMap.cs

@@ -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 };
-                                          }
-                                      };
-
-        _hScrollBar.SliderPositionChanged += (sender, args) =>
-                                             {
-                                                 if (Viewport.Width > 0)
-                                                 {
-                                                     Viewport = Viewport with { X = _hScrollBar.ContentPosition };
-                                                 }
-                                             };
+        _vScrollBar.ContentPositionChanged += (sender, args) =>
+                                              {
+                                                  if (Viewport.Height > 0)
+                                                  {
+                                                      Viewport = Viewport with
+                                                      {
+                                                          Y = Math.Min (args.CurrentValue, GetContentSize ().Height - (Viewport.Height - 2))
+                                                      };
+                                                  }
+                                              };
+
+        _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,57 +584,47 @@ 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);
-
-        if (row - Viewport.Y < 0)
+        // Adjust vertical scrolling
+        if (newCursor.Y < 1) // Header is at Y = 0
         {
-            // Moving up.
-            Viewport = Viewport with { Y = Viewport.Y + (row - Viewport.Y) };
+            ScrollVertical (newCursor.Y - 1);
         }
-        else if (row - Viewport.Y >= height)
+        else if (newCursor.Y >= Viewport.Height)
         {
-            // Moving down.
-            Viewport = Viewport with { Y = Math.Min (Viewport.Y + scroll, GetContentSize ().Height - height ) };
+            ScrollVertical (newCursor.Y - Viewport.Height + 1);
         }
-        _vScrollBar.ContentPosition = row;
-    }
 
-    private void ScrollToMakeColumnVisible (int col, int prevCursorX)
-    {
-        int width = Viewport.Width - RowLabelWidth;
-        int delta = col - (Viewport.X + width);
-        int scroll = Viewport.Width - (prevCursorX - delta);
+        _vScrollBar.ContentPosition = Viewport.Y;
 
-        if (col - Viewport.X < 0)
+        // Adjust horizontal scrolling
+        if (newCursor.X < RowLabelWidth + 1)
         {
-            // Moving left.
-            Viewport = Viewport with { X = Viewport.X + (col - Viewport.X) };
-            _hScrollBar.ContentPosition = col;
+            ScrollHorizontal (newCursor.X - (RowLabelWidth + 1));
         }
-        else if (col  - Viewport.X >= width)
+        else if (newCursor.X >= Viewport.Width)
         {
-            // Moving right.
-            Viewport = Viewport with { X = Math.Min (Viewport.X + scroll, GetContentSize ().Width - width) };
-            _hScrollBar.ContentPosition = col;
+            ScrollHorizontal (newCursor.X - Viewport.Width + 1);
         }
-    }
 
+        _hScrollBar.ContentPosition = Viewport.X;
+    }
 
     public bool ShowGlyphWidths
     {
@@ -997,7 +1000,7 @@ internal class CharMap : View, IDesignable
                                                         document.RootElement,
                                                         new
                                                             JsonSerializerOptions
-                                                        { WriteIndented = true }
+                                                            { WriteIndented = true }
                                                        );
             }