feat(ui): D.2b inventory finish — bind contents-grid gutter scrollbar

InventoryController binds 0x100001C7 (factory Type-11 UiScrollbar) to the
contents grid's UiScrollable + the shared 0x2100003E scrollbar sprites,
mirroring ChatWindowController. The grid now scrolls instead of overflowing.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Erik 2026-06-21 20:29:53 +02:00
parent 0d3117596c
commit fad807587d
2 changed files with 38 additions and 2 deletions

View file

@ -20,6 +20,16 @@ public sealed class InventoryController
public const uint BurdenTextId = 0x100001D8u; // gmBackpackUI m_burdenText ("%d%%") public const uint BurdenTextId = 0x100001D8u; // gmBackpackUI m_burdenText ("%d%%")
public const uint BurdenCaptionId = 0x100001D7u; // "Burden" public const uint BurdenCaptionId = 0x100001D7u; // "Burden"
public const uint ContentsCaptionId = 0x100001C5u; // "Contents of Backpack" public const uint ContentsCaptionId = 0x100001C5u; // "Contents of Backpack"
public const uint ContentsScrollbarId = 0x100001C7u; // gm3DItemsUI gutter scrollbar (right of the grid)
// Scrollbar chrome from base layout 0x2100003E (shared with the chat scrollbar; see
// ChatWindowController). Both inventory + chat scrollbars inherit this base.
private const uint ScrollTrackSprite = 0x06004C5Fu;
private const uint ScrollThumbSprite = 0x06004C63u; // 3-slice middle tile
private const uint ScrollThumbTop = 0x06004C60u; // 3-slice top cap
private const uint ScrollThumbBot = 0x06004C66u; // 3-slice bottom cap
private const uint ScrollUpSprite = 0x06004C6Cu; // up arrow (top button)
private const uint ScrollDownSprite = 0x06004C69u; // down arrow (bottom button)
// 3D-items grid: 192x96 → 6 cols x 3 rows of the 32x32 UIItem cell (template 0x21000037). // 3D-items grid: 192x96 → 6 cols x 3 rows of the 32x32 UIItem cell (template 0x21000037).
private const int ContentsColumns = 6; private const int ContentsColumns = 6;
@ -66,6 +76,21 @@ public sealed class InventoryController
_contentsGrid.CellWidth = CellPx; _contentsGrid.CellWidth = CellPx;
_contentsGrid.CellHeight = CellPx; _contentsGrid.CellHeight = CellPx;
} }
// Bind the gutter scrollbar to the contents grid's scroll model (the factory built
// 0x100001C7 as a bare Type-11 UiScrollbar; wire it like ChatWindowController does).
if (_contentsGrid is not null
&& layout.FindElement(ContentsScrollbarId) is UiScrollbar bar)
{
bar.Model = _contentsGrid.Scroll;
bar.SpriteResolve = _contentsGrid.SpriteResolve; // chrome resolve from the factory ctor
bar.TrackSprite = ScrollTrackSprite;
bar.ThumbSprite = ScrollThumbSprite;
bar.ThumbTopSprite = ScrollThumbTop;
bar.ThumbBotSprite = ScrollThumbBot;
bar.UpSprite = ScrollUpSprite;
bar.DownSprite = ScrollDownSprite;
}
if (_containerList is not null) if (_containerList is not null)
{ {
_containerList.Columns = 1; _containerList.Columns = 1;

View file

@ -22,6 +22,7 @@ public class InventoryControllerTests
private const uint BurdenText = 0x100001D8u; private const uint BurdenText = 0x100001D8u;
private const uint BurdenCaption = 0x100001D7u; private const uint BurdenCaption = 0x100001D7u;
private const uint ContentsCaption = 0x100001C5u; private const uint ContentsCaption = 0x100001C5u;
private const uint ContentsScrollbar = 0x100001C7u;
private static (ImportedLayout layout, UiItemList grid, UiItemList containers, private static (ImportedLayout layout, UiItemList grid, UiItemList containers,
UiItemList top, UiMeter meter, UiElement burdenText, UiItemList top, UiMeter meter, UiElement burdenText,
@ -34,15 +35,16 @@ public class InventoryControllerTests
var burdenText = new TestElement { Width = 36, Height = 15 }; var burdenText = new TestElement { Width = 36, Height = 15 };
var burdenCap = new TestElement { Width = 36, Height = 15 }; var burdenCap = new TestElement { Width = 36, Height = 15 };
var contentsCap = new TestElement { Width = 192, Height = 15 }; var contentsCap = new TestElement { Width = 192, Height = 15 };
var scrollbar = new UiScrollbar { Width = 16, Height = 96 };
var root = new TestElement { Width = 300, Height = 362 }; var root = new TestElement { Width = 300, Height = 362 };
root.AddChild(grid); root.AddChild(containers); root.AddChild(top); root.AddChild(grid); root.AddChild(containers); root.AddChild(top);
root.AddChild(meter); root.AddChild(burdenText); root.AddChild(burdenCap); root.AddChild(meter); root.AddChild(burdenText); root.AddChild(burdenCap);
root.AddChild(contentsCap); root.AddChild(contentsCap); root.AddChild(scrollbar);
var byId = new Dictionary<uint, UiElement> var byId = new Dictionary<uint, UiElement>
{ {
[ContentsGrid] = grid, [ContainerList] = containers, [TopContainer] = top, [ContentsGrid] = grid, [ContainerList] = containers, [TopContainer] = top,
[BurdenMeter] = meter, [BurdenText] = burdenText, [BurdenCaption] = burdenCap, [BurdenMeter] = meter, [BurdenText] = burdenText, [BurdenCaption] = burdenCap,
[ContentsCaption] = contentsCap, [ContentsCaption] = contentsCap, [ContentsScrollbar] = scrollbar,
}; };
return (new ImportedLayout(root, byId), grid, containers, top, meter, return (new ImportedLayout(root, byId), grid, containers, top, meter,
burdenText, burdenCap, contentsCap); burdenText, burdenCap, contentsCap);
@ -192,6 +194,15 @@ public class InventoryControllerTests
Assert.Equal(0.2f, meter.Fill() ?? -1f, 3); // 9000/15000/3 Assert.Equal(0.2f, meter.Fill() ?? -1f, 3); // 9000/15000/3
} }
[Fact]
public void Contents_grid_scrollbar_binds_to_the_grid_scroll_model()
{
var (layout, grid, _, _, _, _, _, _) = BuildLayout();
Bind(layout, new ClientObjectTable());
var bar = (UiScrollbar)layout.FindElement(ContentsScrollbar)!;
Assert.Same(grid.Scroll, bar.Model); // the bar drives the grid's scroll
}
// Reads the text of the UiText caption child attached by the controller. // Reads the text of the UiText caption child attached by the controller.
private static string CaptionText(UiElement host) private static string CaptionText(UiElement host)
{ {