- dialogs carry the reference's toplevel bit, so the stack-split window opens above Bagnon instead of behind it - the failure line fills its blank — "Requires Blacksmith Hammer", not "Requires" — and Opening casts stop printing a label the spell itself marks "no text" - the pet bar's Attack/Follow/Stay tooltips name their keybinding, and typed text reaches the focused frame through the client's own delivery walk - AtlasLoot parses again: our Lua keeps 5.0's compatibility semicolon Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
315 lines
17 KiB
XML
315 lines
17 KiB
XML
<Ui>
|
||
<!--
|
||
The stack-split spinner (decision 0216 §6/§D, scratchpad `ref-framexml/StackSplitFrame.xml`/
|
||
`.lua`, re-expressed onto our own cursor seam — `SplitContainerItem`/
|
||
`OpenStackSplitFrame` already exist in benilla-ui's `cursor.rs`; this file is purely the
|
||
window + the Left/Right/Okay/Cancel plumbing around it). One shared instance, like the real
|
||
client's `StackSplitFrame` — any owner (a bag slot today; the paper doll / bank / mail slices
|
||
reuse it unmodified later) sets `owner.SplitStack = function(owner, n) ... end` before calling
|
||
`OpenStackSplitFrame`, and Okay calls it generically (`StackSplitFrameOkay_Click`).
|
||
|
||
Textures: `Interface\MoneyFrame\UI-MoneyFrame` (the little parchment plate) and
|
||
`Interface\MoneyFrame\Arrow-Left/Right-Up/Down/Disabled` are the ref's own — both already
|
||
resolve through the shared sprite loader the money-purse coin icons use
|
||
(`Interface\MoneyFrame\UI-MoneyIcons`, BagFrame.xml/MerchantFrame.xml), so they're quoted
|
||
verbatim rather than substituted. The Okay/Cancel button face (`UI-Panel-Button-*`) is our
|
||
own local virtual template, NOT a cross-file `inherits` of `BenillaQuestPanelButtonTemplate`
|
||
(QuestFrame.xml) — this file loads BEFORE QuestFrame.xml (see ui_script/mod.rs's load order),
|
||
so that template isn't registered yet; the house convention already answered this exact case
|
||
for GossipFrame/QuestFrame/QuestLogFrame ("our engine ships no UIPanelButtonTemplate, so we
|
||
draw it") — this is one more independent draw of the same real art.
|
||
|
||
TYPED-DIGIT ENTRY IS LIVE (decision 1319). It used to be this file's headline deferral
|
||
("this engine has no plain-frame OnChar/OnKeyDown capture, only EditBox focus"), and the
|
||
engine now has the delivery walk that unblocks it (`script::keyboard`, off wow-re's
|
||
byte-verified `frame-key-script-delivery.md`). The handlers below are OUR OWN spinner
|
||
logic, written from the widget's behaviour, not a transcription of the reference's Lua
|
||
(their text is never copied into this repo). `enableKeyboard` on the frame is
|
||
what puts it in the walk's bucket at all.
|
||
|
||
NAMED DEFERRAL:
|
||
- The ref's shift-click "insert the item link into the chat editbox" (StackSplitFrame isn't
|
||
even opened in that branch — ContainerFrame.lua:568-569 diverts to
|
||
`ChatFrameEditBox:Insert` instead of `OpenStackSplitFrame`) never triggers here: the
|
||
shift-click gesture itself has no plumbing in this engine (BagFrame.xml's own comment on
|
||
`BenillaBagSlot_OnClick` — no modifier-key global exists at all), so there is no path that
|
||
reaches this branch to defer. `EditBox:Insert` DOES exist on our engine (editbox.rs) for
|
||
when that gap closes.
|
||
-->
|
||
<Script><![CDATA[
|
||
-- OpenStackSplitFrame(maxStack, owner, anchor, anchorTo) — ref StackSplitFrame.lua:2-24,
|
||
-- verbatim shape. `owner` must already carry `owner.SplitStack = function(owner, n) ... end`
|
||
-- (BagFrame.xml's `BenillaBagSlot_OnClick` sets it right before calling this, mirroring
|
||
-- ContainerFrameItemButton_OnClick's own closure). maxStack < 2 hides instead of showing (a
|
||
-- 1-stack has nothing to split) — the ref's own guard.
|
||
function OpenStackSplitFrame(maxStack, owner, anchor, anchorTo)
|
||
local frame = StackSplitFrame
|
||
if frame.owner then
|
||
frame.owner.hasStackSplit = nil
|
||
end
|
||
frame.maxStack = maxStack
|
||
if frame.maxStack < 2 then
|
||
frame:Hide()
|
||
return
|
||
end
|
||
frame.owner = owner
|
||
owner.hasStackSplit = true
|
||
frame.split = 1
|
||
-- `typed` distinguishes "1 because nothing has been typed yet" from "1 because the
|
||
-- player typed it": the first digit REPLACES the seed, later digits append. Cleared on
|
||
-- every open, so a dialog reopened after typing starts fresh.
|
||
frame.typed = nil
|
||
StackSplitText:SetText(frame.split)
|
||
StackSplitLeftButton:Disable()
|
||
StackSplitRightButton:Enable()
|
||
-- ref l.21-22: ClearAllPoints first — SetPoint only replaces an anchor of the SAME
|
||
-- point, and the anchor callers choose (BagFrame.xml passes "BOTTOMRIGHT") differs from
|
||
-- this frame's authored placeholder ("CENTER"), so skipping this would leave both live.
|
||
frame:ClearAllPoints()
|
||
frame:SetPoint(anchor, owner, anchorTo, 0, 0)
|
||
frame:Show()
|
||
end
|
||
|
||
-- UpdateStackSplitFrame(maxStack) — ref l.26-52: the owner's stack shrank/grew out from
|
||
-- under an OPEN spinner (e.g. a server correction). Not called by anything in benilla yet
|
||
-- (no caller has a live reason to resize an open spinner today) — kept so a later caller
|
||
-- (the paper doll / bank slice) drops in without a new function.
|
||
function UpdateStackSplitFrame(maxStack)
|
||
local frame = StackSplitFrame
|
||
frame.maxStack = maxStack
|
||
if frame.maxStack < 2 then
|
||
if frame.owner then
|
||
frame.owner.hasStackSplit = nil
|
||
end
|
||
frame:Hide()
|
||
return
|
||
end
|
||
if frame.split > frame.maxStack then
|
||
frame.split = frame.maxStack
|
||
StackSplitText:SetText(frame.split)
|
||
end
|
||
if frame.split == frame.maxStack then
|
||
StackSplitRightButton:Disable()
|
||
else
|
||
StackSplitRightButton:Enable()
|
||
end
|
||
if frame.split == 1 then
|
||
StackSplitLeftButton:Disable()
|
||
else
|
||
StackSplitLeftButton:Enable()
|
||
end
|
||
end
|
||
|
||
-- The arrows go through the shared setter (below) rather than nudging the count and the
|
||
-- two buttons themselves: with typed entry live there are now three writers of
|
||
-- `frame.split`, and three copies of the enable/disable rule is how they drift apart.
|
||
function BenillaStackSplitLeft_Click()
|
||
BenillaStackSplitSet(StackSplitFrame.split - 1)
|
||
end
|
||
|
||
function BenillaStackSplitRight_Click()
|
||
BenillaStackSplitSet(StackSplitFrame.split + 1)
|
||
end
|
||
|
||
function BenillaStackSplitOkay_Click()
|
||
local frame = StackSplitFrame
|
||
if frame.owner then
|
||
frame.owner.SplitStack(frame.owner, frame.split)
|
||
end
|
||
frame:Hide()
|
||
end
|
||
|
||
function BenillaStackSplitCancel_Click()
|
||
StackSplitFrame:Hide()
|
||
end
|
||
|
||
-- The one place the count is written, so the buttons' enable state and the label can
|
||
-- never disagree with `frame.split` (the arrow handlers below call it too). Clamped to
|
||
-- [1, maxStack] because every caller can overshoot: typing 9 into a 5-stack, or the
|
||
-- arrows at their ends.
|
||
function BenillaStackSplitSet(n)
|
||
local frame = StackSplitFrame
|
||
if n < 1 then n = 1 end
|
||
if n > frame.maxStack then n = frame.maxStack end
|
||
frame.split = n
|
||
StackSplitText:SetText(n)
|
||
if n <= 1 then
|
||
StackSplitLeftButton:Disable()
|
||
else
|
||
StackSplitLeftButton:Enable()
|
||
end
|
||
if n >= frame.maxStack then
|
||
StackSplitRightButton:Disable()
|
||
else
|
||
StackSplitRightButton:Enable()
|
||
end
|
||
end
|
||
|
||
-- Typed digits (decision 1319). The engine hands OnChar the literal character, so this is
|
||
-- a digit test and an append: the first keystroke replaces the seeded 1, each later one
|
||
-- shifts a decimal place in. A digit that would overshoot the stack is CLAMPED rather than
|
||
-- rejected -- typing 9 against a 5-stack leaves 5, which is what the player meant -- and
|
||
-- clamping keeps `typed` set, so a following digit still appends against the clamp.
|
||
-- Anything that is not a digit is ignored here; the frame consumes the key regardless
|
||
-- (the engine's existence gate decides that, not this handler's return).
|
||
function StackSplitFrame_OnChar()
|
||
local c = arg1
|
||
if c < "0" or c > "9" then return end
|
||
local frame = StackSplitFrame
|
||
local digit = tonumber(c)
|
||
local n
|
||
if frame.typed then
|
||
n = frame.split * 10 + digit
|
||
else
|
||
n = digit
|
||
end
|
||
-- A leading 0 types nothing: the count can never be 0, so it stays 1 and unlatched,
|
||
-- and the next digit still reads as the first one.
|
||
if n < 1 then return end
|
||
frame.typed = true
|
||
BenillaStackSplitSet(n)
|
||
end
|
||
|
||
-- The editing/commit keys for typed entry. BACKSPACE drops the last digit (and unlatches
|
||
-- at the end, so the next digit starts over rather than appending to a stale 1); ENTER
|
||
-- commits exactly as Okay does and ESCAPE cancels, which is what a player expects of a
|
||
-- dialog they are typing into. The arrow keys drive the spinner. Every other key is
|
||
-- swallowed by the frame -- the reference's own consequence of having a key script bound
|
||
-- at all, and the reason this dialog must not stay open invisibly.
|
||
function StackSplitFrame_OnKeyDown()
|
||
local key = arg1
|
||
local frame = StackSplitFrame
|
||
if key == "BACKSPACE" then
|
||
local n = floor(frame.split / 10)
|
||
if n < 1 then
|
||
frame.typed = nil
|
||
BenillaStackSplitSet(1)
|
||
else
|
||
BenillaStackSplitSet(n)
|
||
end
|
||
elseif key == "ENTER" then
|
||
BenillaStackSplitOkay_Click()
|
||
elseif key == "ESCAPE" then
|
||
BenillaStackSplitCancel_Click()
|
||
elseif key == "LEFT" then
|
||
BenillaStackSplitLeft_Click()
|
||
elseif key == "RIGHT" then
|
||
BenillaStackSplitRight_Click()
|
||
end
|
||
end
|
||
|
||
-- OnHide (ref l.158-162): ANY hide — Okay, Cancel, or ToggleGameMenu's chain hook below —
|
||
-- clears the owner's `hasStackSplit` flag (ref ItemButtonTemplate's OnHide, ContainerFrame.xml
|
||
-- l.35-39, checks `this.hasStackSplit` to auto-close the spinner if the SLOT itself goes away
|
||
-- mid-split — no consumer needs that half here, but the flag's own clear is this function's
|
||
-- job regardless of which button caused the hide).
|
||
function StackSplitFrame_OnHide()
|
||
local frame = StackSplitFrame
|
||
if frame.owner then
|
||
frame.owner.hasStackSplit = nil
|
||
end
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- The Okay/Cancel face — our own local draw (see the file header) of the real
|
||
`UIPanelButtonTemplate` art (`Interface\Buttons\UI-Panel-Button-*`). -->
|
||
<Button name="BenillaStackSplitButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="64" y="24"/></Size>
|
||
<NormalFont inherits="GameFontNormal"/>
|
||
<HighlightFont inherits="GameFontHighlight"/>
|
||
<DisabledFont inherits="GameFontDisable"/>
|
||
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
|
||
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
|
||
<DisabledTexture file="Interface\Buttons\UI-Panel-Button-Disabled"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></DisabledTexture>
|
||
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
|
||
</Button>
|
||
|
||
<!-- The frame attrs are the ref's own (l.4), and `toplevel` is load-bearing: Bagnon's bag
|
||
windows sit in the SAME HIGH stratum, and without the bit the dialog's Show raises
|
||
nothing — it loses the level tie to the later-shown bag window and the plate draws
|
||
UNDERNEATH the slots while the child buttons (level+1) poke through (the director's
|
||
08-14 Bagnon report; decision 1318). With the bit, Show runs the verified raise (compact,
|
||
then top-occupied+1). `enableKeyboard` alone is left off: its only consumer is the
|
||
typed-digit deferral in the file header. -->
|
||
<Frame name="StackSplitFrame" frameStrata="HIGH" parent="UIParent" toplevel="true" enableMouse="true" enableKeyboard="true" hidden="true">
|
||
<!-- ref StackSplitFrame.xml l.4-7: 172×96. The CENTER anchor here is only the pre-first-open
|
||
placeholder — OpenStackSplitFrame always repositions before the first Show. -->
|
||
<Size><AbsDimension x="172" y="96"/></Size>
|
||
<Anchors>
|
||
<Anchor point="CENTER" relativePoint="CENTER"/>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- ref l.9-15 verbatim: the plate, authored 256×32 with NO anchors. The size is
|
||
VESTIGIAL and stays that way by the byte-verified law (decision 1310): an
|
||
anchor-less region gets the creation-path implicit SetAllPoints(parent), whose
|
||
two corners pin all four edges and leave the authored size unread — the
|
||
TexCoords crop exactly 172×96 out of the 256×128 art, the frame's own size,
|
||
drawn 1:1 filling the dialog. 1308 dropped this size to dodge the old centered
|
||
fallback (B180's squashed bar); with the fallback corrected the ref's own text
|
||
is back, and the bag test pins the render either way. -->
|
||
<Texture file="Interface\MoneyFrame\UI-MoneyFrame">
|
||
<Size><AbsDimension x="256" y="32"/></Size>
|
||
<TexCoords left="0" right="0.671875" top="0" bottom="0.75"/>
|
||
</Texture>
|
||
<!-- ref l.16-24: the count, GameFontHighlight, right-justified, RIGHT -50/+18. -->
|
||
<FontString name="StackSplitText" inherits="GameFontHighlight" justifyH="RIGHT">
|
||
<Anchors>
|
||
<Anchor point="RIGHT" relativePoint="RIGHT"><Offset><AbsDimension x="-50" y="18"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- ref l.28-47: the left/right arrows, 16×16, RIGHT/LEFT-of-CENTER ±59/64, +18. -->
|
||
<Button name="StackSplitLeftButton">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="RIGHT" relativePoint="CENTER"><Offset><AbsDimension x="-59" y="18"/></Offset></Anchor>
|
||
</Anchors>
|
||
<NormalTexture file="Interface\MoneyFrame\Arrow-Left-Up"/>
|
||
<PushedTexture file="Interface\MoneyFrame\Arrow-Left-Down"/>
|
||
<DisabledTexture file="Interface\MoneyFrame\Arrow-Left-Disabled"/>
|
||
<Scripts>
|
||
<OnClick>BenillaStackSplitLeft_Click()</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<Button name="StackSplitRightButton">
|
||
<Size><AbsDimension x="16" y="16"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativePoint="CENTER"><Offset><AbsDimension x="64" y="18"/></Offset></Anchor>
|
||
</Anchors>
|
||
<NormalTexture file="Interface\MoneyFrame\Arrow-Right-Up"/>
|
||
<PushedTexture file="Interface\MoneyFrame\Arrow-Right-Down"/>
|
||
<DisabledTexture file="Interface\MoneyFrame\Arrow-Right-Disabled"/>
|
||
<Scripts>
|
||
<OnClick>BenillaStackSplitRight_Click()</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- ref l.68-84: Okay, RIGHT-of-BOTTOM -3/+32. -->
|
||
<Button name="StackSplitButton1" inherits="BenillaStackSplitButtonTemplate" text="OKAY">
|
||
<Anchors>
|
||
<Anchor point="RIGHT" relativePoint="BOTTOM"><Offset><AbsDimension x="-3" y="32"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnClick>BenillaStackSplitOkay_Click()</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- ref l.85-101: Cancel, LEFT-of-BOTTOM +5/+32. -->
|
||
<Button name="StackSplitButton2" inherits="BenillaStackSplitButtonTemplate" text="CANCEL">
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativePoint="BOTTOM"><Offset><AbsDimension x="5" y="32"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnClick>BenillaStackSplitCancel_Click()</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnHide>StackSplitFrame_OnHide()</OnHide>
|
||
<OnChar>StackSplitFrame_OnChar()</OnChar>
|
||
<OnKeyDown>StackSplitFrame_OnKeyDown()</OnKeyDown>
|
||
</Scripts>
|
||
</Frame>
|
||
</Ui>
|