From bb536e2fcfa0a8e6976cd1ab8376d97a5aa34274 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 19:15:18 +0000 Subject: [PATCH] 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> --- Terminal.Gui/Views/TextInput/TextView.cs | 90 +++++++++++++++++++++++- 1 file changed, 88 insertions(+), 2 deletions(-) diff --git a/Terminal.Gui/Views/TextInput/TextView.cs b/Terminal.Gui/Views/TextInput/TextView.cs index 5e2124d38..e8606744a 100644 --- a/Terminal.Gui/Views/TextInput/TextView.cs +++ b/Terminal.Gui/Views/TextInput/TextView.cs @@ -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 }; + } + } } /// @@ -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 (); + } } /// @@ -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 (); } + + /// + /// Configures the ScrollBars to work with the modern View scrolling system. + /// + 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; + } + + /// + /// Updates the content size based on the text model dimensions. + /// + 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 (); }