fix(ui): restore effect detail scrolling

Reflow selected Helpful/Harmful spell descriptions through the retained retail text shaper, bind their authored 0x10000127 information scrollbar independently from the list scrollbar, and allow display-only text to consume wheel scrolling without becoming selectable.

Format Vitae recovery experience through retail's grouped integer presentation. Release build succeeds and all 5,832 tests pass with five intentional skips.

Co-authored-by: OpenAI Codex <codex@openai.com>
This commit is contained in:
Erik 2026-07-17 13:28:18 +02:00
parent 16c21e299c
commit 82789eea88
9 changed files with 117 additions and 18 deletions

View file

@ -107,6 +107,58 @@ public sealed class EffectsUiControllerTests
Assert.False(second.Selected);
}
[Theory]
[InlineData(true)]
[InlineData(false)]
public void SelectedEffect_WrapsDescription_AndBindsAuthoredInfoScrollbar(
bool positive)
{
string flags = positive ? "0x4" : "0x0";
var table = SpellTable.LoadFromReader(new StringReader(
"Spell ID,Name,Description,Flags [Hex]\n"
+ $"42,Boon,This description contains enough words to wrap across several lines.,{flags}\n"));
var spellbook = new Spellbook(table);
ImportedLayout layout = LayoutImporter.Build(
positive
? FixtureLoader.LoadPositiveEffectsInfos()
: FixtureLoader.LoadNegativeEffectsInfos(),
NoTex,
datFont: null);
UiText info = Assert.IsType<UiText>(
layout.FindElement(EffectsUiController.InfoTextId));
info.Width = 96f;
using EffectsUiController controller = EffectsUiController.Bind(
layout, spellbook, positive, () => 0d, NoTex, id => id,
Templates(), "SELECT A SPELL")!;
spellbook.OnEnchantmentAdded(new ActiveEnchantmentRecord(
42u, 1u, 60f, 1u, Bucket: 1u, SpellCategory: 42u));
UiElement listHost = Assert.IsType<UiDatElement>(
layout.FindElement(EffectsUiController.ListId));
UiItemList list = Assert.IsType<UiItemList>(Assert.Single(listHost.Children));
UiTemplateListSlot row = Assert.IsType<UiTemplateListSlot>(list.GetItem(0));
row.OnEvent(new UiEvent(0, row, UiEventType.Click));
IReadOnlyList<UiText.Line> lines = info.LinesProvider();
Assert.Equal("Boon", lines[0].Text);
Assert.Equal(string.Empty, lines[1].Text);
Assert.True(lines.Count > 3);
Assert.All(lines, line => Assert.True(line.Text.Length <= 12));
Assert.True(info.WheelScrollEnabled);
Assert.False(info.ClickThrough);
UiScrollbar listScrollbar = Assert.IsType<UiScrollbar>(
layout.FindElement(EffectsUiController.ListScrollbarId));
UiScrollbar infoScrollbar = Assert.IsType<UiScrollbar>(
layout.FindElement(EffectsUiController.InfoScrollbarId));
Assert.Same(list.Scroll, listScrollbar.Model);
Assert.Same(info.Scroll, infoScrollbar.Model);
info.Scroll.SetExtents(contentHeight: 100, viewHeight: 20);
Assert.True(info.OnEvent(new UiEvent(
0, info, UiEventType.Scroll, Data0: -1)));
Assert.Equal(info.Scroll.LineHeight, info.Scroll.ScrollY);
}
[Fact]
public void PermanentEffect_LeavesRetailDurationColumnEmpty()
{
@ -179,7 +231,8 @@ public sealed class EffectsUiControllerTests
Assert.NotNull(controller);
Assert.NotNull(layout.FindElement(EffectsUiController.ListId));
Assert.IsType<UiText>(layout.FindElement(EffectsUiController.InfoTextId));
Assert.IsType<UiScrollbar>(layout.FindElement(EffectsUiController.ScrollbarId));
Assert.IsType<UiScrollbar>(layout.FindElement(EffectsUiController.ListScrollbarId));
Assert.IsType<UiScrollbar>(layout.FindElement(EffectsUiController.InfoScrollbarId));
Assert.Equal(expectedRoot, layout.Root.DatElementId);
UiButton close = Assert.IsType<UiButton>(layout.FindElement(EffectsUiController.CloseId));
close.OnEvent(new UiEvent(0, close, UiEventType.Click));