Files
benilla-octo/crates/benilla-app/assets/ui/StackSplit.xml
T
SamandClaude Fable 5 2c1ffca2ca dialogs come to the front, and the red line names what it needs
- 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>
2026-08-14 19:38:18 +07:00

315 lines
17 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>