From fad807587dc7b1bc9d7547c28f39f27ef2202003 Mon Sep 17 00:00:00 2001 From: Erik Date: Sun, 21 Jun 2026 20:29:53 +0200 Subject: [PATCH] =?UTF-8?q?feat(ui):=20D.2b=20inventory=20finish=20?= =?UTF-8?q?=E2=80=94=20bind=20contents-grid=20gutter=20scrollbar?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .../UI/Layout/InventoryController.cs | 25 +++++++++++++++++++ .../UI/Layout/InventoryControllerTests.cs | 15 +++++++++-- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/AcDream.App/UI/Layout/InventoryController.cs b/src/AcDream.App/UI/Layout/InventoryController.cs index 41fd325a..9aa6ac45 100644 --- a/src/AcDream.App/UI/Layout/InventoryController.cs +++ b/src/AcDream.App/UI/Layout/InventoryController.cs @@ -20,6 +20,16 @@ public sealed class InventoryController public const uint BurdenTextId = 0x100001D8u; // gmBackpackUI m_burdenText ("%d%%") public const uint BurdenCaptionId = 0x100001D7u; // "Burden" 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). private const int ContentsColumns = 6; @@ -66,6 +76,21 @@ public sealed class InventoryController _contentsGrid.CellWidth = 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) { _containerList.Columns = 1; diff --git a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs index d1fb4746..53844cd5 100644 --- a/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs +++ b/tests/AcDream.App.Tests/UI/Layout/InventoryControllerTests.cs @@ -22,6 +22,7 @@ public class InventoryControllerTests private const uint BurdenText = 0x100001D8u; private const uint BurdenCaption = 0x100001D7u; private const uint ContentsCaption = 0x100001C5u; + private const uint ContentsScrollbar = 0x100001C7u; private static (ImportedLayout layout, UiItemList grid, UiItemList containers, UiItemList top, UiMeter meter, UiElement burdenText, @@ -34,15 +35,16 @@ public class InventoryControllerTests var burdenText = new TestElement { Width = 36, Height = 15 }; var burdenCap = new TestElement { Width = 36, 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 }; root.AddChild(grid); root.AddChild(containers); root.AddChild(top); root.AddChild(meter); root.AddChild(burdenText); root.AddChild(burdenCap); - root.AddChild(contentsCap); + root.AddChild(contentsCap); root.AddChild(scrollbar); var byId = new Dictionary { [ContentsGrid] = grid, [ContainerList] = containers, [TopContainer] = top, [BurdenMeter] = meter, [BurdenText] = burdenText, [BurdenCaption] = burdenCap, - [ContentsCaption] = contentsCap, + [ContentsCaption] = contentsCap, [ContentsScrollbar] = scrollbar, }; return (new ImportedLayout(root, byId), grid, containers, top, meter, burdenText, burdenCap, contentsCap); @@ -192,6 +194,15 @@ public class InventoryControllerTests 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. private static string CaptionText(UiElement host) {