Files
benilla-octo/crates/benilla-app/assets/ui/MoneyFrame.xml
T
SamandClaude Fable 5.1 7e68267395 the raid and reputation panes go stock, and a faction header folds
- the raid and reputation panes are the reference's own files: two manifest lines replace our `RaidFrame.xml` (83 of 84 frame names were already the reference's; the tear-off pullouts arrive) and one replaces our `ReputationFrame.xml`; the raid swap surfaced a loader defect (a font object inherited one level down was the 26 unexplained warnings), rehomed `RaidFrame_LoadUI` and two popup dialogs, and reproduces one reference DEFECT: an emptied lockout list keeps its last row painted (1874); the reputation swap moved the WATCH BAR out of `ActionBar.xml`, where it would have shadowed the chain's own, and exposed an ENGINE gap: `CollapseFactionHeader`/`ExpandFactionHeader` fire `UPDATE_FACTION` (`0x4d5dab`) and ours fired nothing, so with the stock pane repainting on that event alone, a fold painted nothing and the header kept its minus — invisible while our own file repainted itself (1875)
- a wanted poster is a questgiver on the wire as a creature is, and benilla never asked about one; the RE round came back in two halves: the reference DOES send `CMSG_QUESTGIVER_STATUS_QUERY` for a GameObject, gated on `GAMEOBJECT_FLAGS` bit 2, seen in 20 of 62 sniffed sessions — and it can never render the answer (the status handler's typemask excludes a GameObject, its status slot lies past the object's end, and no poster, sign or plaque carries attachment 18); vmangos DOES answer, so the sweep gains a GameObject arm on the reference's own gate and the apply side refuses a non-unit guid, without which a gold `!` would hang over a poster no 1.12 client draws; two shipped claims fall with it (`0x5eb3c0` is the FULL re-query sweep; a status never suppresses a GameObject's minimap dot), and `WOW_PROBE_GOQUEST` at the Goldshire wanted poster reads the server's 1 then 5, both refused (1872)
- the money entry comes off the chain: Trade, Mail and Auction switch to the reference's `MoneyInputFrameTemplate` and its verbs, and our `BenillaMoneyInput_*` kit retires — it was the exact answer while benilla fired `OnTextChanged` synchronously inside `SetText`, and the engine now raises it deferred and coalesced, matching `0x77d3e0` (1831), so the reference's `expectChanges` guard works and our bracket flag was a duplicate; most of the 430 deleted lines are verbatim art — all three windows carried an inline copy of the template's three edit boxes, AuctionFrame a whole second copy; one visible change: TradeFrame now makes the reference's own `MoneyInputFrame_SetMode(this, "compact")` call, which tightens the trade money boxes (Gold 58→56 wide, Silver's offset 26→22, Copper's 16→11), and two stale claims in the kit's comments are corrected, and a verb nothing called goes (1882)
- the `UIFrameFlash` family is transcribed — six FrameXML names from `UIParent.lua`, which we do not load — unblocking SpellBookFrame and HelpFrame, which called it into silence; re-arming a flashing frame is a RETURN and `-1` flashes forever (1879); UIParent is recounted — `UpdateNameplates` and the `*_Show`/`*_Hide` family around it are FrameXML, not engine, missed by a survey that skipped the LoadOnDemand addons — so its engine surface is four verbs, not a wall: `GetLocale` answers `enUS`, `IsMacClient` answers nil (the whole verb on the PC build), and `CheckReadyCheckTime` and `RequestBattlefieldPositions` are the two it waits on (1880); and `BasicControls.xml` comes off the chain by pure deletion — ours differed by nothing but line-wrapping and one comment — with `FailedAddOnLoad` and `UIParentLoadAddOn` moving to `UIParent.xml`, their reference home, below their four callers (1881)

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-04 20:50:12 +07:00

718 lines
40 KiB
XML

<Ui>
<!--
THE MONEY FRAME KIT — `MoneyFrameTemplate` / `SmallMoneyFrameTemplate`, the `MoneyTypeInfo`
table and the `MoneyFrame_*` API that drives them.
This is **our implementation** of that kit. The specification it implements is the extracted
1.12 `Interface\FrameXML\MoneyFrame.lua` (356 lines) and `MoneyFrame.xml` (297), cited below
as `ref l.N`: the names, the published field names, the constants and every branch of the
collapse rule are the specification's, because addons call this by name and a compatible kit
has no freedom in any of that. How it is written — the structure, the helpers, the local
names, the comments — is ours.
WHY IT IS HERE: it is FrameXML's surface, not Rust's (decision 1190 — a name the shipped 1.12
UI defines belongs in a UI file, never hardcoded), and the addon corpus asks for it by name.
`MoneyFrame_SetType` is wanted by 11 addons, `MoneyFrame_Update` by 9, `SmallMoneyFrame_OnLoad`
by 9, and 13 declare a frame `inherits="SmallMoneyFrameTemplate"`. `MoneyTypeInfo` was one of
the top runtime walls the frame rename left standing.
── THE OTHER COIN KIT, and why there are still two ──────────────────────────────────────────
Eight of our windows (merchant, bank, trade, mail, quest, quest log, taxi, trainer) do NOT use
this file. They render coins through `BenillaMoney_Set`/`_Clear`/`_SetColor`, which live
below in this same file (they were in MerchantFrame.xml until 1751 took that window to the
stock chain): a three-SLOT chain (`<prefix>Coin1..3`) that fills only the non-zero
denominations in display order and re-flows, rather than three fixed GoldButton/SilverButton/
CopperButton frames that hide themselves. That kit takes its coin TexCoords, its collapse and
its shrink from the same specification as this one, and its layout is the one the director has
signed off across those eight windows.
The auction house is the exception, and the reason the reconciliation started there rather
than anywhere else: the three-slot kit has no notion of a money TYPE, so it cannot express
`showSmallerCoins` or `fixedWidth` — and those two flags ARE the auction house's price column
(`MoneyTypeInfo["AUCTION"]`, declared below and, until AuctionFrame.xml moved, used by
nothing). On the three-slot kit "1g" showed as one lone gold coin where the reference shows
`1g 0s 0c`, and a column of prices packed tight instead of lining up. That is not a layout
preference to re-approve, it is the kit being the wrong kit. See decision 1522.
So the two still coexist, and the split is now by KIT CAPABILITY as much as by audience:
· `BenillaMoney_*` + `<prefix>Coin1..3` — the eight windows whose look is signed off and
whose money is all plain collapsing STATIC amounts, which that kit renders correctly.
· `MoneyFrame_*` + `MoneyTypeInfo` here — the names and structure an addon writes against,
what an addon's `inherits="SmallMoneyFrameTemplate"` resolves to, and what the auction
house is laid out on.
Finishing the migration — moving those eight onto `SmallMoneyFrameTemplate` and retiring
`BenillaMoney_*` — is still the right end state; it is a re-layout of hand-authored art in
eight files (an approved look), so it stays named here rather than done in passing.
── WHAT IS NOT CARRIED, and why (each is guarded at its own site below) ─────────────────────
· THE COIN PICKUP. `OpenCoinPickupFrame`/`UpdateCoinPickupFrame`/`CoinPickupFrame` belong to
CoinPickupFrame.lua/.xml, which we do not ship — and the engine verbs underneath them
(`PickupPlayerMoney`, `DropCursorMoney`, `PickupTradeMoney`, `AddTradeMoney`) are not built
either, so a picked-up coin would have nowhere to go. The coin buttons still take the mouse
for a `canPickup` type (that is the specified state) and the click is guarded, so it does
nothing.
· `GetCursorMoney` (PLAYER's amount, ref l.17) — the engine verb for coins held on the cursor.
With no coin pickup there is no cursor money, so its term is not approximated away: it is
structurally zero. Stated at the spot.
· `GetSendMailMoney` / `GetSendMailCOD` (ref l.73, l.90) — 1.12 engine verbs we have not
built. We ship their SETTERS (`SetSendMailMoney`/`SetSendMailCOD`, which MailFrame.xml
drives); nothing reads the amount back yet, so those two types read 0.
· `message()` (ref l.145, l.173, l.181) — the one-line StaticPopup shim, which belongs to
BasicControls.xml, a file family we have not built. Guarded, so a misuse of the API stays
silent instead of erroring on the diagnostic.
· `Button:GetTextWidth()` (ref l.202 etc.) — BUILT, and called here, exactly as the
specification calls it. It forwards to the label FontString's `GetStringWidth`, which the
engine now answers inside the tick that asked (`script::measure`).
· The four `Pickup*`/`Add*` mail verbs the pickup closures name are not in 1.12's `_G` either
(they are absent from reference/1.12-globals.tsv) — so those closures are as unreachable
here as they are there. They exist because the table's shape is the contract.
-->
<Script><![CDATA[
-- ══ THE THREE-SLOT COIN KIT (`BenillaMoney_*`), rehomed from MerchantFrame.xml ═══════════
--
-- The OTHER kit — see this file's header for why there are two and what the split is. It
-- lives beside `MoneyFrame_*` rather than inside a window because eight windows share it:
-- merchant, bank, trade, mail, quest, quest log, taxi and trainer. Nothing about the kit
-- changed in the move; the `SmallMoneyFrameTemplate` migration the header names is still
-- outstanding and still the director's call.
-- The coin display, quoted from the real SmallMoneyFrame (ref MoneyFrame.xml/lua): a copper
-- amount renders as a number + a 13px coin icon per NONZERO denomination, cut from the one
-- shared texture Interface\MoneyFrame\UI-MoneyIcons by TexCoord (gold 0..0.25, silver
-- 0.25..0.5, copper 0.5..0.75 — ref l.115/74/33), the real collapse that hides zero coins
-- (MoneyFrame.lua l.221-267). Each money display owns three position-"slots"; the XML chains
-- them either left-to-right (row prices + the quest reward: Coin1 leftmost, gold first) or
-- right-to-left (the purses: Coin1 rightmost, copper first). BenillaMoney_Set fills the
-- visible denominations into slots 1.. in DISPLAY order (highestFirst=true for the
-- left-aligned chains) and — the real shrink, MoneyFrame.lua l.202/269 — sizes each slot to
-- exactly its number + its 13px coin (`SetWidth(GetTextWidth() + iconWidth)` there; the
-- same read here, off the number FontString the slot already owns). The static ±4px
-- chain-anchor offsets are the real MONEY_BUTTON_SPACING gap, so a shrunk group packs tight,
-- left-flush in the ltr chains and right-flush in the rtl ones. BenillaMoney_Clear hides
-- all three slots (the real collapse hides, see the helper).
--
-- NOTE (for the loot window's fold): the reusable helpers are
-- BenillaMoney_Set(prefix, copper) and BenillaMoney_Clear(prefix)
-- where `prefix` names the slot frames `<prefix>Coin1..3` (each with a `<prefix>CoinNIcon`
-- texture + `<prefix>CoinNNum` fontstring child). They live in this file.
--
-- They lived in MerchantFrame.xml until 1751 came for that window: the stock
-- `MerchantFrame.xml` off the player's chain has no room for eight other windows'
-- shared helpers, and this file is where the reference keeps money code anyway. A pure
-- move — not the `SmallMoneyFrameTemplate` migration named at the top of this file,
-- which is a re-layout of an approved look and is the director's to call.
BENILLA_COIN_TC = {
{ 0.0, 0.25 }, -- [1] gold
{ 0.25, 0.5 }, -- [2] silver
{ 0.5, 0.75 }, -- [3] copper
}
-- Unused slots HIDE (the real collapse — MoneyFrame.lua l.230/247 goldButton:Hide()), never
-- SetWidth(0): a 0 width means "derive from opposing anchors" to the layout (the client's
-- own G+0x50 convention), not "zero px". Fills are contiguous from Coin1, so a chain never
-- anchors THROUGH a hidden slot.
function BenillaMoney_Clear(prefix)
for i = 1, 3 do
getglobal(prefix .. "Coin" .. i):Hide()
end
end
function BenillaMoney_Set(prefix, copper, highestFirst)
copper = copper or 0
local vals = {
math.floor(copper / 10000), -- gold
math.floor((copper % 10000) / 100), -- silver
copper % 100, -- copper
}
-- Fill slots 1.. in display order (see the module comment); a zero denomination is
-- skipped (the real collapse), except copper always shows when the whole amount is
-- zero so a free item / empty purse isn't blank.
local first, last, step = 3, 1, -1
if highestFirst then first, last, step = 1, 3, 1 end
local slot = 0
for d = first, last, step do
if vals[d] > 0 or (d == 3 and vals[1] == 0 and vals[2] == 0) then
slot = slot + 1
-- The real shrink: this slot is exactly its number + its 13px coin, and the
-- XML anchor chain re-flows off the new width.
-- Text first, then measure it: the engine answers a width read inside the
-- tick that set the text, so the number IS this number and not the last one.
local num = getglobal(prefix .. "Coin" .. slot .. "Num")
num:SetText(vals[d])
local numW = num:GetStringWidth()
local slotFrame = getglobal(prefix .. "Coin" .. slot)
slotFrame:SetWidth(numW + 13)
slotFrame:Show()
local icon = getglobal(prefix .. "Coin" .. slot .. "Icon")
icon:SetTexture("Interface\\MoneyFrame\\UI-MoneyIcons")
icon:SetTexCoord(BENILLA_COIN_TC[d][1], BENILLA_COIN_TC[d][2], 0, 1)
num:SetWidth(numW)
end
end
for i = slot + 1, 3 do
getglobal(prefix .. "Coin" .. i):Hide()
end
end
-- Recolor the coin *numbers* (the ref's SetMoneyFrameColor — the digits redden, the coin icons
-- keep their own texture). The trainer detail cost calls this white when affordable / red
-- (1.0, 0.1, 0.1) when not; a plain purse never calls it, so its coins stay default. Applies to
-- all 3 slots (hidden ones are harmless) and must be re-called after each BenillaMoney_Set,
-- which fills the numbers but does not reset their color.
function BenillaMoney_SetColor(prefix, r, g, b)
for i = 1, 3 do
getglobal(prefix .. "Coin" .. i .. "Num"):SetTextColor(r, g, b)
end
end
-- ── What a coin is worth ─────────────────────────────────────────────────────────────────
-- Published globals: an addon that formats a price does the same split we do below, and
-- reads these rather than spelling 100 twice.
COPPER_PER_SILVER = 100
SILVER_PER_GOLD = 100
COPPER_PER_GOLD = COPPER_PER_SILVER * SILVER_PER_GOLD
-- ── How wide a coin draws ────────────────────────────────────────────────────────────────
-- One coin icon's width at each of the two sizes the templates below come in, and the gap
-- between two coin buttons — NEGATIVE, because the three deliberately bite into each other
-- so a group reads as one number rather than as three. `COIN_BUTTON_WIDTH` is the fixed slot
-- a `fixedWidth` type pads its smaller coins out to, so a column of prices lines up.
-- The values are the specification's (ref l.2-12): measurements of the shipped coin art, not
-- ours to re-derive.
MONEY_ICON_WIDTH = 19
MONEY_ICON_WIDTH_SMALL = 13
MONEY_BUTTON_SPACING = -4
MONEY_BUTTON_SPACING_SMALL = -4
COIN_BUTTON_WIDTH = 32
-- ── The money types (ref l.14-103) ───────────────────────────────────────────────────────
--
-- A money frame carries no amount of its own: it carries a TYPE, and the type says where the
-- number comes from and how the coins behave. An entry has three parts:
--
-- · `UpdateFunc()` returns the amount, in copper. It is called with the global `this` bound
-- to the frame being painted, so a type may read the frame as well as the engine.
-- · `PickupFunc(amount)` / `DropFunc()` are the cursor verbs a click-to-pick-up type drives.
-- · the display flags `MoneyFrame_Update` reads: `collapse` (drop leading zero
-- denominations), `showSmallerCoins` (keep the trailing ones on even at zero; the value
-- "Backpack" additionally keeps copper on by itself), `canPickup` (the coins take the
-- mouse), `fixedWidth` (pad the smaller coins to COIN_BUTTON_WIDTH), `truncateSmallCoins`
-- (drop copper once there is gold).
--
-- Nothing else is consulted. That is why those field names and the two function signatures
-- are the published contract and not an implementation detail: addons register types of their
-- own into this table and expect exactly these parts to be honoured.
MoneyTypeInfo = { }
-- Read an amount through an engine verb that may not be built here yet: absent, it reads 0
-- rather than calling into nil. Two of the seven types below need it — see the header.
local function AmountFrom(verbName)
local verb = getglobal(verbName)
if verb then
return verb()
end
return 0
end
-- The purse. The specification subtracts a third term here (ref l.17,
-- `GetMoney() - GetCursorMoney() - GetPlayerTradeMoney()`): coins lifted onto the cursor by
-- the coin-pickup frame. benilla ships neither that frame nor the `PickupPlayerMoney` verb
-- behind it, so the term is structurally 0 rather than approximated away — it comes back the
-- moment either lands.
MoneyTypeInfo["PLAYER"] = {
UpdateFunc = function() return GetMoney() - GetPlayerTradeMoney() end,
PickupFunc = function(amount) PickupPlayerMoney(amount) end,
DropFunc = function() DropCursorMoney() end,
collapse = 1,
canPickup = 1,
showSmallerCoins = "Backpack",
}
-- A number the owner parked on the frame itself — what `MoneyFrame_Update(name, amount)`
-- leaves behind, and how an addon shows a price it worked out for itself.
MoneyTypeInfo["STATIC"] = {
UpdateFunc = function() return this.staticMoney end,
collapse = 1,
}
-- The auction house's price column: the same source as STATIC, but the smaller coins pad out
-- to a fixed slot and stay on at zero, so a stack of prices reads as a column of digits.
MoneyTypeInfo["AUCTION"] = {
UpdateFunc = function() return this.staticMoney end,
collapse = 1,
showSmallerCoins = "Backpack",
fixedWidth = 1,
truncateSmallCoins = nil,
}
-- Our side of an open trade, and the other party's. Only ours is click-to-pick-up.
MoneyTypeInfo["PLAYER_TRADE"] = {
UpdateFunc = function() return GetPlayerTradeMoney() end,
PickupFunc = function(amount) PickupTradeMoney(amount) end,
DropFunc = function() AddTradeMoney() end,
collapse = 1,
canPickup = 1,
}
MoneyTypeInfo["TARGET_TRADE"] = {
UpdateFunc = function() return GetTargetTradeMoney() end,
collapse = 1,
}
-- Mail postage and cash-on-delivery. Postage explicitly does NOT collapse (ref l.84): the
-- send-mail frame wants all three coins standing whatever the amount, because you are typing
-- into them. Both read 0 until their engine getters exist — see the header.
MoneyTypeInfo["SEND_MAIL"] = {
UpdateFunc = function() return AmountFrom("GetSendMailMoney") end,
PickupFunc = function(amount) PickupSendMailMoney(amount) end,
DropFunc = function() AddSendMailMoney() end,
collapse = nil,
canPickup = 1,
showSmallerCoins = "Backpack",
}
MoneyTypeInfo["SEND_MAIL_COD"] = {
UpdateFunc = function() return AmountFrom("GetSendMailCOD") end,
PickupFunc = function(amount) PickupSendMailCOD(amount) end,
DropFunc = function() AddSendMailCOD() end,
collapse = 1,
canPickup = 1,
}
-- ── Hearing about a change (ref l.105-140) ───────────────────────────────────────────────
--
-- Every money frame registers all five money events; each then decides whether the one that
-- arrived is about the number IT is showing. That pairing is data, not control flow, so it is
-- written as data: one row per event, in the order the templates register them, naming the
-- money types that event can change.
local MONEY_EVENTS = {
{ "PLAYER_MONEY", { PLAYER = 1 } },
{ "PLAYER_TRADE_MONEY", { PLAYER = 1, PLAYER_TRADE = 1 } },
{ "TRADE_MONEY_CHANGED", { TARGET_TRADE = 1 } },
{ "SEND_MAIL_MONEY_CHANGED", { PLAYER = 1, SEND_MAIL = 1 } },
{ "SEND_MAIL_COD_CHANGED", { PLAYER = 1, SEND_MAIL_COD = 1 } },
}
-- The same rows keyed by event name, built once, so the handler below is a lookup.
local moneyEventTypes = { }
for _, row in ipairs(MONEY_EVENTS) do
moneyEventTypes[row[1]] = row[2]
end
local function MoneyFrameListen(frame)
for _, row in ipairs(MONEY_EVENTS) do
frame:RegisterEvent(row[1])
end
end
-- The two OnLoads differ in exactly one thing: `small`, which is what picks the 13px coin and
-- its tighter spacing over the 19px one in MoneyFrame_Update below. Both then take type
-- PLAYER, so a frame nobody tells otherwise shows the purse.
function MoneyFrame_OnLoad()
MoneyFrameListen(this)
MoneyFrame_SetType("PLAYER")
end
function SmallMoneyFrame_OnLoad()
MoneyFrameListen(this)
this.small = 1
MoneyFrame_SetType("PLAYER")
end
function MoneyFrame_OnEvent()
if not this.info or not this:IsVisible() then
return
end
local affected = moneyEventTypes[event]
if affected and affected[this.moneyType] then
MoneyFrame_UpdateMoney()
end
end
-- ── Painting ─────────────────────────────────────────────────────────────────────────────
-- A money frame's three coin buttons, biggest first. Every verb below addresses them as a
-- trio, so this is the one place their `$parent<Denomination>Button` names are spelled.
local function MoneyFrameCoins(frameName)
return getglobal(frameName .. "GoldButton"),
getglobal(frameName .. "SilverButton"),
getglobal(frameName .. "CopperButton")
end
-- Point a frame at a money type (ref l.142-162): remember it, wire the mouse to match, and
-- repaint. Reads `this` rather than taking a frame — that is the convention the eleven corpus
-- callers write against.
function MoneyFrame_SetType(type)
local info = MoneyTypeInfo[type]
if not info then
-- ref l.145 diagnoses this with `message()`, which we DO have: it comes off the
-- chain in `Interface\FrameXML\BasicControls.xml` (1881 migrated that file; before
-- it, our own copy carried the same function). The `if` is therefore belt-and-braces
-- for a harness that loads this file without BasicControls, not a missing verb.
if message then
message("Invalid money type: " .. type)
end
return
end
this.info = info
this.moneyType = type
-- Only a click-to-pick-up type wants the mouse; on the rest a click falls through to
-- whatever is behind the coins.
local gold, silver, copper = MoneyFrameCoins(this:GetName())
local pickable = info.canPickup and true or false
gold:EnableMouse(pickable)
silver:EnableMouse(pickable)
copper:EnableMouse(pickable)
MoneyFrame_UpdateMoney()
end
-- Re-read the amount from the type and repaint (ref l.164-175). `this` again.
function MoneyFrame_UpdateMoney()
if not this.info then
if message then
message("Error moneyType not set")
end
return
end
local money = this.info.UpdateFunc()
MoneyFrame_Update(this:GetName(), money)
-- ref l.169-171 also feeds the coin-pickup frame while one is open. `hasPickup` is set
-- only by the coin click, which is guarded off (see the header), so this is unreachable
-- today rather than removed.
if this.hasPickup == 1 and UpdateCoinPickupFrame then
UpdateCoinPickupFrame(money)
end
end
-- Lay one denomination out: its digits, and the width those digits plus the coin icon need.
--
-- `Button:GetTextWidth()` right after `SetText`, which is what the specification writes
-- (ref l.202 and kin) and what the real client answers synchronously off its inline font
-- engine, so the coin is the right width on the frame that set its text.
--
-- This read used to return 0 — our measure landed at extract, a frame after the tick — and
-- left all three coins at exactly one bare icon width with their digits overlapping: the
-- director's "gold numbers are all cramped up when I first open the bags; close and open
-- again and it looks good", the reopen being the first open's cached measure. It was served
-- for a while by a digits-only advance table, since a number's width is a pure sum of
-- per-glyph advances. The engine answers the general question now (`script::measure`), so
-- this asks it the way the specification does.
local function ShowCoin(coin, coinName, amount, iconWidth)
coin:SetText(amount)
coin:SetWidth(coin:GetTextWidth() + iconWidth)
coin:Show()
end
-- Chain the next-bigger coin onto `coin`, and report what `coin` contributes to the group's
-- width.
--
-- Showing: the bigger coin hangs off this one's LEFT edge by `spacing` (negative — they
-- overlap), and the group grows by this coin's width less that overlap, but only when the
-- bigger coin is actually on screen to overlap with. Not showing: this coin hides and the
-- bigger one collapses onto its RIGHT edge, contributing nothing. `padToSlot` is the
-- `fixedWidth` exception, which sizes the coin to the fixed column slot first.
local function ChainCoin(bigger, coin, coinName, showing, padToSlot, spacing)
if not showing then
coin:Hide()
bigger:SetPoint("RIGHT", coinName, "RIGHT", 0, 0)
return 0
end
if padToSlot then
coin:SetWidth(COIN_BUTTON_WIDTH)
end
local grown = coin:GetWidth()
bigger:SetPoint("RIGHT", coinName, "LEFT", spacing, 0)
if bigger:IsVisible() then
grown = grown - spacing
end
return grown
end
-- THE PAINTER (ref l.177-270). Split a copper amount into the three denominations, size each
-- coin to its own digits plus its icon, and then — if the type collapses — drop the leading
-- zeros, re-chain what is left so the group packs tight, and report the frame's true width.
--
-- This one takes a frame NAME, not `this`: it is the only entry point in the family an owner
-- can call about a frame other than the one currently dispatching, which is how an addon
-- paints a STATIC frame it has just filled.
function MoneyFrame_Update(frameName, money)
local frame = getglobal(frameName)
local info = frame.info
if not info then
-- ref l.181 diagnoses and, notably, does NOT return: the coins below are still laid
-- out, and the collapse read further down is what actually stops it. Kept as-is.
if message then
message("Error moneyType not set")
end
end
-- Copper is the low two digits, silver the next two, gold everything above. Silver comes
-- out by subtracting the gold back off rather than by a second `mod`: `mod` here is 1.12's
-- `math.mod`, which truncates towards zero, so `mod(-1, 100)` is -1 and not 99 — the two
-- forms part company the moment `money` goes negative (which `GetMoney()` minus a trade
-- offer can), and this one is the specified answer.
-- A STATIC/AUCTION frame's amount is whatever its owner last parked on it, and on the
-- very first paint — `MoneyFrame_SetType`'s own repaint, in OnLoad, before any owner has
-- filled it — that is nil. The specification divides straight into it and throws there;
-- we answer 0, because the throw would take the whole `<OnLoad>`/`<OnShow>` chain down
-- with it and an empty price column is the honest reading of "nothing set yet".
money = money or 0
local gold = floor(money / COPPER_PER_GOLD)
local silver = floor((money - gold * COPPER_PER_GOLD) / COPPER_PER_SILVER)
local copper = mod(money, COPPER_PER_SILVER)
local goldCoin, silverCoin, copperCoin = MoneyFrameCoins(frameName)
local iconWidth, spacing = MONEY_ICON_WIDTH, MONEY_BUTTON_SPACING
if frame.small then
iconWidth, spacing = MONEY_ICON_WIDTH_SMALL, MONEY_BUTTON_SPACING_SMALL
end
ShowCoin(goldCoin, frameName .. "GoldButton", gold, iconWidth)
ShowCoin(silverCoin, frameName .. "SilverButton", silver, iconWidth)
ShowCoin(copperCoin, frameName .. "CopperButton", copper, iconWidth)
-- Remember what the frame is displaying — STATIC's own reader reads it back.
frame.staticMoney = money
-- A type that does not collapse is finished: all three coins stand, at the width their
-- digits asked for, and the frame keeps whatever width it was given.
if not info.collapse then
return
end
-- Gold leads. Present, it stays and sets the terms for the two below it; absent, it goes.
-- The running width starts at one icon's worth of frame padding.
local width = iconWidth
local keepSmaller, dropCopper
if gold > 0 then
width = width + goldCoin:GetWidth()
keepSmaller = info.showSmallerCoins and 1 or nil
dropCopper = info.truncateSmallCoins and 1 or nil
else
goldCoin:Hide()
end
-- Silver, then copper: each either shows — padded, chained and counted — or hides and lets
-- the coin above it collapse onto its edge. Silver showing under a `showSmallerCoins` type
-- is what carries the exemption down to copper; copper also survives at zero for a
-- "Backpack" type, and dies whatever it is once gold truncated it.
local showSilver = silver > 0 or keepSmaller
width = width + ChainCoin(goldCoin, silverCoin, frameName .. "SilverButton",
showSilver, keepSmaller and info.fixedWidth, spacing)
if showSilver and info.showSmallerCoins then
keepSmaller = 1
end
local showCopper = (copper > 0 or keepSmaller or info.showSmallerCoins == "Backpack")
and not dropCopper
width = width + ChainCoin(silverCoin, copperCoin, frameName .. "CopperButton",
showCopper, keepSmaller and info.fixedWidth, spacing)
frame:SetWidth(width)
end
-- A NO-OP, deliberately. The specification's whole body for this one sits inside a `--[[ ]]`
-- block (ref l.272-346) — an earlier painter that MoneyFrame_Update replaced. The name is in
-- the shipped client's `_G`, and an addon that calls it there gets exactly this: nothing.
function RefreshMoneyFrame(frameName, money, small, collapse, showSmallerCoins)
end
-- Recolour the DIGITS; the coin icons keep their own texture (ref l.348-356). This is how a
-- price you cannot afford turns red.
function SetMoneyFrameColor(frameName, r, g, b)
local gold, silver, copper = MoneyFrameCoins(frameName)
gold:SetTextColor(r, g, b)
silver:SetTextColor(r, g, b)
copper:SetTextColor(r, g, b)
end
-- A click on a coin opens the pickup frame on that denomination, seeded with what the frame is
-- currently showing; `denomination` is one coin's worth in copper, which is the unit the
-- pickup frame counts in.
--
-- Guarded, because we ship no coin pickup (see the header) — so today the click does nothing,
-- and the mouse-enabled state a `canPickup` type gives the coins is all that is observable.
-- Note the amount is read by calling the type's own `UpdateFunc` while `this` is still the
-- BUTTON rather than the frame, which is what the specification's own coin `<OnClick>` does
-- (ref MoneyFrame.xml l.20-23) and what a frame-reading type would see through.
function BenillaMoneyFrameCoin_OnClick(coin, denomination)
if not OpenCoinPickupFrame then
return
end
local frame = coin:GetParent()
OpenCoinPickupFrame(denomination, MoneyTypeInfo[frame.moneyType].UpdateFunc(), frame)
frame.hasPickup = 1
end
-- Closing a money frame takes any pickup it opened down with it.
function BenillaMoneyFrame_OnHide()
if this.hasPickup == 1 and CoinPickupFrame then
CoinPickupFrame:Hide()
this.hasPickup = 0
end
end
]]></Script>
<!--
MoneyFrameTemplate (ref MoneyFrame.xml l.3-149) — the LARGE kit: 192x28, 19px coins, the
NumberFontNormalLarge digits. The three buttons chain RIGHT-to-LEFT from the copper end, which
is what lets MoneyFrame_Update above re-point them past a hidden denomination.
`<PushedTextOffset>` is carried for provenance; our loader does not model it and ignores it.
-->
<Frame name="MoneyFrameTemplate" virtual="true">
<Size><AbsDimension x="192" y="28"/></Size>
<Frames>
<Button name="$parentCopperButton">
<Size><AbsDimension x="47" y="19"/></Size>
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, 1)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="19" y="19"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0.5" right="0.75" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
<Button name="$parentSilverButton">
<Size><AbsDimension x="47" y="19"/></Size>
<Anchors>
<Anchor point="RIGHT" relativeTo="$parentCopperButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_SILVER)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="19" y="19"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0.25" right="0.5" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
<Button name="$parentGoldButton">
<Size><AbsDimension x="47" y="19"/></Size>
<Anchors>
<Anchor point="RIGHT" relativeTo="$parentSilverButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_GOLD)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="19" y="19"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0" right="0.25" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormalLarge" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
</Frames>
<Scripts>
<OnLoad>MoneyFrame_OnLoad()</OnLoad>
<OnEvent>MoneyFrame_OnEvent()</OnEvent>
<OnShow>MoneyFrame_UpdateMoney()</OnShow>
<OnHide>BenillaMoneyFrame_OnHide()</OnHide>
</Scripts>
</Frame>
<!--
SmallMoneyFrameTemplate (ref MoneyFrame.xml l.150-296) — the same kit at 128x13 with 13px
coins and NumberFontNormal digits, and `SmallMoneyFrame_OnLoad` in place of `MoneyFrame_OnLoad`
so `this.small` is set. This is the one the shipped UI uses nearly everywhere (merchant prices,
the purse, mail postage, trade offers) and the one 13 corpus addons inherit.
-->
<Frame name="SmallMoneyFrameTemplate" virtual="true">
<Size><AbsDimension x="128" y="13"/></Size>
<Frames>
<Button name="$parentCopperButton">
<Size><AbsDimension x="32" y="13"/></Size>
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, 1)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="13" y="13"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0.5" right="0.75" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
<Button name="$parentSilverButton">
<Size><AbsDimension x="32" y="13"/></Size>
<Anchors>
<Anchor point="RIGHT" relativeTo="$parentCopperButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_SILVER)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="13" y="13"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0.25" right="0.5" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
<Button name="$parentGoldButton">
<Size><AbsDimension x="32" y="13"/></Size>
<Anchors>
<Anchor point="RIGHT" relativeTo="$parentSilverButton" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnClick>BenillaMoneyFrameCoin_OnClick(this, COPPER_PER_GOLD)</OnClick>
</Scripts>
<NormalTexture file="Interface\MoneyFrame\UI-MoneyIcons">
<Size><AbsDimension x="13" y="13"/></Size>
<Anchors><Anchor point="RIGHT"/></Anchors>
<TexCoords left="0" right="0.25" top="0" bottom="1"/>
</NormalTexture>
<ButtonText name="$parentText">
<Anchors>
<Anchor point="RIGHT"><Offset><AbsDimension x="-13" y="0"/></Offset></Anchor>
</Anchors>
</ButtonText>
<NormalFont inherits="NumberFontNormal" justifyH="RIGHT"/>
<PushedTextOffset><AbsDimension x="0" y="0"/></PushedTextOffset>
</Button>
</Frames>
<Scripts>
<OnLoad>SmallMoneyFrame_OnLoad()</OnLoad>
<OnEvent>MoneyFrame_OnEvent()</OnEvent>
<OnShow>MoneyFrame_UpdateMoney()</OnShow>
<OnHide>BenillaMoneyFrame_OnHide()</OnHide>
</Scripts>
</Frame>
</Ui>