Files
sbox-public/engine/Sandbox.Engine/Systems/UI/VirtualLayouts/LayoutUtility/VerticalLayout.cs
s&box team 71f266059a Open source release
This commit imports the C# engine code and game files, excluding C++ source code.

[Source-Commit: ceb3d758046e50faa6258bc3b658a30c97743268]
2025-11-24 09:05:18 +00:00

107 lines
2.8 KiB
C#

namespace Sandbox.UI.Layout;
/// <summary>
/// Layout items in a vertical list
/// </summary>
internal class VerticalListLayout
{
private float _itemHeight = 100f;
private Rect _rect; // inner/content (local)
private Rect _outerRect; // outer/viewport (local)
private float _scrollOffset;
private Vector2 _cellSize;
private int _updateHash;
/// <summary>
/// Fixed item height (≥ 1).
/// Values set below 1 are clamped to 1.
/// </summary>
public float ItemHeight
{
get => _itemHeight;
set => _itemHeight = MathF.Max( 1f, value );
}
/// <summary>
/// Gaps between items.
/// X is ignored; Y is row gap.
/// </summary>
public Vector2 Spacing { get; set; } = 0;
/// <summary>
/// Update layout state from the given box, scale, and scroll.
/// Returns true when internal state changed.
/// </summary>
public bool Update( Box box, float scaleFromScreen, float scrollOffset )
{
var hash = HashCode.Combine( box.RectInner, scaleFromScreen, scrollOffset, _itemHeight, Spacing );
if ( hash == _updateHash ) return false;
_updateHash = hash;
var inner = box.RectInner;
inner.Position = box.RectInner.Position - box.Rect.Position;
_rect = inner * scaleFromScreen;
_outerRect = box.Rect * scaleFromScreen;
_scrollOffset = scrollOffset;
_cellSize.x = MathF.Max( 1f, _rect.Width ); // fill width
_cellSize.y = _itemHeight;
return true;
}
/// <summary>
/// Compute the visible index range [firstIndex, lastIndex).
/// Compensates for inner top offset so top padding does not cull early items.
/// </summary>
public void GetVisibleRange( out int firstIndex, out int lastIndex )
{
float step = MathF.Max( 1f, _cellSize.y + Spacing.y );
int top = ((_scrollOffset - _rect.Top) / step).FloorToInt();
if ( top < 0 ) top = 0;
int fit = (_outerRect.Height / step).CeilToInt() + 1;
firstIndex = top;
lastIndex = firstIndex + fit; // exclusive
}
/// <summary>
/// Get the rectangle for the item at the given index.
/// </summary>
public Rect GetPosition( int index )
{
float step = _cellSize.y + Spacing.y;
return new Rect( _rect.Left, _rect.Top + index * step, _cellSize.x, _cellSize.y );
}
/// <summary>
/// Apply the item rectangle for <paramref name="index"/> to <paramref name="panel"/>.
/// </summary>
public void Position( int index, Panel panel )
{
var r = GetPosition( index );
panel.Style.Left = r.Left;
panel.Style.Top = r.Top;
panel.Style.Width = r.Width;
panel.Style.Height = r.Height;
panel.Style.Dirty();
}
/// <summary>
/// Calculate total content height for the given item count.
/// </summary>
public float GetHeight( int count )
{
float step = _cellSize.y + Spacing.y;
if ( step <= 0f ) return MathF.Max( 0f, _outerRect.Height - _rect.Height );
float paddingY = _outerRect.Height - _rect.Height;
return count * step + MathF.Max( 0f, paddingY );
}
}