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>
This commit is contained in:
Sam
2026-08-14 19:38:18 +07:00
co-authored by Claude Fable 5
parent 606a72bde5
commit 2c1ffca2ca
57 changed files with 2163 additions and 275 deletions
+103 -22
View File
@@ -19,11 +19,15 @@
for GossipFrame/QuestFrame/QuestLogFrame ("our engine ships no UIPanelButtonTemplate, so we
draw it") — this is one more independent draw of the same real art.
NAMED DEFERRALS (both cited on the functions below, not just here):
- Typed-digit entry (`StackSplitFrame_OnChar`/`_OnKeyDown` in the ref): this engine has no
plain-frame OnChar/OnKeyDown keyboard capture, only EditBox focus (grepped; no such hook
exists anywhere in benilla-ui). The Left/Right spinner buttons are the only way to change
the split count.
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
@@ -51,6 +55,10 @@
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()
@@ -92,26 +100,15 @@
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()
local frame = StackSplitFrame
if frame.split == 1 then return end
frame.split = frame.split - 1
StackSplitText:SetText(frame.split)
if frame.split == 1 then
StackSplitLeftButton:Disable()
end
StackSplitRightButton:Enable()
BenillaStackSplitSet(StackSplitFrame.split - 1)
end
function BenillaStackSplitRight_Click()
local frame = StackSplitFrame
if frame.split == frame.maxStack then return end
frame.split = frame.split + 1
StackSplitText:SetText(frame.split)
if frame.split == frame.maxStack then
StackSplitRightButton:Disable()
end
StackSplitLeftButton:Enable()
BenillaStackSplitSet(StackSplitFrame.split + 1)
end
function BenillaStackSplitOkay_Click()
@@ -126,6 +123,81 @@
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
@@ -152,7 +224,14 @@
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
</Button>
<Frame name="StackSplitFrame" frameStrata="HIGH" hidden="true">
<!-- 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>
@@ -228,6 +307,8 @@
</Frames>
<Scripts>
<OnHide>StackSplitFrame_OnHide()</OnHide>
<OnChar>StackSplitFrame_OnChar()</OnChar>
<OnKeyDown>StackSplitFrame_OnKeyDown()</OnKeyDown>
</Scripts>
</Frame>
</Ui>