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
62
src/AcDream.Core/Items/StackSplitQuantityState.cs
Normal file
62
src/AcDream.Core/Items/StackSplitQuantityState.cs
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace AcDream.Core.Items;
|
||||
|
||||
/// <summary>
|
||||
/// Shared owner for retail <c>GenItemHolder::splitSize</c> and
|
||||
/// <c>GenItemHolder::maxSplitSize</c>. The toolbar entry/slider edits this state and
|
||||
/// item-holder operations consume the same value.
|
||||
/// </summary>
|
||||
public sealed class StackSplitQuantityState
|
||||
{
|
||||
public uint Value { get; private set; } = 1u;
|
||||
public uint Maximum { get; private set; } = 1u;
|
||||
public float Ratio => Value / (float)Maximum;
|
||||
|
||||
public event Action? Changed;
|
||||
|
||||
public void Reset(uint stackSize, uint? initialValue = null)
|
||||
{
|
||||
uint maximum = Math.Max(stackSize, 1u);
|
||||
Set(initialValue ?? maximum, maximum);
|
||||
}
|
||||
|
||||
public void SetFromText(string? text)
|
||||
{
|
||||
// Retail uses wcstoul, whose invalid/empty result is zero, then clamps to one.
|
||||
uint parsed = uint.TryParse(text, NumberStyles.None, CultureInfo.InvariantCulture, out uint value)
|
||||
? value
|
||||
: 0u;
|
||||
SetValue(parsed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ports <c>gmToolbarUI::ListenToElementMessage @ 0x004BEFE0</c> and
|
||||
/// <c>UIElement_Scrollbar::SetScrollbarPosition @ 0x00470EC0</c>. The scrollbar
|
||||
/// first truncates its normalized position to an integer in [0,1000], then the
|
||||
/// toolbar converts it to <c>1 + floor(position * max / 1000)</c> and clamps.
|
||||
/// </summary>
|
||||
public void SetFromSliderRatio(float ratio)
|
||||
{
|
||||
float clamped = Math.Clamp(ratio, 0f, 1f);
|
||||
uint positionMillis = (uint)MathF.Truncate(clamped * 1000f);
|
||||
ulong scaled = (ulong)positionMillis * Maximum;
|
||||
uint value = 1u + (uint)(scaled / 1000u);
|
||||
SetValue(value);
|
||||
}
|
||||
|
||||
public void SetValue(uint value) => Set(value, Maximum);
|
||||
|
||||
private void Set(uint value, uint maximum)
|
||||
{
|
||||
maximum = Math.Max(maximum, 1u);
|
||||
value = Math.Clamp(value, 1u, maximum);
|
||||
if (Value == value && Maximum == maximum)
|
||||
return;
|
||||
|
||||
Value = value;
|
||||
Maximum = maximum;
|
||||
Changed?.Invoke();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue