Files
benilla-octo/crates/benilla-app/assets/ui/PetActionBar.xml
T
SamandClaude Fable 5 5ce93810b0 the autocast shine squares up, and a pill watches the tail
- the shine's stars size by the screen diagonal instead of a frozen 4:3 constant, so a widescreen window stops blobbing them
- each button's shine wears its own authored geometry: the spell book's 36x36 model rect at 1.22 scale, not the pet bar's — and its autocast brackets reach the button's corners with the glow sharing their centre
- one deliberate deviation, recorded as such: the spell book's shine borrows the pet button's rim ratio, because the reference's own washes out there
- the perf HUD gains a pill that measures frame cost and its tail, and latches the spike that would otherwise scroll away

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-17 17:03:18 +07:00

559 lines
34 KiB
XML

<Ui>
<!--
The pet action bar (ref PetActionBarFrame.xml/.lua, extracted from patch.MPQ; decision
0982). Hunter, warlock, and every possessed/charmed unit: ten buttons over the ten packed
words the server sends in SMSG_PET_SPELLS, driven by `crate::ui_pet`'s feed through the
engine's pet seam (GetPetActionInfo & co.).
THE THING TO KNOW ABOUT THIS BAR: the server owns its CONTENTS (all ten slots arrive in
one SMSG_PET_SPELLS) but the CLIENT owns its lit state. A press repaints immediately and
is never confirmed, because the server does not answer these packets at all — the real
client writes its own state before the send (decision 0988). So a button whose ring does
not light on click is BROKEN, not patient.
The ten slots are not a layout. `GetPetActionInfo` reports each slot's own class off its
type byte, and the familiar 3 commands / 4 spells / 3 reactions arrangement is just what
vmangos's CharmInfo::InitPetActionBar happens to write; a charmed creature arrives with a
different mix through the same ten words. Nothing below indexes by position.
Token slots (the commands and reactions) return the NAMES OF GLOBALS rather than values,
which is the reference's own convention (`PetActionBarFrame.lua:98-104`): the name keys are
GlobalStrings' (PET_ACTION_ATTACK, PET_MODE_DEFENSIVE, …), and the texture globals are
declared below. Both halves of that mapping are a contract we do not get to choose — the
names are what `ui_pet/bar.rs` hands back and the icons are real files in the install — so
the specification for them is `PetActionBarFrame.lua:6-12`; see the declaration's own note
for what IS ours about the layout.
Geometry QUOTED from the ref (PetActionBarFrame.xml): frame 509x43; the two UI-PetBar shelf
strips (256x44 at TOPLEFT with TexCoords 0..1 / 0.015625..0.359375, then 184x44 to its
right with 0..0.71875 / 0.375..0.71875); buttons 30x30, button1 at frame BOTTOMLEFT +(36,2),
chained +8 (with the ref's own +7 between 6 and 7); the 54x54 UI-Quickslot2 ring CENTER
(0,-1). The bar rides `MainMenuBar` BOTTOMLEFT — our MainMenuBar, the same 1024x53
frame at screen BOTTOM the ref anchors to — at the managed PETACTIONBAR_YPOS (UIParent.xml
declares the row already).
Ref divergences, each deliberate:
- ONE refresh event. The app diffs the whole pushed bar and fires PET_BAR_UPDATE on any
change, so the ref's PET_BAR_UPDATE / PET_BAR_UPDATE_COOLDOWN / UNIT_PET / the
UNIT_FLAGS+UNIT_AURA pair it filters for arg1=="pet" collapse into one — the same
collapse StanceBar.xml makes, for the same reason.
- The drag is ONE verb. PickupPetAction serves shift-click, OnDragStart and OnReceiveDrag
alike (decision 1010) — that is the reference's own shape, not a collapse of ours: the
binding itself forks on whether the cursor is already carrying a pet payload, so there is
no "place" counterpart the way PlaceAction answers PickupAction on the main bar. Nothing
else can produce that payload, which is why the pet bar can be rearranged and never
populated: its contents are the server's.
- PET_BAR_SHOWGRID/HIDEGRID are live, and are the PET payload's alone. The engine derives
them off the same cursor transition that drives ACTIONBAR_SHOWGRID/HIDEGRID but on a
disjoint predicate, so a held spell lights the action bar's empty slots and a held pet
action lights this bar's — never both, because neither payload can land on the other bar.
- NO HotKey corner, and none is missing (1052). The ref's button inherits
ActionButtonTemplate's $parentHotKey and repaints it on UPDATE_BINDINGS — but
PetActionButton_SetHotkeys (l.311-319) writes the label only when
`bindingSuffix == this:GetID()`, a STRING against a NUMBER, which Lua never equates. So
the real 1.12 pet bar renders an empty hotkey on every slot, always, and the reporter's
own reference shot for B218 shows exactly that: CTRL-1..CTRL-5 bound in the window, no
glyph on any pet button. An empty corner is the faithful render; a filled one would be
the divergence. (Our own action bar's corners are real — ActionBar.xml — because the
ref's ActionButton_UpdateHotkeys has no such bug.)
- The autocast sparkle is TEXTURES, not the ref's <Model>. benilla has no Model widget
(the Cooldown and the bag ItemAnim took the same posture): model the mechanism, let our
own renderer draw it. The numbers below are the shipped model's own — see BENILLA_PET_
AUTOCAST_* .
-->
<Script><![CDATA[
NUM_PET_ACTION_SLOTS = 10
-- ── The token icons ───────────────────────────────────────────────────────────────────
--
-- These are GLOBALS on purpose, and the names are a contract, not a choice:
-- `GetPetActionInfo` hands back the NAME of one of them for a token slot and the paint
-- below resolves it with getglobal — the reference's own convention, and the one
-- `ui_pet/bar.rs` writes against. The icon paths are the reference's too; they name real
-- .blp files in the install, so there is nothing to choose there either.
--
-- What IS ours is the grouping. The bar's tokens come in two families and `ui_pet/bar.rs`
-- already separates them at the seam that names these — `command_token` (the orders you
-- give, keyed `PET_ACTION_*`) and `reaction_token` (the stance the pet holds, keyed
-- `PET_MODE_*`) — so they are laid out in those two families, in that file's own order,
-- and the two halves of the mapping read side by side. The reference keeps them in one
-- undifferentiated block, which hides the split.
-- Commands — the four orders, in `ui_pet/bar.rs`'s `command_token` order.
PET_WAIT_TEXTURE = "Interface\\Icons\\Spell_Nature_TimeStop"
PET_FOLLOW_TEXTURE = "Interface\\Icons\\Ability_Tracking"
PET_ATTACK_TEXTURE = "Interface\\Icons\\Ability_GhoulFrenzy"
PET_DISMISS_TEXTURE = "Interface\\Icons\\Spell_Shadow_Teleport"
-- Reactions — the three stances, in `ui_pet/bar.rs`'s `reaction_token` order.
PET_PASSIVE_TEXTURE = "Interface\\Icons\\Ability_Seal"
PET_DEFENSIVE_TEXTURE = "Interface\\Icons\\Ability_Defend"
PET_AGGRESSIVE_TEXTURE = "Interface\\Icons\\Ability_Racial_BloodRage"
-- ── The autocast sparkle ──────────────────────────────────────────────────────────────
--
-- NATIVE since decision 1383 (B282). The reference's "autocast is on" mark is a <Model>
-- playing Interface\Buttons\UI-AutoCastButton.mdx — a widget OUTSIDE the FrameXML layout
-- tree — and our old emulation here (a per-button pool of ~92 OVERLAY textures SetPoint'ed
-- every frame from OnUpdate) made the most expensive possible use of the script layer:
-- every frame it dirtied the layout epoch and forced two whole-roster anchor solves,
-- +4.25 ms/frame for ONE autocasting button at a default roster, scaling with the whole
-- UI (B282, nazriel's ~11 ms under addons). What a button carries now is ONE hidden
-- marker texture, $parentShine, whose path is the `benilla:autocast-shine` token: the
-- extract's conversion records its rect/layer as a shine site and draws nothing, and
-- `autocast_shine::emit_shine` (benilla-app) animates the four comet trails there on the
-- UI quad append lane — zero per-frame script traffic, the reference's own architecture
-- on our substrate. The M2 derivation (bones, emitters, ramps, the 30.72-unit projection,
-- decision 1321's truncating millisecond clock) moved whole into that module's header;
-- this file's only job is showing the marker exactly while autocast is enabled.
-- ── The buttons ───────────────────────────────────────────────────────────────────────
function BenillaPetActionButton_OnLoad(button, index)
-- Ref PetActionButton_OnLoad l.234-235. The right-click registration is load-bearing,
-- not boilerplate: it is the whole autocast toggle, and without it a right click is
-- simply never delivered.
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
-- The drag trio (decision 1010). Both buttons drag, ref-verbatim
-- (PetActionButton_OnLoad l.234) — and unlike the action bar's, ALL THREE gestures here
-- reach the same verb: the reference's OnClick-under-shift, OnDragStart and
-- OnReceiveDrag all call PickupPetAction, which forks on whether the cursor is already
-- carrying. Both drag ends honour LOCK_ACTIONBAR since 1136 — the same uvar the main bar
-- declares (ActionBar.xml), which is the ref's own arrangement too: PetActionBarFrame.lua
-- reads a global UIOptionsFrame owns.
button:RegisterForDrag("LeftButton", "RightButton")
button.index = index
button.icon = getglobal(button:GetName() .. "Icon")
button.cooldown = getglobal(button:GetName() .. "Cooldown")
button.autocastable = getglobal(button:GetName() .. "AutoCastable")
button.shine = getglobal(button:GetName() .. "Shine")
end
-- Ref PetActionButton_OnClick. The CheckButton widget toggles itself BEFORE OnClick fires,
-- and the reference immediately clears that (`this:SetChecked(0)`) — nothing about this bar
-- is locally latched, because the server owns every bit of it. Left = run the slot, unless
-- the pet is already on the attack, in which case it calls off; right = flip autocast.
function PetActionButton_OnClick(mouseButton)
local button = this
button:SetChecked(0)
-- Shift outranks the button fork entirely (ref l.253-255): a shift-click on the pet bar
-- is a pick-up whichever mouse button it came from, and it never toggles autocast.
if IsShiftKeyDown() then
PickupPetAction(button.index)
PetActionBar_Update()
elseif mouseButton == "RightButton" then
TogglePetAutocast(button.index)
elseif IsPetAttackActive(button.index) then
PetStopAttack()
else
CastPetAction(button.index)
end
end
-- The KEYBIND pair (decision 1052) — ref PetActionButtonDown/Up l.218-231, which is where
-- the BONUSACTIONBUTTONn bindings land: their bodies call BonusActionButtonDown/Up, and
-- those are one-liners onto these (BonusActionBarFrame.lua:106-112). The ref's own
-- runOnUp shape: DOWN only shows the pushed art, UP fires.
--
-- Note what the key path does NOT do — it is a bare CastPetAction, with none of the click
-- path's forks: no shift pick-up, no right-button autocast, and no IsPetAttackActive
-- call-off. That is the reference's split, not a simplification of ours: the fork lives in
-- PetActionButton_OnClick, which a key press never reaches.
--
-- On a PET bar none of that is visible, because the call-off fork is DEAD CODE there:
-- IsPetAttackActive needs the [0xb714b0] latch, and `0x4bd420` raises it only when the
-- bar's unit is the one you are POSSESSING (ui_pet.rs's `possessing` carries the carve —
-- UNIT_FLAG_POSSESSED, never a hunter's or warlock's pet). A pet's Attack button can never
-- latch lit, so click and key both simply order the attack. The split is visible on a
-- POSSESS bar (Mind Control, Eye of Kilrogg, Eyes of the Beast): there a click sends
-- PetStopAttack and CTRL-1 re-issues the order instead.
function PetActionButtonDown(id)
local button = getglobal("PetActionButton" .. id)
if ( button:GetButtonState() == "NORMAL" ) then
button:SetButtonState("PUSHED")
end
end
function PetActionButtonUp(id)
local button = getglobal("PetActionButton" .. id)
if ( button:GetButtonState() == "PUSHED" ) then
button:SetButtonState("NORMAL")
CastPetAction(id)
end
end
-- Both drag ends are the SAME verb (ref l.269-283, byte-verified at `0x4be180`): with an
-- empty cursor it picks the slot up, with a pet payload already held it drops onto this
-- slot. There is no separate "place" binding on this bar, which is also why a spell or an
-- item can never land here — nothing but a pet slot produces the payload it accepts.
--
-- Both also honour the lock (the ref wraps each body in `if LOCK_ACTIONBAR ~= "1" then …`).
-- The shift-click in OnClick above is unguarded on this bar too, exactly as the reference
-- leaves it (l.253-255): the lock stops the drag, shift-click stays the way through.
function PetActionButton_OnDragStart()
local button = this
if LOCK_ACTIONBAR == "1" then return end
PickupPetAction(button.index)
PetActionBar_Update()
end
function PetActionButton_OnReceiveDrag()
local button = this
if LOCK_ACTIONBAR == "1" then return end
PickupPetAction(button.index)
PetActionBar_Update()
end
-- Ref PetActionButton_OnEnter (PetActionBarFrame.lua l.285-305). ONE predicate,
-- `isToken or UberTooltips == "0"`, decides two things at once:
--
-- * WHICH TOOLTIP. A token slot has no spell to look up, so its tooltip is built here out
-- of the two strings the info call already returned — and the BINDING (B230). Anything
-- else is a real pet SPELL and goes through the engine's tooltip channel, which appends
-- nothing. So the parenthesised key belongs to Attack/Follow/Stay and the reactions, and
-- NOT to the pet's own spells: putting it on every slot would be the wrong fix.
-- * WHERE IT SITS. Only the UberTooltips-off leg moves the tooltip beside the button
-- (ANCHOR_RIGHT); a token with the CVar on, and every spell, take the default corner.
-- The CVar registers "1" (cvars.rs carries the byte-read), so the off leg is the
-- player's to reach and not what ships.
--
-- The suffix is the ref's own expression and it is UNCONDITIONAL — no `if the key exists`
-- guard, unlike MicroButtonTooltipText's version of the same idea (MicroMenu.xml). That is
-- visible, not academic: GetBindingText answers "" for a nil key (UIParent.xml, transcribed
-- from the ref's own l.1819-1821), so 1.12 really does render "Attack ()" on a token slot
-- whose row is unbound. Stock, none are: BONUSACTIONBUTTON1-10 default to CTRL-1..CTRL-0
-- (bindings/commands.rs, byte-real from the install's bindings-cache.wtf).
--
-- Those same ten rows are the binding B218 found the CORNER hotkey glyph can never show
-- (the header's ref-divergence list has the `bindingSuffix == this:GetID()` string-vs-number
-- reason). Corner empty, tooltip parenthesised — both faithful, at once.
function PetActionButton_OnEnter()
local button = this
if not GameTooltip then return end -- engine tests load file subsets
if not button.tooltipName then return end
local uber = GetCVar("UberTooltips")
if button.isToken or uber == "0" then
if uber == "0" then
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
else
GameTooltip_SetDefaultAnchor(GameTooltip, button)
end
local key = GetBindingText(GetBindingKey("BONUSACTIONBUTTON" .. button.index), "KEY_")
GameTooltip:SetText(button.tooltipName .. NORMAL_FONT_COLOR_CODE .. " (" .. key .. ")"
.. FONT_COLOR_CODE_CLOSE, 1.0, 1.0, 1.0)
if button.tooltipSubtext then
GameTooltip:AddLine(button.tooltipSubtext, "", 0.5, 0.5, 0.5)
end
GameTooltip:Show()
else
GameTooltip_SetDefaultAnchor(GameTooltip, button)
GameTooltip:SetPetAction(button.index)
end
end
function PetActionButton_OnLeave()
local button = this
if GameTooltip then GameTooltip:Hide() end
end
-- ── The bar ───────────────────────────────────────────────────────────────────────────
-- Ref PetActionBar_Update + PetActionBar_UpdateCooldowns, fused (one refresh event, see the
-- header). Every branch below is the reference's, including the two that are easy to lose:
-- an unnamed slot HIDES its button, and a slot with no texture swaps the button's ring from
-- the filled UI-Quickslot2 to the empty UI-Quickslot.
function PetActionBar_Update()
local usable = GetPetActionsUsable()
for i = 1, NUM_PET_ACTION_SLOTS do
local button = getglobal("PetActionButton" .. i)
local name, subtext, texture, isToken, isActive, autoCastAllowed, autoCastEnabled = GetPetActionInfo(i)
button.isToken = isToken
button.tooltipSubtext = subtext
if isToken then
-- The token convention: what came back are the NAMES of globals.
button.icon:SetTexture(getglobal(texture))
button.tooltipName = getglobal(name)
else
button.icon:SetTexture(texture)
button.tooltipName = name
end
if isActive then button:SetChecked(1) else button:SetChecked(0) end
if autoCastAllowed then button.autocastable:Show() else button.autocastable:Hide() end
-- The shine marker (decision 1383, header above): showing it IS the whole
-- enable — the native producer draws the trails wherever a shown marker sits.
if autoCastEnabled then button.shine:Show() else button.shine:Hide() end
-- An unnamed slot hides — UNLESS the drop grid is up, which is the whole point of
-- the grid: while a pet action rides the cursor every slot shows, so you can see
-- where it may land (ref PetActionBar_Update l.122-128 + PetActionBar_ShowGrid).
if name or (PetActionBarFrame.showgrid or 0) > 0 then
button:Show()
else
button:Hide()
end
if texture then
SetDesaturation(button.icon, not usable and 1 or nil)
button.icon:Show()
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot2")
else
button.icon:Hide()
button:SetNormalTexture("Interface\\Buttons\\UI-Quickslot")
end
local start, duration, enable = GetPetActionCooldown(i)
CooldownFrame_SetTimer(button.cooldown, start, duration, enable)
end
end
function PetActionBar_OnLoad()
local frame = this
frame:RegisterEvent("PLAYER_ENTERING_WORLD")
frame:RegisterEvent("PET_BAR_UPDATE")
-- The drop grid (decision 1010). A counter rather than a flag, ref-verbatim
-- (PetActionBar_ShowGrid/_HideGrid): our engine derives the pair off one payload edge
-- so they strictly alternate, but the counter is what the reference keeps and it costs
-- nothing to keep it too.
frame:RegisterEvent("PET_BAR_SHOWGRID")
frame:RegisterEvent("PET_BAR_HIDEGRID")
frame.showgrid = 0
PetActionBar_OnEvent()
end
-- The bar exists exactly while PetHasActionBar() does. Its show/hide re-fires the managed
-- bottom-stack pass (UIParent.xml, decision 0272) — the pet term of that stack is what
-- lifts the cast bar and the chat frame clear of it, which is the ref's own
-- PetActionBarFrame.xml:205-210.
-- Declares nothing and reads the ambient `event`, exactly as the reference does
-- (PetActionBarFrame.lua:33 / .xml's bare `PetActionBar_OnEvent();`). PetActionBar_OnLoad's
-- own tail call therefore lands with `event` nil, which is the no-grid-edge path — the same
-- thing the old explicit `eventName` argument did when omitted.
function PetActionBar_OnEvent()
local frame = this
if event == "PET_BAR_SHOWGRID" then
frame.showgrid = frame.showgrid + 1
elseif event == "PET_BAR_HIDEGRID" and frame.showgrid > 0 then
frame.showgrid = frame.showgrid - 1
end
if PetHasActionBar() then
PetActionBar_Update()
frame:Show()
else
frame:Hide()
end
end
-- ── The pet menu's two dialogs (ref StaticPopup.lua l.738-748 + l.1069-1102 + l.365-374,
-- decision 1066) ────────────────────────────────────────────────────────────────────
-- They live here rather than with the rows that raise them (UnitPopup.xml) for the same
-- reason every other dialog in this codebase lives with its arc's frame file: registering
-- into StaticPopupDialogs is a LOAD-TIME dependency on UiPanels.xml's engine, and
-- UnitPopup.xml is a shared engine that eight isolation prefixes load without it. Raising
-- them is not a dependency — StaticPopup_Show resolves at click time.
--
-- ABANDON_PET is the plain confirm. RENAME_PET is a CHAIN of two, which is the reference's
-- own shape and not a flourish: the first collects the name, the second reads it back
-- ("Name your pet 'Bruce'?") before anything is sent — because a rename is one-shot, so a
-- typo is permanent in exactly the way the abandon is. That second Show happens while the
-- first dialog is still up, which is why the engine's two instances are both needed.
StaticPopupDialogs["ABANDON_PET"] = {
text = ABANDON_PET,
button1 = OKAY,
button2 = CANCEL,
OnAccept = function()
PetAbandon();
end,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
}
StaticPopupDialogs["RENAME_PET"] = {
text = PET_RENAME_LABEL,
button1 = ACCEPT,
button2 = CANCEL,
hasEditBox = 1,
-- The 12-letter cap is the reference's, and it is the client's only name check: the
-- server re-validates with CheckPetName and refuses with SMSG_PET_NAME_INVALID.
maxLetters = 12,
OnAccept = function()
-- `this` is the clicked button, so its parent is THIS dialog — never
-- StaticPopup1 by name, which would read the wrong box the moment this dialog
-- lands in the second instance (the chain below is exactly that case).
local text = getglobal(this:GetParent():GetName() .. "EditBox"):GetText()
local dialog = StaticPopup_Show("PETRENAMECONFIRM", text)
if dialog then
dialog.data = text
end
end,
EditBoxOnEnterPressed = function()
local text = getglobal(this:GetParent():GetName() .. "EditBox"):GetText()
local dialog = StaticPopup_Show("PETRENAMECONFIRM", text)
if dialog then
dialog.data = text
end
this:GetParent():Hide()
end,
OnShow = function()
getglobal(this:GetName() .. "EditBox"):SetFocus()
end,
timeout = 0,
exclusive = 1,
hideOnEscape = 1,
}
StaticPopupDialogs["PETRENAMECONFIRM"] = {
-- "Name your pet '%s'?" — the name arrives as text_arg1 AND as `data`; the first
-- fills the question, the second is what actually gets sent.
text = PET_RENAME_CONFIRMATION,
button1 = YES,
button2 = NO,
OnAccept = function(data)
PetRename(data);
end,
timeout = 0,
hideOnEscape = 1,
}
]]></Script>
<!--
One pet button (ref PetActionButtonTemplate: 30x30, inherits ActionButtonTemplate's art
set, plus the AutoCastable ring on OVERLAY at 58x58 CENTER). The Shine texture is the
ref's $parentAutoCast <Model> re-expressed as the native lane's marker (decision 1383,
the sparkle header above): its token path draws nothing itself — a shown marker is where
`autocast_shine` plays the trails, at exactly this OVERLAY layer's paint order.
-->
<CheckButton name="BenillaPetActionButtonTemplate" virtual="true">
<Size><AbsDimension x="30" y="30"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentIcon"/>
</Layer>
<Layer level="OVERLAY">
<Texture name="$parentAutoCastable" file="Interface\Buttons\UI-AutoCastableOverlay" hidden="true">
<Size><AbsDimension x="58" y="58"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
</Anchors>
</Texture>
<!-- The ref's $parentAutoCast <Model> here IS setAllPoints on the 30x30 button,
at scale="1.2" — the suffix carries that scale the way the ref's own XML
does (its sibling in SpellBookFrame.xml writes a different one). -->
<Texture name="$parentShine" file="benilla:autocast-shine:1.2" hidden="true">
<Anchors>
<Anchor point="TOPLEFT"/>
<Anchor point="BOTTOMRIGHT"/>
</Anchors>
</Texture>
</Layer>
</Layers>
<Frames>
<!-- Ref PetActionButton_OnLoad sizes this 33x33 at CENTER (-2,-1) by hand rather than
taking the template's own rect — quoted. -->
<Cooldown name="$parentCooldown" hidden="true">
<Size><AbsDimension x="33" y="33"/></Size>
<Anchors>
<Anchor point="CENTER" relativePoint="CENTER"><Offset><AbsDimension x="-2" y="-1"/></Offset></Anchor>
</Anchors>
</Cooldown>
</Frames>
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
<Size><AbsDimension x="54" y="54"/></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>PetActionButton_OnClick(arg1)</OnClick>
<OnDragStart>PetActionButton_OnDragStart()</OnDragStart>
<OnReceiveDrag>PetActionButton_OnReceiveDrag()</OnReceiveDrag>
<OnEnter>PetActionButton_OnEnter()</OnEnter>
<OnLeave>PetActionButton_OnLeave()</OnLeave>
</Scripts>
</CheckButton>
<Frame name="PetActionBarFrame" hidden="true">
<Size><AbsDimension x="509" y="43"/></Size>
<!-- This anchor is the LOAD-TIME value only: UIParent_ManageFramePositions owns the real
one and re-seats the frame on every pass (OnShow/OnHide below fire it), because the
Y is a managed var — the ref's own PETACTIONBAR_YPOS, 97 here and 97+43 with the
bottom-left multibar up. Leaving it seated at the base 97 is what drew the bar across
that bar's buttons (decision 0988). The X likewise: 36, or past the last stance
button when the two bars share this row.
The ref also slides the frame in over 0.09 s; we don't. The slide's start and end
anchors are the same two points, and a frame that only ever appears with a pet reads
the same arriving seated as it does arriving in 90 ms (the stance bar's own posture,
decision 0270). -->
<Anchors>
<Anchor point="TOPLEFT" relativeTo="MainMenuBar" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="36" y="97"/></Offset></Anchor>
</Anchors>
<Layers>
<Layer level="OVERLAY">
<Texture name="SlidingActionBarTexture0" file="Interface\PetActionBar\UI-PetBar">
<Size><AbsDimension x="256" y="44"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<TexCoords left="0" right="1.0" top="0.015625" bottom="0.359375"/>
</Texture>
<Texture name="SlidingActionBarTexture1" file="Interface\PetActionBar\UI-PetBar">
<Size><AbsDimension x="184" y="44"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="SlidingActionBarTexture0" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0" right="0.71875" top="0.375" bottom="0.71875"/>
</Texture>
</Layer>
</Layers>
<Frames>
<CheckButton name="PetActionButton1" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="36" y="2"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 1)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton2" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton1" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 2)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton3" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton2" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 3)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton4" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton3" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 4)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton5" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton4" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 5)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton6" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton5" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 6)</OnLoad></Scripts>
</CheckButton>
<!-- The ref's own +7 here, not +8 (PetActionBarFrame.xml l.158-166). -->
<CheckButton name="PetActionButton7" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton6" relativePoint="RIGHT"><Offset><AbsDimension x="7" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 7)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton8" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton7" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 8)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton9" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton8" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 9)</OnLoad></Scripts>
</CheckButton>
<CheckButton name="PetActionButton10" inherits="BenillaPetActionButtonTemplate">
<Anchors><Anchor point="LEFT" relativeTo="PetActionButton9" relativePoint="RIGHT"><Offset><AbsDimension x="8" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaPetActionButton_OnLoad(self, 10)</OnLoad></Scripts>
</CheckButton>
</Frames>
<Scripts>
<OnLoad>PetActionBar_OnLoad()</OnLoad>
<OnEvent>PetActionBar_OnEvent()</OnEvent>
<OnShow>UIParent_ManageFramePositions()</OnShow>
<OnHide>UIParent_ManageFramePositions()</OnHide>
</Scripts>
</Frame>
</Ui>