/************************************************************************* * 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]; }