namespace Terminal.Gui.Views;
/// Viewport, scrolling, and content area management methods for TextView
public partial class TextView
{
///
/// Configures the ScrollBars to work with the modern View scrolling system.
///
private void ConfigureLayout ()
{
// Vertical ScrollBar: AutoShow enabled by default as per requirements
VerticalScrollBar.AutoShow = true;
// Horizontal ScrollBar: AutoShow tracks WordWrap as per requirements
HorizontalScrollBar.AutoShow = !WordWrap;
}
private void TextView_LayoutComplete (object? sender, LayoutEventArgs e)
{
_topRow = Viewport.Y;
_leftColumn = Viewport.X;
WrapTextModel ();
UpdateContentSize ();
AdjustScrollPosition ();
}
///
/// INTERNAL: Adjusts the scroll position and cursor to ensure the cursor is visible in the viewport.
/// This method handles both horizontal and vertical scrolling, word wrap considerations, and syncs
/// the internal scroll fields with the Viewport property.
///
private void AdjustScrollPosition ()
{
(int width, int height) offB = GetViewportClipping ();
List| line = GetCurrentLine ();
bool need = NeedsDraw || _wrapNeeded || !Used;
(int size, int length) tSize = TextModel.DisplaySize (line, -1, -1, false, TabWidth);
(int size, int length) dSize = TextModel.DisplaySize (line, _leftColumn, CurrentColumn, true, TabWidth);
if (!_wordWrap && CurrentColumn < _leftColumn)
{
_leftColumn = CurrentColumn;
need = true;
}
else if (!_wordWrap
&& (CurrentColumn - _leftColumn + 1 > Viewport.Width + offB.width || dSize.size + 1 >= Viewport.Width + offB.width))
{
_leftColumn = TextModel.CalculateLeftColumn (
line,
_leftColumn,
CurrentColumn,
Viewport.Width + offB.width,
TabWidth
);
need = true;
}
else if ((_wordWrap && _leftColumn > 0) || (dSize.size < Viewport.Width + offB.width && tSize.size < Viewport.Width + offB.width))
{
if (_leftColumn > 0)
{
_leftColumn = 0;
need = true;
}
}
if (CurrentRow < _topRow)
{
_topRow = CurrentRow;
need = true;
}
else if (CurrentRow - _topRow >= Viewport.Height + offB.height)
{
_topRow = Math.Min (Math.Max (CurrentRow - Viewport.Height + 1, 0), CurrentRow);
need = true;
}
else if (_topRow > 0 && CurrentRow < _topRow)
{
_topRow = Math.Max (_topRow - 1, 0);
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)
{
WrapTextModel ();
_wrapNeeded = false;
}
SetNeedsDraw ();
}
else
{
if (IsInitialized)
{
PositionCursor ();
}
}
OnUnwrappedCursorPosition ();
}
///
/// INTERNAL: Calculates the viewport clipping caused by the view extending beyond the SuperView's boundaries.
/// Returns negative width and height offsets when the viewport extends beyond the SuperView, representing
/// how much of the viewport is clipped.
///
/// A tuple containing the width and height clipping offsets (negative when clipped).
private (int width, int height) GetViewportClipping ()
{
var w = 0;
var h = 0;
if (SuperView?.Viewport.Right - Viewport.Right < 0)
{
w = SuperView!.Viewport.Right - Viewport.Right - 1;
}
if (SuperView?.Viewport.Bottom - Viewport.Bottom < 0)
{
h = SuperView!.Viewport.Bottom - Viewport.Bottom - 1;
}
return (w, h);
}
///
/// INTERNAL: Updates the content size based on the text model dimensions.
/// When word wrap is enabled, content width equals viewport width.
/// Otherwise, calculates the maximum line width from the entire text model.
/// Content height is always the number of lines in the model.
///
private void UpdateContentSize ()
{
int contentHeight = Math.Max (_model.Count, 1);
// For horizontal size: if word wrap is enabled, content width equals viewport width
// Otherwise, calculate the maximum line width from the entire text model
int contentWidth;
if (_wordWrap)
{
// Word wrap: content width follows viewport width
contentWidth = Math.Max (Viewport.Width, 1);
}
else
{
// No word wrap: calculate max line width
// Cache the current value to avoid recalculating on every call
contentWidth = Math.Max (_model.GetMaxVisibleLine (0, _model.Count, TabWidth), 1);
}
SetContentSize (new Size (contentWidth, contentHeight));
}
}
|