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

@ -32,6 +32,7 @@ public sealed class EffectsUiController : IRetainedPanelController
private readonly string _selectPrompt;
private readonly UiItemList _list;
private readonly UiText? _info;
private readonly UiTextLayoutCache<string>? _infoLayout;
private readonly UiButton? _close;
private readonly UiScrollbar? _listScrollbar;
private readonly UiScrollbar? _infoScrollbar;
@ -70,7 +71,7 @@ public sealed class EffectsUiController : IRetainedPanelController
_list.CellHeight = templates.Height;
if (_listScrollbar is not null)
_listScrollbar.Model = _list.Scroll;
ConfigureInfo();
_infoLayout = ConfigureInfo();
_spellbook.EnchantmentsChanged += Rebuild;
Rebuild();
}
@ -155,6 +156,7 @@ public sealed class EffectsUiController : IRetainedPanelController
&& !_rows.Values.Any(row => row.Slot.EntryId == selected))
_selectedSpellId = null;
SyncSelection();
UpdateInfoText();
}
private IEnumerable<ActiveEnchantmentRecord> VisibleEnchantments()
@ -189,6 +191,7 @@ public sealed class EffectsUiController : IRetainedPanelController
// token's spell stat, not its enchantment layer identity.
_selectedSpellId = _selectedSpellId == spellId ? null : spellId;
SyncSelection();
UpdateInfoText();
}
private void SyncSelection()
@ -197,30 +200,39 @@ public sealed class EffectsUiController : IRetainedPanelController
row.Slot.SetSelected(row.Slot.EntryId == _selectedSpellId);
}
private void ConfigureInfo()
private UiTextLayoutCache<string>? ConfigureInfo()
{
if (_info is null) return;
if (_info is null) return null;
_info.PreserveEndOnLayout = false;
_info.WheelScrollEnabled = true;
_info.ClickThrough = false;
if (_infoScrollbar is not null)
_infoScrollbar.Model = _info.Scroll;
_info.LinesProvider = () =>
var cache = new UiTextLayoutCache<string>(
_info,
static (target, value) => IndicatorDetailText.Shape(target, value),
_selectPrompt,
StringComparer.Ordinal);
_info.LinesProvider = cache.Provider;
return cache;
}
private void UpdateInfoText()
{
if (_infoLayout is null)
return;
string value = _selectPrompt;
if (_selectedSpellId is uint spellId
&& _spellbook.ActiveEnchantmentSnapshot.Any(
record => record.SpellId == spellId)
&& _spellbook.TryGetMetadata(spellId, out SpellMetadata metadata))
{
if (_selectedSpellId is not uint spellId)
return IndicatorDetailText.Shape(_info, _selectPrompt);
ActiveEnchantmentRecord? selected = _spellbook.ActiveEnchantmentSnapshot
.Cast<ActiveEnchantmentRecord?>()
.FirstOrDefault(record => record?.SpellId == spellId);
if (selected is null || !_spellbook.TryGetMetadata(selected.Value.SpellId, out SpellMetadata metadata))
return IndicatorDetailText.Shape(_info, _selectPrompt);
// gmEffectsUI::UpdateSelection @ 0x004B7F90 supplies one literal
// name + blank line + description to UIElement_Text::SetText. The
// text widget reflows it to the authored information width.
return IndicatorDetailText.Shape(
_info,
metadata.Name + "\n\n" + metadata.Description);
};
// name + blank line + description to UIElement_Text::SetText.
value = metadata.Name + "\n\n" + metadata.Description;
}
_infoLayout.SetValue(value);
}
public void OnShown() => Rebuild();