B2: add Dalamud-free CardClipPlanner variable-height clip-plan helper

This commit is contained in:
2026-06-16 19:58:02 +02:00
parent b048a51534
commit 1d69d0cc30
@@ -0,0 +1,74 @@
using System.Collections.Generic;
namespace HellionChat.Ui.Components;
// B2 (PERF-B2): variable-height clip plan for card mode. ImGuiListClipper needs
// a constant row height, so card mode (rows wrap to arbitrary heights) computes
// its own plan from the cached per-row heights: a lead dummy for the rows above
// the viewport, the [first..last] index range that overlaps the viewport, and
// an end dummy for the rows below. This is the prefix-sum analogue of
// OtterGui's GetNecessarySkips, but for non-uniform heights — kept Dalamud-free
// so the Build Suite pins every edge case.
// TEST-MIRROR: ../../../../Hellion Build test/Ui/CardClipPlanTests.cs
internal readonly record struct CardClipPlan(
int FirstVisible,
int LastVisible,
float LeadDummyHeight,
float EndDummyHeight
);
internal static class CardClipPlanner
{
// heights[i] is the cached, spacing-inclusive height of row i in draw order.
// A row overlaps the viewport iff rowTop < windowBottom && rowBottom >
// windowTop. FirstVisible/LastVisible are -1 when nothing overlaps (empty
// list); callers then submit a single end dummy of the full content height
// so the scrollbar stays correct.
internal static CardClipPlan Plan(
IReadOnlyList<float> heights,
float scrollY,
float viewportHeight
)
{
var count = heights.Count;
if (count == 0)
return new CardClipPlan(-1, -1, 0f, 0f);
var windowTop = scrollY;
var windowBottom = scrollY + viewportHeight;
var first = -1;
var last = -1;
var leadDummy = 0f;
var endDummy = 0f;
var cursor = 0f; // running prefix sum = top edge of the current row
for (var i = 0; i < count; i++)
{
var rowTop = cursor;
var rowBottom = cursor + heights[i];
var overlaps = rowTop < windowBottom && rowBottom > windowTop;
if (overlaps)
{
if (first < 0)
first = i;
last = i;
}
else if (first < 0)
{
// Still above the visible window -> grows the lead dummy.
leadDummy += heights[i];
}
else
{
// Already past the visible window -> grows the end dummy.
endDummy += heights[i];
}
cursor = rowBottom;
}
return new CardClipPlan(first, last, leadDummy, endDummy);
}
}