perf(ui): retain shaped report text

This commit is contained in:
Erik 2026-07-25 05:11:12 +02:00
parent c0bec56dfe
commit f2a015be8e
11 changed files with 525 additions and 38 deletions

View file

@ -67,12 +67,13 @@ public static class CharacterController
internal static IReadOnlyList<string> AugmentationStringKeys
=> Augmentations.Select(descriptor => descriptor.StringKey).ToArray();
public static void Bind(
public static CharacterInformationUiController Bind(
ImportedLayout layout,
Func<CharacterSheet> data,
UiDatFont? datFont = null,
Action? close = null,
CharacterInfoStrings? strings = null)
CharacterInfoStrings? strings = null,
Func<Action, IDisposable>? subscribeChanged = null)
{
ArgumentNullException.ThrowIfNull(layout);
ArgumentNullException.ThrowIfNull(data);
@ -96,11 +97,19 @@ public static class CharacterController
CharacterInfoStrings copy = strings ?? CharacterInfoStrings.English;
text.PreserveEndOnLayout = false;
text.LinesProvider = () => IndicatorDetailText.Shape(text, BuildReport(data(), copy));
if (layout.FindElement(ScrollbarId) is UiScrollbar scrollbar)
UiScrollbar? scrollbar = layout.FindElement(ScrollbarId) as UiScrollbar;
if (scrollbar is not null)
scrollbar.Model = text.Scroll;
if (layout.FindElement(CloseId) is UiButton closeButton)
UiButton? closeButton = layout.FindElement(CloseId) as UiButton;
if (closeButton is not null)
closeButton.OnClick = close;
return new CharacterInformationUiController(
text,
data,
copy,
subscribeChanged,
scrollbar,
closeButton);
}
internal static string BuildReport(CharacterSheet sheet, CharacterInfoStrings strings)
@ -330,6 +339,69 @@ public static class CharacterController
bool IncludeValue);
}
/// <summary>
/// Event-invalidated report owner for retail's character-information text.
/// Stable frames borrow the same shaped line collection.
/// </summary>
public sealed class CharacterInformationUiController : IRetainedPanelController
{
private readonly Func<CharacterSheet> _data;
private readonly CharacterInfoStrings _strings;
private readonly UiTextLayoutCache<string> _layout;
private readonly IDisposable? _changeBinding;
private readonly UiScrollbar? _scrollbar;
private readonly UiButton? _close;
private bool _contentDirty = true;
private bool _disposed;
internal CharacterInformationUiController(
UiText text,
Func<CharacterSheet> data,
CharacterInfoStrings strings,
Func<Action, IDisposable>? subscribeChanged,
UiScrollbar? scrollbar,
UiButton? close)
{
_data = data;
_strings = strings;
_scrollbar = scrollbar;
_close = close;
_layout = new UiTextLayoutCache<string>(
text,
static (target, value) => IndicatorDetailText.Shape(target, value),
string.Empty,
StringComparer.Ordinal);
text.LinesProvider = GetLines;
_changeBinding = subscribeChanged?.Invoke(InvalidateContent);
}
private IReadOnlyList<UiText.Line> GetLines()
{
if (_contentDirty)
{
_layout.SetValue(CharacterController.BuildReport(_data(), _strings));
_contentDirty = false;
}
return _layout.GetLines();
}
private void InvalidateContent() => _contentDirty = true;
public void OnShown() => InvalidateContent();
public void Dispose()
{
if (_disposed)
return;
_disposed = true;
_changeBinding?.Dispose();
if (_scrollbar is not null)
_scrollbar.Model = null;
if (_close is not null)
_close.OnClick = null;
}
}
public sealed record CharacterInfoStrings(
string[] Birth,
string[] Played,