- the social window's third tab is live — the roster in both its views, the member card with its promote/demote/remove verbs, the rank editor's thirteen permissions, the guild information board, and the invite, leave, kick and note dialogs behind them - setting your hearthstone at an innkeeper now asks before it takes and says so afterwards, and the innkeeper's gossip lines get their own icons back - unavailable talents grey out, and every greyed icon in the client now goes through the same luminance fold the original used - the buff tooltip's remaining-time line reads gold with the client's own hour/minute ladder, the macro editor takes a shift-click to paste a spell, trainer rows repaint after a purchase, and a long book page stops flickering its ellipsis Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
184 lines
9.7 KiB
XML
184 lines
9.7 KiB
XML
<!--
|
|
Our implementation of the eight `SetItemButton*` globals every 1.12 window uses to paint an item
|
|
slot: its icon, its stack count, its merchant stock line, and the four vertex-colour verbs. The
|
|
specification is the reference's `ItemButtonTemplate.lua` — the names, the argument order and the
|
|
behaviour of every branch are its contract; the code below is ours, and it is factored the way
|
|
eight near-identical verbs want to be factored (one region lookup, one tint verb) rather than the
|
|
way eight separately-written ones came out.
|
|
|
|
**Why this file exists, and what it cost to be without it.** The director installed Bagnon, hit
|
|
B, and got a frame with a title and a gold line and NO BAG SLOTS. `Bagnon_Core/core/Item.lua`
|
|
ends every slot update with
|
|
|
|
l.204 SetItemButtonDesaturated(item, locked, 0.5, 0.5, 0.5)
|
|
l.209 SetItemButtonTexture(item, texture)
|
|
l.210 SetItemButtonCount(item, itemCount)
|
|
|
|
and all three were nil. The raise lands inside `BagnonFrame_AddBag`'s per-slot loop, so the
|
|
FIRST slot aborts the loop, `frame.size` never advances, `BagnonFrame_Layout` never runs and
|
|
the window is left exactly as wide as its own border. `ui_script::bagnon_render_tests` is the
|
|
reproduction.
|
|
|
|
These are FrameXML, not engine (decision 1190's split): the real client ships them as Lua in
|
|
`Interface\FrameXML\ItemButtonTemplate.lua`, so ours belong here in our own interface rather
|
|
than in a Rust binding. The specification was read off the install's own copy with the
|
|
`mpqcat` example over `WoW/Data/interface.MPQ` — not from memory.
|
|
(An XML comment may not contain a double hyphen, which is why no command line is spelled out
|
|
here; `benilla-mpq/examples/mpqcat.rs`'s own header carries the invocation.)
|
|
|
|
**Corpus demand, counted rather than assumed** (the 218-addon vanilla corpus; `grep -rl`, one
|
|
row per verb, addons/sites):
|
|
|
|
SetItemButtonTexture 4 addons / 19 sites
|
|
SetItemButtonDesaturated 3 addons / 8 sites
|
|
SetItemButtonCount 3 addons / 7 sites
|
|
SetItemButtonTextureVertexColor 3 addons / 7 sites
|
|
SetItemButtonNormalTextureVertexColor 1 addon / 2 sites
|
|
SetItemButtonNameFrameVertexColor 1 addon / 2 sites
|
|
SetItemButtonSlotVertexColor 0 / 0
|
|
SetItemButtonStock 0 / 0
|
|
|
|
Six addons in all — Bagnon_Core, CT_MailMod, CT_MasterMod, AdvancedTradeSkillWindow,
|
|
QuestHistory, SpecialTalentUI. The last two rows have **zero** corpus callers and are here
|
|
anyway because the family is one contract and, once the tint verb below is shared, they are one
|
|
line each; that is stated rather than dressed up as demand (1210).
|
|
|
|
**The virtual `ItemButtonTemplate` Button is deliberately NOT here.** The harness's template
|
|
demand ranks it at zero on both axes — no corpus addon names it in a `CreateFrame` fourth
|
|
argument or an XML `inherits=` (they name their own `*ItemButtonTemplate`s, or
|
|
`ContainerFrameItemButtonTemplate`) — and our own bag windows already have
|
|
`BenillaBagSlotTemplate`. Building an unwanted template is exactly the unmeasured work 1210
|
|
was written about.
|
|
-->
|
|
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
|
<Script><![CDATA[
|
|
-- The naming contract every verb below rests on: an "item button" is a Button whose own
|
|
-- name prefixes its regions — `<name>IconTexture`, `<name>Count`, `<name>Stock`,
|
|
-- `<name>NormalTexture`, `<name>NameFrame`, `<name>SlotTexture`. That is the reference's
|
|
-- `$parent…` template shape, and it is why these are free functions taking a button rather
|
|
-- than widget methods: any button that follows the naming works, whoever built it.
|
|
--
|
|
-- TWO DIFFERENT NILS, and the difference is the whole error policy of this file.
|
|
-- A nil BUTTON is a no-op everywhere: callers hand us whatever their loop is holding, and
|
|
-- the reference tolerates that in all eight verbs, so we do too. A button that is missing
|
|
-- the REGION is not tolerated — the lookup yields nil and the very next `:` raises. That
|
|
-- loudness is deliberate: "this is not an item button" is a real mistake by the caller, and
|
|
-- 1203's lesson is that the silent version of a failure is the expensive one.
|
|
|
|
-- One named region off a button, which is the only address these verbs ever compute. Nil in,
|
|
-- nil out, so the button guard reads as one expression instead of eight copied early
|
|
-- returns.
|
|
local function region(button, suffix)
|
|
return button and getglobal(button:GetName() .. suffix)
|
|
end
|
|
|
|
-- Four of the eight differ in nothing but which region they tint, so they share the verb
|
|
-- and name the region. The guard is on the BUTTON and deliberately not on the region it
|
|
-- resolved: a nil button is the tolerated case, a button carrying no such region is the
|
|
-- loud one. `texture` is a local rather than an inline call only so that the raise names
|
|
-- which lookup came back empty.
|
|
local function tint(button, suffix, r, g, b)
|
|
local texture = region(button, suffix)
|
|
if button then
|
|
texture:SetVertexColor(r, g, b)
|
|
end
|
|
end
|
|
|
|
function SetItemButtonCount(button, count)
|
|
if not button then
|
|
return
|
|
end
|
|
count = count or 0
|
|
button.count = count
|
|
local label = region(button, "Count")
|
|
-- `isBag` is the reference's own flag for a bag button, where even a single item
|
|
-- counts as a number worth showing. Above 999 the slot shows a bare `*` — the stack
|
|
-- is "more than the label can say" — while `button.count` keeps the real number.
|
|
if count > 1 or (button.isBag and count > 0) then
|
|
label:SetText(count > 999 and "*" or count)
|
|
label:Show()
|
|
else
|
|
label:Hide()
|
|
end
|
|
end
|
|
|
|
function SetItemButtonStock(button, numInStock)
|
|
if not button then
|
|
return
|
|
end
|
|
-- The reference defaults this to `""` and then compares it with `> 0`, which is a
|
|
-- string/number comparison Lua refuses — a raise on the only path the default can
|
|
-- reach. Ours defaults to 0, which is what the branch below actually means. Not aping
|
|
-- a quirk (method step 3); the behaviour on every reachable path is identical.
|
|
numInStock = numInStock or 0
|
|
button.numInStock = numInStock
|
|
local label = region(button, "Stock")
|
|
if numInStock > 0 then
|
|
label:SetText(format(MERCHANT_STOCK, numInStock))
|
|
label:Show()
|
|
else
|
|
label:Hide()
|
|
end
|
|
end
|
|
|
|
function SetItemButtonTexture(button, texture)
|
|
if not button then
|
|
return
|
|
end
|
|
local icon = region(button, "IconTexture")
|
|
-- Visibility tracks the ARGUMENT, not the region's contents, and it is settled before
|
|
-- the texture is assigned: clearing a slot with a nil texture must leave the region
|
|
-- hidden, never an empty quad standing where the icon was.
|
|
if texture then
|
|
icon:Show()
|
|
else
|
|
icon:Hide()
|
|
end
|
|
icon:SetTexture(texture)
|
|
end
|
|
|
|
function SetItemButtonTextureVertexColor(button, r, g, b)
|
|
tint(button, "IconTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonNormalTextureVertexColor(button, r, g, b)
|
|
tint(button, "NormalTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonNameFrameVertexColor(button, r, g, b)
|
|
tint(button, "NameFrame", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonSlotVertexColor(button, r, g, b)
|
|
tint(button, "SlotTexture", r, g, b)
|
|
end
|
|
|
|
function SetItemButtonDesaturated(button, desaturated, r, g, b)
|
|
-- The one verb whose own nil guard is on the ICON rather than the button: the
|
|
-- reference checks both, and `region()` collapses them into this single test without
|
|
-- changing which calls no-op and which raise.
|
|
local icon = region(button, "IconTexture")
|
|
if not icon then
|
|
return
|
|
end
|
|
-- The reference asks the texture itself whether the desaturating shader is available
|
|
-- (`local shaderSupported = icon:SetDesaturated(desaturated)`) and falls through to a
|
|
-- flat grey tint when it is not — 1.12 ran on fixed-function hardware where that was
|
|
-- the ordinary answer. Since decision 1327 benilla's answer is YES: the icon greys to
|
|
-- luminance in the shader and wears the caller's own tint on top, which is what the
|
|
-- ref's own `(1, 0.65, 0.65, 0.65)` and `(locked, 0.5, 0.5, 0.5)` calls are asking
|
|
-- for. The `icon.SetDesaturated and` guard stays for a region leaf without the verb;
|
|
-- the flat-grey arm below is then the ref's own fallback, unchanged.
|
|
local shaderSupported = icon.SetDesaturated and icon:SetDesaturated(desaturated)
|
|
-- Undesaturated is full colour whatever the caller passed; desaturated is the caller's
|
|
-- own tint when they named one AND the shader took it, and the flat grey otherwise.
|
|
if not desaturated then
|
|
r, g, b = 1.0, 1.0, 1.0
|
|
elseif not r or not shaderSupported then
|
|
r, g, b = 0.5, 0.5, 0.5
|
|
end
|
|
icon:SetVertexColor(r, g, b)
|
|
end
|
|
]]></Script>
|
|
</Ui>
|