Implement TextView modernization with ViewPort and ScrollBars

- Add SetContentSize/GetContentSize integration
- Configure VerticalScrollBar with AutoShow=true by default
- Configure HorizontalScrollBar to track WordWrap property
- Keep _topRow/_leftColumn in sync with Viewport for backward compatibility
- Update content size on model changes
- All parallelizable tests pass (163/163)

Co-authored-by: tig <585482+tig@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2025-11-21 19:15:18 +00:00
parent 817b90aa33
commit bb536e2fcf

View File

@@ -770,7 +770,13 @@ public class TextView : View, IDesignable
return;
}
_leftColumn = Math.Max (Math.Min (value, Maxlength - 1), 0);
int clampedValue = Math.Max (Math.Min (value, Maxlength - 1), 0);
_leftColumn = clampedValue;
if (IsInitialized && Viewport.X != _leftColumn)
{
Viewport = Viewport with { X = _leftColumn };
}
}
}
@@ -966,7 +972,16 @@ public class TextView : View, IDesignable
public int TopRow
{
get => _topRow;
set => _topRow = Math.Max (Math.Min (value, Lines - 1), 0);
set
{
int clampedValue = Math.Max (Math.Min (value, Lines - 1), 0);
_topRow = clampedValue;
if (IsInitialized && Viewport.Y != _topRow)
{
Viewport = Viewport with { Y = _topRow };
}
}
}
/// <summary>
@@ -1004,6 +1019,14 @@ public class TextView : View, IDesignable
_model = _wrapManager.Model;
}
// Update horizontal scrollbar visibility based on WordWrap
if (IsInitialized)
{
HorizontalScrollBar.AutoShow = !_wordWrap;
HorizontalScrollBar.Visible = !_wordWrap && HorizontalScrollBar.AutoShow;
UpdateContentSize ();
}
SetNeedsDraw ();
}
}
@@ -1777,6 +1800,12 @@ public class TextView : View, IDesignable
ProcessInheritsPreviousScheme (CurrentRow, CurrentColumn);
ProcessAutocomplete ();
// Update content size when content changes
if (IsInitialized)
{
UpdateContentSize ();
}
}
/// <inheritdoc/>
@@ -2139,12 +2168,22 @@ public class TextView : View, IDesignable
if (isRow)
{
_topRow = Math.Max (idx > _model.Count - 1 ? _model.Count - 1 : idx, 0);
if (IsInitialized && Viewport.Y != _topRow)
{
Viewport = Viewport with { Y = _topRow };
}
}
else if (!_wordWrap)
{
int maxlength =
_model.GetMaxVisibleLine (_topRow, _topRow + Viewport.Height, TabWidth);
_leftColumn = Math.Max (!_wordWrap && idx > maxlength - 1 ? maxlength - 1 : idx, 0);
if (IsInitialized && Viewport.X != _leftColumn)
{
Viewport = Viewport with { X = _leftColumn };
}
}
SetNeedsDraw ();
@@ -2342,6 +2381,12 @@ public class TextView : View, IDesignable
need = true;
}
// Sync Viewport with the internal scroll position
if (IsInitialized && (_leftColumn != Viewport.X || _topRow != Viewport.Y))
{
Viewport = new Rectangle (_leftColumn, _topRow, Viewport.Width, Viewport.Height);
}
if (need)
{
if (_wrapNeeded)
@@ -4652,12 +4697,53 @@ public class TextView : View, IDesignable
App?.Popover?.Register (ContextMenu);
KeyBindings.Add (ContextMenu.Key, Command.Context);
// Configure ScrollBars to use modern View scrolling infrastructure
ConfigureScrollBars ();
OnContentsChanged ();
}
/// <summary>
/// Configures the ScrollBars to work with the modern View scrolling system.
/// </summary>
private void ConfigureScrollBars ()
{
// Vertical ScrollBar: AutoShow enabled by default
VerticalScrollBar.AutoShow = true;
// Horizontal ScrollBar: Initially hidden, visibility tracks WordWrap
HorizontalScrollBar.Visible = false;
HorizontalScrollBar.AutoShow = !WordWrap;
// Subscribe to ViewportChanged to sync internal scroll fields
ViewportChanged += TextView_ViewportChanged;
// Update content size based on current model
UpdateContentSize ();
}
private void TextView_ViewportChanged (object? sender, DrawEventArgs e)
{
// Sync internal scroll position fields with Viewport
_topRow = Viewport.Y;
_leftColumn = Viewport.X;
}
/// <summary>
/// Updates the content size based on the text model dimensions.
/// </summary>
private void UpdateContentSize ()
{
int contentHeight = _model.Count;
int contentWidth = _wordWrap ? Viewport.Width : _model.GetMaxVisibleLine (0, _model.Count, TabWidth);
SetContentSize (new Size (contentWidth, contentHeight));
}
private void TextView_LayoutComplete (object? sender, LayoutEventArgs e)
{
WrapTextModel ();
UpdateContentSize ();
Adjust ();
}