Files
benilla-octo/crates/benilla/assets/ui/TalentFrame.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

1197 lines
77 KiB
XML

<Ui>
<!--
benilla's talent window (decision 0304 §4, "The talent window" — same-session sibling of the
engine seam in `benilla-ui/src/script/talent.rs`): the 384x512 book chassis + the class-art
4-corner backdrop + the 3 tree tabs + the scrolled 8-tier x 4-column talent grid with its
prereq branch/arrow lines, dressed from the real 1.12 **load-on-demand addon**
`Blizzard_TalentUI.xml`/`.lua`/`Blizzard_TalentUITemplates.xml` — extracted from the MPQ
chain to `WoW/_extracted_addons/Blizzard_TalentUI/` in this worktree (line cites below are
into those three files directly).
Docks the left UIPanel slot exactly like Character/SpellBook/Gossip/Merchant/Quest/QuestLog
(`UiPanels.xml`'s `UIPanelWindows["BenillaTalentFrame"] = { area = "left", pushable = 6 }`,
the ref's own `pushable = 6` — one tier above the plain NPC-session windows' `0`, matching the
ref's row exactly) — so it competes for/replaces those windows, never stacks with them.
`ToggleTalentFrame()` (ref `TalentFrame_Toggle`, Blizzard_TalentUI.lua:73-79, RENAMED — see
below) is the show/hide entry point; the app's 'N' binding already calls it by this exact name
(`ui_script/input.rs:198-201`, landed ahead of this file per decision 0304's own pin).
ONE deliberate naming deviation from the ref, applied consistently: every frame this file
defines is Benilla-prefixed (`BenillaTalentFrame`, `BenillaTalentFrameTalent1`, …) — the same
posture every other window in this codebase already takes. Layered on top of that, `Toggle-
TalentFrame` (not the ref's own `TalentFrame_Toggle`) matches this codebase's established
`Toggle<Thing>` shape for a window's addon-facing entry point (`ToggleSpellBook`/
`ToggleCharacter`/`ToggleQuestLog` — SpellBookFrame.xml's header comment makes the identical
choice for the identical reason) — kept BARE, not Benilla-prefixed, matching that precedent.
SCOPE CALL — 3 tabs, not 5. The ref's own `MAX_TALENT_TABS = 5` is defensive headroom; 1.12
vanilla ships EXACTLY 3 talent trees for every class with no exception (Warrior Arms/Fury/
Protection, Mage Arcane/Fire/Frost, …, an unchanging 1.12 game-design fact, not a guess). This
file declares exactly 3 static tab buttons and renumbers the constant `BENILLA_MAX_TALENT_TABS
= 3` (Benilla-prefixed, not bare `MAX_TALENT_TABS`, since its VALUE differs from the ref's own
— a reader must not assume it still means 5). `BenillaTalentFrame_Update` additionally clamps
`PanelTemplates_SetNumTabs`'s own argument to this cap (`min(numTabs, BENILLA_MAX_TALENT_TABS)`
— a small defensive addition beyond the ref, which trusts its own 5 static tabs always
suffice): without it, a hypothetical `GetNumTalentTabs() > 3` would ask `PanelTemplates_
UpdateTabs` to index a `BenillaTalentFrameTab4` that doesn't exist and error. 1.12 data never
exceeds 3 (comment above) — the clamp just makes that assumption SAFE rather than merely
"safe by data luck", the honest version of the same trust the ref's own 5-tab headroom
expresses for ITS OWN assumption.
TAB STRIP — reuses the house's EXISTING tab kit, not a hand-rolled `BenillaTalentFrame.
selectedTab` variable. The task brief's own ENGINE API note flagged `PanelTemplates_*` as
unavailable ("implement your own small select/deselect Lua") — verified false this session:
`UiPanels.xml:502-578` already transcribes the FULL kit (`PanelTemplates_SetNumTabs/_SetTab/
_GetSelectedTab/_UpdateTabs/_SelectTab/_DeselectTab/_TabResize`), and `CharacterFrameTab-
ButtonTemplate` (`UiPanels.xml:603-654`) is ALREADY battle-tested by CharacterFrame.xml's own
tab. Better still: the REFERENCE's own `TalentTabTemplate` (Blizzard_TalentUITemplates.xml:3)
itself `inherits="CharacterFrameTabButtonTemplate"` — the exact same base template. Reusing
both here is therefore not a shortcut but the MORE faithful, MORE DRY choice: identical art,
identical selected/deselected mechanics, one less parallel implementation to maintain. Only
`TalentTabTemplate`'s OWN additions get a local sub-template (`BenillaTalentTabTemplate`
below): the OnClick + OnLeave (its `PLAYER_LEVEL_UP` level-9 pulse-flash OnLoad/OnEvent is
OMITTED — see Sounds/OMITs below). `TalentFrame.selectedTab` (read directly by the reference's
own `TalentButtonTemplate` inline OnEnter, Blizzard_TalentUITemplates.xml:92) and
`PanelTemplates_GetSelectedTab(TalentFrame)` (read by TalentFrame.lua's own functions) are
THE SAME FIELD — `PanelTemplates_SetTab` stores straight onto `frame.selectedTab` — so this
file transcribes each call site exactly as the reference itself spells it, not a benilla-
introduced inconsistency.
SCROLL DESIGN — a real ScrollFrame with a FIXED-HEIGHT child, not the reference's own dynamic
`UIPanelScrollFrameTemplate` (which auto-grows its child via a C-side `UpdateScrollChildRect`
this engine has no equivalent for — QuestLogFrame.xml's own documented gap: "the resolver
never grows a frame to fit content that overflows it"). The grid this window scrolls is
UNLIKE QuestLogFrame's variable quest-text pane: it is always EXACTLY 8 tiers of absolutely-
positioned buttons/branches/arrows, never resized by content — so instead of QuestLogFrame's
per-frame convergence loop (`_ResizeChild`/`_OnUpdate`, needed there because its text height is
data-dependent), the scroll child here is simply sized to a CONSTANT
`BENILLA_TALENT_SCROLL_CHILD_H = 504` (8 tiers x the ref's own 63px pitch — the task's own
sizing note): `INITIAL_TALENT_OFFSET_Y(20) + 7*63 + TALENT_BUTTON_SIZE(32) = 493 < 504`
covers even the lowest tier's own button with 11px to spare, and no branch/arrow ever paints
BELOW its own tier's row (verified against `BenillaTalentFrame_DrawLines` below: a talent's
`.down`/arrows only ever touch tiers at or above its own). No `UpdateScrollChildRect` call is
therefore needed anywhere (dropped from the ref's own per-tier-loop tail) — the range is a
load-time constant, not something to re-derive every Update.
The scrollbar reuses `BenillaScrollBarTemplate` (the real, DRAGGABLE `Slider`, decision 0250,
`ScrollTemplates.xml`) directly — NOT QuestLogFrame.xml's own hand-rolled "visual only, no
drag" bar. That file's header comment explicitly says "this engine has no Slider widget"; that
claim is now STALE (the Slider primitive landed after, decision 0250) — reusing the
since-real thing here is a rebase onto a landed primitive, not a fidelity regression. The
Slider's OWN `OnValueChanged` (inherited from `BenillaScrollBarTemplate`, wired to the FAUX
offset bookkeeping `BenillaFauxScrollBar_OnValueChanged`) is REPLACED (not chained past) by
this file's own override below — verified, not assumed: `apply_scripts`
(`benilla-ui/src/loader/scripts.rs`) walks EVERY `<Scripts>` block found after `inherits=`
merge and calls `SetScript` for each handler in order; `merge()` (`framexml.rs`) appends the
base template's children before the instance's own, so the base's `<Scripts>` registers first
and this file's own registers LAST — `SetScript` is a single-slot assignment, so the last
registration wins outright. `BenillaFauxScrollBar_UpdateArrows`/`_Step` (ScrollTemplates.xml)
are reused as-is: both read only a Slider's own value/range/step, nothing faux-specific.
Named deferrals/OMITs (each re-cited at its own site below):
- `SetButtonPulse` (ref `TalentFrame_OnShow`/`_OnHide`/`TalentFrameTab_OnClick`'s
pulse-clear loop, and `TalentTabTemplate`'s own `PLAYER_LEVEL_UP` level-9 flash-hint
OnLoad/OnEvent): decision 0304's own Scope-calls section, "No microbutton pulse — the
reference pulses TalentMicroButton on point gain". The microbar itself has since landed
(`MicroMenu.xml`) and `UpdateMicroButtons()` is live in OnShow/OnHide below — but the
PULSE stays out: it rides `PLAYER_LEVEL_UP`, which this client never fires. All four
pulse sites drop cleanly (none gate anything else).
- `message(...)` (5 sites: the `numTalents > MAX_NUM_TALENTS` reminder, `_GetArrowTexture`/
`_GetBranchTexture`'s pool-exhaustion notices, and 3 sites in `_DrawLines`'s own layout-
blocked guards): no engine `message()` global exists (grepped `crates/benilla-ui/src`;
GossipFrame.xml's own "too many quests" message() is the same-shaped, already-accepted
OMIT in this codebase). Every site is a defensive diagnostic for a data bug 1.12's own
authored `Talent.dbc` never produces (no tree exceeds 20 talents/tab, no tree is laid out
blocked) — dropped outright, control flow (the `return`s / nil-propagation) kept intact.
The two texture-pool getters additionally gain a nil-guard at their TWO call sites
(`_SetArrowTexture`/`_SetBranchTexture`) beyond the reference's own behavior (which lets
its dropped `message()` call's nil flow straight into a `:SetTexCoord` crash) — a small,
documented safety margin now that the diagnostic is gone, not a behavior change for any
reachable (≤30 branches/arrows) input.
- `UIPanelCloseButton`/`UIPanelButtonTemplate`/`ItemButtonTemplate` (ref base templates):
none ship in this house dialect. The close button uses the house minimize-button art
(SpellBookFrame/CharacterFrame's own precedent); the bottom "Close" button locally
reconstructs `UIPanelButtonTemplate`'s art (`UI-Panel-Button-*`, StackSplit.xml's own
precedent for the identical gap); the talent button locally reconstructs exactly what
`ItemButtonTemplate` (`WoW/_extracted_framexml/ItemButtonTemplate.xml`, read this session)
contributes (37x37, the Quickslot2 ring, the icon Texture child) plus `TalentButtonTemplate`
(Blizzard_TalentUITemplates.xml)'s own additions (the EmptySlot backdrop, RankBorder, Rank
text) — CharacterFrame.xml's own `BenillaPaperDollItemSlotTemplate` is the identical-shape
precedent for this whole reconstruction move.
- `SetItemButtonTexture`/`SetItemButtonDesaturated`: no engine equivalent (ENGINE API note).
The icon Texture child (`$parentIconTexture`) is `SetTexture`'d directly; desaturation is
approximated by `SetVertexColor(0.65, 0.65, 0.65)` / restored to `(1, 1, 1)`
(CharacterFrame.xml:873's identical gray-dim convention for its own equip slots).
Engine-gap notes (transcribed anyway per this file's own instructions, or worked around):
- `text=` XML attributes are a LITERAL, verbatim `SetText` in this engine — verified,
`benilla-ui/src/loader/regions.rs:222-223` and `loader/widgets.rs:153-154` both pass the
attribute string straight through, no GlobalStrings lookup. The ref's own `text="TALENTS"`/
`text="CHARACTER_POINTS1_COLON"` (both static — neither FontString is EVER touched by
TalentFrame.lua, confirmed by reading the whole file this session) therefore become the
literal, properly-cased resolved strings ("Talents" / "Talent Points:") directly in this
file's XML, CharacterFrame.xml's own `text="Armor:"` precedent for exactly this class of
static label (its `ARMOR_COLON` Lua declaration exists for tooltip composition elsewhere,
never for driving that XML attribute). `TALENTS`/`CHARACTER_POINTS1_COLON` are still
declared below via the `X = X or "…"` idiom for citation/provenance only (unread by any
Lua in this file) — their exact wording is this session's best-known value for the real
1.12 GlobalStrings entry, NOT re-verified against a live extraction (none is present in
this worktree this session; `MASTERY_POINTS_SPENT`'s wording+line came from the task brief
itself and IS load-bearing, since it's genuinely `format()`-composed at runtime below).
- `HitRectInsets`: transcribed for fidelity; no loader wiring exists yet (CharacterFrame.
xml's own precedent note) — a harmless no-op today.
- `UNIT_PORTRAIT_UPDATE`: registered per the ref (`TalentFrame_OnLoad`); this engine never
fires it (grepped `crates/benilla-ui/src`, `crates/benilla/src`) — forward-compatible,
harmless, the same "transcribe the hook even if nothing drives it yet" posture as
HitRectInsets above.
- Bare `min`/`max`/`select` (used verbatim in `_DrawLines`/`_SetPrereqs` below): CONFIRMED
available — `min`/`max` are house-stdlib aliases for `math.min`/`math.max`
(`benilla-ui/src/script/stdlib.rs`), `select` is stock Lua 5.1. The ref's own `arg`/`arg.n`
Lua-5.0 vararg table (`_SetPrereqs`) has no equivalent here — `LUA_COMPAT_VARARG` isn't
shipped (WorldMapFrame.xml's own identical note) — replaced with `select("#", ...)`/
`select(i, ...)` over a trailing `...`, WorldMapFrame.xml's own precedent for the identical
substitution.
- `GetTalentTabInfo` called TWICE in a row by the ref's own `TalentFrame_Update` (l.152-154:
once truncated to just its first return for the `talentTabName` truthiness check, then
again for the full tuple — a real redundancy in the reference's own code, not a
byte-meaningful pattern) collapses to the ONE call already shared by both uses,
SpellBookFrame.xml's own `ToggleSpellBook` precedent for this exact class of cleanup
("a redundant-code cleanup, not a behavior change").
Sounds (decision 0090, cited verified this session per the task brief): `TalentScreenOpen`/
`TalentScreenClose` on Show/Hide (ids 6144/6145, ref `TalentFrame_OnShow`/`_OnHide`),
`igCharacterInfoTab` on tab click (id 841, ref `TalentFrameTab_OnClick`).
-->
<Script><![CDATA[
-- ── GlobalStrings values this window cites (ref-GlobalStrings.lua) — TALENTS/CHARACTER_
-- POINTS1_COLON are provenance-only (this file's header comment: their FontStrings are
-- static XML text, never Lua-read); MASTERY_POINTS_SPENT IS genuinely format()-composed
-- below and its wording+line are the task brief's own (GlobalStrings:2674). ────────────────
TALENTS = TALENTS or "Talents"
CHARACTER_POINTS1_COLON = CHARACTER_POINTS1_COLON or "Talent Points:"
MASTERY_POINTS_SPENT = MASTERY_POINTS_SPENT or "Points spent in %s Talents:" -- :2674
-- ── This window's own constants (ref Blizzard_TalentUI.lua:2-11, bare-named exactly as the
-- ref names them — none collide with anything else in this engine, grepped) ─────────────────
MAX_NUM_TALENTS = 20
MAX_NUM_TALENT_TIERS = 8
NUM_TALENT_COLUMNS = 4
TALENT_BRANCH_ARRAY = {}
TALENT_BUTTON_SIZE = 32
MAX_NUM_BRANCH_TEXTURES = 30
MAX_NUM_ARROW_TEXTURES = 30
INITIAL_TALENT_OFFSET_X = 35
INITIAL_TALENT_OFFSET_Y = 20
-- Benilla-prefixed + renumbered (5 -> 3): this file's header comment, "SCOPE CALL".
BENILLA_MAX_TALENT_TABS = 3
-- The scroll child's fixed height: this file's header comment, "SCROLL DESIGN".
BENILLA_TALENT_SCROLL_CHILD_H = 504
BENILLA_TALENT_SCROLL_FRAME_H = 332 -- the ref's own TalentFrameScrollFrame height (ref l.278)
BENILLA_TALENT_SCROLL_MAX = BENILLA_TALENT_SCROLL_CHILD_H - BENILLA_TALENT_SCROLL_FRAME_H
BENILLA_TALENT_SCROLL_STEP = 20 -- one wheel notch / arrow-click step (QuestLogFrame.xml's
-- own BENILLA_QUESTLOG_SCROLL_STEP precedent for a real
-- ScrollFrame's per-notch pixel step)
-- ref Blizzard_TalentUI.lua:13-69, verbatim.
TALENT_BRANCH_TEXTURECOORDS = {
up = {
[1] = {0.12890625, 0.25390625, 0, 0.484375},
[-1] = {0.12890625, 0.25390625, 0.515625, 1.0}
},
down = {
[1] = {0, 0.125, 0, 0.484375},
[-1] = {0, 0.125, 0.515625, 1.0}
},
left = {
[1] = {0.2578125, 0.3828125, 0, 0.5},
[-1] = {0.2578125, 0.3828125, 0.5, 1.0}
},
right = {
[1] = {0.2578125, 0.3828125, 0, 0.5},
[-1] = {0.2578125, 0.3828125, 0.5, 1.0}
},
topright = {
[1] = {0.515625, 0.640625, 0, 0.5},
[-1] = {0.515625, 0.640625, 0.5, 1.0}
},
topleft = {
[1] = {0.640625, 0.515625, 0, 0.5},
[-1] = {0.640625, 0.515625, 0.5, 1.0}
},
bottomright = {
[1] = {0.38671875, 0.51171875, 0, 0.5},
[-1] = {0.38671875, 0.51171875, 0.5, 1.0}
},
bottomleft = {
[1] = {0.51171875, 0.38671875, 0, 0.5},
[-1] = {0.51171875, 0.38671875, 0.5, 1.0}
},
tdown = {
[1] = {0.64453125, 0.76953125, 0, 0.5},
[-1] = {0.64453125, 0.76953125, 0.5, 1.0}
},
tup = {
[1] = {0.7734375, 0.8984375, 0, 0.5},
[-1] = {0.7734375, 0.8984375, 0.5, 1.0}
},
}
TALENT_ARROW_TEXTURECOORDS = {
top = {
[1] = {0, 0.5, 0, 0.5},
[-1] = {0, 0.5, 0.5, 1.0}
},
right = {
[1] = {1.0, 0.5, 0, 0.5},
[-1] = {1.0, 0.5, 0.5, 1.0}
},
left = {
[1] = {0.5, 1.0, 0, 0.5},
[-1] = {0.5, 1.0, 0.5, 1.0}
},
}
-- ── The window: toggle (ref TalentFrame_Toggle, l.73-79) ────────────────────────────────
-- Renamed ToggleTalentFrame — this file's header comment, "ONE deliberate naming deviation".
-- Kept BARE (not Benilla-prefixed) — the addon-facing entry point; the 'N' keybinding
-- already calls this by name (ui_script/input.rs).
function ToggleTalentFrame()
if BenillaTalentFrame:IsVisible() then
HideUIPanel(BenillaTalentFrame)
else
ShowUIPanel(BenillaTalentFrame)
end
end
-- ref TalentFrame_OnLoad (l.81-93).
function BenillaTalentFrame_OnLoad(frame)
PanelTemplates_SetNumTabs(frame, BENILLA_MAX_TALENT_TABS)
PanelTemplates_SetTab(frame, 1)
frame:RegisterEvent("CHARACTER_POINTS_CHANGED")
frame:RegisterEvent("SPELLS_CHANGED")
-- UNIT_PORTRAIT_UPDATE: this file's header comment (engine-gap notes) — never fired
-- today, registered anyway for ref fidelity/forward-compat.
frame:RegisterEvent("UNIT_PORTRAIT_UPDATE")
for i = 1, MAX_NUM_TALENT_TIERS do
TALENT_BRANCH_ARRAY[i] = {}
for j = 1, NUM_TALENT_COLUMNS do
TALENT_BRANCH_ARRAY[i][j] = {
id = nil, up = 0, left = 0, right = 0, down = 0,
leftArrow = 0, rightArrow = 0, topArrow = 0,
}
end
end
end
-- ref TalentFrame_OnShow (l.95-103): SetButtonPulse still OMITTED (decision 0304's own "No
-- microbutton pulse" scope call — the pulse rides PLAYER_LEVEL_UP, which this client never
-- fires). UpdateMicroButtons is LIVE as of the micro-menu slice (MicroMenu.xml).
function BenillaTalentFrame_OnShow()
if UpdateMicroButtons then UpdateMicroButtons() end
PlaySound("TalentScreenOpen")
BenillaTalentFrame_Update()
end
-- ref TalentFrame_OnHide (l.105-108).
function BenillaTalentFrame_OnHide()
if UpdateMicroButtons then UpdateMicroButtons() end
PlaySound("TalentScreenClose")
end
-- ref TalentFrame_OnEvent (l.110-118) — no IsVisible() guard: transcribed exactly as THIS
-- reference does it (it refreshes even while hidden; a different window guarding its own
-- OnEvent, e.g. SpellBookFrame, is that window's own choice, not borrowed here).
function BenillaTalentFrame_OnEvent(frame)
if event == "CHARACTER_POINTS_CHANGED" or event == "SPELLS_CHANGED" then
BenillaTalentFrame_Update()
elseif event == "UNIT_PORTRAIT_UPDATE" then
if arg1 == "player" then
SetPortraitTexture(getglobal("BenillaTalentFramePortrait"), "player")
end
end
end
-- ref TalentFrameTalent_OnEvent (Blizzard_TalentUITemplates.xml:120-124) — the hovered
-- button's own CHARACTER_POINTS_CHANGED re-poll. GameTooltip:IsOwned VERIFIED present
-- (`benilla-ui/src/script/tooltip/verbs.rs:107`), transcribed as-is (not an emulation).
function BenillaTalentButton_OnEvent(button)
if GameTooltip:IsOwned(button) then
GameTooltip:SetTalent(PanelTemplates_GetSelectedTab(BenillaTalentFrame), button:GetID())
end
end
-- ref TalentFrame_Update (l.126-321) — the big one. See this file's header comment for the
-- SetItemButtonTexture/SetItemButtonDesaturated substitution, the message() drops, and the
-- GetTalentTabInfo double-call collapse.
function BenillaTalentFrame_Update()
-- Setup Tabs
local tab, name, iconTexture, pointsSpent, button
local numTabs = GetNumTalentTabs()
for i = 1, BENILLA_MAX_TALENT_TABS do
tab = getglobal("BenillaTalentFrameTab" .. i)
if i <= numTabs then
name, iconTexture, pointsSpent = GetTalentTabInfo(i)
if i == PanelTemplates_GetSelectedTab(BenillaTalentFrame) then
-- If tab is the selected tab set the points spent info
getglobal("BenillaTalentFrameSpentPoints"):SetText(
format(MASTERY_POINTS_SPENT, name) .. " " ..
HIGHLIGHT_FONT_COLOR_CODE .. pointsSpent .. FONT_COLOR_CODE_CLOSE)
BenillaTalentFrame.pointsSpent = pointsSpent
end
tab:SetText(name)
PanelTemplates_TabResize(10, tab)
tab:Show()
else
tab:Hide()
end
end
-- Defensive clamp beyond the ref: this file's header comment ("SCOPE CALL").
PanelTemplates_SetNumTabs(BenillaTalentFrame, min(numTabs, BENILLA_MAX_TALENT_TABS))
PanelTemplates_UpdateTabs(BenillaTalentFrame)
-- Setup Frame
SetPortraitTexture(getglobal("BenillaTalentFramePortrait"), "player")
BenillaTalentFrame_UpdateTalentPoints()
local selectedTab = PanelTemplates_GetSelectedTab(BenillaTalentFrame)
-- ref l.152-154's own double GetTalentTabInfo call collapses to one — this file's
-- header comment ("GetTalentTabInfo called TWICE").
local talentTabName, texture, points, fileName = GetTalentTabInfo(selectedTab)
local base
if talentTabName then
base = "Interface\\TalentFrame\\" .. fileName .. "-"
else
-- temporary default for classes without talents poor guys (ref's own comment)
base = "Interface\\TalentFrame\\MageFire-"
end
getglobal("BenillaTalentFrameBackgroundTopLeft"):SetTexture(base .. "TopLeft")
getglobal("BenillaTalentFrameBackgroundTopRight"):SetTexture(base .. "TopRight")
getglobal("BenillaTalentFrameBackgroundBottomLeft"):SetTexture(base .. "BottomLeft")
getglobal("BenillaTalentFrameBackgroundBottomRight"):SetTexture(base .. "BottomRight")
local numTalents = GetNumTalents(selectedTab)
-- ref's own "Too many talents in talent frame!" message() DROPPED — this file's header
-- comment (no engine message() global; a defensive diagnostic 1.12 data never trips).
BenillaTalentFrame_ResetBranches()
local tier, column, rank, maxRank, isExceptional, meetsPrereq
local forceDesaturated, tierUnlocked
for i = 1, MAX_NUM_TALENTS do
button = getglobal("BenillaTalentFrameTalent" .. i)
if i <= numTalents then
-- Set the button info
name, iconTexture, tier, column, rank, maxRank, isExceptional, meetsPrereq =
GetTalentInfo(selectedTab, i)
local buttonName = "BenillaTalentFrameTalent" .. i
getglobal(buttonName .. "Rank"):SetText(rank)
BenillaSetTalentButtonLocation(button, tier, column)
TALENT_BRANCH_ARRAY[tier][column].id = button:GetID()
-- If player has no talent points then show only talents with points in them
if BenillaTalentFrame.talentPoints <= 0 and rank == 0 then
forceDesaturated = 1
else
forceDesaturated = nil
end
-- If the player has spent at least 5 talent points in the previous tier
if (tier - 1) * 5 <= BenillaTalentFrame.pointsSpent then
tierUnlocked = 1
else
tierUnlocked = nil
end
-- SetItemButtonTexture(button, iconTexture): this file's header comment (ENGINE
-- API note) — set the icon Texture child directly. A nil texture (shouldn't
-- happen once fed, ui_talent.rs's own Talent.dbc x Spell.dbc join) leaves
-- whatever was last shown rather than erroring on a nil SetTexture.
if iconTexture then
getglobal(buttonName .. "IconTexture"):SetTexture(iconTexture)
end
-- Talent must meet prereqs or the player must have no points to spend
if BenillaTalentFrame_SetPrereqs(tier, column, forceDesaturated, tierUnlocked,
GetTalentPrereqs(selectedTab, i)) and meetsPrereq then
-- SetItemButtonDesaturated(button, nil): restore full color.
getglobal(buttonName .. "IconTexture"):SetVertexColor(1, 1, 1)
if rank < maxRank then
-- Rank is green if not maxed out
getglobal(buttonName .. "Slot"):SetVertexColor(0.1, 1.0, 0.1)
getglobal(buttonName .. "Rank"):SetTextColor(
GREEN_FONT_COLOR.r, GREEN_FONT_COLOR.g, GREEN_FONT_COLOR.b)
else
getglobal(buttonName .. "Slot"):SetVertexColor(1.0, 0.82, 0)
getglobal(buttonName .. "Rank"):SetTextColor(
NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
end
getglobal(buttonName .. "RankBorder"):Show()
getglobal(buttonName .. "Rank"):Show()
else
-- SetItemButtonDesaturated(button, 1, 0.65, 0.65, 0.65): gray-dim the icon
-- (this file's header comment, CharacterFrame.xml:873's precedent).
getglobal(buttonName .. "IconTexture"):SetVertexColor(0.65, 0.65, 0.65)
getglobal(buttonName .. "Slot"):SetVertexColor(0.5, 0.5, 0.5)
if rank == 0 then
getglobal(buttonName .. "RankBorder"):Hide()
getglobal(buttonName .. "Rank"):Hide()
else
getglobal(buttonName .. "RankBorder"):SetVertexColor(0.5, 0.5, 0.5)
getglobal(buttonName .. "Rank"):SetTextColor(
GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
end
end
button:Show()
else
button:Hide()
end
end
-- Draw the prereq branches (ref l.232-320, verbatim). UpdateScrollChildRect() DROPPED
-- from the tier loop's tail — this file's header comment ("SCROLL DESIGN": a fixed-
-- height child never needs its range re-derived).
local node
local xOffset, yOffset
local ignoreUp
local tempNode
BenillaTalentFrame_ResetBranchTextureCount()
BenillaTalentFrame_ResetArrowTextureCount()
for i = 1, MAX_NUM_TALENT_TIERS do
for j = 1, NUM_TALENT_COLUMNS do
node = TALENT_BRANCH_ARRAY[i][j]
-- Setup offsets
xOffset = ((j - 1) * 63) + INITIAL_TALENT_OFFSET_X + 2
yOffset = -((i - 1) * 63) - INITIAL_TALENT_OFFSET_Y - 2
if node.id then
-- Has talent
if node.up ~= 0 then
if not ignoreUp then
BenillaTalentFrame_SetBranchTexture(i, j,
TALENT_BRANCH_TEXTURECOORDS["up"][node.up], xOffset, yOffset + TALENT_BUTTON_SIZE)
else
ignoreUp = nil
end
end
if node.down ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j,
TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset, yOffset - TALENT_BUTTON_SIZE + 1)
end
if node.left ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j,
TALENT_BRANCH_TEXTURECOORDS["left"][node.left], xOffset - TALENT_BUTTON_SIZE, yOffset)
end
if node.right ~= 0 then
-- See if any connecting branches are gray and if so color them gray
tempNode = TALENT_BRANCH_ARRAY[i][j + 1]
if tempNode.left ~= 0 and tempNode.down < 0 then
BenillaTalentFrame_SetBranchTexture(i, j - 1,
TALENT_BRANCH_TEXTURECOORDS["right"][tempNode.down], xOffset + TALENT_BUTTON_SIZE, yOffset)
else
BenillaTalentFrame_SetBranchTexture(i, j,
TALENT_BRANCH_TEXTURECOORDS["right"][node.right], xOffset + TALENT_BUTTON_SIZE + 1, yOffset)
end
end
-- Draw arrows
if node.rightArrow ~= 0 then
BenillaTalentFrame_SetArrowTexture(i, j,
TALENT_ARROW_TEXTURECOORDS["right"][node.rightArrow], xOffset + TALENT_BUTTON_SIZE / 2 + 5, yOffset)
end
if node.leftArrow ~= 0 then
BenillaTalentFrame_SetArrowTexture(i, j,
TALENT_ARROW_TEXTURECOORDS["left"][node.leftArrow], xOffset - TALENT_BUTTON_SIZE / 2 - 5, yOffset)
end
if node.topArrow ~= 0 then
BenillaTalentFrame_SetArrowTexture(i, j,
TALENT_ARROW_TEXTURECOORDS["top"][node.topArrow], xOffset, yOffset + TALENT_BUTTON_SIZE / 2 + 5)
end
else
-- Doesn't have a talent
if node.up ~= 0 and node.left ~= 0 and node.right ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["tup"][node.up], xOffset, yOffset)
elseif node.down ~= 0 and node.left ~= 0 and node.right ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["tdown"][node.down], xOffset, yOffset)
elseif node.left ~= 0 and node.down ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["topright"][node.left], xOffset, yOffset)
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset, yOffset - 32)
elseif node.left ~= 0 and node.up ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["bottomright"][node.left], xOffset, yOffset)
elseif node.left ~= 0 and node.right ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["right"][node.right], xOffset + TALENT_BUTTON_SIZE, yOffset)
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["left"][node.left], xOffset + 1, yOffset)
elseif node.right ~= 0 and node.down ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["topleft"][node.right], xOffset, yOffset)
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset, yOffset - 32)
elseif node.right ~= 0 and node.up ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["bottomleft"][node.right], xOffset, yOffset)
elseif node.up ~= 0 and node.down ~= 0 then
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["up"][node.up], xOffset, yOffset)
BenillaTalentFrame_SetBranchTexture(i, j, TALENT_BRANCH_TEXTURECOORDS["down"][node.down], xOffset, yOffset - 32)
ignoreUp = 1
end
end
end
end
-- Hide any unused branch textures
for i = BenillaTalentFrame_GetBranchTextureCount(), MAX_NUM_BRANCH_TEXTURES do
getglobal("BenillaTalentFrameBranch" .. i):Hide()
end
-- Hide any unused arrow textures
for i = BenillaTalentFrame_GetArrowTextureCount(), MAX_NUM_ARROW_TEXTURES do
getglobal("BenillaTalentFrameArrow" .. i):Hide()
end
end
function BenillaTalentFrame_SetArrowTexture(tier, column, texCoords, xOffset, yOffset)
local arrowTexture = BenillaTalentFrame_GetArrowTexture()
-- nil-guard beyond the ref: this file's header comment (message() drop).
if not arrowTexture then return end
arrowTexture:SetTexCoord(texCoords[1], texCoords[2], texCoords[3], texCoords[4])
arrowTexture:SetPoint("TOPLEFT", "BenillaTalentFrameScrollChild", "TOPLEFT", xOffset, yOffset)
end
function BenillaTalentFrame_SetBranchTexture(tier, column, texCoords, xOffset, yOffset)
local branchTexture = BenillaTalentFrame_GetBranchTexture()
if not branchTexture then return end
branchTexture:SetTexCoord(texCoords[1], texCoords[2], texCoords[3], texCoords[4])
branchTexture:SetPoint("TOPLEFT", "BenillaTalentFrameScrollChild", "TOPLEFT", xOffset, yOffset)
end
-- ref TalentFrame_GetArrowTexture (l.335-344): message() DROPPED (this file's header
-- comment) — the exhaustion path otherwise matches the ref's own (nil past the 30th; both
-- callers above now guard it, unlike the ref's own dropped-message-then-crash shape).
function BenillaTalentFrame_GetArrowTexture()
local arrowTexture = getglobal("BenillaTalentFrameArrow" .. BenillaTalentFrame.arrowIndex)
BenillaTalentFrame.arrowIndex = BenillaTalentFrame.arrowIndex + 1
if arrowTexture then
arrowTexture:Show()
end
return arrowTexture
end
function BenillaTalentFrame_GetBranchTexture()
local branchTexture = getglobal("BenillaTalentFrameBranch" .. BenillaTalentFrame.textureIndex)
BenillaTalentFrame.textureIndex = BenillaTalentFrame.textureIndex + 1
if branchTexture then
branchTexture:Show()
end
return branchTexture
end
function BenillaTalentFrame_ResetArrowTextureCount()
BenillaTalentFrame.arrowIndex = 1
end
function BenillaTalentFrame_ResetBranchTextureCount()
BenillaTalentFrame.textureIndex = 1
end
function BenillaTalentFrame_GetArrowTextureCount()
return BenillaTalentFrame.arrowIndex
end
function BenillaTalentFrame_GetBranchTextureCount()
return BenillaTalentFrame.textureIndex
end
-- ref TalentFrame_SetPrereqs (l.373-395) — the Lua 5.0 `arg`/`arg.n` vararg table replaced
-- with `select` (this file's header comment, WorldMapFrame.xml's own precedent). The four
-- named leading params already consume the ref's arg[1..4]; the trailing `...` is exactly
-- the ref's arg[5..n] prereq triplets.
function BenillaTalentFrame_SetPrereqs(buttonTier, buttonColumn, forceDesaturated, tierUnlocked, ...)
local requirementsMet
if tierUnlocked and not forceDesaturated then
requirementsMet = 1
else
requirementsMet = nil
end
local n = select("#", ...)
for i = 1, n, 3 do
local tier, column, isLearnable = select(i, ...)
if not isLearnable or forceDesaturated then
requirementsMet = nil
end
BenillaTalentFrame_DrawLines(buttonTier, buttonColumn, tier, column, requirementsMet)
end
return requirementsMet
end
-- ref TalentFrame_DrawLines (l.398-527) — verbatim (bare min/max confirmed available,
-- this file's header comment, `benilla-ui/src/script/stdlib.rs`). message() calls DROPPED
-- at their 3 sites (this file's header comment); the `return`s that followed them are kept.
function BenillaTalentFrame_DrawLines(buttonTier, buttonColumn, tier, column, requirementsMet)
if requirementsMet then
requirementsMet = 1
else
requirementsMet = -1
end
-- Check to see if are in the same column
if buttonColumn == column then
-- Check for blocking talents
if (buttonTier - tier) > 1 then
-- If more than one tier difference
for i = tier + 1, buttonTier - 1 do
if TALENT_BRANCH_ARRAY[i][buttonColumn].id then
-- If there's an id, there's a blocker
return
end
end
end
-- Draw the lines
for i = tier, buttonTier - 1 do
TALENT_BRANCH_ARRAY[i][buttonColumn].down = requirementsMet
if (i + 1) <= (buttonTier - 1) then
TALENT_BRANCH_ARRAY[i + 1][buttonColumn].up = requirementsMet
end
end
-- Set the arrow
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].topArrow = requirementsMet
return
end
-- Check to see if they're in the same tier
if buttonTier == tier then
local left = min(buttonColumn, column)
local right = max(buttonColumn, column)
-- See if the distance is greater than one space
if (right - left) > 1 then
-- Check for blocking talents
for i = left + 1, right - 1 do
if TALENT_BRANCH_ARRAY[tier][i].id then
-- If there's an id, there's a blocker
return
end
end
end
-- If we get here then we're in the clear
for i = left, right - 1 do
TALENT_BRANCH_ARRAY[tier][i].right = requirementsMet
TALENT_BRANCH_ARRAY[tier][i + 1].left = requirementsMet
end
-- Determine where the arrow goes
if buttonColumn < column then
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].rightArrow = requirementsMet
else
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].leftArrow = requirementsMet
end
return
end
-- Now we know the prereq is diagonal from us
local left = min(buttonColumn, column)
local right = max(buttonColumn, column)
-- Don't check the location of the current button
if left == column then
left = left + 1
else
right = right - 1
end
-- Check for blocking talents
local blocked = nil
for i = left, right do
if TALENT_BRANCH_ARRAY[tier][i].id then
-- If there's an id, there's a blocker
blocked = 1
end
end
left = min(buttonColumn, column)
right = max(buttonColumn, column)
if not blocked then
TALENT_BRANCH_ARRAY[tier][buttonColumn].down = requirementsMet
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].up = requirementsMet
for i = tier, buttonTier - 1 do
TALENT_BRANCH_ARRAY[i][buttonColumn].down = requirementsMet
TALENT_BRANCH_ARRAY[i + 1][buttonColumn].up = requirementsMet
end
for i = left, right - 1 do
TALENT_BRANCH_ARRAY[tier][i].right = requirementsMet
TALENT_BRANCH_ARRAY[tier][i + 1].left = requirementsMet
end
-- Place the arrow
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].topArrow = requirementsMet
return
end
-- If we're here then we were blocked trying to go vertically first so we have to go
-- over first, then up
if left == buttonColumn then
left = left + 1
else
right = right - 1
end
-- Check for blocking talents
for i = left, right do
if TALENT_BRANCH_ARRAY[buttonTier][i].id then
-- If there's an id, then this layout is undrawable
return
end
end
-- If we're here we can draw the line
left = min(buttonColumn, column)
right = max(buttonColumn, column)
for i = tier, buttonTier - 1 do
TALENT_BRANCH_ARRAY[i][column].up = requirementsMet
TALENT_BRANCH_ARRAY[i + 1][column].down = requirementsMet
end
-- Determine where the arrow goes
if buttonColumn < column then
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].rightArrow = requirementsMet
else
TALENT_BRANCH_ARRAY[buttonTier][buttonColumn].leftArrow = requirementsMet
end
end
-- ref TalentFrameTalent_OnClick (Blizzard_TalentUITemplates.xml:97-99).
function BenillaTalentButton_OnClick(button)
LearnTalent(PanelTemplates_GetSelectedTab(BenillaTalentFrame), button:GetID())
end
-- ref TalentFrameTab_OnClick (l.533-540): the SetButtonPulse-clearing loop OMITTED — this
-- file's header comment (decision 0304's own "No microbutton pulse" scope call).
function BenillaTalentFrameTab_OnClick(tab)
PanelTemplates_SetTab(BenillaTalentFrame, tab:GetID())
BenillaTalentFrame_Update()
PlaySound("igCharacterInfoTab")
end
-- ref TalentFrame_UpdateTalentPoints (l.543-547).
function BenillaTalentFrame_UpdateTalentPoints()
local cp1, cp2 = UnitCharacterPoints("player")
getglobal("BenillaTalentFrameTalentPointsText"):SetText(cp1)
BenillaTalentFrame.talentPoints = cp1
end
-- ref SetTalentButtonLocation (l.549-553).
function BenillaSetTalentButtonLocation(button, tier, column)
local x = ((column - 1) * 63) + INITIAL_TALENT_OFFSET_X
local y = -((tier - 1) * 63) - INITIAL_TALENT_OFFSET_Y
button:SetPoint("TOPLEFT", button:GetParent(), "TOPLEFT", x, y)
end
-- ref TalentFrame_ResetBranches (l.555-568).
function BenillaTalentFrame_ResetBranches()
for i = 1, MAX_NUM_TALENT_TIERS do
for j = 1, NUM_TALENT_COLUMNS do
TALENT_BRANCH_ARRAY[i][j].id = nil
TALENT_BRANCH_ARRAY[i][j].up = 0
TALENT_BRANCH_ARRAY[i][j].down = 0
TALENT_BRANCH_ARRAY[i][j].left = 0
TALENT_BRANCH_ARRAY[i][j].right = 0
TALENT_BRANCH_ARRAY[i][j].rightArrow = 0
TALENT_BRANCH_ARRAY[i][j].leftArrow = 0
TALENT_BRANCH_ARRAY[i][j].topArrow = 0
end
end
end
-- ref TalentButtonTemplate's own inline OnEnter (Blizzard_TalentUITemplates.xml:90-93) —
-- `BenillaTalentFrame.selectedTab` read directly, matching the ref's own choice at THIS
-- site (this file's header comment, "TAB STRIP").
function BenillaTalentButton_OnEnter(button)
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
GameTooltip:SetTalent(BenillaTalentFrame.selectedTab, button:GetID())
end
-- ── The scroll frame (this file's header comment, "SCROLL DESIGN") ──────────────────────
function BenillaTalentFrameScroll_OnLoad(scrollFrame)
scrollFrame:SetScrollChild(getglobal("BenillaTalentFrameScrollChild"))
local bar = getglobal("BenillaTalentFrameScrollBar")
bar:SetMinMaxValues(0, BENILLA_TALENT_SCROLL_MAX)
bar:SetValueStep(BENILLA_TALENT_SCROLL_STEP)
bar:SetValue(0)
end
-- The real-ScrollFrame half of the documented Slider<->ScrollFrame wiring (`slider.rs`'s
-- own header comment: "OnValueChanged -> this:GetParent():SetVerticalScroll(arg1)") —
-- REPLACES BenillaScrollBarTemplate's inherited faux-offset handler (this file's header
-- comment, verified via loader/scripts.rs). BenillaFauxScrollBar_UpdateArrows is faux-
-- agnostic (ScrollTemplates.xml's own body) and reused as-is for the arrow enable state.
function BenillaTalentFrameScrollBar_OnValueChanged(scrollBar, value)
scrollBar:GetParent():SetVerticalScroll(value)
BenillaFauxScrollBar_UpdateArrows(scrollBar)
end
]]></Script>
<!--
The tree tab (ref TalentTabTemplate, Blizzard_TalentUITemplates.xml:3-23): inherits the
house's own CharacterFrameTabButtonTemplate (UiPanels.xml) — this file's header comment,
"TAB STRIP". The ref's own PLAYER_LEVEL_UP OnLoad/OnEvent (the level-9 pulse-flash hint) is
OMITTED with SetButtonPulse (this file's header comment); the ref's own blank OnEnter has
nothing to transcribe either.
-->
<Button name="BenillaTalentTabTemplate" inherits="CharacterFrameTabButtonTemplate" virtual="true">
<Scripts>
<OnClick>BenillaTalentFrameTab_OnClick(self)</OnClick>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</Button>
<!-- The branch/arrow art pool templates (ref TalentBranchTemplate/TalentArrowTemplate,
Blizzard_TalentUITemplates.xml:24-39, verbatim: 32x32, default TOPLEFT anchor — every
instance below is repositioned every Update via SetPoint, this default is just where an
instance sits before its first placement). -->
<Texture name="BenillaTalentBranchTemplate" file="Interface\TalentFrame\UI-TalentBranches" virtual="true">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<Texture name="BenillaTalentArrowTemplate" file="Interface\TalentFrame\UI-TalentArrows" virtual="true">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
</Texture>
<!--
The talent button (ref TalentButtonTemplate inherits ItemButtonTemplate,
Blizzard_TalentUITemplates.xml:40-101 + WoW/_extracted_framexml/ItemButtonTemplate.xml, both
read in full this session — self-contained rather than inheriting a base ItemButtonTemplate,
this file's header comment ("OMITs") and CharacterFrame.xml's own BenillaPaperDollItemSlot-
Template precedent for the identical gap. 37x37 (ItemButtonTemplate's own real size —
TALENT_BUTTON_SIZE=32 is a GRID PITCH constant, not this button's rendered size; the two are
independent, verified against ItemButtonTemplate.xml's own unsized $parentIconTexture +
64x64 NormalTexture ring, which visually overlaps neighboring buttons by design at the 63px
pitch — matches the ref exactly). Cooldown/AutoCast (ItemButtonTemplate has neither anyway)
don't apply here. CHARACTER_POINTS_CHANGED re-poll while hovered (ref l.85-89).
-->
<Button name="BenillaTalentButtonTemplate" virtual="true">
<Size><AbsDimension x="37" y="37"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentSlot" file="Interface\Buttons\UI-EmptySlot-White">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors><Anchor point="CENTER"/></Anchors>
</Texture>
</Layer>
<Layer level="BORDER">
<!-- ItemButtonTemplate's own icon Texture child: unsized/unanchored, verbatim (its
own XML, l.10) — CharacterFrame.xml's $parentIconTexture precedent for the
identical shape. -->
<Texture name="$parentIconTexture"/>
</Layer>
<Layer level="OVERLAY">
<Texture name="$parentRankBorder" file="Interface\TalentFrame\TalentFrame-RankBorder">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
</Texture>
<FontString name="$parentRank" inherits="GameFontNormalSmall">
<Anchors><Anchor point="CENTER" relativeTo="$parentRankBorder"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<NormalTexture name="$parentNormalTexture" file="Interface\Buttons\UI-Quickslot2">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors><Anchor point="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"/>
<Scripts>
<OnLoad>self:RegisterEvent("CHARACTER_POINTS_CHANGED")</OnLoad>
<OnEvent>BenillaTalentButton_OnEvent(self)</OnEvent>
<OnEnter>BenillaTalentButton_OnEnter(self)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
<OnClick>BenillaTalentButton_OnClick(self)</OnClick>
</Scripts>
</Button>
<Frame name="BenillaTalentFrame" hidden="true">
<!-- The real 384x512 window (ref Blizzard_TalentUI.xml:5-8). 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 (this file's
header comment) — a harmless no-op today. -->
<HitRectInsets><AbsInset left="0" right="30" top="0" bottom="45"/></HitRectInsets>
<Layers>
<!-- Portrait 60x60 TOPLEFT (7,-6) (ref l.19-31). Set in OnShow/on refresh via
SetPortraitTexture (CharacterFrame.xml's own precedent) — "player" is a fixed
token here, this window only ever shows the local player. -->
<Layer level="BACKGROUND">
<Texture name="BenillaTalentFramePortrait">
<Size><AbsDimension x="60" y="60"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<!-- The static chassis (ref l.34-82): the PaperDollInfoFrame top (shared with
CharacterFrame) + the TalentFrame-specific bottom, both class-agnostic. -->
<Layer level="BORDER">
<Texture file="Interface\PaperDollInfoFrame\UI-Character-General-TopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\PaperDollInfoFrame\UI-Character-General-TopRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\TalentFrame\UI-TalentFrame-BotLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\TalentFrame\UI-TalentFrame-BotRight">
<Size><AbsDimension x="128" y="256"/></Size>
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="2" y="-1"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<!-- The class-specific tree art (ref l.84-202): the 4 dynamic quadrants (retextured
every Update from `base`), the title, and the points-remaining/points-spent bar. -->
<Layer level="ARTWORK">
<Texture name="BenillaTalentFrameBackgroundTopLeft">
<Size><AbsDimension x="256" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="23" y="-77"/></Offset></Anchor></Anchors>
</Texture>
<Texture name="BenillaTalentFrameBackgroundTopRight">
<Size><AbsDimension x="64" y="256"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTalentFrameBackgroundTopLeft" relativePoint="TOPRIGHT"/></Anchors>
</Texture>
<Texture name="BenillaTalentFrameBackgroundBottomLeft">
<Size><AbsDimension x="256" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTalentFrameBackgroundTopLeft" relativePoint="BOTTOMLEFT"/></Anchors>
</Texture>
<Texture name="BenillaTalentFrameBackgroundBottomRight">
<Size><AbsDimension x="64" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTalentFrameBackgroundTopLeft" relativePoint="BOTTOMRIGHT"/></Anchors>
</Texture>
<!-- text="Talents": this file's header comment (engine-gap notes, text= is
literal — never Lua-read, the ref's own TalentFrameTitleText isn't either). -->
<FontString name="BenillaTalentFrameTitleText" inherits="GameFontNormal" text="Talents">
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="-18"/></Offset></Anchor></Anchors>
</FontString>
<Texture name="BenillaTalentFramePointsLeft" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="8" y="20"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTalentFrame"><Offset><AbsDimension x="75" y="-48"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="0.0625" top="0" bottom="0.625"/>
</Texture>
<Texture name="BenillaTalentFramePointsMiddle" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="248" y="20"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTalentFramePointsLeft" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0.0625" right="0.9375" top="0" bottom="0.625"/>
</Texture>
<Texture name="BenillaTalentFramePointsRight" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="8" y="20"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTalentFramePointsMiddle" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0.9375" right="1.0" top="0" bottom="0.625"/>
</Texture>
<!-- Fed every Update by format(MASTERY_POINTS_SPENT, name) — the live global, not
static text (this file's header comment). -->
<FontString name="BenillaTalentFrameSpentPoints" inherits="GameFontNormalSmall">
<Anchors><Anchor point="TOP" relativeTo="BenillaTalentFramePointsMiddle"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor></Anchors>
</FontString>
<!-- Fed by BenillaTalentFrame_UpdateTalentPoints (the unspent-points NUMBER). -->
<FontString name="BenillaTalentFrameTalentPointsText" inherits="GameFontHighlightSmall">
<Anchors><Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="252" y="87"/></Offset></Anchor></Anchors>
</FontString>
<!-- text="Talent Points:": this file's header comment (engine-gap notes, same as
the title — TalentFrame.lua never touches this FontString either). -->
<FontString name="BenillaTalentFrameTalentPoints" inherits="GameFontNormalSmall" text="Talent Points:">
<Anchors><Anchor point="RIGHT" relativeTo="BenillaTalentFrameTalentPointsText" relativePoint="LEFT"><Offset><AbsDimension x="-3" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- Close: the house minimize-button art (no UIPanelCloseButton template ships —
SpellBookFrame/CharacterFrame's own precedent), the same TOPRIGHT(-44,-25)
position as every other window's close button (ref l.205-213). -->
<Button name="BenillaTalentFrameCloseButton">
<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(BenillaTalentFrame)</OnClick>
</Scripts>
</Button>
<!-- The bottom "Close" text button (ref TalentFrameCancelButton, l.214-230): a second,
redundant close affordance the ref genuinely ships (identical HideUIPanel action to
the X button above) — included for completeness, this file's header comment
("OMITs") notes no reason to drop it. UIPanelButtonTemplate reconstructed locally
(StackSplit.xml's own precedent for the identical gap); text="Close" is this file's
header comment's literal-text convention, not a GlobalStrings token read. -->
<Button name="BenillaTalentFrameCancelButton" text="Close">
<Size><AbsDimension x="80" y="22"/></Size>
<Anchors><Anchor point="CENTER" relativePoint="TOPLEFT"><Offset><AbsDimension x="305" y="-420"/></Offset></Anchor></Anchors>
<NormalFont inherits="GameFontNormal"/>
<HighlightFont inherits="GameFontHighlight"/>
<DisabledFont inherits="GameFontDisable"/>
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
<DisabledTexture file="Interface\Buttons\UI-Panel-Button-Disabled"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></DisabledTexture>
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
<Scripts>
<OnClick>HideUIPanel(BenillaTalentFrame)</OnClick>
</Scripts>
</Button>
<!-- The 3 tree tabs (ref l.231-275, MAX_TALENT_TABS renumbered 5 -> 3, this file's
header comment "SCOPE CALL"). -->
<Button name="BenillaTalentFrameTab1" inherits="BenillaTalentTabTemplate" id="1">
<Anchors><Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="15" y="46"/></Offset></Anchor></Anchors>
</Button>
<Button name="BenillaTalentFrameTab2" inherits="BenillaTalentTabTemplate" id="2">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTalentFrameTab1" relativePoint="RIGHT"><Offset><AbsDimension x="-15" y="0"/></Offset></Anchor></Anchors>
</Button>
<Button name="BenillaTalentFrameTab3" inherits="BenillaTalentTabTemplate" id="3">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTalentFrameTab2" relativePoint="RIGHT"><Offset><AbsDimension x="-15" y="0"/></Offset></Anchor></Anchors>
</Button>
<!-- The scrolled talent grid (ref l.276-419: TalentFrameScrollFrame, UIPanelScrollFrame-
Template) — this file's header comment, "SCROLL DESIGN": a real ScrollFrame (decision
0112) with a FIXED-height child (BENILLA_TALENT_SCROLL_CHILD_H) and a reused real
Slider scrollbar (BenillaScrollBarTemplate, ScrollTemplates.xml), not the ref's own
dynamic UIPanelScrollFrameTemplate. -->
<ScrollFrame name="BenillaTalentFrameScroll">
<Size><AbsDimension x="296" y="332"/></Size>
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-65" y="-77"/></Offset></Anchor></Anchors>
<Frames>
<!-- The scrollbar: BenillaScrollBarTemplate's own geometry (ScrollTemplates.xml's
BenillaFauxScrollFrameTemplate, whose Slider anchor shape is reused directly
here without the Frame wrapper around it — this file's header comment). Its
inherited OnValueChanged is REPLACED below (not chained — verified). -->
<Slider name="BenillaTalentFrameScrollBar" inherits="BenillaScrollBarTemplate">
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="6" y="-16"/></Offset></Anchor>
<Anchor point="BOTTOMLEFT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="6" y="16"/></Offset></Anchor>
</Anchors>
<Scripts>
<OnValueChanged>BenillaTalentFrameScrollBar_OnValueChanged(self, arg1)</OnValueChanged>
</Scripts>
</Slider>
<!-- The scroll child (SetScrollChild'd in BenillaTalentFrameScroll_OnLoad below,
QuestLogFrame.xml's own real-ScrollFrame precedent for the wiring shape):
320 wide (the ref's own TalentFrameScrollChildFrame width, ref l.318-321),
504 tall (this file's header comment, "SCROLL DESIGN" — a load-time constant,
never resized). -->
<Frame name="BenillaTalentFrameScrollChild">
<Size><AbsDimension x="320" y="504"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture name="BenillaTalentFrameBranch1" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch2" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch3" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch4" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch5" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch6" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch7" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch8" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch9" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch10" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch11" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch12" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch13" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch14" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch15" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch16" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch17" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch18" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch19" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch20" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch21" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch22" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch23" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch24" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch25" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch26" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch27" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch28" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch29" inherits="BenillaTalentBranchTemplate"/>
<Texture name="BenillaTalentFrameBranch30" inherits="BenillaTalentBranchTemplate"/>
</Layer>
</Layers>
<Frames>
<!-- The 20 talent buttons (ref l.360-379, MAX_NUM_TALENTS) — id=1..20 is
the button's own within-tab index (GetTalentInfo's 2nd arg / GetID()).
No static Anchors: BenillaSetTalentButtonLocation SetPoints each one
every Update, off its OWN template's default TOPLEFT. -->
<Button name="BenillaTalentFrameTalent1" inherits="BenillaTalentButtonTemplate" id="1"/>
<Button name="BenillaTalentFrameTalent2" inherits="BenillaTalentButtonTemplate" id="2"/>
<Button name="BenillaTalentFrameTalent3" inherits="BenillaTalentButtonTemplate" id="3"/>
<Button name="BenillaTalentFrameTalent4" inherits="BenillaTalentButtonTemplate" id="4"/>
<Button name="BenillaTalentFrameTalent5" inherits="BenillaTalentButtonTemplate" id="5"/>
<Button name="BenillaTalentFrameTalent6" inherits="BenillaTalentButtonTemplate" id="6"/>
<Button name="BenillaTalentFrameTalent7" inherits="BenillaTalentButtonTemplate" id="7"/>
<Button name="BenillaTalentFrameTalent8" inherits="BenillaTalentButtonTemplate" id="8"/>
<Button name="BenillaTalentFrameTalent9" inherits="BenillaTalentButtonTemplate" id="9"/>
<Button name="BenillaTalentFrameTalent10" inherits="BenillaTalentButtonTemplate" id="10"/>
<Button name="BenillaTalentFrameTalent11" inherits="BenillaTalentButtonTemplate" id="11"/>
<Button name="BenillaTalentFrameTalent12" inherits="BenillaTalentButtonTemplate" id="12"/>
<Button name="BenillaTalentFrameTalent13" inherits="BenillaTalentButtonTemplate" id="13"/>
<Button name="BenillaTalentFrameTalent14" inherits="BenillaTalentButtonTemplate" id="14"/>
<Button name="BenillaTalentFrameTalent15" inherits="BenillaTalentButtonTemplate" id="15"/>
<Button name="BenillaTalentFrameTalent16" inherits="BenillaTalentButtonTemplate" id="16"/>
<Button name="BenillaTalentFrameTalent17" inherits="BenillaTalentButtonTemplate" id="17"/>
<Button name="BenillaTalentFrameTalent18" inherits="BenillaTalentButtonTemplate" id="18"/>
<Button name="BenillaTalentFrameTalent19" inherits="BenillaTalentButtonTemplate" id="19"/>
<Button name="BenillaTalentFrameTalent20" inherits="BenillaTalentButtonTemplate" id="20"/>
<!-- The arrow-art pool (ref l.380-415: TalentFrameArrowFrame,
setAllPoints="true") — an explicit Size matching the scroll child
instead, the equivalent shape CharacterFrame.xml's paper doll takes. -->
<Frame name="BenillaTalentFrameArrowFrame">
<Size><AbsDimension x="320" y="504"/></Size>
<Anchors><Anchor point="TOPLEFT"/></Anchors>
<Layers>
<Layer level="OVERLAY">
<Texture name="BenillaTalentFrameArrow1" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow2" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow3" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow4" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow5" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow6" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow7" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow8" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow9" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow10" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow11" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow12" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow13" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow14" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow15" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow16" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow17" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow18" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow19" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow20" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow21" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow22" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow23" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow24" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow25" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow26" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow27" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow28" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow29" inherits="BenillaTalentArrowTemplate"/>
<Texture name="BenillaTalentFrameArrow30" inherits="BenillaTalentArrowTemplate"/>
</Layer>
</Layers>
</Frame>
</Frames>
</Frame>
</Frames>
<Scripts>
<OnLoad>BenillaTalentFrameScroll_OnLoad(self)</OnLoad>
<!-- WoW wheel convention: +1 per notch up (toward the top, decreasing scroll),
-1 down (ChatFrame.xml's own handler quotes the same convention). Reuses
BenillaFauxScrollBar_Step (this file's header comment: generic, Slider-only). -->
<OnMouseWheel>BenillaFauxScrollBar_Step(getglobal("BenillaTalentFrameScrollBar"), arg1 > 0 and -1 or 1)</OnMouseWheel>
<!-- The real ScrollFrame's own half of the documented pair (this file's header
comment / BenillaTalentFrameScrollBar_OnValueChanged's own comment). -->
<OnVerticalScroll>getglobal("BenillaTalentFrameScrollBar"):SetValue(arg1)</OnVerticalScroll>
</Scripts>
</ScrollFrame>
</Frames>
<Scripts>
<OnLoad>BenillaTalentFrame_OnLoad(self)</OnLoad>
<OnShow>BenillaTalentFrame_OnShow()</OnShow>
<OnHide>BenillaTalentFrame_OnHide()</OnHide>
<OnEvent>BenillaTalentFrame_OnEvent(self)</OnEvent>
</Scripts>
</Frame>
</Ui>