瀏覽代碼

Upgrade hexView

Tig 8 月之前
父節點
當前提交
902577f604

+ 2 - 2
Terminal.Gui/View/View.Hierarchy.cs

@@ -145,7 +145,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     ///     <para>
     ///         Normally Subviews will be disposed when this View is disposed. Removing a Subview causes ownership of the
     ///         Subview's
-    ///         lifecycle to be transferred to the caller; the caller muse call <see cref="Dispose"/>.
+    ///         lifecycle to be transferred to the caller; the caller must call <see cref="Dispose()"/>.
     ///     </para>
     /// </remarks>
     /// <returns>
@@ -214,7 +214,7 @@ public partial class View // SuperView/SubView hierarchy management (SuperView,
     ///     <para>
     ///         Normally Subviews will be disposed when this View is disposed. Removing a Subview causes ownership of the
     ///         Subview's
-    ///         lifecycle to be transferred to the caller; the caller must call <see cref="Dispose"/> on any Views that were
+    ///         lifecycle to be transferred to the caller; the caller must call <see cref="Dispose()"/> on any Views that were
     ///         added.
     ///     </para>
     /// </remarks>

+ 1 - 1
Terminal.Gui/View/View.cs

@@ -262,7 +262,7 @@ public partial class View : IDisposable, ISupportInitializeNotification
 
     private bool _enabled = true;
 
-    /// <summary>Gets or sets a value indicating whether this <see cref="Responder"/> can respond to user interaction.</summary>
+    /// <summary>Gets or sets a value indicating whether this <see cref="View"/> can respond to user interaction.</summary>
     public bool Enabled
     {
         get => _enabled;

+ 151 - 246
Terminal.Gui/Views/HexView.cs

@@ -31,7 +31,6 @@ namespace Terminal.Gui;
 ///         Control the byte at the caret for editing by setting the <see cref="Address"/> property to an offset in the
 ///         stream.
 ///     </para>
-///     <para>Control the first byte shown by setting the <see cref="DisplayStart"/> property to an offset in the stream.</para>
 /// </remarks>
 public class HexView : View, IDesignable
 {
@@ -72,10 +71,10 @@ public class HexView : View, IDesignable
         AddCommand (Command.End, () => MoveEnd ());
         AddCommand (Command.LeftStart, () => MoveLeftStart ());
         AddCommand (Command.RightEnd, () => MoveEndOfLine ());
-        AddCommand (Command.StartOfPage, () => MoveUp (BytesPerLine * ((int)(Address - _displayStart) / BytesPerLine)));
+        AddCommand (Command.StartOfPage, () => MoveUp (BytesPerLine * ((int)(Address - Viewport.Y) / BytesPerLine)));
         AddCommand (
                     Command.EndOfPage,
-                    () => MoveDown (BytesPerLine * (Viewport.Height - 1 - (int)(Address - _displayStart) / BytesPerLine))
+                    () => MoveDown (BytesPerLine * (Viewport.Height - 1 - (int)(Address - Viewport.Y) / BytesPerLine))
                    );
         AddCommand (Command.DeleteCharLeft, () => true);
         AddCommand (Command.DeleteCharRight, () => true);
@@ -104,7 +103,13 @@ public class HexView : View, IDesignable
         KeyBindings.Remove (Key.Space);
         KeyBindings.Remove (Key.Enter);
 
-        SubviewsLaidOut += HexView_LayoutComplete;
+        SubviewsLaidOut += HexViewSubviewsLaidOut;
+    }
+
+    private void HexViewSubviewsLaidOut (object? sender, LayoutEventArgs e)
+    {
+        SetBytesPerLine ();
+        SetContentSize (new (GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH, (int)((GetEditedSize ()) / BytesPerLine) + 1));
     }
 
     /// <summary>Initializes a <see cref="HexView"/> class.</summary>
@@ -118,44 +123,69 @@ public class HexView : View, IDesignable
     public bool AllowEdits { get; set; } = true;
 
     /// <summary>Gets the current edit position.</summary>
-    public Point Position
+    /// <param name="address"></param>
+    public Point GetPosition (long address)
     {
-        get
+        if (_source is null || BytesPerLine == 0)
         {
-            if (_source is null || BytesPerLine == 0)
-            {
-                return Point.Empty;
-            }
+            return Point.Empty;
+        }
 
-            var delta = (int)Address;
+        var line = address / BytesPerLine;
+        var item = address % BytesPerLine;
 
-            int line = delta / BytesPerLine;
-            int item = delta % BytesPerLine;
+        return new ((int)item, (int)line);
+    }
 
-            return new (item, line);
+    /// <summary>Gets cursor location, given an address.</summary>
+    /// <param name="address"></param>
+    public Point GetCursor (long address)
+    {
+        Point position = GetPosition (address);
+        position.Offset (-Viewport.X, -Viewport.Y);
+        return position;
+    }
+
+    private void ScrollToMakeCursorVisible (Point offsetToNewCursor)
+    {
+        // Adjust vertical scrolling
+        if (offsetToNewCursor.Y < 1) // Header is at Y = 0
+        {
+            ScrollVertical (offsetToNewCursor.Y);
+        }
+        else if (offsetToNewCursor.Y >= Viewport.Height)
+        {
+            ScrollVertical (offsetToNewCursor.Y);
         }
     }
 
+
     ///<inheritdoc/>
     public override Point? PositionCursor ()
     {
-        var delta = (int)(Address - _displayStart);
-        int line = delta / BytesPerLine;
-        int item = delta % BytesPerLine;
-        int block = item / NUM_BYTES_PER_HEX_COLUMN;
-        int column = item % NUM_BYTES_PER_HEX_COLUMN * 3;
+        Point position = GetCursor (Address);
+
+        int block = position.X / NUM_BYTES_PER_HEX_COLUMN;
+        int column = position.X % NUM_BYTES_PER_HEX_COLUMN * 3;
 
         int x = GetLeftSideStartColumn () + block * HEX_COLUMN_WIDTH + column + (_firstNibble ? 0 : 1);
-        int y = line;
+        int y = position.Y;
 
         if (!_leftSideHasFocus)
         {
-            x = GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH + item - 1;
+            x = GetLeftSideStartColumn () + BytesPerLine / NUM_BYTES_PER_HEX_COLUMN * HEX_COLUMN_WIDTH + position.X - 1;
         }
 
-        Move (x, y);
-
-        return new (x, y);
+        if (HasFocus
+            && x >= 0
+            && x < Viewport.Width - AddressWidth + 1
+            && y >= 0
+            && y < Viewport.Height)
+        {
+            Move (x, y);
+            return new (x, y);
+        }
+        return null;
     }
 
     private SortedDictionary<long, byte> _edits = [];
@@ -167,6 +197,51 @@ public class HexView : View, IDesignable
     /// <value>The edits.</value>
     public IReadOnlyDictionary<long, byte> Edits => _edits;
 
+    private long GetEditedSize ()
+    {
+        if (_edits.Count == 0)
+        {
+            return _source!.Length;
+        }
+
+        long maxEditAddress = _edits.Keys.Max ();
+
+        return Math.Max (_source!.Length, maxEditAddress + 1);
+    }
+
+
+    /// <summary>
+    ///     Applies and edits made to the <see cref="Stream"/> and resets the contents of the
+    ///     <see cref="Edits"/> property.
+    /// </summary>
+    /// <param name="stream">If provided also applies the changes to the passed <see cref="Stream"/>.</param>
+    /// .
+    public void ApplyEdits (Stream? stream = null)
+    {
+        foreach (KeyValuePair<long, byte> kv in _edits)
+        {
+            _source!.Position = kv.Key;
+            _source.WriteByte (kv.Value);
+            _source.Flush ();
+
+            if (stream is { })
+            {
+                stream.Position = kv.Key;
+                stream.WriteByte (kv.Value);
+                stream.Flush ();
+            }
+        }
+
+        _edits = new ();
+        SetNeedsDraw ();
+    }
+
+    /// <summary>
+    ///     Discards the edits made to the <see cref="Stream"/> by resetting the contents of the
+    ///     <see cref="Edits"/> property.
+    /// </summary>
+    public void DiscardEdits () { _edits = new (); }
+
     private Stream? _source;
 
     /// <summary>
@@ -186,12 +261,9 @@ public class HexView : View, IDesignable
                 throw new ArgumentException (@"The source stream must be seekable (CanSeek property)");
             }
 
+            DiscardEdits ();
             _source = value;
-
-            if (_displayStart > _source.Length)
-            {
-                DisplayStart = 0;
-            }
+            SetBytesPerLine ();
 
             if (Address > _source.Length)
             {
@@ -229,29 +301,15 @@ public class HexView : View, IDesignable
                 return;
             }
 
-            //ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual (value, Source!.Length, $"Position");
+            long newAddress = Math.Clamp (value, 0, GetEditedSize ());
 
-            _address = value;
-            RaisePositionChanged ();
-        }
-    }
+            Point offsetToNewCursor = GetCursor (newAddress);
 
-    private long _displayStart;
+            _address = newAddress;
 
-    // TODO: Use Viewport content scrolling instead
-    /// <summary>
-    ///     Sets or gets the offset into the <see cref="Stream"/> that will be displayed at the top of the
-    ///     <see cref="HexView"/>.
-    /// </summary>
-    /// <value>The display start.</value>
-    public long DisplayStart
-    {
-        get => _displayStart;
-        set
-        {
-            Address = value;
-
-            SetDisplayStart (value);
+            // Ensure the new cursor position is visible
+            ScrollToMakeCursorVisible (offsetToNewCursor);
+            RaisePositionChanged ();
         }
     }
 
@@ -278,56 +336,6 @@ public class HexView : View, IDesignable
 
     private int GetLeftSideStartColumn () { return AddressWidth == 0 ? 0 : AddressWidth + 1; }
 
-    internal void SetDisplayStart (long value)
-    {
-        if (value > 0 && value >= _source?.Length)
-        {
-            _displayStart = _source.Length - 1;
-        }
-        else if (value < 0)
-        {
-            _displayStart = 0;
-        }
-        else
-        {
-            _displayStart = value;
-        }
-
-        SetNeedsDraw ();
-    }
-
-    /// <summary>
-    ///     Applies and edits made to the <see cref="Stream"/> and resets the contents of the
-    ///     <see cref="Edits"/> property.
-    /// </summary>
-    /// <param name="stream">If provided also applies the changes to the passed <see cref="Stream"/>.</param>
-    /// .
-    public void ApplyEdits (Stream? stream = null)
-    {
-        foreach (KeyValuePair<long, byte> kv in _edits)
-        {
-            _source!.Position = kv.Key;
-            _source.WriteByte (kv.Value);
-            _source.Flush ();
-
-            if (stream is { })
-            {
-                stream.Position = kv.Key;
-                stream.WriteByte (kv.Value);
-                stream.Flush ();
-            }
-        }
-
-        _edits = new ();
-        SetNeedsDraw ();
-    }
-
-    /// <summary>
-    ///     Discards the edits made to the <see cref="Stream"/> by resetting the contents of the
-    ///     <see cref="Edits"/> property.
-    /// </summary>
-    public void DiscardEdits () { _edits = new (); }
-
     /// <inheritdoc/>
     protected override bool OnMouseEvent (MouseEventArgs me)
     {
@@ -351,14 +359,14 @@ public class HexView : View, IDesignable
 
         if (me.Flags == MouseFlags.WheeledDown)
         {
-            DisplayStart = Math.Min (DisplayStart + BytesPerLine, GetEditedSize ());
+            ScrollVertical (1);
 
             return true;
         }
 
         if (me.Flags == MouseFlags.WheeledUp)
         {
-            DisplayStart = Math.Max (DisplayStart - BytesPerLine, 0);
+            ScrollVertical (-1);
 
             return true;
         }
@@ -368,8 +376,8 @@ public class HexView : View, IDesignable
             return true;
         }
 
-        int nblocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
-        int blocksSize = nblocks * HEX_COLUMN_WIDTH;
+        int blocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
+        int blocksSize = blocks * HEX_COLUMN_WIDTH;
         int blocksRightOffset = GetLeftSideStartColumn () + blocksSize - 1;
 
         if (me.Position.X > blocksRightOffset + BytesPerLine - 1)
@@ -378,7 +386,7 @@ public class HexView : View, IDesignable
         }
 
         bool clickIsOnLeftSide = me.Position.X >= blocksRightOffset;
-        long lineStart = me.Position.Y * BytesPerLine + _displayStart;
+        long lineStart = me.Position.Y * BytesPerLine + Viewport.Y * BytesPerLine;
         int x = me.Position.X - GetLeftSideStartColumn () + 1;
         int block = x / HEX_COLUMN_WIDTH;
         x -= block * 2;
@@ -413,10 +421,9 @@ public class HexView : View, IDesignable
             {
                 _firstNibble = true;
             }
+            SetNeedsDraw ();
         }
 
-        SetNeedsDraw ();
-
         return true;
     }
 
@@ -433,35 +440,38 @@ public class HexView : View, IDesignable
         SetAttribute (current);
         Move (0, 0);
 
+        long addressOfFirstLine = Viewport.Y * BytesPerLine;
+
         int nBlocks = BytesPerLine / NUM_BYTES_PER_HEX_COLUMN;
         var data = new byte [nBlocks * NUM_BYTES_PER_HEX_COLUMN * Viewport.Height];
-        Source.Position = _displayStart;
-        int n = _source!.Read (data, 0, data.Length);
+        Source.Position = addressOfFirstLine;
+        long bytesRead = Source!.Read (data, 0, data.Length);
 
         Attribute selectedAttribute = GetHotNormalColor ();
         Attribute editedAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
         Attribute editingAttribute = new Attribute (GetFocusColor ().Background, GetFocusColor ().Foreground);
+        Attribute addressAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
         for (var line = 0; line < Viewport.Height; line++)
         {
-            Rectangle lineRect = new (0, line, Viewport.Width, 1);
+            Move (0, line);
+            long addressOfLine = addressOfFirstLine + line * nBlocks * NUM_BYTES_PER_HEX_COLUMN;
 
-            if (!Viewport.Contains (lineRect))
+            if (addressOfLine <= GetEditedSize ())
             {
-                continue;
+                SetAttribute (addressAttribute);
             }
-
-            Move (0, line);
-            currentAttribute = new Attribute (GetNormalColor ().Foreground.GetHighlightColor (), GetNormalColor ().Background);
-            SetAttribute (currentAttribute);
-            var address = $"{_displayStart + line * nBlocks * NUM_BYTES_PER_HEX_COLUMN:x8}";
-            Driver?.AddStr ($"{address.Substring (8 - AddressWidth)}");
-
-            if (AddressWidth > 0)
+            else
             {
-                Driver?.AddStr (" ");
+                SetAttribute (new Attribute (GetNormalColor ().Background.GetHighlightColor (), addressAttribute.Background));
             }
+            var address = $"{addressOfLine:x8}";
+            AddStr ($"{address.Substring (8 - AddressWidth)}");
 
             SetAttribute (GetNormalColor ());
+            if (AddressWidth > 0)
+            {
+                AddStr (" ");
+            }
 
             for (var block = 0; block < nBlocks; block++)
             {
@@ -470,7 +480,7 @@ public class HexView : View, IDesignable
                     int offset = line * nBlocks * NUM_BYTES_PER_HEX_COLUMN + block * NUM_BYTES_PER_HEX_COLUMN + b;
                     byte value = GetData (data, offset, out bool edited);
 
-                    if (offset + _displayStart == Address)
+                    if (offset + addressOfFirstLine == Address)
                     {
                         // Selected
                         SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
@@ -480,12 +490,12 @@ public class HexView : View, IDesignable
                         SetAttribute (edited ? editedAttribute : GetNormalColor ());
                     }
 
-                    Driver?.AddStr (offset >= n && !edited ? "  " : $"{value:x2}");
+                    AddStr (offset >= bytesRead && !edited ? "  " : $"{value:x2}");
                     SetAttribute (GetNormalColor ());
-                    Driver?.AddRune (_spaceCharRune);
+                    AddRune (_spaceCharRune);
                 }
 
-                Driver?.AddStr (block + 1 == nBlocks ? " " : $"{_columnSeparatorRune} ");
+                AddStr (block + 1 == nBlocks ? " " : $"{_columnSeparatorRune} ");
             }
 
             for (var byteIndex = 0; byteIndex < nBlocks * NUM_BYTES_PER_HEX_COLUMN; byteIndex++)
@@ -496,7 +506,7 @@ public class HexView : View, IDesignable
 
                 var utf8BytesConsumed = 0;
 
-                if (offset >= n && !edited)
+                if (offset >= bytesRead && !edited)
                 {
                     c = _spaceCharRune;
                 }
@@ -528,7 +538,7 @@ public class HexView : View, IDesignable
                     }
                 }
 
-                if (offset + _displayStart == Address)
+                if (offset + Source.Position == Address)
                 {
                     // Selected
                     SetAttribute (_leftSideHasFocus ? editingAttribute : (edited ? editedAttribute : selectedAttribute));
@@ -538,26 +548,17 @@ public class HexView : View, IDesignable
                     SetAttribute (edited ? editedAttribute : GetNormalColor ());
                 }
 
-                Driver?.AddRune (c);
+                AddRune (c);
 
                 for (var i = 1; i < utf8BytesConsumed; i++)
                 {
                     byteIndex++;
-                    Driver?.AddRune (_periodCharRune);
+                    AddRune (_periodCharRune);
                 }
             }
         }
 
         return true;
-
-        void SetAttribute (Attribute attribute)
-        {
-            if (currentAttribute != attribute)
-            {
-                currentAttribute = attribute;
-                SetAttribute (attribute);
-            }
-        }
     }
 
     /// <summary>Raises the <see cref="Edited"/> event.</summary>
@@ -576,24 +577,22 @@ public class HexView : View, IDesignable
     protected virtual void OnEdited (HexViewEditEventArgs e) { }
 
     /// <summary>
-    ///     Call this when <see cref="Position"/> (and <see cref="Address"/>) has changed. Raises the
+    ///     Call this when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed. Raises the
     ///     <see cref="PositionChanged"/> event.
     /// </summary>
     protected void RaisePositionChanged ()
     {
-        SetNeedsDraw ();
-
-        HexViewEventArgs args = new (Address, Position, BytesPerLine);
+        HexViewEventArgs args = new (Address, GetPosition (Address), BytesPerLine);
         OnPositionChanged (args);
         PositionChanged?.Invoke (this, args);
     }
 
     /// <summary>
-    ///     Called when <see cref="Position"/> (and <see cref="Address"/>) has changed.
+    ///     Called when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed.
     /// </summary>
     protected virtual void OnPositionChanged (HexViewEventArgs e) { }
 
-    /// <summary>Raised when <see cref="Position"/> (and <see cref="Address"/>) has changed.</summary>
+    /// <summary>Raised when the position (see <see cref="GetPosition"/>) and <see cref="Address"/> have changed.</summary>
     public event EventHandler<HexViewEventArgs>? PositionChanged;
 
     /// <inheritdoc/>
@@ -642,9 +641,6 @@ public class HexView : View, IDesignable
                 b = (byte)_source.ReadByte ();
             }
 
-            // BUGBUG: This makes no sense here.
-            RedisplayLine (Address);
-
             if (_firstNibble)
             {
                 _firstNibble = false;
@@ -701,13 +697,13 @@ public class HexView : View, IDesignable
     //
     // This is used to support editing of the buffer on a peer List<>,
     // the offset corresponds to an offset relative to DisplayStart, and
-    // the buffer contains the contents of a screenful of data, so the
+    // the buffer contains the contents of a Viewport of data, so the
     // offset is relative to the buffer.
     //
     // 
     private byte GetData (byte [] buffer, int offset, out bool edited)
     {
-        long pos = DisplayStart + offset;
+        long pos = Viewport.Y * BytesPerLine + offset;
 
         if (_edits.TryGetValue (pos, out byte v))
         {
@@ -726,7 +722,7 @@ public class HexView : View, IDesignable
         var returnBytes = new byte [count];
         edited = false;
 
-        long pos = DisplayStart + offset;
+        long pos = Viewport.Y + offset;
         for (long i = pos; i < pos + count; i++)
         {
             if (_edits.TryGetValue (i, out byte v))
@@ -746,7 +742,7 @@ public class HexView : View, IDesignable
         return returnBytes;
     }
 
-    private void HexView_LayoutComplete (object? sender, LayoutEventArgs e)
+    private void SetBytesPerLine ()
     {
         // Small buffers will just show the position, with the bsize field value (4 bytes)
         BytesPerLine = NUM_BYTES_PER_HEX_COLUMN;
@@ -761,16 +757,14 @@ public class HexView : View, IDesignable
 
     private bool MoveDown (int bytes)
     {
-        RedisplayLine (Address);
-
         if (Address + bytes < GetEditedSize ())
         {
             // We can move down lines cleanly (without extending stream)
             Address += bytes;
         }
-        else if ((bytes == BytesPerLine * Viewport.Height && _source!.Length >= DisplayStart + BytesPerLine * Viewport.Height)
+        else if ((bytes == BytesPerLine * Viewport.Height && _source!.Length >= Viewport.Y * BytesPerLine + BytesPerLine * Viewport.Height)
                  || (bytes <= BytesPerLine * Viewport.Height - BytesPerLine
-                     && _source!.Length <= DisplayStart + BytesPerLine * Viewport.Height))
+                     && _source!.Length <= Viewport.Y * BytesPerLine + BytesPerLine * Viewport.Height))
         {
             long p = Address;
 
@@ -783,16 +777,6 @@ public class HexView : View, IDesignable
             Address = p;
         }
 
-        if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
-        {
-            SetDisplayStart (DisplayStart + bytes);
-            SetNeedsDraw ();
-        }
-        else
-        {
-            RedisplayLine (Address);
-        }
-
         return true;
     }
 
@@ -800,17 +784,6 @@ public class HexView : View, IDesignable
     {
         // This lets address go past the end of the stream one, enabling adding to the stream.
         Address = GetEditedSize ();
-
-        if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
-        {
-            SetDisplayStart (Address);
-            SetNeedsDraw ();
-        }
-        else
-        {
-            RedisplayLine (Address);
-        }
-
         return true;
     }
 
@@ -818,23 +791,17 @@ public class HexView : View, IDesignable
     {
         // This lets address go past the end of the stream one, enabling adding to the stream.
         Address = Math.Min (Address / BytesPerLine * BytesPerLine + BytesPerLine - 1, GetEditedSize ());
-        SetNeedsDraw ();
-
         return true;
     }
 
     private bool MoveHome ()
     {
-        DisplayStart = 0;
-        SetNeedsDraw ();
-
+        Address = 0;
         return true;
     }
 
     private bool MoveLeft ()
     {
-        RedisplayLine (Address);
-
         if (_leftSideHasFocus)
         {
             if (!_firstNibble)
@@ -852,16 +819,6 @@ public class HexView : View, IDesignable
             return true;
         }
 
-        if (Address - 1 < DisplayStart)
-        {
-            SetDisplayStart (_displayStart - BytesPerLine);
-            SetNeedsDraw ();
-        }
-        else
-        {
-            RedisplayLine (Address);
-        }
-
         Address--;
 
         return true;
@@ -869,8 +826,6 @@ public class HexView : View, IDesignable
 
     private bool MoveRight ()
     {
-        RedisplayLine (Address);
-
         if (_leftSideHasFocus)
         {
             if (_firstNibble)
@@ -889,74 +844,24 @@ public class HexView : View, IDesignable
             Address++;
         }
 
-        if (Address >= DisplayStart + BytesPerLine * Viewport.Height)
-        {
-            SetDisplayStart (DisplayStart + BytesPerLine);
-            SetNeedsDraw ();
-        }
-        else
-        {
-            RedisplayLine (Address);
-        }
-
         return true;
     }
 
-    private long GetEditedSize ()
-    {
-        if (_edits.Count == 0)
-        {
-            return _source!.Length;
-        }
-
-        long maxEditAddress = _edits.Keys.Max ();
-
-        return Math.Max (_source!.Length, maxEditAddress + 1);
-    }
 
     private bool MoveLeftStart ()
     {
         Address = Address / BytesPerLine * BytesPerLine;
-        SetNeedsDraw ();
 
         return true;
     }
 
     private bool MoveUp (int bytes)
     {
-        RedisplayLine (Address);
-
-        if (Address - bytes > -1)
-        {
-            Address -= bytes;
-        }
-
-        if (Address < DisplayStart)
-        {
-            SetDisplayStart (DisplayStart - bytes);
-            SetNeedsDraw ();
-        }
-        else
-        {
-            RedisplayLine (Address);
-        }
+        Address -= bytes;
 
         return true;
     }
 
-    private void RedisplayLine (long pos)
-    {
-        if (BytesPerLine == 0)
-        {
-            return;
-        }
-
-        var delta = (int)(pos - DisplayStart);
-        int line = delta / BytesPerLine;
-
-        SetNeedsDraw (new (0, line, Viewport.Width, 1));
-    }
-
     /// <inheritdoc />
     protected override bool OnAdvancingFocus (NavigationDirection direction, TabBehavior? behavior)
     {
@@ -969,8 +874,8 @@ public class HexView : View, IDesignable
             || (direction == NavigationDirection.Backward && !_leftSideHasFocus))
         {
             _leftSideHasFocus = !_leftSideHasFocus;
-            RedisplayLine (Address);
             _firstNibble = true;
+            SetNeedsDraw ();
 
             return true;
         }

+ 1 - 1
Terminal.Gui/Views/ScrollBar/ScrollBar.cs

@@ -30,7 +30,7 @@ namespace Terminal.Gui;
 public class ScrollBar : View, IOrientation, IDesignable
 {
     private readonly Button _decreaseButton;
-    internal readonly ScrollSlider _slider;
+    private readonly ScrollSlider _slider;
     private readonly Button _increaseButton;
 
     /// <inheritdoc/>

+ 3 - 1
UICatalog/Scenarios/HexEditor.cs

@@ -148,6 +148,8 @@ public class HexEditor : Scenario
         };
         app.Add (_statusBar);
 
+        _hexView.VerticalScrollBar.AutoShow = true;
+
         _hexView.Source = LoadFile ();
 
         Application.Run (app);
@@ -252,7 +254,7 @@ public class HexEditor : Scenario
         {
             _fileName = d.FilePaths [0];
             _hexView.Source = LoadFile ();
-            _hexView.DisplayStart = 0;
+            //_hexView.DisplayStart = 0;
         }
 
         d.Dispose ();

+ 2 - 2
UICatalog/Scenarios/Scrolling.cs

@@ -112,14 +112,14 @@ public class Scrolling : Scenario
 
         var pulsing = true;
 
-        bool timer ()
+        bool TimerFn ()
         {
             progress.Pulse ();
 
             return pulsing;
         }
 
-        Application.AddTimeout (TimeSpan.FromMilliseconds (300), timer);
+        Application.AddTimeout (TimeSpan.FromMilliseconds (300), TimerFn);
 
         app.Unloaded += AppUnloaded;
 

+ 12 - 38
UnitTests/Views/HexViewTests.cs

@@ -156,24 +156,24 @@ public class HexViewTests
         Assert.Equal (63, hv.Source!.Length);
         Assert.Equal (20, hv.BytesPerLine);
 
-        Assert.Equal (new (0, 0), hv.Position);
+        Assert.Equal (new (0, 0), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.Tab));
-        Assert.Equal (new (0, 0), hv.Position);
+        Assert.Equal (new (0, 0), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight.WithCtrl));
-        Assert.Equal (hv.BytesPerLine - 1, hv.Position.X);
+        Assert.Equal (hv.BytesPerLine - 1, hv.GetPosition (hv.Address).X);
 
         Assert.True (Application.RaiseKeyDownEvent (Key.Home));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight));
-        Assert.Equal (new (1, 0), hv.Position);
+        Assert.Equal (new (1, 0), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown));
-        Assert.Equal (new (1, 1), hv.Position);
+        Assert.Equal (new (1, 1), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.End));
-        Assert.Equal (new (3, 3), hv.Position);
+        Assert.Equal (new (3, 3), hv.GetPosition (hv.Address));
 
         Assert.Equal (hv.Source!.Length, hv.Address);
         Application.Top.Dispose ();
@@ -194,23 +194,23 @@ public class HexViewTests
         Assert.Equal (126, hv.Source!.Length);
         Assert.Equal (20, hv.BytesPerLine);
 
-        Assert.Equal (new (0, 0), hv.Position);
+        Assert.Equal (new (0, 0), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.Tab));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight.WithCtrl));
-        Assert.Equal (hv.BytesPerLine - 1, hv.Position.X);
+        Assert.Equal (hv.BytesPerLine - 1, hv.GetPosition (hv.Address).X);
 
         Assert.True (Application.RaiseKeyDownEvent (Key.Home));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorRight));
-        Assert.Equal (new (1, 0), hv.Position);
+        Assert.Equal (new (1, 0), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.CursorDown));
-        Assert.Equal (new (1, 1), hv.Position);
+        Assert.Equal (new (1, 1), hv.GetPosition (hv.Address));
 
         Assert.True (Application.RaiseKeyDownEvent (Key.End));
-        Assert.Equal (new (6, 6), hv.Position);
+        Assert.Equal (new (6, 6), hv.GetPosition (hv.Address));
 
         Assert.Equal (hv.Source!.Length, hv.Address);
         Application.Top.Dispose ();
@@ -236,27 +236,6 @@ public class HexViewTests
         Assert.Empty (hv.Edits);
     }
 
-    [Fact]
-    public void DisplayStart_Source ()
-    {
-        var hv = new HexView (LoadStream (null, out _, true)) { Width = 20, Height = 20 };
-
-        // Needed because HexView relies on LayoutComplete to calc sizes
-        hv.LayoutSubviews ();
-
-        Assert.Equal (0, hv.DisplayStart);
-
-        Assert.True (hv.NewKeyDownEvent (Key.PageDown));
-        Assert.Equal (4 * hv.Frame.Height, hv.DisplayStart);
-        Assert.Equal (hv.Source!.Length, hv.Source.Position);
-
-        Assert.True (hv.NewKeyDownEvent (Key.End));
-
-        // already on last page and so the DisplayStart is the same as before
-        Assert.Equal (4 * hv.Frame.Height, hv.DisplayStart);
-        Assert.Equal (hv.Source.Length, hv.Source.Position);
-    }
-
     [Fact]
     public void Edited_Event ()
     {
@@ -365,7 +344,7 @@ public class HexViewTests
     }
 
     [Fact]
-    public void Source_Sets_DisplayStart_And_Position_To_Zero_If_Greater_Than_Source_Length ()
+    public void Source_Sets_Address_To_Zero_If_Greater_Than_Source_Length ()
     {
         var hv = new HexView (LoadStream (null, out _)) { Width = 10, Height = 5 };
         Application.Top = new Toplevel ();
@@ -374,28 +353,23 @@ public class HexViewTests
         Application.Top.Layout ();
 
         Assert.True (hv.NewKeyDownEvent (Key.End));
-        Assert.Equal (MEM_STRING_LENGTH - 1, hv.DisplayStart);
         Assert.Equal (MEM_STRING_LENGTH, hv.Address);
 
         hv.Source = new MemoryStream ();
         Application.Top.Layout ();
-        Assert.Equal (0, hv.DisplayStart);
         Assert.Equal (0, hv.Address);
 
         hv.Source = LoadStream (null, out _);
         hv.Width = Dim.Fill ();
         hv.Height = Dim.Fill ();
         Application.Top.Layout ();
-        Assert.Equal (0, hv.DisplayStart);
         Assert.Equal (0, hv.Address);
 
         Assert.True (hv.NewKeyDownEvent (Key.End));
-        Assert.Equal (0, hv.DisplayStart);
         Assert.Equal (MEM_STRING_LENGTH, hv.Address);
 
         hv.Source = new MemoryStream ();
         Application.Top.Layout ();
-        Assert.Equal (0, hv.DisplayStart);
         Assert.Equal (0, hv.Address);
 
         Application.Top.Dispose ();