diff --git a/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs new file mode 100644 index 000000000..5166124e5 --- /dev/null +++ b/Projects/UOContent.Tests/Tests/Engines/AdvancedSearch/AdvancedSearchPagingTests.cs @@ -0,0 +1,17 @@ +using Server.Engines.AdvancedSearch; +using Xunit; + +namespace UOContent.Tests; + +public class AdvancedSearchPagingTests +{ + [Theory] + [InlineData(20, 0, 18, 18)] // full first page + [InlineData(20, 18, 18, 2)] // partial last page -> 2 visible (bug rendered 0 in descending) + [InlineData(5, 0, 18, 5)] + [InlineData(0, 0, 18, 0)] + public void VisibleCount_IsCorrect(int total, int from, int max, int expected) + { + Assert.Equal(expected, AdvancedSearchGump.VisibleCount(total, from, max)); + } +} diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs index 8d4222aaf..9045adf08 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs @@ -130,6 +130,12 @@ public class AdvancedSearchGump : Gump public AdvancedSearchGump() : base(50, 50) => Build(); + // Number of result entries that actually fit on the current page, given how many + // remain after DisplayFrom. Prevents the paging loop from reading/rendering past + // the end of SearchResults on a partial last page. + internal static int VisibleCount(int total, int displayFrom, int maxEntries) => + Math.Clamp(total - displayFrom, 0, maxEntries); + private void Build() { const int height = 500; @@ -325,15 +331,14 @@ public class AdvancedSearchGump : Gump var allDisplayedSelected = true; - for (var i = 0; i < MaxEntries; i++) - { - var offset = SortDescending ? MaxEntries - 1 - i : i; - var index = offset + DisplayFrom; + // Bounded to the entries that actually exist on this page so a partial last + // page (fewer than MaxEntries remaining) still renders in descending mode. + var visibleCount = VisibleCount(SearchResults.Length, DisplayFrom, MaxEntries); - if (index >= SearchResults.Length) - { - break; - } + for (var i = 0; i < visibleCount; i++) + { + var offset = SortDescending ? visibleCount - 1 - i : i; + var index = offset + DisplayFrom; var entry = SearchResults[index];