/*************************************************************************
* ModernUO *
* Copyright 2019-2026 - ModernUO Development Team *
* Email: hi@modernuo.com *
* File: ListViewLayout.cs *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see . *
*************************************************************************/
using System;
using System.Runtime.CompilerServices;
namespace Server.Gumps;
///
/// Calculates pagination and provides helper methods for a paginated list view.
/// This struct does NOT hold column data - column positions/widths are passed
/// separately via spans to avoid ref struct limitations.
///
public ref struct ListViewLayout
{
private readonly int _originY;
private readonly int _headerHeight;
private readonly int _rowHeight;
///
/// The total number of items in the list.
///
public readonly int TotalItems;
///
/// The number of items that can be displayed per page.
///
public readonly int ItemsPerPage;
///
/// The current page index (0-based).
///
public readonly int CurrentPage;
///
/// The total number of pages.
///
public readonly int TotalPages;
///
/// The index of the first item on the current page.
///
public readonly int StartIndex;
///
/// The number of items visible on the current page.
///
public readonly int VisibleCount;
///
/// Whether there is a previous page.
///
public readonly bool CanGoBack;
///
/// Whether there is a next page.
///
public readonly bool CanGoNext;
///
/// The number of columns in the list.
///
public readonly int ColumnCount;
///
/// The height of each data row.
///
public int RowHeight
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _rowHeight;
}
///
/// The height of the header row.
///
public int HeaderHeight
{
[MethodImpl(MethodImplOptions.AggressiveInlining)]
get => _headerHeight;
}
///
/// Creates a new ListViewLayout and computes column positions into the provided spans.
///
/// The X origin of the list area.
/// The Y origin of the list area.
/// The total width of the list area.
/// The total height of the list area.
/// The total number of items in the data source.
/// The current page index (0-based).
/// The height of each data row.
/// The height of the header row (0 for no header).
/// Space-separated column size specification.
/// Span to receive calculated column X positions.
/// Span to receive calculated column widths.
/// The configured ListViewLayout.
public static ListViewLayout Create(
int originX,
int originY,
int width,
int height,
int totalItems,
int currentPage,
int rowHeight,
int headerHeight,
ReadOnlySpan columnSpec,
Span columnPositions,
Span columnWidths
)
{
// Parse and compute column widths
var columnCount = GridCalculator.ComputeFromSpec(columnSpec, width, originX, columnPositions, columnWidths);
return new ListViewLayout(
originY,
headerHeight, rowHeight,
totalItems, currentPage,
height,
columnCount
);
}
private ListViewLayout(
int originY,
int headerHeight,
int rowHeight,
int totalItems,
int currentPage,
int totalHeight,
int columnCount
)
{
_originY = originY;
_headerHeight = headerHeight;
_rowHeight = rowHeight;
ColumnCount = columnCount;
TotalItems = totalItems;
// Calculate items per page based on available height
var contentHeight = totalHeight - headerHeight;
ItemsPerPage = contentHeight / rowHeight;
// Calculate pagination
if (totalItems > 0 && ItemsPerPage > 0)
{
TotalPages = (totalItems + ItemsPerPage - 1) / ItemsPerPage;
CurrentPage = Math.Clamp(currentPage, 0, TotalPages - 1);
StartIndex = CurrentPage * ItemsPerPage;
VisibleCount = Math.Min(ItemsPerPage, totalItems - StartIndex);
CanGoBack = CurrentPage > 0;
CanGoNext = CurrentPage < TotalPages - 1;
}
else
{
TotalPages = 1;
CurrentPage = 0;
StartIndex = 0;
VisibleCount = 0;
CanGoBack = false;
CanGoNext = false;
}
}
///
/// Gets the header cell for the specified column using pre-computed column data.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GridCell GetHeaderCell(int column, ReadOnlySpan columnPositions, ReadOnlySpan columnWidths) =>
new(columnPositions[column], _originY, columnWidths[column], _headerHeight);
///
/// Gets the data cell for the specified visible row and column.
///
/// The 0-based index within visible rows (not data index).
/// The column index.
/// Pre-computed column X positions.
/// Pre-computed column widths.
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GridCell GetRowCell(
int visibleRowIndex, int column, ReadOnlySpan columnPositions, ReadOnlySpan columnWidths
) => new(
columnPositions[column],
_originY + _headerHeight + visibleRowIndex * _rowHeight,
columnWidths[column],
_rowHeight
);
///
/// Gets the data index for a visible row.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetDataIndex(int visibleRowIndex) => StartIndex + visibleRowIndex;
///
/// Gets the Y position for a visible row.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public int GetRowY(int visibleRowIndex) => _originY + _headerHeight + visibleRowIndex * _rowHeight;
///
/// Gets the X position of a column.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetColumnX(ReadOnlySpan columnPositions, int column) => columnPositions[column];
///
/// Gets the width of a column.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetColumnWidth(ReadOnlySpan columnWidths, int column) => columnWidths[column];
///
/// Gets the horizontal center of a column.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetColumnCenterX(ReadOnlySpan columnPositions, ReadOnlySpan columnWidths, int column) =>
columnPositions[column] + columnWidths[column] / 2;
///
/// Gets the right edge of a column.
///
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int GetColumnRightX(ReadOnlySpan columnPositions, ReadOnlySpan columnWidths, int column) =>
columnPositions[column] + columnWidths[column];
}