Files
benilla-octo/crates/benilla/assets/ui/SpellBookFrame.xml
T
SamandClaude Fable 5 c1ba444119 the micro menu, the ping meter, and the PvP flag
- The main menu bar's two empty recesses fill. The micro menu — the eight arched
  buttons: character, spellbook, talents, quest log, socials, world map, main menu,
  help — sits in the dark panel between the page arrows and the bags, transcribed
  from the reference's own layout: the character button wears your portrait, a button
  holds itself down while its panel is up, and the talent button appears at level 10.
  The latency meter fills the one slot the bar art leaves transparent for exactly it,
  tinted green/yellow/red on the reference's 300/600 ms thresholds and drawn UNDER
  the bar art, seen through its window, the way 1.12 actually does it. The socials
  and main-menu buttons find their panels one snapshot later; help still waits.
- The UI explains itself on hover: 1.12 ships its detailed two-line tooltips ON — a
  FrameXML default, not the opt-in extra it is often remembered as — so the micro
  buttons, your own portrait, the XP bar, the five bag buttons and the ping meter
  all answer a hover with the reference's own explanations.
- The PvP flag, end to end: crossed swords on the player, target and party frames,
  driven by the two descriptor bits the wire actually carries, and `/pvp` speaks —
  the yellow toast plus the system chat line, the OFF sentence being the one that
  warns the flag lingers for five minutes. It really does linger: turning it off
  waits out the server's timer, which looks like a bug and is the reference.
- Quest markers stopped freezing. The grey `!` over a questgiver above your level
  goes gold the moment you ding, an NPC re-entering view is asked afresh instead of
  wearing a session-old answer, and the marker drops when the questgiver bit goes
  off — the re-ask triggers mirror the reference's own set, plus one named addition
  (your own level changing).
- Water and heights: swimming downhill no longer bounces between swim and fall ten
  times a second — the surface line is a two-sided constraint, so the swimmer
  settles with the river instead of being left behind by it. And collision height
  is per-unit now, read from the unit's own model: a gnome used to swim with a
  third of a yard of water over her head because five depth lines were quoting a
  human's number.
- A round of pixel honesty in text and chat: the newest chat line no longer wears a
  wider drop shadow than every line above it (shadows move in whole pixels now),
  keeps its descenders, and hover boxes stop painting over the chat body. The chat
  input's history browse survives programmatic edits, a failed script chunk warns
  instead of vanishing, and recall deepens to 64 lines. Idle animation got honest
  too: a model's idle is Stand resolved through its own lookup table — never
  file order, which had the duel flag bobbing on its spawn clip — and an armed idle
  is culled on the box its animation authors, not the bind pose, so the flag stops
  vanishing at most camera angles.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-26 19:17:47 +07:00

663 lines
40 KiB
XML

<Ui>
<!--
benilla's spellbook window (decision 0216 §8, slice 5 — the P-key window, the spell
SOURCE for the cursor-payload arc): the 384x512 book + the 12-spell page grid + the
right-edge skill-line tabs + the page-turn pair, dressed from the real 1.12
`SpellBookFrame.xml`/`.lua` — extracted from the MPQ chain to session scratch this
session (director-local reference artifacts, never committed — the 0068/0138 pattern;
line cites below are into that extraction's own `ref-SpellBookFrame.xml`/`.lua`).
Docks the left UIPanel slot exactly like Character/Merchant/Gossip/Quest/QuestLog
(`UiPanels.xml`'s `UIPanelWindows["BenillaSpellBookFrame"] = { area = "left", pushable =
0 }`, mirroring the ref's own `SpellBookFrame` row) — so it competes for/replaces those
windows, never stacks with them. `ToggleSpellBook(bookType)` (ref `SpellBookFrame.lua:
9-50`, kept BARE — not Benilla-prefixed — matching this codebase's own `ToggleCharacter`/
`ToggleQuestLog` precedent) is the show/hide entry point; the app's 'P' binding calls it
with `BOOKTYPE_SPELL` (`ui_script/input.rs`, ref `Bindings.xml:582-583`).
ONE deliberate naming deviation from the ref, applied consistently: every frame this file
defines is Benilla-prefixed (`BenillaSpellBookFrame`, `BenillaSpellButton1`, …) — the
same posture every other window in this codebase already takes.
The book MODEL (tabs = skill lines, the flat slot list, offsets) is built entirely by the
app from `PlayerActions.spells` through the `Spell.dbc`/`SkillLine.dbc` join
(`crate::ui_spellbook`) and pushed through `benilla_ui::script::spellbook`'s snapshot seam
— this file only ever reads it through `GetNumSpellTabs`/`GetSpellTabInfo`/`GetSpellName`/
`GetSpellTexture`/`IsSpellPassive`, exactly the ref's own Era API shape. `BOOKTYPE_SPELL`/
`BOOKTYPE_PET` are installed by the ENGINE (`spellbook.rs`'s `install()`), not this file's
own `<Script>` — the one deliberate exception to the ref's own home for them
(`SpellBookFrame.lua:5-6`), so the engine's OWN tests can exercise the pet-deferral path
without this XML (see that module's doc for the full reasoning). `SPELLS_PER_PAGE`/
`MAX_SKILLLINE_TABS`/`SPELLBOOK_PAGENUMBERS` stay here — genuine per-window UI state, the
ref's own home for them.
Named deferrals/OMITs (each re-cited at its own site below):
- The PET spellbook: no pets stream yet (decision 0216's own scope note). The ref's
Spell/Pet toggle tab row (`SpellBookFrameTabButton1/2/3`, `HasPetSpells()`-gated) is
dropped OUTRIGHT — this window only ever shows the one book that exists, the 0138
merchant-tab precedent ("tabs arrive with their pages"). `LEARNED_SPELL_IN_TAB`'s tab
flash (`SpellBookTabFlashFrame`, `CheckButtonGlow`) is dropped with it — no such event
exists yet (no "just learned a spell" signal split out of the general `SPELLS_CHANGED`
diff).
- Cooldowns (`GetSpellCooldown`/`CooldownFrameTemplate`) and pet autocast
(`GetSpellAutocast`/`ToggleSpellAutocast`/`UI-AutoCastableOverlay`/the `AutoCast`
model): no cooldown feed exists (the same named gap as CharacterFrame's own item
cooldowns), and autocast is pet-only. Right-click pet-autocast toggle
(`SpellButton_OnClick`'s `arg1 ~= "LeftButton"` branch) is out of scope this slice
per the same brief — every click routes straight to cast/pickup.
- The "currently casting" checked ring (`SpellButton_UpdateSelection`/`IsCurrentCast`,
`CURRENT_SPELL_CAST_CHANGED`): no cast-selection tracking exists for the spellbook
yet — the `CheckedTexture` art ships (harmless, matches the ref) but nothing ever
calls `SetChecked(1)` on a spell button this slice.
- The MacroFrame branch (`SpellButton_OnClick`'s shift-click "already in a macro editor"
fork): no macro window exists — shift-click always falls to the ref's own "else"
(`PickupSpell`), exactly what the ref itself does whenever no MacroFrame is visible.
- `GameTooltip:SetSpell(id, bookType)` (the full spell-effect/cost/range tooltip): no
such content channel exists (the item-stats precedent's own gap — no spell-stat feed).
`BenillaSpellButton_OnEnter` shows a plain name + rank two-liner instead
(`GetSpellName`'s own two return values) — the `this.updateTooltip`/`OnUpdate`
re-poll loop is dropped with it (nothing about a static name/rank line changes
per-frame the way a live cooldown would).
- `maxLines="3"` on `$parentSpellName` (word-wrap line cap): no such FontString
attribute is modeled (grepped `benilla-ui/src/loader/widgets.rs`) — the name just
wraps unbounded inside its box width, a harmless cosmetic gap on the rare long name.
- `MultiActionBar_ShowAllGrids/_HideAllGrids()` (ref `SpellBookFrame_OnShow`/`_OnHide`):
no multi-bar grid-show mechanism exists in this engine yet — dropped, the same class of
gap CharacterFrame's own OnShow/OnHide already carry. Its neighbour in that pair,
`UpdateMicroButtons()`, is LIVE since the micro-menu slice (`MicroMenu.xml`) — nil-
guarded at the call sites only because several test harnesses load this window without
the action bar behind it.
Sounds (decision 0090): `igSpellBookOpen`/`igSpellBookClose` on show/hide (ref
`SpellBookFrame_Update`/`_OnHide`, VERIFIED present in the real `SoundEntries.dbc` this
session), `igAbiliityPageTurn` (the ref's own literal spelling — VERIFIED as the real
registered kit name, id 836, `Sound\interface\iAbilitiesTurnPage{A,B,C}.wav`; not a typo
to "fix") on both page-turn clicks.
-->
<Script><![CDATA[
-- ── GlobalStrings values this window uses (ref-GlobalStrings.lua, cited per line — the
-- `X = X or "..."` idiom matches CastingBar.xml/CharacterFrame.xml's own convention) ────
SPELLBOOK = SPELLBOOK or "Spellbook" -- :3820
PAGE_NUMBER = PAGE_NUMBER or "Page %d" -- :2981
-- ── This window's own constants (ref SpellBookFrame.lua:1-7, Benilla-prefixed where the
-- ref's own name would collide with the engine's BOOKTYPE_SPELL/BOOKTYPE_PET globals —
-- this file's header comment on that one deviation) ─────────────────────────────────────
SPELLS_PER_PAGE = 12
MAX_SKILLLINE_TABS = 8
SPELLBOOK_PAGENUMBERS = {}
-- ── The window: toggle (ref ToggleSpellBook, l.9-50) ─────────────────────────────────────
-- Kept BARE (not Benilla-prefixed) — the addon-facing entry point, matching this
-- codebase's own ToggleCharacter/ToggleQuestLog precedent. The ref's `HasPetSpells()`
-- short-circuit (blocking a toggle INTO an empty pet book) is dropped outright — this
-- file's header comment, the pet book is never fed so `bookType` is always
-- BOOKTYPE_SPELL in practice. The ref's own DUPLICATED prev/next enable/disable (once
-- inside the `currentPage > maxPages` clamp branch, once again unconditionally right
-- after — both produce the identical end state) collapses to the ONE
-- BenillaSpellBook_UpdatePageArrows() call already shared by every other page-turn site
-- below — a redundant-code cleanup, not a behavior change.
function ToggleSpellBook(bookType)
local isVisible = BenillaSpellBookFrame:IsVisible()
HideUIPanel(BenillaSpellBookFrame)
if (not isVisible or (BenillaSpellBookFrame.bookType ~= bookType)) then
BenillaSpellBookFrame.bookType = bookType
ShowUIPanel(BenillaSpellBookFrame)
end
BenillaSpellBook_ClampPage()
BenillaSpellBook_UpdatePageArrows()
BenillaSpellBookPageText:SetText(format(PAGE_NUMBER, BenillaSpellBook_GetCurrentPage()))
end
-- Clamp the selected tab's page back onto range (ref's own inline logic inside
-- ToggleSpellBook, l.22-26) — split out so both ToggleSpellBook and a future re-entry
-- share it, rather than inlining it twice.
function BenillaSpellBook_ClampPage()
local currentPage, maxPages = BenillaSpellBook_GetCurrentPage()
if currentPage > maxPages then
SPELLBOOK_PAGENUMBERS[BenillaSpellBookFrame.selectedSkillLine] = maxPages
BenillaUpdateSpells()
end
end
function BenillaSpellBookFrame_OnLoad(frame)
frame:RegisterEvent("SPELLS_CHANGED")
-- LEARNED_SPELL_IN_TAB OMITTED — this file's header comment.
frame.bookType = BOOKTYPE_SPELL
for i = 1, MAX_SKILLLINE_TABS do
SPELLBOOK_PAGENUMBERS[i] = 1
end
BenillaSpellBookSkillLineTab_OnClick(1)
end
-- ref SpellBookFrame_OnEvent (l.75-92), the SPELLS_CHANGED branch only (LEARNED_SPELL_IN_TAB
-- OMITTED, header comment).
function BenillaSpellBookFrame_OnEvent(frame)
if event == "SPELLS_CHANGED" and frame:IsVisible() then
BenillaSpellBookFrame_Update()
BenillaSpellBook_UpdatePageArrows()
end
end
-- ref SpellBookFrame_OnShow (l.94-105): MultiActionBar_ShowAllGrids OMITTED (header
-- comment); the tab-flash UIFrameFlash call goes with LEARNED_SPELL_IN_TAB.
-- UpdateMicroButtons is LIVE as of the micro-menu slice (MicroMenu.xml).
function BenillaSpellBookFrame_OnShow()
if UpdateMicroButtons then UpdateMicroButtons() end
BenillaSpellBookFrame_Update(1)
end
-- ref SpellBookFrame_Update (l.107-164): the Spell/Pet toggle-tab hide loop and the
-- HasPetSpells()/petTitle branch are DROPPED outright (header comment — no pet book, no
-- toggle row exists at all this slice).
function BenillaSpellBookFrame_Update(showing)
if showing then
BenillaSpellBookSkillLineTab_OnClick(BenillaSpellBookFrame.selectedSkillLine)
BenillaUpdateSpells()
end
local numSkillLineTabs = GetNumSpellTabs()
for i = 1, MAX_SKILLLINE_TABS do
local skillLineTab = getglobal("BenillaSpellBookSkillLineTab" .. i)
if i <= numSkillLineTabs then
local name, texture = GetSpellTabInfo(i)
skillLineTab:SetNormalTexture(texture)
skillLineTab.tooltip = name
skillLineTab:Show()
skillLineTab:SetChecked(BenillaSpellBookFrame.selectedSkillLine == i)
else
skillLineTab:Hide()
end
end
BenillaSpellBookTitleText:SetText(SPELLBOOK)
if showing then
PlaySound("igSpellBookOpen")
end
end
-- ref SpellBookFrame_OnHide (l.186-204): MultiActionBar_HideAllGrids and the tab-flash
-- cleanup OMITTED (header comment); always the spell-book close kit (no pet book to branch
-- on). UpdateMicroButtons is LIVE as of the micro-menu slice (MicroMenu.xml).
function BenillaSpellBookFrame_OnHide()
if UpdateMicroButtons then UpdateMicroButtons() end
PlaySound("igSpellBookClose")
end
-- UpdateSpells(): the ref's OWN global (`SpellBookFrame.lua:26/116/411/426/450` — never
-- DEFINED in this session's extraction, a base-UI/C-level helper outside this file)
-- transcribed here as BenillaUpdateSpells(): refresh every spell button on the current
-- page (this file's own convention: explicit button params, not ambient `this`).
function BenillaUpdateSpells()
for i = 1, SPELLS_PER_PAGE do
BenillaSpellButton_UpdateButton(getglobal("BenillaSpellButton" .. i))
end
end
-- ── The spell button (ref SpellButton_*, l.206-404) ──────────────────────────────────────
-- CRAFT_SHOW/_CLOSE/TRADE_SKILL_SHOW/_CLOSE/PET_BAR_UPDATE/CURRENT_SPELL_CAST_CHANGED/
-- SPELL_UPDATE_COOLDOWN are all OMITTED (header comment: no craft/trade-skill window, no
-- pet bar, no cast-selection tracking, no cooldown feed) — SPELLS_CHANGED alone drives a
-- refresh.
function BenillaSpellButton_OnLoad(button)
button:RegisterEvent("SPELLS_CHANGED")
button:RegisterForDrag("LeftButton")
button:RegisterForClicks("LeftButtonUp", "RightButtonUp")
BenillaSpellButton_UpdateButton(button)
end
function BenillaSpellButton_OnEvent(button)
if event == "SPELLS_CHANGED" then
BenillaSpellButton_UpdateButton(button)
end
end
-- ref SpellButton_OnEnter (l.235-244): the engine's SetSpell — the byte-verified spell
-- line law (decision 0276: cost|range one line, casttime|cooldown one line, passives
-- omit it, the $-substituted gold description; no rank column from the book — the
-- verified showRank=0).
function BenillaSpellButton_OnEnter(button)
if not GameTooltip then return end -- engine tests load file subsets
local id = BenillaSpellBook_GetSpellID(button:GetID())
if not GetSpellName(id, BenillaSpellBookFrame.bookType) then
return
end
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
GameTooltip:SetSpell(id, BenillaSpellBookFrame.bookType)
end
-- ref SpellButton_OnClick (l.263-290): the MacroFrame branch and the right-click pet
-- autocast fork are both OMITTED (header comment) — shift-click always picks up (the
-- ref's own "no MacroFrame visible" fallthrough), every other click casts. The
-- `id > MAX_SPELLS` guard is dropped too: GetSpellName/CastSpell/PickupSpell already
-- answer nil/no-op for any id past the book's real length, so the extra guard checks
-- nothing a MAX_SPELLS constant would add here.
function BenillaSpellButton_OnClick(button, drag)
local id = BenillaSpellBook_GetSpellID(button:GetID())
button:SetChecked("false")
if drag or IsShiftKeyDown() then
PickupSpell(id, BenillaSpellBookFrame.bookType)
else
CastSpell(id, BenillaSpellBookFrame.bookType)
-- SpellButton_UpdateSelection() OMITTED — header comment, no cast-selection ring.
end
end
-- ref SpellButton_UpdateButton (l.307-404): the cooldown sweep, autocast overlay/model,
-- and the "currently casting" checked-ring call are all OMITTED (header comment). Passive
-- gates CastSpell engine-side (spellbook.rs), not here — this only grays the presentation.
function BenillaSpellButton_UpdateButton(button)
if not button:IsVisible() then
return
end
if GameTooltip:IsOwned(button) then
BenillaSpellButton_OnEnter(button)
end
if not BenillaSpellBookFrame.selectedSkillLine then
BenillaSpellBookFrame.selectedSkillLine = 1
end
local temp, tabTexture, offset, numSpells = GetSpellTabInfo(BenillaSpellBookFrame.selectedSkillLine)
offset = offset or 0
numSpells = numSpells or 0
BenillaSpellBookFrame.selectedSkillLineOffset = offset
local id = BenillaSpellBook_GetSpellID(button:GetID())
local name = button:GetName()
local iconTexture = getglobal(name .. "IconTexture")
local spellString = getglobal(name .. "SpellName")
local subSpellString = getglobal(name .. "SubSpellName")
if id > (offset + numSpells) then
button:Disable()
iconTexture:Hide()
spellString:Hide()
subSpellString:Hide()
button:SetChecked(0)
getglobal(name .. "NormalTexture"):SetVertexColor(1.0, 1.0, 1.0)
return
else
button:Enable()
end
local texture = GetSpellTexture(id, BenillaSpellBookFrame.bookType)
local highlightTexture = getglobal(name .. "Highlight")
local normalTexture = getglobal(name .. "NormalTexture")
if (not texture) or (texture == "") then
iconTexture:Hide()
spellString:Hide()
subSpellString:Hide()
highlightTexture:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
button:SetChecked(0)
normalTexture:SetVertexColor(1.0, 1.0, 1.0)
return
end
local spellName, subSpellName = GetSpellName(id, BenillaSpellBookFrame.bookType)
local isPassive = IsSpellPassive(id, BenillaSpellBookFrame.bookType)
if isPassive then
normalTexture:SetVertexColor(0, 0, 0)
highlightTexture:SetTexture("Interface\\Buttons\\UI-PassiveHighlight")
spellString:SetTextColor(PASSIVE_SPELL_FONT_COLOR.r, PASSIVE_SPELL_FONT_COLOR.g, PASSIVE_SPELL_FONT_COLOR.b)
else
normalTexture:SetVertexColor(1.0, 1.0, 1.0)
highlightTexture:SetTexture("Interface\\Buttons\\ButtonHilight-Square")
spellString:SetTextColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
end
iconTexture:SetTexture(texture)
iconTexture:SetVertexColor(1.0, 1.0, 1.0) -- no cooldown/enable feed: always full bright
spellString:SetText(spellName)
subSpellString:SetText(subSpellName)
if subSpellName and subSpellName ~= "" then
spellString:SetPoint("LEFT", button, "RIGHT", 4, 4)
else
spellString:SetPoint("LEFT", button, "RIGHT", 4, 2)
end
iconTexture:Show()
spellString:Show()
subSpellString:Show()
end
-- ── Page turn (ref PrevPageButton_OnClick/NextPageButton_OnClick, l.406-434) ─────────────
-- Always the spell-book page-turn kit (no pet book to branch on, header comment).
function BenillaSpellBookPrevPageButton_OnClick()
local pageNum = BenillaSpellBook_GetCurrentPage() - 1
SPELLBOOK_PAGENUMBERS[BenillaSpellBookFrame.selectedSkillLine] = pageNum
BenillaSpellBook_UpdatePageArrows()
BenillaSpellBookPageText:SetText(format(PAGE_NUMBER, pageNum))
BenillaUpdateSpells()
PlaySound("igAbiliityPageTurn")
end
function BenillaSpellBookNextPageButton_OnClick()
local pageNum = BenillaSpellBook_GetCurrentPage() + 1
SPELLBOOK_PAGENUMBERS[BenillaSpellBookFrame.selectedSkillLine] = pageNum
BenillaSpellBook_UpdatePageArrows()
BenillaSpellBookPageText:SetText(format(PAGE_NUMBER, pageNum))
BenillaUpdateSpells()
PlaySound("igAbiliityPageTurn")
end
-- ── The skill-line tabs (ref SpellBookSkillLineTab_OnClick, l.436-457) ───────────────────
function BenillaSpellBookSkillLineTab_OnClick(id)
local update
if not id then
update = 1
id = this:GetID()
end
BenillaSpellBookFrame.selectedSkillLine = id
local name, texture, offset, numSpells = GetSpellTabInfo(id)
BenillaSpellBookFrame.selectedSkillLineOffset = offset or 0
BenillaSpellBookFrame.selectedSkillLineNumSpells = numSpells or 0
BenillaSpellBook_UpdatePageArrows()
BenillaSpellBookFrame_Update()
BenillaSpellBookPageText:SetText(format(PAGE_NUMBER, BenillaSpellBook_GetCurrentPage()))
if update then
BenillaUpdateSpells()
end
end
-- ref SpellBook_GetSpellID (l.459-465): the pet-book `return id` branch is DROPPED
-- outright (header comment — bookType is always BOOKTYPE_SPELL, dead code for this
-- window). This is the book-id the engine's own byte-verified seam expects verbatim
-- (`benilla-ui/src/script/spellbook.rs`'s own module doc).
function BenillaSpellBook_GetSpellID(id)
return id + (BenillaSpellBookFrame.selectedSkillLineOffset or 0)
+ (SPELLS_PER_PAGE * ((SPELLBOOK_PAGENUMBERS[BenillaSpellBookFrame.selectedSkillLine] or 1) - 1))
end
-- ref SpellBook_UpdatePageArrows (l.467-479).
function BenillaSpellBook_UpdatePageArrows()
local currentPage, maxPages = BenillaSpellBook_GetCurrentPage()
if currentPage == 1 then
BenillaSpellBookPrevPageButton:Disable()
else
BenillaSpellBookPrevPageButton:Enable()
end
if currentPage == maxPages then
BenillaSpellBookNextPageButton:Disable()
else
BenillaSpellBookNextPageButton:Enable()
end
end
-- ref SpellBook_GetCurrentPage (l.481-493): the pet-book branch is DROPPED outright
-- (header comment). `maxPages` is clamped to at least 1 (a defensive addition this file's
-- own — an empty/not-yet-fed book must still show ONE (empty) page rather than disabling
-- both page buttons into a currentPage=1 > maxPages=0 dead state).
function BenillaSpellBook_GetCurrentPage()
local currentPage = SPELLBOOK_PAGENUMBERS[BenillaSpellBookFrame.selectedSkillLine] or 1
local temp, texture, offset, numSpells = GetSpellTabInfo(BenillaSpellBookFrame.selectedSkillLine)
local maxPages = max(ceil((numSpells or 0) / SPELLS_PER_PAGE), 1)
return currentPage, maxPages
end
]]></Script>
<!--
The skill-line tab (ref SpellBookSkillLineTabTemplate, l.4-39): 32x32, the
SpellBook-SkillLineTab 64x64 backdrop offset (-3,11), an empty NormalTexture placeholder
(filled at runtime by SetNormalTexture — SetNormalTexture creates the region lazily on
first call, `benilla-ui/src/script/button.rs`'s `ensure_slot`, verified this session), the
ADD highlight + CheckedTexture ring for the SELECTED tab.
-->
<CheckButton name="BenillaSpellBookSkillLineTabTemplate" virtual="true">
<Size><AbsDimension x="32" y="32"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture file="Interface\SpellBook\SpellBook-SkillLineTab">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-3" y="11"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
</Layers>
<NormalTexture/>
<HighlightTexture file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
<CheckedTexture file="Interface\Buttons\CheckButtonHilight" alphaMode="ADD"/>
<Scripts>
<OnClick>BenillaSpellBookSkillLineTab_OnClick()</OnClick>
<OnEnter>
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
if self.tooltip then
GameTooltip:SetText(self.tooltip)
GameTooltip:Show()
end
</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</CheckButton>
<!--
The spell button (ref SpellButtonTemplate, l.74-201): 37x37, the UI-Spellbook-SpellBackground
64x64 backdrop offset (-3,3), the icon (setAllPoints, hidden until fed), the name/rank
FontStrings anchored off the button's own RIGHT edge, the UI-Quickslot2 ring + Depress push +
ADD highlight + the (currently-unused, header comment) CheckedTexture ring. Cooldown
($parentCooldown, CooldownFrameTemplate) and the AutoCast overlay/model are OMITTED (this
file's header comment — no cooldown feed, pet-only autocast).
-->
<CheckButton name="BenillaSpellButtonTemplate" virtual="true">
<Size><AbsDimension x="37" y="37"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentBackground" file="Interface\Spellbook\UI-Spellbook-SpellBackground">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-3" y="3"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
<Layer level="BORDER">
<Texture name="$parentIconTexture" setAllPoints="true" hidden="true"/>
<!-- maxLines="3" unmodeled (this file's header comment) — wraps unbounded instead. -->
<FontString name="$parentSpellName" inherits="GameFontNormal" hidden="true" justifyH="LEFT">
<Size><AbsDimension x="103" y="0"/></Size>
<Anchors>
<Anchor point="LEFT" relativePoint="RIGHT"><Offset><AbsDimension x="4" y="0"/></Offset></Anchor>
</Anchors>
</FontString>
<FontString name="$parentSubSpellName" inherits="SubSpellFont" hidden="true" justifyH="LEFT">
<Size><AbsDimension x="79" y="18"/></Size>
<Anchors>
<Anchor point="TOPLEFT" relativeTo="$parentSpellName" relativePoint="BOTTOMLEFT">
<Offset><AbsDimension x="0" y="4"/></Offset>
</Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnLoad>BenillaSpellButton_OnLoad(self)</OnLoad>
<OnEvent>BenillaSpellButton_OnEvent(self)</OnEvent>
<OnClick>BenillaSpellButton_OnClick(self)</OnClick>
<OnShow>BenillaSpellButton_UpdateButton(self)</OnShow>
<!-- The reference routes both drag entries to the plain click path with drag=1
(SpellButtonTemplate l.169-174) — the bag/doll/action slots' own precedent. -->
<OnDragStart>BenillaSpellButton_OnClick(self, 1)</OnDragStart>
<OnReceiveDrag>BenillaSpellButton_OnClick(self, 1)</OnReceiveDrag>
<OnEnter>BenillaSpellButton_OnEnter(self)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors><Anchor point="CENTER"/></Anchors>
</NormalTexture>
<PushedTexture file="Interface\Buttons\UI-Quickslot-Depress"/>
<HighlightTexture name="$parentHighlight" file="Interface\Buttons\ButtonHilight-Square" alphaMode="ADD"/>
<CheckedTexture file="Interface\Buttons\CheckButtonHilight" alphaMode="ADD"/>
</CheckButton>
<Frame name="BenillaSpellBookFrame" hidden="true">
<!-- The real 384x512 window (ref l.202-205). No <Anchors> — ShowUIPanel/SetLeftFrame
SetPoints this to the left slot on open (UiPanels.xml's own convention). -->
<Size><AbsDimension x="384" y="512"/></Size>
<!-- HitRectInsets: transcribed for fidelity; no loader wiring exists yet (CharacterFrame's
own precedent note) — a harmless no-op today. -->
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="70"/></HitRectInsets>
<Layers>
<Layer level="BACKGROUND">
<Texture file="Interface\Spellbook\Spellbook-Icon">
<Size><AbsDimension x="58" y="58"/></Size>
<Anchors>
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-8"/></Offset></Anchor>
</Anchors>
</Texture>
</Layer>
<Layer level="ARTWORK">
<!-- The four quadrant plates, anchored off the frame's own corners exactly like
the ref (l.231-263) — simpler than CharacterFrame's TOPLEFT-offset scheme
since the ref itself uses the corner anchors directly here. -->
<Texture file="Interface\Spellbook\UI-SpellbookPanel-TopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<Texture file="Interface\Spellbook\UI-SpellbookPanel-TopRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPRIGHT"/></Anchors>
</Texture>
<Texture file="Interface\Spellbook\UI-SpellbookPanel-BotLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
</Texture>
<Texture file="Interface\Spellbook\UI-SpellbookPanel-BotRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
</Texture>
<FontString name="BenillaSpellBookTitleText" inherits="GameFontNormal" text="SPELLBOOK">
<Anchors>
<Anchor point="CENTER"><Offset><AbsDimension x="6" y="230"/></Offset></Anchor>
</Anchors>
</FontString>
<FontString name="BenillaSpellBookPageText" inherits="GameFontNormal">
<Size><AbsDimension x="102" y="0"/></Size>
<Anchors>
<Anchor point="BOTTOM"><Offset><AbsDimension x="-14" y="96"/></Offset></Anchor>
</Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- Close: the house minimize-button art (no UIPanelCloseButton template ships —
Gossip/Bag/Merchant/CharacterFrame's own precedent), the same TOPRIGHT(-44,-25)
position as every other window's close button. -->
<Button name="BenillaSpellBookFrameCloseButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-44" y="-25"/></Offset></Anchor></Anchors>
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
<Scripts>
<OnClick>HideUIPanel(BenillaSpellBookFrame)</OnClick>
</Scripts>
</Button>
<Button name="BenillaSpellBookPrevPageButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors>
<Anchor point="CENTER" relativeTo="BenillaSpellBookFrame" relativePoint="BOTTOMLEFT">
<Offset><AbsDimension x="50" y="105"/></Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<FontString inherits="GameFontNormal" justifyH="LEFT" text="PREV">
<Anchors><Anchor point="LEFT" relativeTo="BenillaSpellBookPrevPageButton" relativePoint="RIGHT"/></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts><OnClick>BenillaSpellBookPrevPageButton_OnClick()</OnClick></Scripts>
<NormalTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Up"/>
<PushedTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Down"/>
<DisabledTexture file="Interface\Buttons\UI-SpellbookIcon-PrevPage-Disabled"/>
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
</Button>
<Button name="BenillaSpellBookNextPageButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors>
<Anchor point="CENTER" relativeTo="BenillaSpellBookFrame" relativePoint="BOTTOMLEFT">
<Offset><AbsDimension x="314" y="105"/></Offset>
</Anchor>
</Anchors>
<Layers>
<Layer level="BACKGROUND">
<FontString inherits="GameFontNormal" justifyH="RIGHT" text="NEXT">
<Anchors><Anchor point="RIGHT" relativeTo="BenillaSpellBookNextPageButton" relativePoint="LEFT"/></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts><OnClick>BenillaSpellBookNextPageButton_OnClick()</OnClick></Scripts>
<NormalTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Up"/>
<PushedTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Down"/>
<DisabledTexture file="Interface\Buttons\UI-SpellbookIcon-NextPage-Disabled"/>
<HighlightTexture file="Interface\Buttons\UI-Common-MouseHilight" alphaMode="ADD"/>
</Button>
<!-- The 12-spell page grid (ref l.387-494): 2 columns x 6 rows, ids assigned the
ref's own column-major pattern (col1 ids 1-6, col2 ids 7-12) — SpellBook_GetSpellID
reads a button's `id=` as its within-page/within-tab 1-based position, not its
screen order. -->
<CheckButton name="BenillaSpellButton1" inherits="BenillaSpellButtonTemplate" id="1">
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="34" y="-85"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton2" inherits="BenillaSpellButtonTemplate" id="7">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton1"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton3" inherits="BenillaSpellButtonTemplate" id="2">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton4" inherits="BenillaSpellButtonTemplate" id="8">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton3"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton5" inherits="BenillaSpellButtonTemplate" id="3">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton3" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton6" inherits="BenillaSpellButtonTemplate" id="9">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton5"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton7" inherits="BenillaSpellButtonTemplate" id="4">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton5" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton8" inherits="BenillaSpellButtonTemplate" id="10">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton7"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton9" inherits="BenillaSpellButtonTemplate" id="5">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton7" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton10" inherits="BenillaSpellButtonTemplate" id="11">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton9"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton11" inherits="BenillaSpellButtonTemplate" id="6">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton9" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellButton12" inherits="BenillaSpellButtonTemplate" id="12">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellButton11"><Offset><AbsDimension x="157" y="0"/></Offset></Anchor></Anchors>
</CheckButton>
<!-- The right-edge skill-line tabs, up to MAX_SKILLLINE_TABS (8) — ref l.495-566. -->
<CheckButton name="BenillaSpellBookSkillLineTab1" inherits="BenillaSpellBookSkillLineTabTemplate" id="1">
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-32" y="-65"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab2" inherits="BenillaSpellBookSkillLineTabTemplate" id="2">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab3" inherits="BenillaSpellBookSkillLineTabTemplate" id="3">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab2" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab4" inherits="BenillaSpellBookSkillLineTabTemplate" id="4">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab3" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab5" inherits="BenillaSpellBookSkillLineTabTemplate" id="5">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab4" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab6" inherits="BenillaSpellBookSkillLineTabTemplate" id="6">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab5" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab7" inherits="BenillaSpellBookSkillLineTabTemplate" id="7">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab6" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
<CheckButton name="BenillaSpellBookSkillLineTab8" inherits="BenillaSpellBookSkillLineTabTemplate" id="8">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaSpellBookSkillLineTab7" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</CheckButton>
</Frames>
<Scripts>
<OnLoad>BenillaSpellBookFrame_OnLoad(self)</OnLoad>
<OnEvent>BenillaSpellBookFrame_OnEvent(self)</OnEvent>
<OnShow>BenillaSpellBookFrame_OnShow()</OnShow>
<OnHide>BenillaSpellBookFrame_OnHide()</OnHide>
</Scripts>
</Frame>
</Ui>