feat(ui): port retail selected-stack quantity
Bind the authored stack count entry and horizontal slider to one Core split-quantity owner, preserve retail count-first naming and exact 1000-step rounding, refresh on stack changes, and consume the selected amount during merges. Conformance covers the production DAT fixture and retained pointer/focus paths. Co-Authored-By: Codex <codex@openai.com>
This commit is contained in:
parent
dc1649c493
commit
a20e5c68c7
19 changed files with 595 additions and 51 deletions
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using System.Numerics;
|
||||
using AcDream.App.UI;
|
||||
using AcDream.App.UI.Layout;
|
||||
using AcDream.Core.Items;
|
||||
using AcDream.Core.Selection;
|
||||
using Xunit;
|
||||
|
||||
|
|
@ -59,6 +60,14 @@ public class SelectedObjectControllerTests
|
|||
dict[SelectedObjectController.HealthMeterId] = healthMeterEl;
|
||||
root.AddChild(healthMeterEl);
|
||||
|
||||
var stackEntry = new UiField { Width = 50, Height = 14, Visible = true };
|
||||
dict[SelectedObjectController.StackSizeEntryId] = stackEntry;
|
||||
root.AddChild(stackEntry);
|
||||
|
||||
var stackSlider = new UiScrollbar { Width = 90, Height = 14, Visible = true };
|
||||
dict[SelectedObjectController.StackSizeSliderId] = stackSlider;
|
||||
root.AddChild(stackSlider);
|
||||
|
||||
return (new ImportedLayout(root, dict), nameEl, overlayEl, healthMeterEl);
|
||||
}
|
||||
|
||||
|
|
@ -67,7 +76,9 @@ public class SelectedObjectControllerTests
|
|||
private sealed class Harness
|
||||
{
|
||||
public readonly SelectionState Selection = new();
|
||||
public readonly StackSplitQuantityState SplitQuantity = new();
|
||||
public Action<uint, float>? HealthHandler;
|
||||
public Action<ClientObject>? ObjectUpdatedHandler;
|
||||
public readonly List<uint> QueryHealthCalls = new();
|
||||
|
||||
public readonly Dictionary<uint, bool> HealthTargetMap = new();
|
||||
|
|
@ -100,7 +111,13 @@ public class SelectedObjectControllerTests
|
|||
hasHealth: g => HasHealthMap.TryGetValue(g, out var v) && v,
|
||||
stackSize: g => StackMap.TryGetValue(g, out var v) ? v : 0u,
|
||||
sendQueryHealth: g => QueryHealthCalls.Add(g),
|
||||
datFont: datFont);
|
||||
datFont: datFont,
|
||||
splitQuantity: SplitQuantity,
|
||||
subscribeObjectUpdated: h => ObjectUpdatedHandler = h,
|
||||
unsubscribeObjectUpdated: h =>
|
||||
{
|
||||
if (ObjectUpdatedHandler == h) ObjectUpdatedHandler = null;
|
||||
});
|
||||
}
|
||||
|
||||
// ── B1: Bind initialisation ──────────────────────────────────────────────
|
||||
|
|
@ -247,6 +264,58 @@ public class SelectedObjectControllerTests
|
|||
Assert.False(healthMeterEl.Visible);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void SelectStackedItem_showsRetailCountEntryAndSlider_atFullStack()
|
||||
{
|
||||
const uint Guid = 0xBB03u;
|
||||
var (layout, nameEl, _, _) = FakeLayout();
|
||||
var h = new Harness();
|
||||
h.NameMap[Guid] = "Healing Kits";
|
||||
h.StackMap[Guid] = 17u;
|
||||
h.Bind(layout);
|
||||
|
||||
h.FireSelection(Guid);
|
||||
|
||||
var entry = Assert.IsType<UiField>(layout.FindElement(SelectedObjectController.StackSizeEntryId));
|
||||
var slider = Assert.IsType<UiScrollbar>(layout.FindElement(SelectedObjectController.StackSizeSliderId));
|
||||
Assert.True(entry.Visible);
|
||||
Assert.True(slider.Visible);
|
||||
Assert.Equal("17", entry.Text);
|
||||
Assert.Equal(17u, h.SplitQuantity.Value);
|
||||
Assert.Equal(17u, h.SplitQuantity.Maximum);
|
||||
Assert.Equal(1f, slider.ScalarValue!());
|
||||
Assert.Equal("17 Healing Kits", nameEl.Children.OfType<UiText>().Single().LinesProvider().Single().Text);
|
||||
|
||||
slider.ScalarChanged!(0.5f);
|
||||
Assert.Equal(9u, h.SplitQuantity.Value);
|
||||
Assert.Equal("9", entry.Text);
|
||||
|
||||
entry.SetText("999");
|
||||
entry.Submit();
|
||||
Assert.Equal(17u, h.SplitQuantity.Value);
|
||||
Assert.Equal("17", entry.Text);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void StackSizeUpdate_refreshesSelectedControls_andHidesThemAtOne()
|
||||
{
|
||||
const uint Guid = 0xBB04u;
|
||||
var (layout, _, _, _) = FakeLayout();
|
||||
var h = new Harness();
|
||||
h.NameMap[Guid] = "Healing Kits";
|
||||
h.StackMap[Guid] = 5u;
|
||||
h.Bind(layout);
|
||||
h.FireSelection(Guid);
|
||||
|
||||
h.StackMap[Guid] = 1u;
|
||||
h.ObjectUpdatedHandler!(new ClientObject { ObjectId = Guid, StackSize = 1 });
|
||||
|
||||
Assert.False(layout.FindElement(SelectedObjectController.StackSizeEntryId)!.Visible);
|
||||
Assert.False(layout.FindElement(SelectedObjectController.StackSizeSliderId)!.Visible);
|
||||
Assert.Equal(1u, h.SplitQuantity.Value);
|
||||
Assert.Equal(1u, h.SplitQuantity.Maximum);
|
||||
}
|
||||
|
||||
// ── H3: Non-health target (friendly NPC / scenery / Door) ───────────────
|
||||
|
||||
[Fact]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue