Files
benilla-octo/crates/benilla/assets/ui/ActionBar.xml
T
SamandClaude Fable 5 34fdbd1d16 the keyring lands, keys open doors, and a building can own the sky
- The keyring is built. Your first key makes the keyring button appear by
  the bags, opening the window the original keeps its dungeon keys in — the
  server had been sending the keys all along; nothing had ever shown them.
- Keys open their doors now. The client used to cast the open as a bare
  spell, which the server rightly refuses; it now sends the item-use the
  original sends, carrying which key from which slot. The tooltip plays
  along: "Locked" turns the soft green when something you carry opens it,
  and a key stops inventing a "Use: Opening" line it never had.
- A building can own the sky: a WMO can name its own painted skybox, and
  Stratholme's burning city now draws its near-black-to-red dome instead of
  the zone's khaki gradient. The exact rule for when it applies settles in
  the next snapshot.
- Signpost tooltips follow the cursor again — the tooltip anchor forks per
  object class, corner for doors, cursor for signs — and the Blasted Lands
  lightning stops striking the same spot for ever: a placed doodad re-rolls
  its animation variation every play-window, like the original.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-29 19:54:18 +07:00

967 lines
59 KiB
XML

<Ui>
<!--
benilla's main action bar, RE-SKINNED to the real 1.12 MainMenuBar (decision 0068 slice-1;
dressing arc, decision 0084). The geometry + art below are QUOTED from the real FrameXML
(scratchpad refs, line citations inline): ref-MainMenuBar.xml (the bar art, end caps, XP bar),
ref-ActionBarFrame.xml (the 12-button anchor chain + page buttons), ref-ActionButtonTemplate.xml
(the button template). The Lua data flow is UNCHANGED — the vanilla page formula, the
ACTIONBAR_SLOT_CHANGED repaint, HasAction/GetActionTexture in, UseAction out.
Structure mirrors the real hierarchy so the draw order falls out of the frame tree:
BenillaActionBar (1024x53, BOTTOM of screen)
├─ BenillaExpBar (StatusBar, TOP) — drawn first ⇒ lowest
└─ BenillaActionBarArtFrame (fills the bar) — dwarf strips + end caps over the XP bar
└─ 12 buttons + page up/down — child frames ⇒ over the art
(The real client gets the same order via RaiseFrameLevel(MainMenuBarArtFrame); our loader's
later-sibling-is-higher + child-over-parent rules reproduce it without the explicit raise.)
SCOPE / real-client divergences (noted, not bugs):
- No frame scale: the real bar is UIParent-scaled; we render 1:1. Geometry is otherwise exact.
- Page up/down + the page number are ART ONLY (we have no bar paging — only the bonus-bar
offset; ref-ActionBarFrame.xml l.159-217). The page number renders a static "1".
- HotKey renders the static binding labels — always non-empty, so the ref's RANGE_INDICATOR
dot path (shown only when a button has NO binding text, ActionButton_UpdateHotkeys) never
applies; the out-of-range read is the hotkey's red tint (the OnUpdate range timer below).
- The state/feedback layer (decision 0137 phase 4) is the ref ActionButton.lua transcribed:
UpdateState (checked on IsCurrentAction/IsAutoRepeatAction), UpdateUsable (the 1/1/1 ·
0.5/0.5/1 · 0.4/0.4/0.4 vertex-color trio), UpdateCooldown (CooldownFrame_SetTimer),
the Border's equipped green, and the 0.4 s Flash blink — with ONE structural deviation:
the event set registers on the BAR frame and fans to all 12 buttons, instead of the
ref's per-button Register/Unregister churn on HasAction (12 fixed buttons make the
skip-empties optimization noise; the handlers themselves are the ref's).
- Count (decision 0216 §7, slice 4): the ref gates it on IsConsumableAction; we simplify to
"an ITEM-kind action with a positive bag count" (GetActionCount always answers 0 for a
non-item kind, so the gate is really just `count > 0`) — not every consumable is
detectable as such without the full consumable-flag column, which nothing else needs yet.
- Empty-slot grid (ref ActionButton.lua:241-259's ShowGrid/HideGrid, a per-button COUNTER):
simplified to one bar-level BOOLEAN — the cursor is a single global payload, so no two
"shows" can ever nest, and a counter would just be a boolean with extra steps. Unlike the
ref (which HIDES an empty, non-grid slot entirely), our bar always shows all 12 wells; the
grid only swaps an EMPTY slot's ring art between the occupied and empty Quickslot textures
— the default (nothing held) look is unchanged from before this slice, no capture regression.
- Exhaustion tick + rest fill, XP text (hover/cvar-gated), MaxLevelBar: OUT. The bar's
right-hand cluster is otherwise complete — the backpack toggle + four bag slots live in
BagFrame.xml and the eight micro buttons in MicroMenu.xml (both anchored into
BenillaActionBarArtFrame and seated over the art by BenillaActionBarArt_SeatAbove below),
and the performance meter is at the foot of this file. The KEYRING landed with decision
0765: its button and container live in BagFrame.xml (the ref's own split —
MainMenuBarBagButtons.xml / ContainerFrame.lua), and the bar's half of it — swapping the
two right-hand strips to the keyring art and sliding the performance meter over to make
room — is BenillaMainMenuBar_UpdateKeyRing below, exactly where the ref keeps it
(MainMenuBar.lua l.174-183).
-->
<Script><![CDATA[
BENILLA_FALLBACK_ICON = "Interface\\Icons\\INV_Misc_QuestionMark"
-- Ref ActionButton.lua:4 / UIParent.lua: the flash blink period and the range-check cadence.
ATTACK_BUTTON_FLASH_TIME = 0.4
TOOLTIP_UPDATE_TIME = 0.2
-- CooldownFrame_SetTimer moved to Cooldown.xml (the ref's own file split) — the bag
-- slots share it, so it loads before both consumers.
-- The static key-binding label each button i shows in its HotKey corner (our bar binds
-- 1..9,0,-,= to slots 1..12). The real client derives these from GetBindingKey; we render
-- them statically since the bindings are fixed.
BENILLA_ACTIONBAR_HOTKEYS = { "1","2","3","4","5","6","7","8","9","0","-","=" }
-- Whether a placeable cursor payload is held (decision 0216 §7's ACTIONBAR_SHOWGRID/
-- _HIDEGRID pair, simplified to a boolean — see the SCOPE note above).
BENILLA_ACTIONBAR_GRID_SHOWN = false
-- Every live action button — the main bar's 12 plus the multibars' (MultiBars.xml joins
-- at OnLoad). The fan-out roster for the bar-level events below: the ref registers the
-- feedback events per-button; our bar-level fan needs the full set in one place.
BENILLA_ACTION_BUTTONS = {}
-- Seat a TOP-LEVEL frame that anchors INTO the bar art (the bag bar + backpack toggle,
-- BagFrame.xml; the micro-button row, MicroMenu.xml) one frame level above the art, so its
-- icons draw over the bar plate instead of under it.
--
-- Why it's needed: those frames are top-level, so they default to a LOWER frame level than
-- the bar's own art (a child hierarchy — the ExpBar's dwarf notches + the metal/well art sit
-- at the bar's child level). Frame level dominates the flat painter's order, so the bar art
-- would paint OVER them — the ring shows but the centered icon is fully covered (the "empty
-- rings, no bag icons" bug). The bar's OWN 12 buttons avoid this by being CHILDREN of the art
-- frame (child-over-parent). (SetParent onto the art frame would be the structural twin, but
-- the engine keys draw order at creation and does not re-key on a runtime reparent, so the
-- explicit level is the reliable lever.) Lives here, with the frame it's about, because two
-- files now need it — it began as BagFrame.xml's private BenillaBagBar_SeatAboveActionBar.
function BenillaActionBarArt_SeatAbove(frame)
local art = getglobal("BenillaActionBarArtFrame")
if art then frame:SetFrameLevel(art:GetFrameLevel() + 1) end
end
-- ── The keyring's half of the bar (ref MainMenuBar_UpdateKeyRing, MainMenuBar.lua
-- l.174-183; decision 0765) ──────────────────────────────────────────────────────────────
-- The keyring is not just a button bolted onto the bar: the bar's two RIGHT-hand strips are
-- re-textured to a variant plate that has a socket for it, and the performance meter slides
-- 8px further left out of the way. Both atlases are 256-wide 43px strips at the same on-
-- screen size and anchors, so only the file + TexCoords change (UI-MainMenuBar-KeyRing is
-- 256x128 and holds exactly the two bands the ref names: 0.3359375 * 128 = 43px each).
--
-- Called by BenillaKeyRingButton's own OnEvent (BagFrame.xml). Everything is nil-guarded:
-- this file loads without BagFrame.xml in several harnesses, and BagFrame.xml loads without
-- this one in ~25 bag/bank/escape/merchant tests — either way the missing half no-ops.
--
-- DIVERGENCE (decision 0765): the ref latches this ON forever the first time HasKey() is
-- true, via the saved variable SHOW_KEYRING (and pulses the button for 60s to announce it).
-- This engine has no saved variables, and a session-scoped latch would leak across a
-- character switch — showing a keyring on a character with no key — so the state is derived
-- from HasKey() on every BAG_UPDATE instead, in BOTH directions. Observably identical
-- unless you destroy your last key, which then un-shows the keyring rather than leaving an
-- empty one behind. The 60s pulse is out, consistently with decision 0304's "no button
-- pulse" for the micro buttons.
function BenillaMainMenuBar_UpdateKeyRing()
local show = HasKey and HasKey()
-- The two right-hand strips (BenillaActionBarTexture2 at x=+128, Texture3 at x=+384),
-- keyring plate or the ordinary dwarf plate. The dwarf coords are this file's own
-- authored ones (see the strips below) — restated here because this is the only code
-- that ever changes them back.
local strips = {
{ name = "BenillaActionBarTexture2",
keyring = 0.6640625, keyringBottom = 1.0,
dwarf = 0.33203125, dwarfBottom = 0.5 },
{ name = "BenillaActionBarTexture3",
keyring = 0.1640625, keyringBottom = 0.5,
dwarf = 0.08203125, dwarfBottom = 0.25 },
}
for _, strip in ipairs(strips) do
local tex = getglobal(strip.name)
if tex then
if show then
tex:SetTexture("Interface\\MainMenuBar\\UI-MainMenuBar-KeyRing")
tex:SetTexCoord(0, 1, strip.keyring, strip.keyringBottom)
else
tex:SetTexture("Interface\\MainMenuBar\\UI-MainMenuBar-Dwarf")
tex:SetTexCoord(0, 1, strip.dwarf, strip.dwarfBottom)
end
end
end
-- The performance meter's recess moves with the plate: -227 without the keyring,
-- -235 with it (ref l.180). Its own comment at the foot of this file has the geometry.
local perf = getglobal("BenillaPerformanceBarFrame")
local bar = getglobal("BenillaActionBar")
if perf and bar then
perf:SetPoint("BOTTOMRIGHT", bar, "BOTTOMRIGHT", (show and -235) or -227, -10)
end
local button = getglobal("BenillaKeyRingButton")
if button then
if show then button:Show() else button:Hide() end
end
-- Losing the last key with the keyring open would strand an orphan window; the ref
-- can't reach this state (its latch is one-way), so this is ours to decide.
local frame = getglobal("BenillaKeyRingFrame")
if frame and not show and frame:IsShown() then frame:Hide() end
end
-- The wire/API action id the MAIN bar's button i displays (the vanilla page formula).
-- INTERNAL to BenillaActionButton_Action below — the ONLY caller. A button path that
-- reaches past it computes a MAIN-bar id for a multibar button (base ignored), which
-- reads as "the slot below": the tooltip hover did exactly that until 2026-07-21.
function BenillaActionBar_ActionFor(index)
local offset = GetBonusBarOffset()
local base = 0
if offset > 0 then
base = (6 + offset - 1) * 12
end
return base + index
end
-- The action id a specific BUTTON displays: the ref forks on the parent frame's NAME
-- (ActionButton_GetPagedID:446-467 — MultiBarBottomLeft ⇒ page 6, MultiBarBottomRight ⇒
-- page 5, else the paged/bonus formula); we carry the fork as data — a fixed per-button
-- page base set at OnLoad (MultiBars.xml), nil for the bonus-paged main bar.
function BenillaActionButton_Action(button)
if button.base then
return button.base + button.index
end
return BenillaActionBar_ActionFor(button.index)
end
-- Hover: SetAction's pure delegation (decision 0276 — a SPELL slot renders the spell
-- tooltip with its rank column, an ITEM slot the shared item renderer; ref
-- ActionButton_SetTooltip). The re-enter keeps it live across page flips.
-- Anchor: ref ActionButton_SetTooltip (ActionButton.lua l.366-372) branches on the
-- UberTooltips CVar — "1" → the default corner, else RIGHT/LEFT by bar. UberTooltips
-- registers with default "1" (byte-read: WoW.exe 0x48fdd9, default string 0x82e748;
-- sibling rows BlockTrades→"0", UnitNameRenderMode→"2" confirm the layout), and benilla
-- ships no CVar state, so the branch collapses to the stock-client one: action-bar
-- tooltips live at the screen's bottom-right, above the bags.
function BenillaActionButton_OnEnter(button)
if not GameTooltip then return end -- engine tests load file subsets
GameTooltip_SetDefaultAnchor(GameTooltip, button)
GameTooltip:SetAction(BenillaActionButton_Action(button))
end
function BenillaActionButton_OnLoad(button, index, base)
button.index = index
button.base = base
button.icon = getglobal(button:GetName() .. "Icon")
button.hotkey = getglobal(button:GetName() .. "HotKey")
button.count = getglobal(button:GetName() .. "Count")
button.flash = getglobal(button:GetName() .. "Flash")
button.border = getglobal(button:GetName() .. "Border")
button.cooldown = getglobal(button:GetName() .. "Cooldown")
-- Ref ActionButton_OnLoad:104-106: the flash/range state the OnUpdate loop drives.
button.flashing = 0
button.flashtime = 0
button.rangeTimer = nil
if base then
-- Multibar buttons are unbound (ref Bindings.xml ships no default MULTIACTIONBAR*
-- keys): no static label. Their hotkey corner instead carries the ref's
-- RANGE_INDICATOR dot (ActionButton_UpdateHotkeys' no-binding leg, net effect:
-- a red dot only while out of range), painted by the OnUpdate range timer.
button.hotkey:SetText("")
else
button.hotkey:SetText(BENILLA_ACTIONBAR_HOTKEYS[index])
end
table.insert(BENILLA_ACTION_BUTTONS, button)
-- The drag trio (decision 0216 §3/§7): click-carry and drag share one downstream path
-- by construction (OnClick/OnDragStart both call PickupAction; OnReceiveDrag calls
-- PlaceAction — ref ActionBarFrame.xml:12-38). LOCK_ACTIONBAR stays the vanilla
-- default (unlocked), so unlike the ref's cvar guard, drag is always live here.
button:RegisterForDrag("LeftButton")
BenillaActionButton_Update(button)
end
-- Ref ActionBarFrame.xml:12-22's fork verbatim: shift picks up, else uses (checkCursor=1 —
-- the engine routes a click-with-payload to PlaceAction instead, decision 0216 §7).
function BenillaActionButton_OnClick(button)
local action = BenillaActionButton_Action(button)
if IsShiftKeyDown() then
PickupAction(action)
else
UseAction(action, 1)
end
BenillaActionButton_Update(button)
end
function BenillaActionButton_OnDragStart(button)
PickupAction(BenillaActionButton_Action(button))
BenillaActionButton_Update(button)
end
function BenillaActionButton_OnReceiveDrag(button)
PlaceAction(BenillaActionButton_Action(button))
BenillaActionButton_Update(button)
end
-- The default ACTIONBUTTON1..12 key bindings (ref Bindings.xml:121-127: keys 1..9,0,-,=
-- run ActionButtonDown on keystate "down" and ActionButtonUp on "up" via runOnUp), both
-- transcribed from ActionButton.lua:15-28/30-45 and routed here from the app's key feed.
-- DOWN pushes the button only from NORMAL; UP fires the action only from PUSHED — the
-- state pair is both the pushed visual and the debounce (a key-repeat DOWN or a stray UP
-- is a no-op). Calls UseAction DIRECTLY with NO checkCursor argument — unlike the
-- mouse-click fork above, a keybind never places (decision 0216 §7: the ref itself never
-- routes ActionButtonUp through OnClick, so this no longer goes through Click() either —
-- the two entry points share the id formula, not the click path). The ref's
-- BonusActionBar fork and the SELFACTIONBUTTON onSelf argument are not modeled (no bonus
-- bar frame, no self-cast modifier binding).
function BenillaActionButtonDown(index)
local button = getglobal("BenillaActionButton" .. index)
if ( button:GetButtonState() == "NORMAL" ) then
button:SetButtonState("PUSHED")
end
end
function BenillaActionButtonUp(index)
local button = getglobal("BenillaActionButton" .. index)
if ( button:GetButtonState() == "PUSHED" ) then
button:SetButtonState("NORMAL")
UseAction(BenillaActionButton_Action(button))
-- Ref ActionButtonUp:55-59: the checked ring reflects the use immediately.
BenillaActionButton_UpdateState(button)
end
end
-- Ref ActionButton_UpdateState:261-267 — the checked ring: the current action (Attack
-- while auto-attacking, the in-flight cast) or the live auto-repeat spell.
function BenillaActionButton_UpdateState(button)
local action = BenillaActionButton_Action(button)
if ( IsCurrentAction(action) or IsAutoRepeatAction(action) ) then
button:SetChecked(1)
else
button:SetChecked(0)
end
end
-- Ref ActionButton_UpdateUsable:269-283, verbatim colors: usable white; out of power the
-- blue-grey 0.5/0.5/1.0 on icon AND ring; otherwise-unusable the 0.4 grey icon only.
function BenillaActionButton_UpdateUsable(button)
local action = BenillaActionButton_Action(button)
local normalTexture = getglobal(button:GetName() .. "NormalTexture")
local isUsable, notEnoughMana = IsUsableAction(action)
if ( isUsable ) then
button.icon:SetVertexColor(1.0, 1.0, 1.0)
normalTexture:SetVertexColor(1.0, 1.0, 1.0)
elseif ( notEnoughMana ) then
button.icon:SetVertexColor(0.5, 0.5, 1.0)
normalTexture:SetVertexColor(0.5, 0.5, 1.0)
else
button.icon:SetVertexColor(0.4, 0.4, 0.4)
normalTexture:SetVertexColor(1.0, 1.0, 1.0)
end
end
-- Ref ActionButton_UpdateCooldown:294-298.
function BenillaActionButton_UpdateCooldown(button)
local start, duration, enable = GetActionCooldown(BenillaActionButton_Action(button))
CooldownFrame_SetTimer(button.cooldown, start, duration, enable)
end
-- Ref ActionButton_UpdateFlash/StartFlash/StopFlash/IsFlashing:469-496: the red blink on
-- an engaged Attack button / a live auto-repeat, toggled by the OnUpdate loop below.
function BenillaActionButton_UpdateFlash(button)
local action = BenillaActionButton_Action(button)
if ( (IsAttackAction(action) and IsCurrentAction(action)) or IsAutoRepeatAction(action) ) then
BenillaActionButton_StartFlash(button)
else
BenillaActionButton_StopFlash(button)
end
end
function BenillaActionButton_StartFlash(button)
button.flashing = 1
button.flashtime = 0
BenillaActionButton_UpdateState(button)
end
function BenillaActionButton_StopFlash(button)
button.flashing = 0
button.flash:Hide()
BenillaActionButton_UpdateState(button)
end
-- Ref ActionButton_OnUpdate:384-428, minus the RANGE_INDICATOR dot (our hotkeys always
-- carry binding text — SCOPE note) and the tooltip refresh (no per-button tooltip yet):
-- the 0.4 s flash toggle, and the 0.2 s range recheck tinting the hotkey red/grey.
function BenillaActionButton_OnUpdate(button, elapsed)
if ( button.flashing == 1 ) then
button.flashtime = button.flashtime - elapsed
if ( button.flashtime <= 0 ) then
local overtime = -button.flashtime
if ( overtime >= ATTACK_BUTTON_FLASH_TIME ) then
overtime = 0
end
button.flashtime = ATTACK_BUTTON_FLASH_TIME - overtime
if ( button.flash:IsVisible() ) then
button.flash:Hide()
else
button.flash:Show()
end
end
end
if ( button.rangeTimer ) then
button.rangeTimer = button.rangeTimer - elapsed
if ( button.rangeTimer <= 0 ) then
if ( IsActionInRange(BenillaActionButton_Action(button)) == 0 ) then
-- An unbound (multibar) button has no label to tint: paint the ref's
-- RANGE_INDICATOR dot instead (net ref behavior — red dot iff out of range).
if button.base then
button.hotkey:SetText("●")
end
button.hotkey:SetVertexColor(1.0, 0.1, 0.1)
else
if button.base then
button.hotkey:SetText("")
end
button.hotkey:SetVertexColor(0.6, 0.6, 0.6)
end
button.rangeTimer = TOOLTIP_UPDATE_TIME
end
end
end
function BenillaActionButton_Update(button)
local action = BenillaActionButton_Action(button)
if HasAction(action) then
button.icon:SetTexture(GetActionTexture(action) or BENILLA_FALLBACK_ICON)
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
button:Show()
button.rangeTimer = -1
local count = GetActionCount(action)
if count > 0 then
button.count:SetText(count)
else
button.count:SetText("")
end
BenillaActionButton_UpdateState(button)
BenillaActionButton_UpdateUsable(button)
BenillaActionButton_UpdateCooldown(button)
BenillaActionButton_UpdateFlash(button)
else
button.icon:SetTexture(nil)
button.count:SetText("")
button.cooldown:Hide()
button.rangeTimer = nil
button.hotkey:SetVertexColor(0.6, 0.6, 0.6)
button:SetChecked(0)
BenillaActionButton_StopFlash(button)
if button.base then
-- An EMPTY multibar well is the ref's own default: hidden entirely, appearing
-- as a drop-target ring only while a payload is held (ActionButton_Update's
-- showgrid==0 Hide; MultiActionBar_ShowAllGrids). Only the MAIN bar keeps our
-- always-visible wells divergence (the SCOPE note) — its art has sockets;
-- floating rings over bare world would not read as a bar.
button.hotkey:SetText("")
if BENILLA_ACTIONBAR_GRID_SHOWN then
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
button:Show()
else
button:Hide()
end
else
-- While a payload is held, an empty slot shows the ref's own "no action" ring
-- (Interface\Buttons\UI-Quickslot) so it reads as a drop target; otherwise the
-- plain occupied-style ring, unchanged from before this slice (SCOPE note).
if BENILLA_ACTIONBAR_GRID_SHOWN then
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
else
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
end
end
end
-- Ref ActionButton_Update:221-228: the equipped-item green border.
if ( IsEquippedAction(action) ) then
button.border:SetVertexColor(0, 1.0, 0, 0.35)
button.border:Show()
else
button.border:Hide()
end
end
function BenillaActionBar_OnLoad(frame)
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("ACTIONBAR_SLOT_CHANGED")
frame:RegisterEvent("UPDATE_BONUS_ACTIONBAR")
frame:RegisterEvent("ACTIONBAR_SHOWGRID")
frame:RegisterEvent("ACTIONBAR_HIDEGRID")
-- The state/feedback events (ref ActionButton_OnLoad's per-button set, registered
-- bar-level here — the SCOPE note): the app fires each on the byte-mapped client
-- edge (wow-re action-button-state-api.md).
frame:RegisterEvent("ACTIONBAR_UPDATE_STATE")
frame:RegisterEvent("ACTIONBAR_UPDATE_USABLE")
frame:RegisterEvent("ACTIONBAR_UPDATE_COOLDOWN")
frame:RegisterEvent("PLAYER_TARGET_CHANGED")
frame:RegisterEvent("PLAYER_AURAS_CHANGED")
frame:RegisterEvent("PLAYER_ENTER_COMBAT")
frame:RegisterEvent("PLAYER_LEAVE_COMBAT")
frame:RegisterEvent("START_AUTOREPEAT_SPELL")
frame:RegisterEvent("STOP_AUTOREPEAT_SPELL")
end
-- Run one ref per-button handler over the OCCUPIED buttons (the bar-level fan — SCOPE
-- note). The HasAction gate is the ref's own semantics, load-bearing, not an optimization:
-- ActionButton.lua registers these handlers only while the button has an action ("All
-- event handlers below this line are only set when the button has an action"), so an
-- empty button never runs them — UpdateUsable on an empty would land its 0.4 grey
-- SetVertexColor on the texture-less icon region, which draws as a solid grey plate
-- (the director's empty-wells report, 2026-07-10).
function BenillaActionBar_ForEach(fn)
for i = 1, table.getn(BENILLA_ACTION_BUTTONS) do
local button = BENILLA_ACTION_BUTTONS[i]
if HasAction(BenillaActionButton_Action(button)) then
fn(button)
end
end
end
function BenillaActionBar_OnEvent(frame)
-- ACTIONBAR_SLOT_CHANGED names its action in arg1; repaint just the button showing it.
if event == "ACTIONBAR_SLOT_CHANGED" and arg1 then
for i = 1, table.getn(BENILLA_ACTION_BUTTONS) do
local button = BENILLA_ACTION_BUTTONS[i]
if BenillaActionButton_Action(button) == arg1 then
BenillaActionButton_Update(button)
end
end
return
end
-- The ref ActionButton_OnEvent dispatch (l.332-362), fanned bar-level:
if event == "ACTIONBAR_UPDATE_STATE" then
BenillaActionBar_ForEach(BenillaActionButton_UpdateState)
return
end
if event == "ACTIONBAR_UPDATE_USABLE" or event == "ACTIONBAR_UPDATE_COOLDOWN" then
BenillaActionBar_ForEach(BenillaActionButton_UpdateUsable)
BenillaActionBar_ForEach(BenillaActionButton_UpdateCooldown)
return
end
if event == "PLAYER_TARGET_CHANGED" or event == "PLAYER_AURAS_CHANGED" then
BenillaActionBar_ForEach(BenillaActionButton_UpdateUsable)
return
end
if event == "PLAYER_ENTER_COMBAT" then
BenillaActionBar_ForEach(function(b)
if IsAttackAction(BenillaActionButton_Action(b)) then
BenillaActionButton_StartFlash(b)
end
end)
return
end
if event == "PLAYER_LEAVE_COMBAT" then
BenillaActionBar_ForEach(function(b)
if IsAttackAction(BenillaActionButton_Action(b)) then
BenillaActionButton_StopFlash(b)
end
end)
return
end
if event == "START_AUTOREPEAT_SPELL" then
BenillaActionBar_ForEach(function(b)
if IsAutoRepeatAction(BenillaActionButton_Action(b)) then
BenillaActionButton_StartFlash(b)
end
end)
return
end
if event == "STOP_AUTOREPEAT_SPELL" then
BenillaActionBar_ForEach(function(b)
if b.flashing == 1 and not IsAttackAction(BenillaActionButton_Action(b)) then
BenillaActionButton_StopFlash(b)
end
end)
return
end
if event == "ACTIONBAR_SHOWGRID" or event == "ACTIONBAR_HIDEGRID" then
BENILLA_ACTIONBAR_GRID_SHOWN = (event == "ACTIONBAR_SHOWGRID")
end
for i = 1, table.getn(BENILLA_ACTION_BUTTONS) do
BenillaActionButton_Update(BENILLA_ACTION_BUTTONS[i])
end
end
-- The XP bar (ref-MainMenuBar.lua MainMenuExpBar_Update): value = current XP within the
-- level, max = XP-to-level. Both from the player-global UnitXP feed (PLAYER_XP /
-- PLAYER_NEXT_LEVEL_XP). currXP >= 0 so min(0,currXP) is just 0.
function BenillaExpBar_Update(bar)
local currXP = UnitXP("player")
local nextXP = UnitXPMax("player")
bar:SetMinMaxValues(0, nextXP)
bar:SetValue(currXP)
end
function BenillaExpBar_OnLoad(bar)
bar:RegisterEvent("PLAYER_ENTERING_WORLD")
bar:RegisterEvent("PLAYER_XP_UPDATE")
BenillaExpBar_Update(bar)
end
function BenillaExpBar_OnEvent(bar)
BenillaExpBar_Update(bar)
end
-- The XP bar's hover (ref-MainMenuBar.xml l.136-147, MainMenuExpBar's own OnEnter/OnLeave).
-- ref GlobalStrings.lua l.5461 / l.2789 (enUS), in the `X = X or` FALLBACK form: the app
-- runs the real localized GlobalStrings.lua off the patch chain BEFORE this file
-- (ui_script/mod.rs load_global_strings), so the installed locale wins and these copies
-- only fill in for the test harnesses, which load no patch chain. CombatText.xml's shape.
XPBAR_LABEL = XPBAR_LABEL or "XP Bar"
NEWBIE_TOOLTIP_XPBAR = NEWBIE_TOOLTIP_XPBAR or "The amount of experience (XP) you have earned. The color of the XP bar indicates your rest state: light blue for Rested, and purple for Normal. Rested players earn twice the experience they would normally gain from slaying a monster. Characters become less rested when they kill monsters, and more rested by spending time at or logging out at an inn or city."
-- The ref's OnEnter does three more things, all of them machinery we don't have yet, and
-- each one its own slice rather than a gap in this one:
-- TextStatusBar_UpdateTextString() + ShowTextStatusBarText(this) — the "1234/5678" numerals
-- that appear ON the bar while hovered. No TextStatusBar text layer exists here at all.
-- ExhaustionTick.timer = 1 and GameTooltip.canAddRestStateLine = 1 — the rest-state line
-- ("You should rest at an Inn", EXHAUST_TOOLTIP1-4) the exhaustion tick appends. There is
-- no rest-state feed, so there is nothing to append.
-- The trailing 1 is the ref's noNormalText: with detailed tips OFF this hover shows NOTHING
-- rather than a bare "XP Bar". Transcribed, though 1.12's default (0661) never takes it.
function BenillaExpBar_OnEnter(bar)
if not GameTooltip_AddNewbieTip then return end -- engine tests load file subsets
GameTooltip_AddNewbieTip(bar, XPBAR_LABEL, 1.0, 1.0, 1.0, NEWBIE_TOOLTIP_XPBAR, 1)
end
function BenillaExpBar_OnLeave()
if GameTooltip then GameTooltip:Hide() end
end
-- ── The performance ("ping") meter ────────────────────────────────────────────────────
-- ref-MainMenuBar.xml l.344-407: a 16x64 frame in the bar's own right-hand recess whose
-- 20x66 bar texture is TINTED green/yellow/red by the connection's latency, plus an
-- all-points button on top for the hover tooltip. The thresholds, the 10 s poll and the
-- inline handlers below are the ref's, transcribed out of its XML into named functions.
-- The meter shows LATENCY ONLY: 1.12 never scales the bar's height, only its color.
PERFORMANCEBAR_LOW_LATENCY = 300
PERFORMANCEBAR_MEDIUM_LATENCY = 600
PERFORMANCEBAR_UPDATE_INTERVAL = 10
-- ref-GlobalStrings.lua l.2659 / l.2689 / l.2746 (enUS), fallback form — see the XP bar's
-- note above for why these never override a real localized install.
MAINMENUBAR_LATENCY_LABEL = MAINMENUBAR_LATENCY_LABEL or "Latency:"
MILLISECONDS_ABBR = MILLISECONDS_ABBR or "ms"
NEWBIE_TOOLTIP_LATENCY = NEWBIE_TOOLTIP_LATENCY or "The average time it takes to talk with the game server. A low latency will display as a green bar, higher latencies will be yellow or even red. Consistently high latencies may indicate a problem with your Internet connection."
function BenillaPerformanceBar_OnLoad(frame)
frame.hover = nil
-- 0, not the interval: the ref's first OnUpdate tick runs the poll immediately, so the
-- bar is never briefly untinted (its texture is grey until the first SetVertexColor).
frame.updateInterval = 0
end
function BenillaPerformanceBar_TooltipText()
local _, _, latency = GetNetStats()
return MAINMENUBAR_LATENCY_LABEL .. " " .. latency .. MILLISECONDS_ABBR
end
-- ref l.389 (the held-open refresh) and l.400 (OnEnter), both the same call:
-- GameTooltip_AddNewbieTip(MAINMENUBAR_LATENCY_LABEL.." "..latency..MILLISECONDS_ABBR,
-- 1.0, 1.0, 1.0, NEWBIE_TOOLTIP_LATENCY)
-- So the reference hover is TWO lines — the number, then the wrapped explanation — seated
-- at the default screen corner, not beside the meter (GameTooltip.xml, decision 0661).
function BenillaPerformanceBar_ShowTooltip(owner)
if not GameTooltip_AddNewbieTip then return end -- engine tests load file subsets
GameTooltip_AddNewbieTip(owner, BenillaPerformanceBar_TooltipText(), 1.0, 1.0, 1.0,
NEWBIE_TOOLTIP_LATENCY)
end
function BenillaPerformanceBar_OnUpdate(frame, elapsed)
if frame.updateInterval > 0 then
frame.updateInterval = frame.updateInterval - elapsed
return
end
frame.updateInterval = PERFORMANCEBAR_UPDATE_INTERVAL
local _, _, latency = GetNetStats()
if latency > PERFORMANCEBAR_MEDIUM_LATENCY then
BenillaPerformanceBar:SetVertexColor(1, 0, 0)
elseif latency > PERFORMANCEBAR_LOW_LATENCY then
BenillaPerformanceBar:SetVertexColor(1, 1, 0)
else
BenillaPerformanceBar:SetVertexColor(0, 1, 0)
end
-- Held-open tooltips re-read the number on the same 10 s beat (the ref's own refresh).
if frame.hover then
BenillaPerformanceBar_ShowTooltip(frame)
end
end
function BenillaPerformanceBar_OnEnter(button)
BenillaPerformanceBarFrame.hover = 1
BenillaPerformanceBar_ShowTooltip(button)
end
function BenillaPerformanceBar_OnLeave()
BenillaPerformanceBarFrame.hover = nil
if GameTooltip then GameTooltip:Hide() end
end
]]></Script>
<!--
One action button (ref-ActionButtonTemplate.xml, now the FULL template — decision 0137
phase 4): a CheckButton, 36x36 (l.3-6); icon fills the button (BACKGROUND, owner-sized,
l.8-10); Flash (UI-QuickslotRed, hidden — the auto-attack/auto-repeat blink) + HotKey
(l.11-24) + Count (l.25-33) on ARTWORK; Border (UI-ActionButton-Border 62x62 CENTER, ADD,
hidden — the equipped-item green ring, l.48-59) on OVERLAY; the Cooldown child (the ref's
CooldownFrameTemplate Model at 36x36 CENTER (0,-1), l.62-73 — ours is the native Cooldown
widget playing the same machine); NormalTexture = the 66x66 UI-Quickslot2 ring centered at
(0,-1) (l.74-85); PushedTexture UI-Quickslot-Depress (l.86); HighlightTexture
ButtonHilight-Square ADD (l.87); CheckedTexture CheckButtonHilight ADD (l.88 — the
IsCurrentAction/IsAutoRepeatAction ring). Only Name (macros) stays OUT.
-->
<CheckButton name="BenillaActionButtonTemplate" virtual="true">
<Size><AbsDimension x="36" y="36"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentIcon"/>
</Layer>
<Layer level="ARTWORK">
<Texture name="$parentFlash" file="Interface\Buttons\UI-QuickslotRed" hidden="true"/>
<FontString name="$parentHotKey" inherits="NumberFontNormalSmallGray" justifyH="RIGHT">
<Size><AbsDimension x="36" y="10"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="-2" y="-2"/></Offset></Anchor>
</Anchors>
</FontString>
<FontString name="$parentCount" inherits="NumberFontNormal" justifyH="RIGHT">
<Anchors>
<Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-2" y="2"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
<Layer level="OVERLAY">
<Texture name="$parentBorder" file="Interface\Buttons\UI-ActionButton-Border" hidden="true" alphaMode="ADD">
<Size><AbsDimension x="62" y="62"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<Cooldown name="$parentCooldown" hidden="true">
<Size><AbsDimension x="36" y="36"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
</Anchors>
</Cooldown>
</Frames>
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
<Size><AbsDimension x="66" y="66"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="-1"/></Offset></Anchor>
</Anchors>
</NormalTexture>
<PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
<CheckedTexture file="Interface\Buttons\CheckButtonHilight" alphaMode="ADD"/>
<Scripts>
<OnClick>BenillaActionButton_OnClick(self)</OnClick>
<OnDragStart>BenillaActionButton_OnDragStart(self)</OnDragStart>
<OnReceiveDrag>BenillaActionButton_OnReceiveDrag(self)</OnReceiveDrag>
<OnUpdate>BenillaActionButton_OnUpdate(self, arg1)</OnUpdate>
<OnEnter>BenillaActionButton_OnEnter(self)</OnEnter>
<OnLeave>if GameTooltip then GameTooltip:Hide() end</OnLeave>
</Scripts>
</CheckButton>
<!-- The bar: 1024x53 anchored BOTTOM of the screen (ref-MainMenuBar.xml l.4-10). -->
<Frame name="BenillaActionBar">
<Size><AbsDimension x="1024" y="53"/></Size>
<Anchors>
<Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<Frames>
<!--
The XP StatusBar: 1024x13 anchored TOP of the bar (ref-MainMenuBar.xml l.12-18).
BarTexture UI-StatusBar, BarColor purple (0.58,0,0.55) (l.158-159); a solid black
a=0.5 BACKGROUND (l.89-93); and the 4 dwarf-notch OVERLAY strips (256x10, BOTTOM
x=-384/-128/128/384 y=3, the tick TexCoord bands l.36-87) that frame the fill.
Drawn first among the bar's children ⇒ lowest, so the dwarf art overlaps it (the
real RaiseFrameLevel(art) order).
-->
<!-- enableMouse: the ref's MainMenuExpBar takes the mouse for its own hover (l.136-147).
The strip is the bar's top 13 px, clear of every button on it. -->
<StatusBar name="BenillaExpBar" enableMouse="true">
<Size><AbsDimension x="1024" y="13"/></Size>
<Anchors>
<Anchor point="TOP" relativePoint="TOP"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture>
<Color r="0.0" g="0.0" b="0.0" a="0.5"/>
</Texture>
</Layer>
<Layer level="OVERLAY">
<Texture name="BenillaXPBarTexture0" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="10"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="-384" y="3"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.79296875" bottom="0.83203125"/>
</Texture>
<Texture name="BenillaXPBarTexture1" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="10"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="-128" y="3"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.54296875" bottom="0.58203125"/>
</Texture>
<Texture name="BenillaXPBarTexture2" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="10"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="128" y="3"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.29296875" bottom="0.33203125"/>
</Texture>
<Texture name="BenillaXPBarTexture3" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="10"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="384" y="3"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.04296875" bottom="0.08203125"/>
</Texture>
</Layer>
</Layers>
<BarTexture file="Interface\TargetingFrame\UI-StatusBar"/>
<BarColor r="0.58" g="0.0" b="0.55"/>
<Scripts>
<OnLoad>BenillaExpBar_OnLoad(self)</OnLoad>
<OnEvent>BenillaExpBar_OnEvent(self)</OnEvent>
<OnEnter>BenillaExpBar_OnEnter(self)</OnEnter>
<OnLeave>BenillaExpBar_OnLeave()</OnLeave>
</Scripts>
</StatusBar>
<!--
The art frame: fills the bar (ref-MainMenuBar.xml l.225-229). Carries the 4 dwarf
strips (ARTWORK) + the 2 end caps and page number (OVERLAY); the buttons are its
children so they draw over the art.
-->
<Frame name="BenillaActionBarArtFrame">
<Anchors>
<Anchor point="TOPLEFT" relativeTo="BenillaActionBar" relativePoint="TOPLEFT"/>
<Anchor point="BOTTOMRIGHT" relativeTo="BenillaActionBar" relativePoint="BOTTOMRIGHT"/>
</Anchors>
<Layers>
<!-- 4 dwarf strips: 256x43, BOTTOM x=-384/-128/128/384 y=0, TexCoord bands
(ref-MainMenuBar.xml l.232-283). Adjacent atlas rows ⇒ the joins are seamless. -->
<Layer level="ARTWORK">
<Texture name="BenillaActionBarTexture0" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="43"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="-384" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.83203125" bottom="1.0"/>
</Texture>
<Texture name="BenillaActionBarTexture1" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="43"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="-128" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.58203125" bottom="0.75"/>
</Texture>
<Texture name="BenillaActionBarTexture2" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="43"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="128" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.33203125" bottom="0.5"/>
</Texture>
<Texture name="BenillaActionBarTexture3" file="Interface\MainMenuBar\UI-MainMenuBar-Dwarf">
<Size><AbsDimension x="256" y="43"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="384" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0.08203125" bottom="0.25"/>
</Texture>
</Layer>
<!-- End caps: UI-MainMenuBar-EndCap-Dwarf 128x128, BOTTOM x=-544 / +544; the RIGHT
cap mirrors its art with TexCoords (1.0, 0.0, 0.0, 1.0) so the lion faces
outward on both sides (ref-MainMenuBar.xml l.286-310; the mirrored-UV engine
fix landed in 87c97d7). Page number: GameFontNormalSmall CENTER (30,-5),
static "1" (l.311-319). -->
<Layer level="OVERLAY">
<Texture name="BenillaActionBarLeftEndCap" file="Interface\MainMenuBar\UI-MainMenuBar-EndCap-Dwarf">
<Size><AbsDimension x="128" y="128"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="-544" y="0"/></Offset></Anchor></Anchors>
</Texture>
<Texture name="BenillaActionBarRightEndCap" file="Interface\MainMenuBar\UI-MainMenuBar-EndCap-Dwarf">
<Size><AbsDimension x="128" y="128"/></Size>
<Anchors><Anchor point="BOTTOM" relativePoint="BOTTOM"><Offset><AbsDimension x="544" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="1.0" right="0.0" top="0.0" bottom="1.0"/>
</Texture>
<FontString name="BenillaActionBarPageNumber" inherits="GameFontNormalSmall" text="1">
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="30" y="-5"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- The 12 buttons: Button1 at the art frame BOTTOMLEFT (8,4); 2..12 chain LEFT of
the previous's RIGHT +6,0 (ref-ActionBarFrame.xml l.51-158). Replaces our old
fixed 38px stride with the real 42px chain (36 wide + 6 gap). -->
<CheckButton name="BenillaActionButton1" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="8" y="4"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 1)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton2" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton1" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 2)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton3" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton2" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 3)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton4" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton3" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 4)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton5" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton4" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 5)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton6" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton5" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 6)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton7" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton6" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 7)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton8" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton7" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 8)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton9" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton8" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 9)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton10" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton9" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 10)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton11" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton10" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 11)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="BenillaActionButton12" inherits="BenillaActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaActionButton11" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaActionButton_OnLoad(self, 12)</OnLoad></Scripts>
</CheckButton>
<!-- Page up/down: 32x32 at CENTER of the art frame's TOPLEFT +(522,-22)/(522,-42)
(ref-ActionBarFrame.xml l.159-217). ART ONLY — we have no bar paging, so the
clicks are no-ops (no OnClick handler). -->
<Button name="BenillaActionBarUpButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors>
<Anchor point="CENTER" relativeTo="BenillaActionBarArtFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="522" y="-22"/></Offset></Anchor>
</Anchors>
<NormalTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Up"/>
<PushedTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Down"/>
<HighlightTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollUpButton-Highlight" alphaMode="ADD"/>
</Button>
<Button name="BenillaActionBarDownButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors>
<Anchor point="CENTER" relativeTo="BenillaActionBarArtFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="522" y="-42"/></Offset></Anchor>
</Anchors>
<NormalTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Up"/>
<PushedTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Down"/>
<HighlightTexture file="Interface\MainMenuBar\UI-MainMenu-ScrollDownButton-Highlight" alphaMode="ADD"/>
</Button>
</Frames>
</Frame>
<!-- The performance ("ping") meter (ref-MainMenuBar.xml l.344-407). 16x64 pinned to the
bar's BOTTOMRIGHT +(-227,-10) — the empty recess between the last micro button and
the bag slots, which the bar art leaves TRANSPARENT for exactly this. The 20x66
texture hangs off the frame's TOPRIGHT (so it overspills 4 left and 2 below), and
the frame itself hangs 10 below the bar, putting the bar's grey column in the slot.
frameStrata="LOW" is load-bearing, not decoration: the meter draws UNDER the bar art
and shows through its transparent window. At the default MEDIUM it would paint over
the metal surround, since the texture is wider than the slot. (The ref gets the same
order via strata too — MainMenuBar's OnLoad RaiseFrameLevel can't reach across it.)
The hover button is the ref's own split: a HIGH-strata all-points button, because the
LOW frame beneath the art can't take the mouse itself. The ref declares it as a
sibling with parent="MainMenuBarPerformanceBarFrame"; nesting it is the same frame
tree, and its explicit strata survives the parent's LOW subtree-force (attributes
apply before nested <Frames> are built).
The -227 authored here is the NO-keyring position; BenillaMainMenuBar_UpdateKeyRing
(top of this file) re-points it to -235 while the keyring is shown, the ref's own
shift (MainMenuBar.lua l.180). Both offsets live in that one function. -->
<Frame name="BenillaPerformanceBarFrame" frameStrata="LOW">
<Size><AbsDimension x="16" y="64"/></Size>
<Anchors>
<Anchor point="BOTTOMRIGHT" relativeTo="BenillaActionBar" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-227" y="-10"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture name="BenillaPerformanceBar" file="Interface\MainMenuBar\UI-MainMenuBar-PerformanceBar">
<Size><AbsDimension x="20" y="66"/></Size>
<Anchors><Anchor point="TOPRIGHT"/></Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<Button name="BenillaPerformanceBarButton" setAllPoints="true" frameStrata="HIGH">
<Scripts>
<OnEnter>BenillaPerformanceBar_OnEnter(self)</OnEnter>
<OnLeave>BenillaPerformanceBar_OnLeave()</OnLeave>
</Scripts>
</Button>
</Frames>
<Scripts>
<OnLoad>BenillaPerformanceBar_OnLoad(self)</OnLoad>
<OnUpdate>BenillaPerformanceBar_OnUpdate(self, arg1)</OnUpdate>
</Scripts>
</Frame>
</Frames>
<Scripts>
<OnLoad>BenillaActionBar_OnLoad(self)</OnLoad>
<OnEvent>BenillaActionBar_OnEvent(self)</OnEvent>
</Scripts>
</Frame>
</Ui>