Files
benilla-octo/crates/benilla-app/assets/ui/TradeSkillFrame.xml
T
SamandClaude Fable 5 462602c44b the world becomes the UI's backdrop, and recipe rows go white on hover
- every translucent UI pixel over the 3D world composited in the wrong space: the world lane and the UI lane each blend in gamma bytes the way the reference's fixed-function device does, but the one blend that mixes them — the UI camera's finished image onto the sRGB swapchain — ran in linear, so a chat tab whose body is black at alpha 102/255 left the scene at 77.5% of its brightness instead of 60%, and the dock's hover glow (an ADD with no plate under it) read as a lozenge floating on grass; the world camera now renders to an off-screen image drawn as the first quad of the UI pass, so every UI blend over the world is the same blend as every UI blend over UI — after, the selected tab plate lands at 0.593–0.600 of the bare scene against 0.60 predicted, and bare-world pixels move at most 1 LSB
- the backdrop image is un-encoded `Rgba16Float`, because the UI arc composites in gamma and takes its one decode at the end — pre-encoding would land a second encode in that chain, and quantizing un-encoded values to 8 bits is the banding collapse measured long ago; and hiding the UI keeps the world: the toggle used to retire every batch in the pass, which would now be a black screen — hidden means the two UI lanes draw nothing while the backdrop, which is the world, still draws, with the no-world glue screens pinned on the old behaviour
- a button's highlighted label is its own font instance: the client swaps which of three embedded instances the label reads — it never merges colour across states — so the `highlight_color.or(normal_color)` fallback is gone, and `LockHighlight()` now enters the label, not just the highlight texture (the reference blanks a recipe row's highlight texture to "" and then locks the row it selected — with nothing left to pin, the lock's only possible effect is the label, and that is the white text on a selected recipe)
- tradeskill, craft and class-trainer rows read white on hover and on select: all three windows carried the row name as a child FontString no per-state font can reach, behind a "no engine method" note that was true when written and stale ever since — the name is now the button's own `<ButtonText>` under the base template's `<NormalFont>`/`<HighlightFont>`/`<DisabledFont>` trio, and the windows call `SetTextColor`/`LockHighlight`/`UnlockHighlight` where the reference calls them

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-08-27 15:06:05 +07:00

1330 lines
90 KiB
XML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<Ui>
<!--
benilla's crafting window (decision 0437 phase 2): the 1.12 `TradeSkillFrame`, re-expressed from
the reference `Blizzard_TradeSkillUI.xml`/`.lua` (session scratchpad `ref-ui/`, decision 0246
discipline — never committed) onto our own engine/feeds. Geometry/art-paths/structure below are
QUOTED from the ref (line citations inline); the Lua is the ref's logic turned onto benilla's
Era API (`GetTradeSkillLine`/`GetNumTradeSkills`/`GetTradeSkillInfo`/reagents/tools/cooldown/
`DoTradeSkill`/`CloseTradeSkill`, decision 0437's engine surface, built alongside this file).
NOT an NPC session (unlike Gossip/Merchant/Trainer): the window opens client-side off your own
cast of an effect-47 opener spell — no NPC, no server round trip to open it (0437 §"What
scoping verified": `Spell_C::TryCast` special-branches on `SPELL_EFFECT_TRADE_SKILL` and
returns before the cast-send). It registers `TRADE_SKILL_SHOW`/`_UPDATE`/`_CLOSE`/
`UPDATE_TRADESKILL_RECAST` on ITSELF (ref l.40-45's `TradeSkillFrame_OnLoad` doesn't register
`TRADE_SKILL_SHOW` — some other FrameXML driver dispatches it directly to `TradeSkillFrame_Show`
in the reference addon load model; this engine has no such driver, so the window listens for its
own SHOW like every other benilla panel does, TrainerFrame.xml's own TRAINER_SHOW precedent).
Modeled on the two known-good dressed windows for the framing (MerchantFrame.xml's own header
comment, TrainerFrame.xml's own precedent for this exact art family):
- MerchantFrame.xml/TrainerFrame.xml — the 384×512 four-quadrant window art (no <Backdrop>:
the art IS the frame), the "player" portrait binding (`SetPortraitTexture(region, "player")`
— CharacterFrame.xml/TalentFrame.xml's own precedent for the player-token, not the "npc"
token those NPC-session windows use), the UIPanel slot system (carries NO hardcoded
<Anchors> — ShowUIPanel/SetLeftFrame SetPoints it), OnShow/OnHide PlaySound pair.
- TrainerFrame.xml — the FLAT layout constraint (below), the faux-scroll list kit
(FauxScrollFrameTemplate, ScrollTemplates.xml), the locally-reconstructed
UIPanelButtonTemplate/close-button/item-button idioms (no such templates ship in this house
dialect — TalentFrame.xml's own ENGINE API note), the persistent selection-highlight Frame.
FLAT layout — a CHOICE inherited from TrainerFrame.xml, which stated it as a house constraint
it never was. A runtime-shown child <Frame> DOES render its own <Layers> FontStrings in this
engine (probed; decision 1517). The detail-pane FontStrings + the title live on
BenillaTradeSkillFrame's OWN <Layers> (OVERLAY, above the window's own BACKGROUND/BORDER/
ARTWORK art groups) because that is how this window was built, not because nesting fails. The ref's
`TradeSkillDetailScrollFrame`/`TradeSkillDetailScrollChildFrame` therefore don't exist here —
the detail is a FIXED (non-scrolling) pane, computed once from the flattened ScrollFrame+
ScrollChild offsets (TOPLEFT 20,-234 + each child's own ref-quoted offset). Per-region regions
that DO need Show()/Hide() live on Button/Frame children (Buttons render their own Layers) —
the product icon and the 8 reagent slots.
v1 DEVIATIONS (pinned to decision 0437's in-flight wow-re §5 dispatch where noted):
1. GROUPED LIST, HEADERS LANDED (wow-re `tradeskill` TU-B, VERIFIED — this deviation's own
earlier revision named it an INTERIM pinned to the dispatch; folded back this session).
`GetTradeSkillInfo`'s `skillType` is `"header"` for a group row (the created item's
`(ItemClass, ItemSubClass)`, named from `ItemSubClass.dbc`, engine-sorted) — the ref's
header branch is restored below (ref l.118-127: `SetText`, the +/- fold icon +
hover-glow swap on `isExpanded`, `TradeSkillTypeColor["header"]` now reachable) and so is
`SetSelection`'s header branch (ref l.184-192: a header click folds its group and never
selects). `Expand/CollapseTradeSkillSubClass` are real now (engine-side); the row
template gained the +/- fold icon + `$parentHighlight` column this window never carried
in v1 (TrainerFrame.xml's own `BenillaTrainerServiceTemplate` geometry —
`ClassTrainerSkillButtonTemplate`, `TradeSkillSkillButtonTemplate`'s base, isn't in this
session's ref extraction either, TrainerFrame.xml's own reagent-slot precedent for
reconstructing an un-extracted inherited template).
THE COLLAPSE-ALL TAB + FILTER DROPDOWNS NOW SHIP (this deviation's own earlier revision
named them STILL OMITTED, a narrower remaining scope; closed this session).
`TradeSkillExpandButtonFrame`/`BenillaTradeSkillCollapseAllButton` (ref l.229-294,
`Expand/CollapseTradeSkillSubClass(0)`'s own ALL-groups semantics, already engine-side) is
a standalone Button — NOT this file's row template (`BenillaTradeSkillSkillButtonTemplate`'s
fold icon is baked in for the 293-wide list row and its `$parentName` column would overflow
a 40×22 tab): TrainerFrame.xml's own `BenillaTrainerCollapseAllButton` is the precedent
this mirrors byte-for-byte instead (its own bespoke NormalTexture/HighlightTexture/
ButtonText), reconstructing the same un-extracted `ClassTrainerSkillButtonTemplate` base
the row template ALSO stands in for, above. `BenillaTradeSkillSubClassDropDown`/
`BenillaTradeSkillInvSlotDropDown` (ref l.295-328) are the `UIDropDownMenuTemplate` kit
(`UIDropDownMenu.xml`) — TrainerFrame.xml's own `BenillaTrainerFilterDropDown` consumer is
the calling-convention precedent (every dropdown call below passes the frame explicitly by
name, never the bare `this` fallback `UIDropDownMenu_SetWidth`/`_SetText`/`_Initialize`
allow — the two `*DropDownButton_OnClick` handlers are the one exception, REQUIRED to read
the clicked row off the real `this` global the way `info.func` is invoked, ref l.406-414).
The engine surface these ride — `GetTradeSkillSubClasses`, `Get/SetTradeSkillSubClassFilter`,
`GetTradeSkillInvSlots`, `Get/SetTradeSkillInvSlotFilter` — is real and tested
(`tradeskill.rs`'s own module doc + test suite): each `Set*`/`Expand`/`CollapseTradeSkillSubClass`
raises the engine's `trade_skill_touched` flag, answered with a `TRADE_SKILL_UPDATE` event
the same frame (`ui_tradeskill.rs`'s `drain_trade_skill`) — so neither the dropdown click
handlers nor the CollapseAll click below call `BenillaTradeSkillFrame_Update()` directly,
matching the ref's own restraint (ref l.303-312/406-414 never do either).
2. THE SPELL-FOCUS TOOL NEVER REDDENS. `BuildColoredListString` (below) is
byte-faithful — it reds an unmet tool — but `GetTradeSkillTools`'s `hasTool` has no
client-side proximity check yet (0437 INTERIM, pinned to the §5 dispatch), so v1 never
actually reds a tool in practice. The util is ready for when the check lands.
3. NO DRESSING-ROOM / CHAT-LINK INSERT. The ref's Ctrl+click `DressUpItemLink` / Shift+click
`ChatFrameEditBox:Insert` on the product icon and reagent buttons are dropped — the
cursor-item arc hasn't landed (MerchantFrame.xml's own identical-shaped "a no-op here
until the cursor-item arc" precedent for its own left-click).
4. NO `SKILL_LINES_CHANGED` self-listen on the rank StatusBar (ref l.217-224): the skill-up
feedback/diff-watcher is 0437 phase 4, not yet built — the bar's value/color/text refresh
only rides this window's own SHOW/UPDATE/selection paint.
5. NO `PLAYER_ENTERING_WORLD` hide, matching TrainerFrame.xml's own choice for this session
shape (the task brief calls this optional) — opening this window is your own client-side
cast, not an NPC session, so a world-enter close isn't the same lifecycle concern.
6. The ref's own dead code is dropped, not transcribed: `TradeSkillFrame_SetSelection`'s
`reagentToAnchorTo`/`mod(numReagents,2)` computation (ref l.264-268) is computed then never
read anywhere in the reference — a real vestige, not a benilla omission (TalentFrame.xml's
own precedent for dropping a verified-inert ref computation). The list row width toggle
(ref l.106-110, `if TradeSkillListScrollFrame:IsVisible() then 293 else 323`) is similarly
dropped: the ScrollFrame itself is always visible in both references and ours (only its
*scrollbar* toggles on overflow), so the ref's own condition is always true — rows are
fixed at 293, TrainerFrame.xml's own row width. `SetSelection`'s header branch
(restored, deviation 1) drops the ref's own `TradeSkillHighlightFrame:Show()` immediately
followed by `:Hide()` (ref l.183/185) — not dead in the ref's own sense, but ARCHITECTURALLY
redundant here: the row click handler that calls `SetSelection` always calls
`BenillaTradeSkillFrame_Update()` right after (ref's own `TradeSkillSkillButton_OnClick`
shape), which unconditionally hides the highlight and only re-shows it for a matching
recipe selection — a header index never is one.
7. ENGINE API gaps (TalentFrame.xml's own "no engine equivalent" posture):
- `EditBox:SetNumber` has no native method (grepped `crates/benilla-ui/src`) — patched
onto the ONE input-box instance in `BenillaTradeSkillFrame_OnLoad`
(`function box:SetNumber(n) self:SetText(tostring(floor(n or 0))) end`), so every call
site below still reads `TradeSkillInputBox:SetNumber(...)` exactly like the reference.
- (CLOSED — the row is the reference's shape now.) An earlier revision of this bullet
recorded `Button:SetTextColor`/`:LockHighlight`/`:UnlockHighlight` as having no engine
method, and routed row colouring through a child `$parentName` FontString instead of
the button's own label. The engine has shipped all three for a long time; the note went
stale and the workaround outlived it, which cost this window the white hover/selected
label the real client has (decision 1605). The row now carries a `<ButtonText>` under
the base's `<NormalFont>/<HighlightFont>/<DisabledFont>` trio, Update calls
`button:SetTextColor` / `LockHighlight` / `UnlockHighlight` exactly where the ref does
(l.113/127/144/146), and the white text is the HighlightFont instance, not a repaint.
**The selection is still TWO things.** The orange bar is
`BenillaTradeSkillHighlightFrame` — and **the Frame is what is shown, hidden and
positioned; `BenillaTradeSkillHighlight` is only its texture, and only ever gets
`SetVertexColor`** (ref l.99/142-143 vs l.200). Update addressed the texture for all
four until decision 1598, which left the frame `hidden="true"` for the window's whole
life and meant NO selection ever painted. The white label is the row's own highlight
lock, and the two are independent: 1598 restored the bar and the row stayed unlit.
- `SecondsToTime`/`BuildColoredListString` (ref `UIParent.lua`) have no engine equivalent
(they're Lua-side FrameXML utilities, not C API) — transcribed as GUARDED GLOBALS
(`if not X then function X(...) ... end end`), BuffFrame.xml's own
`SecondsToTimeAbbrev`/`DAY_ONELETTER_ABBR` precedent for exactly this "generic
UIParent.lua utility other windows may also need" shape (`BuildColoredListString` is
shared by the reference's own Craft UI too — decision 0437 phase 3's `CraftFrame.xml`
reuses this same guarded definition once loaded after this file). `SecondsToTime`'s own
`GetText("*_ABBR", nil, n)` locale-pluralization dispatch collapses to English
singular/plural directly — `UiPanels.xml`'s `StaticPopupTimeUnit` is the identical-shaped
precedent for this SAME `GetText` call elsewhere in the reference.
- Lua-5.0 `arg`/`arg.n` varargs are written the 5.1-native way
(WorldMapFrame.xml/TalentFrame.xml's own identical note) — `BuildColoredListString`'s
varargs use `select("#", ...)`/`select(i, ...)`, their own precedent for the
substitution; the new `TradeSkillFilterFrame_LoadSubClasses`/`_LoadInvSlots` (ref l.331/
377) use the same substitution.
- `TEXT(x)` (the ref's localization-tooling identity macro) has no engine equivalent and
needs none — it's a build-time no-op wrapper in the real client, so every `TEXT(FOO)`
call below (and `TRADE_SKILL_TITLE` above, already transcribed this way before this
session) is transcribed as the bare `FOO` local (CharacterFrame.xml's own identical
"needs none" note for the same macro).
8. GlobalStrings the ref reads (`TRADE_SKILL_TITLE`, `REQUIRES_LABEL`, `SPELL_REAGENTS`,
`COOLDOWN_REMAINING`, `CREATE`, `CREATE_ALL`, `EXIT`, and now `ALL`/`ALL_SUBCLASSES`/
`ALL_INVENTORY_SLOTS` for the CollapseAll tab + filter dropdowns) are declared LOCALLY
below as the byte-exact 1.12 enUS values (extracted this session with the `mpqx` bin,
decision 0246's extraction discipline, then grepped — line-cited per constant), the
TrainerFrame.xml idiom (each window keeps its own local copies rather than a shared file).
`RED_FONT_COLOR_CODE`/`FONT_COLOR_CODE_CLOSE`/`GRAY_FONT_COLOR`/`HIGHLIGHT_FONT_COLOR` are
Fonts.xml globals (CharacterFrame.xml's own precedent for reading them unqualified).
Registers at the left UIPanel slot (UiPanels.xml, `pushable = 3` — the ref Lua's own line 13),
ESC-closable through the standard UIPanel chain. `CloseTradeSkill()` fires on hide for ANY
reason (ESC, close X, Exit, `TRADE_SKILL_CLOSE`, or another left-slot window displacing it) —
MerchantFrame.xml's own `CloseMerchant`-on-hide precedent, adapted (no server round trip per
0437, purely client-side session teardown).
-->
<Script><![CDATA[
-- ── Constants (ref Blizzard_TradeSkillUI.lua:2-11) ──────────────────────────────────────────
local TRADE_SKILLS_DISPLAYED = 8 -- TRADE_SKILLS_DISPLAYED (ref l.2)
local MAX_TRADE_SKILL_REAGENTS = 8 -- MAX_TRADE_SKILL_REAGENTS (ref l.3)
local TRADE_SKILL_HEIGHT = 16 -- TRADE_SKILL_HEIGHT (ref l.4)
-- The recipe-row color table (ref l.6-11), transcribed verbatim including "header" — which is
-- UNREACHABLE in v1 (this file's header comment, deviation 1).
TradeSkillTypeColor = {}
TradeSkillTypeColor["optimal"] = { r = 1.00, g = 0.50, b = 0.25 }
TradeSkillTypeColor["medium"] = { r = 1.00, g = 1.00, b = 0.00 }
TradeSkillTypeColor["easy"] = { r = 0.25, g = 0.75, b = 0.25 }
TradeSkillTypeColor["trivial"] = { r = 0.50, g = 0.50, b = 0.50 }
TradeSkillTypeColor["header"] = { r = 1.00, g = 0.82, b = 0 }
-- ── GlobalStrings the ref reads (byte-exact 1.12 enUS, this file's header comment cites the
-- extraction command) — chunk-local, the TrainerFrame.xml idiom. ─────────────────────────────
local TRADE_SKILL_TITLE = "%s" -- TRADE_SKILL_TITLE (GlobalStrings.lua:4278)
local REQUIRES_LABEL = "Requires:" -- REQUIRES_LABEL (3339)
local SPELL_REAGENTS = "Reagents: " -- SPELL_REAGENTS (4095) -- trailing space, verbatim
local COOLDOWN_REMAINING = "Cooldown remaining:" -- COOLDOWN_REMAINING (864)
local CREATE_LABEL = "Create" -- CREATE (871)
local CREATE_ALL_LABEL = "Create All" -- CREATE_ALL (874)
local EXIT_LABEL = "Exit" -- EXIT (1933)
local ALL_SUBCLASSES = "All Subclasses" -- ALL_SUBCLASSES (48)
local ALL_INVENTORY_SLOTS = "All Slots" -- ALL_INVENTORY_SLOTS (47)
-- SecondsToTime's plural pairs (ref UIParent.lua:1004-1032 calls GetText("*_ABBR", nil, n)) —
-- guarded GLOBALS, BuffFrame.xml's own DAY_ONELETTER_ABBR precedent (a generic UIParent.lua
-- utility's own supporting constants, not a window-local label).
DAYS_ABBR = DAYS_ABBR or "Day" -- DAYS_ABBR (895)
DAYS_ABBR_P1 = DAYS_ABBR_P1 or "Days" -- DAYS_ABBR_P1 (896)
HOURS_ABBR = HOURS_ABBR or "Hr" -- HOURS_ABBR (2296)
HOURS_ABBR_P1 = HOURS_ABBR_P1 or "Hrs" -- HOURS_ABBR_P1 (2297)
MINUTES_ABBR = MINUTES_ABBR or "Min" -- MINUTES_ABBR (2693)
MINUTES_ABBR_P1 = MINUTES_ABBR_P1 or "Mins" -- MINUTES_ABBR_P1 (2694)
SECONDS_ABBR = SECONDS_ABBR or "Sec" -- SECONDS_ABBR (3405)
SECONDS_ABBR_P1 = SECONDS_ABBR_P1 or "Secs" -- SECONDS_ABBR_P1 (3404)
local function pluralAbbr(n, singular, plural)
if n == 1 then return singular end
return plural
end
-- SecondsToTime (ref UIParent.lua:1004-1032) — guarded global (this file's header comment,
-- deviation 7): GetText's locale pluralization dispatch collapses to English singular/plural
-- directly (UiPanels.xml's StaticPopupTimeUnit is the identical-shaped precedent for the same
-- GetText call elsewhere in the reference).
if not SecondsToTime then
function SecondsToTime(seconds, noSeconds)
local time, count = "", 0
seconds = floor(seconds)
if seconds > 86400 then
local t = floor(seconds / 86400)
time = time .. t .. " " .. pluralAbbr(t, DAYS_ABBR, DAYS_ABBR_P1) .. " "
seconds = mod(seconds, 86400)
count = count + 1
end
if seconds > 3600 then
local t = floor(seconds / 3600)
time = time .. t .. " " .. pluralAbbr(t, HOURS_ABBR, HOURS_ABBR_P1) .. " "
seconds = mod(seconds, 3600)
count = count + 1
end
if count < 2 and seconds >= 60 then
local t = floor(seconds / 60)
time = time .. t .. " " .. pluralAbbr(t, MINUTES_ABBR, MINUTES_ABBR_P1) .. " "
seconds = mod(seconds, 60)
count = count + 1
end
if count < 2 and seconds > 0 and not noSeconds then
time = time .. seconds .. " " .. pluralAbbr(seconds, SECONDS_ABBR, SECONDS_ABBR_P1) .. " "
end
return time
end
end
-- BuildColoredListString (ref UIParent.lua:1059-1082) — guarded global (this file's header
-- comment, deviation 7): odd args are text, even args say whether it's "met" (white) or not
-- (red |cffff2020 ... |r). GetTradeSkillTools' own alternating (name, hasTool) return is
-- exactly this shape. Lua-5.0 arg/arg.n replaced with select(), WorldMapFrame.xml's precedent.
if not BuildColoredListString then
function BuildColoredListString(...)
local n = select("#", ...)
if n == 0 then return nil end
local a1, a2 = select(1, ...)
local s
if a2 then
s = a1
else
s = RED_FONT_COLOR_CODE .. a1 .. FONT_COLOR_CODE_CLOSE
end
for i = 3, n, 2 do
local text, met = select(i, ...)
if met then
s = s .. ", " .. text
else
s = s .. ", " .. RED_FONT_COLOR_CODE .. text .. FONT_COLOR_CODE_CLOSE
end
end
return s
end
end
-- ── Detail pane ─────────────────────────────────────────────────────────────────────────────
-- No trade skills known at all (ref l.79-89): blank the whole detail pane. Regions on this
-- window's own Layers "hide" via SetText("") — TrainerFrame.xml's own convention, which
-- predates region Show/Hide (region.rs has the real pair now); equivalent either way.
function BenillaTradeSkillFrame_HideDetails()
getglobal("BenillaTradeSkillFrameTitleText"):SetText(format(TRADE_SKILL_TITLE, GetTradeSkillLine()))
getglobal("BenillaTradeSkillSkillName"):SetText("")
getglobal("BenillaTradeSkillSkillIcon"):Hide()
getglobal("BenillaTradeSkillSkillIconCount"):SetText("")
getglobal("BenillaTradeSkillRequirementLabel"):SetText("")
getglobal("BenillaTradeSkillRequirementText"):SetText("")
getglobal("BenillaTradeSkillSkillCooldown"):SetText("")
for i = 1, MAX_TRADE_SKILL_REAGENTS do
getglobal("BenillaTradeSkillReagent" .. i):Hide()
end
getglobal("BenillaTradeSkillCreateButton"):Disable()
getglobal("BenillaTradeSkillCreateAllButton"):Disable()
end
-- Fill the detail pane + the rank statusbar for the selected recipe `id` (ref
-- TradeSkillFrame_SetSelection, l.181-294, header branch restored — deviation 1, wow-re
-- `tradeskill` TU-B). Also the SOURCE of the selection highlight's color (set once here, ref
-- l.198-201). GetTradeSkillInfo(id) is called ONCE up top, ref's own shape (l.182) — a header
-- click folds its group and returns before ever selecting (ref l.184-192; the ref's own
-- Show()/Hide() pair here is dropped, deviation 6's own added note).
function BenillaTradeSkillFrame_SetSelection(id)
if not id or id == 0 then
BenillaTradeSkillFrame.selectedSkill = 0
BenillaTradeSkillFrame_HideDetails()
return
end
local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(id)
if skillType == "header" then
if isExpanded then
CollapseTradeSkillSubClass(id)
else
ExpandTradeSkillSubClass(id)
end
return
end
BenillaTradeSkillFrame.selectedSkill = id
SelectTradeSkill(id)
if GetTradeSkillSelectionIndex() > GetNumTradeSkills() then
return
end
local color = TradeSkillTypeColor[skillType]
if color then
getglobal("BenillaTradeSkillHighlight"):SetVertexColor(color.r, color.g, color.b)
end
-- General info (ref l.203-212).
local skillLineName, skillLineRank, skillLineMaxRank = GetTradeSkillLine()
getglobal("BenillaTradeSkillFrameTitleText"):SetText(format(TRADE_SKILL_TITLE, skillLineName))
local rankFrame = getglobal("BenillaTradeSkillRankFrame")
getglobal("BenillaTradeSkillRankFrameSkillName"):SetText(skillLineName)
rankFrame:SetStatusBarColor(0.0, 0.0, 1.0, 0.5)
getglobal("BenillaTradeSkillRankFrameBackground"):SetVertexColor(0.0, 0.0, 0.75, 0.5)
rankFrame:SetMinMaxValues(0, skillLineMaxRank)
rankFrame:SetValue(skillLineRank)
getglobal("BenillaTradeSkillRankFrameSkillRank"):SetText(skillLineRank .. "/" .. skillLineMaxRank)
getglobal("BenillaTradeSkillSkillName"):SetText(skillName)
local cooldown = GetTradeSkillCooldown(id)
if cooldown then
getglobal("BenillaTradeSkillSkillCooldown"):SetText(COOLDOWN_REMAINING .. " " .. SecondsToTime(cooldown))
else
getglobal("BenillaTradeSkillSkillCooldown"):SetText("")
end
local icon = getglobal("BenillaTradeSkillSkillIcon")
icon:Show()
icon:SetNormalTexture(GetTradeSkillIcon(id) or BENILLA_FALLBACK_ICON)
local iconCount = getglobal("BenillaTradeSkillSkillIconCount")
local minMade, maxMade = GetTradeSkillNumMade(id)
if maxMade > 1 then
if minMade == maxMade then
iconCount:SetText(minMade)
else
iconCount:SetText(minMade .. "-" .. maxMade)
end
if iconCount:GetWidth() > 39 then
iconCount:SetText("~" .. floor((minMade + maxMade) / 2))
end
else
iconCount:SetText("")
end
-- Reagents (ref l.235-272): up to MAX_TRADE_SKILL_REAGENTS, gray-out + creatable gate.
local creatable = true
local numReagents = GetTradeSkillNumReagents(id)
for i = 1, MAX_TRADE_SKILL_REAGENTS do
local reagent = getglobal("BenillaTradeSkillReagent" .. i)
if i <= numReagents then
local reagentName, reagentTexture, need, have = GetTradeSkillReagentInfo(id, i)
if not reagentName or not reagentTexture then
-- Item template not landed yet for this reagent — hide it, like the ref does
-- for any nil reagent (engine API note, decision 0437).
reagent:Hide()
else
reagent:Show()
reagent.icon:SetTexture(reagentTexture)
reagent.name:SetText(reagentName)
have = have or 0
if have < need then
reagent.icon:SetVertexColor(0.5, 0.5, 0.5)
reagent.name:SetTextColor(GRAY_FONT_COLOR.r, GRAY_FONT_COLOR.g, GRAY_FONT_COLOR.b)
creatable = false
else
reagent.icon:SetVertexColor(1.0, 1.0, 1.0)
reagent.name:SetTextColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
end
local haveText = have
if have >= 100 then haveText = "*" end
reagent.count:SetText(haveText .. " /" .. need)
end
else
reagent:Hide()
end
end
-- Requires: line (ref l.274-281). "Hidden" means both FontStrings blank
-- (TrainerFrame.xml's own convention — see BenillaTradeSkillFrame_HideDetails).
local spellFocus = BuildColoredListString(GetTradeSkillTools(id))
if spellFocus then
getglobal("BenillaTradeSkillRequirementLabel"):SetText(REQUIRES_LABEL)
getglobal("BenillaTradeSkillRequirementText"):SetText(spellFocus)
else
getglobal("BenillaTradeSkillRequirementLabel"):SetText("")
getglobal("BenillaTradeSkillRequirementText"):SetText("")
end
if creatable then
getglobal("BenillaTradeSkillCreateButton"):Enable()
getglobal("BenillaTradeSkillCreateAllButton"):Enable()
else
getglobal("BenillaTradeSkillCreateButton"):Disable()
getglobal("BenillaTradeSkillCreateAllButton"):Disable()
end
-- Reset the number of items to be created (ref l.293).
getglobal("BenillaTradeSkillInputBox"):SetNumber(GetTradeskillRepeatCount())
end
-- ── The list page (ref TradeSkillFrame_Update, l.75-178 — the header/expand-icon tail,
-- l.118-127, is restored below at the row loop; the CollapseAll Enable/Disable (l.86/94) and
-- the tail texture logic (l.155-178) are restored too, deviation 1 closed this session) ──────
function BenillaTradeSkillFrame_Update()
local numTradeSkills = GetNumTradeSkills()
if numTradeSkills == 0 then
BenillaTradeSkillFrame_HideDetails()
getglobal("BenillaTradeSkillCollapseAllButton"):Disable() -- ref l.86
else
getglobal("BenillaTradeSkillCollapseAllButton"):Enable() -- ref l.94
end
local scrollFrame = getglobal("BenillaTradeSkillListScrollFrame")
FauxScrollFrame_Update(scrollFrame, numTradeSkills, TRADE_SKILLS_DISPLAYED, TRADE_SKILL_HEIGHT)
local skillOffset = FauxScrollFrame_GetOffset(scrollFrame)
-- The FRAME is what shows/hides/positions (ref l.99/142-143); its texture is only ever
-- vertex-coloured (SetSelection, ref l.200). Addressing the texture here instead left the
-- glow's frame `hidden="true"` forever, so no selection ever painted (decision 1598) —
-- CraftFrame.xml and TrainerFrame.xml both already split the two correctly.
local highlight = getglobal("BenillaTradeSkillHighlightFrame")
highlight:Hide()
local selection = GetTradeSkillSelectionIndex()
for i = 1, TRADE_SKILLS_DISPLAYED do
local skillIndex = i + skillOffset
local button = getglobal("BenillaTradeSkillSkill" .. i)
if skillIndex <= numTradeSkills then
local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(skillIndex)
if not skillName then
button.skillIndex = nil
button:Hide()
else
button.skillIndex = skillIndex
local color = TradeSkillTypeColor[skillType]
if color then
button:SetTextColor(color.r, color.g, color.b)
end
-- Header row (ref l.118-127, wow-re `tradeskill` TU-B): the group name
-- verbatim, no "[n]" suffix, the +/- fold icon + its hover glow swapped on
-- isExpanded.
if skillType == "header" then
button:SetText(skillName)
if isExpanded then
button:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
else
button:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
end
getglobal(button:GetName() .. "Highlight"):SetTexture("Interface\\Buttons\\UI-PlusButton-Hilight")
else
button:SetNormalTexture("")
getglobal(button:GetName() .. "Highlight"):SetTexture("")
if (numAvailable or 0) == 0 then
button:SetText(" " .. skillName)
else
button:SetText(" " .. skillName .. " [" .. numAvailable .. "]")
end
end
button:Show()
-- The selection glow only ever lands on a recipe row — SetSelection never
-- selects a header (deviation 1), so `selection` can't be one's index.
--
-- TWO separate things paint a selected row, and the ref does both here
-- (l.142-146): the orange bar is BenillaTradeSkillHighlightFrame, and the
-- WHITE LABEL is the row's own LockHighlight() — the base's
-- `<HighlightFont inherits="GameFontHighlight">` over the difficulty colour
-- SetTextColor just wrote. The lock reaches nothing else on a recipe row: the
-- branch above blanked that row's HighlightTexture. Unlocking every other row
-- covers both of the ref's own UnlockHighlight calls at once — l.146's for a
-- passed-over recipe and l.127's for a header, which can never be `selection`.
if selection == skillIndex then
highlight:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0)
highlight:Show()
button:LockHighlight()
else
button:UnlockHighlight()
end
end
else
button.skillIndex = nil
button:Hide()
end
end
-- The max makeable count for Create All (ref l.166-169's own full-list scan collapses to a
-- direct lookup of the selected index — this file's header comment, deviation 6).
if selection >= 1 and selection <= numTradeSkills then
local _, _, numAvailable = GetTradeSkillInfo(selection)
BenillaTradeSkillFrame.numAvailable = numAvailable or 0
else
BenillaTradeSkillFrame.numAvailable = 0
end
-- Count headers + how many stay folded (ref l.156-170), to drive the CollapseAll tab's own
-- texture below — restored, deviation 1 closed this session.
local numHeaders, notExpanded = 0, 0
for i = 1, numTradeSkills do
local skillName, skillType, numAvailable, isExpanded = GetTradeSkillInfo(i)
if skillName and skillType == "header" then
numHeaders = numHeaders + 1
if not isExpanded then
notExpanded = notExpanded + 1
end
end
end
-- If all headers are not expanded then show the collapse texture, otherwise the expand one
-- (ref l.171-178).
local collapseAll = getglobal("BenillaTradeSkillCollapseAllButton")
if notExpanded ~= numHeaders then
collapseAll.collapsed = nil
collapseAll:SetNormalTexture("Interface\\Buttons\\UI-MinusButton-Up")
else
collapseAll.collapsed = true
collapseAll:SetNormalTexture("Interface\\Buttons\\UI-PlusButton-Up")
end
end
-- A row click selects it OR folds its group (ref TradeSkillSkillButton_OnClick +
-- SetSelection's now-restored header branch, deviation 1) — SetSelection itself forks on
-- the row's type, this handler doesn't need to. Left-button only.
function BenillaTradeSkillSkillButton_OnClick(button, mouseButton)
if mouseButton ~= "LeftButton" then return end
local index = button.skillIndex
if not index then return end
BenillaTradeSkillFrame_SetSelection(index)
BenillaTradeSkillFrame_Update()
end
-- The window's wheel handler (bubbled up the parent chain from a row) → one row via the faux
-- kit (TrainerFrame.xml's own note: the engine bubbles a spin up the parent chain, so the
-- handler lives on the window, not a child).
function BenillaTradeSkillFrame_OnMouseWheel(delta)
BenillaFauxScrollFrame_OnMouseWheel(getglobal("BenillaTradeSkillListScrollFrame"), delta)
end
-- ── The CollapseAll tab (ref TradeSkillCollapseAllButton_OnClick, l.303-312) — toggles between
-- Expand/CollapseTradeSkillSubClass(0), the ALL-groups pseudo-index (deviation 1, engine module
-- doc), using the button's own `.collapsed` flag (re-derived every Update() from the real
-- header states above, so it stays honest regardless of how a fold happened). The ref's raw
-- `TradeSkillListScrollFrameScrollBar:SetValue(0)` becomes this file's own
-- `FauxScrollFrame_SetOffset(...,0)` wrapper (already this file's own Show/OnEvent
-- convention). No direct `BenillaTradeSkillFrame_Update()` call — the ref doesn't call one
-- either; `CollapseTradeSkillSubClass`/`ExpandTradeSkillSubClass` raise the engine's touched
-- flag, answered with `TRADE_SKILL_UPDATE` the same frame (header comment, deviation 1). ──────
function BenillaTradeSkillCollapseAllButton_OnClick(self)
if self.collapsed then
self.collapsed = nil
ExpandTradeSkillSubClass(0)
else
self.collapsed = true
FauxScrollFrame_SetOffset(getglobal("BenillaTradeSkillListScrollFrame"), 0)
CollapseTradeSkillSubClass(0)
end
end
-- ── The SubClass filter dropdown (ref TradeSkillSubClassDropDown_*, l.314-358). Every call
-- passes the dropdown frame explicitly by NAME (TrainerFrame.xml's own
-- BenillaTrainerFilterDropDown calling-convention precedent) rather than riding the bare `this`
-- fallback UIDropDownMenu_Initialize/_SetWidth/_SetSelectedID allow. ─────────────────────────
function BenillaTradeSkillSubClassDropDown_OnLoad()
UIDropDownMenu_Initialize(BenillaTradeSkillSubClassDropDown, BenillaTradeSkillSubClassDropDown_Initialize)
UIDropDownMenu_SetWidth(120, BenillaTradeSkillSubClassDropDown)
UIDropDownMenu_SetSelectedID(BenillaTradeSkillSubClassDropDown, 1)
end
function BenillaTradeSkillSubClassDropDown_OnShow()
UIDropDownMenu_Initialize(BenillaTradeSkillSubClassDropDown, BenillaTradeSkillSubClassDropDown_Initialize)
if GetTradeSkillSubClassFilter(0) then
UIDropDownMenu_SetSelectedID(BenillaTradeSkillSubClassDropDown, 1)
end
end
function BenillaTradeSkillSubClassDropDown_Initialize()
BenillaTradeSkillFilterFrame_LoadSubClasses(GetTradeSkillSubClasses())
end
-- ref TradeSkillFilterFrame_LoadSubClasses(...) (l.331-358): Lua-5.0 arg/arg.n → select() (this
-- file's header comment, deviation 7's BuildColoredListString precedent).
function BenillaTradeSkillFilterFrame_LoadSubClasses(...)
local n = select("#", ...)
local allChecked = GetTradeSkillSubClassFilter(0)
if n > 1 then
local info = {}
info.text = ALL_SUBCLASSES
info.func = BenillaTradeSkillSubClassDropDownButton_OnClick
info.checked = allChecked
UIDropDownMenu_AddButton(info)
end
local checked
for i = 1, n do
local name = select(i, ...)
if allChecked and n > 1 then
checked = nil
UIDropDownMenu_SetText(ALL_SUBCLASSES, BenillaTradeSkillSubClassDropDown)
else
checked = GetTradeSkillSubClassFilter(i)
if checked then
UIDropDownMenu_SetText(name, BenillaTradeSkillSubClassDropDown)
end
end
local info = {}
info.text = name
info.func = BenillaTradeSkillSubClassDropDownButton_OnClick
info.checked = checked
UIDropDownMenu_AddButton(info)
end
end
-- ── The InvSlot filter dropdown (ref TradeSkillInvSlotDropDown_*, l.360-404) — the SubClass
-- dropdown's own exact shape. ───────────────────────────────────────────────────────────────
function BenillaTradeSkillInvSlotDropDown_OnLoad()
UIDropDownMenu_Initialize(BenillaTradeSkillInvSlotDropDown, BenillaTradeSkillInvSlotDropDown_Initialize)
UIDropDownMenu_SetWidth(120, BenillaTradeSkillInvSlotDropDown)
UIDropDownMenu_SetSelectedID(BenillaTradeSkillInvSlotDropDown, 1)
end
function BenillaTradeSkillInvSlotDropDown_OnShow()
UIDropDownMenu_Initialize(BenillaTradeSkillInvSlotDropDown, BenillaTradeSkillInvSlotDropDown_Initialize)
if GetTradeSkillInvSlotFilter(0) then
UIDropDownMenu_SetSelectedID(BenillaTradeSkillInvSlotDropDown, 1)
end
end
function BenillaTradeSkillInvSlotDropDown_Initialize()
BenillaTradeSkillFilterFrame_LoadInvSlots(GetTradeSkillInvSlots())
end
-- ref TradeSkillFilterFrame_LoadInvSlots(...) (l.377-404) — the SubClass loader's own shape.
function BenillaTradeSkillFilterFrame_LoadInvSlots(...)
local n = select("#", ...)
local allChecked = GetTradeSkillInvSlotFilter(0)
if n > 1 then
local info = {}
info.text = ALL_INVENTORY_SLOTS
info.func = BenillaTradeSkillInvSlotDropDownButton_OnClick
info.checked = allChecked
UIDropDownMenu_AddButton(info)
end
local checked
for i = 1, n do
local name = select(i, ...)
if allChecked and n > 1 then
checked = nil
UIDropDownMenu_SetText(ALL_INVENTORY_SLOTS, BenillaTradeSkillInvSlotDropDown)
else
checked = GetTradeSkillInvSlotFilter(i)
if checked then
UIDropDownMenu_SetText(name, BenillaTradeSkillInvSlotDropDown)
end
end
local info = {}
info.text = name
info.func = BenillaTradeSkillInvSlotDropDownButton_OnClick
info.checked = checked
UIDropDownMenu_AddButton(info)
end
end
-- The two menu-row handlers (ref l.406-414): each fires as a row's `info.func`, invoked by
-- UIDropDownMenuButton_OnClick as `func(this.arg1, this.arg2)` with the REAL global `this`
-- bound to the clicked row (UIDropDownMenu.xml's own calling convention, confirmed live in
-- this engine — header comment, deviation 1) — no `self` parameter is passed, so `this` is
-- REQUIRED here, not a style choice.
function BenillaTradeSkillSubClassDropDownButton_OnClick()
UIDropDownMenu_SetSelectedID(BenillaTradeSkillSubClassDropDown, this:GetID())
SetTradeSkillSubClassFilter(this:GetID() - 1, 1, 1)
end
function BenillaTradeSkillInvSlotDropDownButton_OnClick()
UIDropDownMenu_SetSelectedID(BenillaTradeSkillInvSlotDropDown, this:GetID())
SetTradeSkillInvSlotFilter(this:GetID() - 1, 1, 1)
end
-- ── The detail product icon (ref TradeSkillSkillIcon OnEnter/OnClick, l.563-590 — the
-- Ctrl/Shift dressing-room/chat-link branches dropped, deviation 3) ─────────────────────────
function BenillaTradeSkillSkillIcon_OnEnter(button)
local selected = BenillaTradeSkillFrame.selectedSkill or 0
if selected ~= 0 then
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
GameTooltip:SetTradeSkillItem(selected)
GameTooltip:Show()
end
end
-- ── The reagent slots (ref TradeSkillItemTemplate OnEnter, l.13-17 — the dressing-room/
-- chat-link OnClick branches dropped, deviation 3) ─────────────────────────────────────────
function BenillaTradeSkillReagent_OnLoad(button, index)
button.reagentIndex = index
local n = button:GetName()
button.icon = getglobal(n .. "Icon")
button.name = getglobal(n .. "Name")
button.count = getglobal(n .. "Count")
end
function BenillaTradeSkillReagent_OnEnter(button)
GameTooltip:SetOwner(button, "ANCHOR_TOPLEFT")
GameTooltip:SetTradeSkillItem(BenillaTradeSkillFrame.selectedSkill, button.reagentIndex)
GameTooltip:Show()
end
-- ── Create / Create All / the repeat-count input (ref l.668-837) ───────────────────────────
function BenillaTradeSkillCreateButton_OnClick()
local box = getglobal("BenillaTradeSkillInputBox")
DoTradeSkill(BenillaTradeSkillFrame.selectedSkill, box:GetNumber())
box:ClearFocus()
end
function BenillaTradeSkillCreateAllButton_OnClick()
local box = getglobal("BenillaTradeSkillInputBox")
box:SetNumber(BenillaTradeSkillFrame.numAvailable)
DoTradeSkill(BenillaTradeSkillFrame.selectedSkill, BenillaTradeSkillFrame.numAvailable)
box:ClearFocus()
end
function BenillaTradeSkillCancelButton_OnClick()
HideUIPanel(getglobal("BenillaTradeSkillFrame"))
end
function BenillaTradeSkillFrameIncrement_OnClick()
local box = getglobal("BenillaTradeSkillInputBox")
if box:GetNumber() < 100 then
box:SetNumber(box:GetNumber() + 1)
end
box:ClearFocus()
end
function BenillaTradeSkillFrameDecrement_OnClick()
local box = getglobal("BenillaTradeSkillInputBox")
if box:GetNumber() > 0 then
box:SetNumber(box:GetNumber() - 1)
end
box:ClearFocus()
end
-- ── The window lifecycle (MerchantFrame.xml/TrainerFrame.xml's own shape) ──────────────────
function BenillaTradeSkillFrame_OnLoad(frame)
frame.selectedSkill = 0
frame.numAvailable = 0
frame:RegisterEvent("TRADE_SKILL_SHOW")
frame:RegisterEvent("TRADE_SKILL_UPDATE")
frame:RegisterEvent("TRADE_SKILL_CLOSE")
frame:RegisterEvent("UPDATE_TRADESKILL_RECAST")
-- Registered per the ref (TradeSkillFrame_OnLoad l.43); this engine may not fire it yet —
-- forward-compatible, harmless (TalentFrame.xml's own identical posture for this event).
frame:RegisterEvent("UNIT_PORTRAIT_UPDATE")
local scrollFrame = getglobal("BenillaTradeSkillListScrollFrame")
scrollFrame.updateFunc = BenillaTradeSkillFrame_Update
-- The EditBox:SetNumber engine-gap shim (this file's header comment, deviation 7): patched
-- onto this ONE instance so every call site above reads exactly like the reference.
local box = getglobal("BenillaTradeSkillInputBox")
function box:SetNumber(n)
self:SetText(tostring(floor(n or 0)))
end
end
-- Fresh session paint (ref TradeSkillFrame_Show, l.15-34 — the dropdown Close/Show dance,
-- l.16-20, is restored below: deviation 1 closed this session, the dropdowns ship). Hide()
-- then Show() re-fires each dropdown's own OnShow handler, forcing a fresh re-init/re-sync of
-- its selected id every time the window opens — the ref's own intent, not just a paint no-op.
function BenillaTradeSkillFrame_Show(frame)
CloseDropDownMenus()
getglobal("BenillaTradeSkillSubClassDropDown"):Hide()
getglobal("BenillaTradeSkillSubClassDropDown"):Show()
getglobal("BenillaTradeSkillInvSlotDropDown"):Hide()
getglobal("BenillaTradeSkillInvSlotDropDown"):Show()
getglobal("BenillaTradeSkillCreateButton"):Disable()
getglobal("BenillaTradeSkillCreateAllButton"):Disable()
if GetTradeSkillSelectionIndex() == 0 then
BenillaTradeSkillFrame_SetSelection(GetFirstTradeSkill())
else
BenillaTradeSkillFrame_SetSelection(GetTradeSkillSelectionIndex())
end
FauxScrollFrame_SetOffset(getglobal("BenillaTradeSkillListScrollFrame"), 0)
SetPortraitTexture(getglobal("BenillaTradeSkillFramePortrait"), "player")
BenillaTradeSkillFrame_Update()
ShowUIPanel(frame)
end
function BenillaTradeSkillFrame_OnEvent(frame)
if event == "TRADE_SKILL_SHOW" then
BenillaTradeSkillFrame_Show(frame)
elseif event == "TRADE_SKILL_UPDATE" then
-- ref l.54-62: re-anchor the selection if it's fallen off a (possibly shorter) list.
local selection = GetTradeSkillSelectionIndex()
if selection > 1 and selection <= GetNumTradeSkills() then
BenillaTradeSkillFrame_SetSelection(selection)
elseif GetNumTradeSkills() > 0 then
BenillaTradeSkillFrame_SetSelection(GetFirstTradeSkill())
FauxScrollFrame_SetOffset(getglobal("BenillaTradeSkillListScrollFrame"), 0)
end
BenillaTradeSkillFrame_Update()
elseif event == "TRADE_SKILL_CLOSE" then
HideUIPanel(frame)
elseif event == "UPDATE_TRADESKILL_RECAST" then
getglobal("BenillaTradeSkillInputBox"):SetNumber(GetTradeskillRepeatCount())
elseif event == "UNIT_PORTRAIT_UPDATE" then
if arg1 == "player" then
SetPortraitTexture(getglobal("BenillaTradeSkillFramePortrait"), "player")
end
end
end
function BenillaTradeSkillFrameCloseButton_OnClick()
HideUIPanel(getglobal("BenillaTradeSkillFrame"))
end
]]></Script>
<!--
One list row (ref TradeSkillSkillButtonTemplate inherits ClassTrainerSkillButtonTemplate,
l.343-414 for the instances): a 293×16 row — the +/- fold slot at the left (meaningful for a
header row, blanked for a recipe row; wow-re `tradeskill` TU-B, deviation 1) + the name text.
`ClassTrainerSkillButtonTemplate` lives in `Interface\FrameXML\ClassTrainerFrameTemplates.xml`
(LOCATED — earlier revisions of this comment said it "isn't in this session's ref extraction"
and reconstructed it from TrainerFrame.xml's own `BenillaTrainerServiceTemplate`; the geometry
that guess produced matches, so it stays). The base is 293×16 with a 16×16 NormalTexture and a
16×16 `$parentHighlight` both at LEFT+3, ButtonText hung off `$parentHighlight`'s RIGHT, and a
BACKGROUND `$parentSubText` this window never fills.
**NO ROW HOVER TOOLTIP** — the base's `OnEnter`/`OnLeave` only recolour `$parentSubText` to
HIGHLIGHT_FONT_COLOR and back, and `TradeSkillSkillButtonTemplate` overrides `OnClick` ALONE.
Nothing in the reference chain calls GameTooltip for a list row: the product tooltip is the
detail pane's `TradeSkillSkillIcon`, the reagent tooltips are the reagent slots, and the list
is bare. An earlier revision shipped a `GameTooltip:SetTradeSkillItem` OnEnter here as if it
were the ref's; it never was, and CraftFrame.xml's own deviation 7 repeated the claim ("TradeSkill's
own row hover IS a real ref behavior") on the strength of it. Both are corrected: no OnEnter.
Since `$parentSubText` is empty for every row of this window, the base's own hover recolour is
invisible here too, so the row carries no hover script at all.
**The row still lights up on hover, and no script does it.** That is the base's
`<HighlightFont inherits="GameFontHighlight">` — the button's own highlighted font instance,
swapped in by the engine while the cursor is on the row and held by `LockHighlight()` on the
selected one. Reading 1598's "bare on hover" as "nothing happens on hover" is the trap this
paragraph exists to close: what the reference has no trace of is a *tooltip*, not the white
label (decision 1605).
-->
<Button name="BenillaTradeSkillSkillButtonTemplate" virtual="true" hidden="true">
<Size><AbsDimension x="293" y="16"/></Size>
<!-- The +/- fold slot + its hover glow (TrainerFrame.xml's own BenillaTrainerServiceTemplate
shape): Update sets the NormalTexture to +/- for a header row and blanks it for a recipe
row (ref l.122-132); the named HighlightTexture is addressed by `getglobal(name.."Highlight")`
the same way (ref l.126/133), rather than TrainerFrame.xml's own `self:GetHighlightTexture()`
cache — this file's established per-row `getglobal($parentX)` convention
(BenillaTradeSkillReagent_OnLoad's icon/name/count caching, below). -->
<NormalTexture file="Interface\Buttons\UI-MinusButton-Up">
<Size><AbsDimension x="16" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</NormalTexture>
<HighlightTexture name="$parentHighlight" file="Interface\Buttons\UI-PlusButton-Hilight" alphaMode="ADD">
<Size><AbsDimension x="16" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
</HighlightTexture>
<!-- THE ROW LABEL IS THE BUTTON'S OWN ButtonText, not a child FontString — that is what
carries the white-on-hover/white-on-selected look (decision 1605). The base declares the
font TRIO, and the per-state instance is the whole mechanism: `GameFontHighlight` is white,
so a row `SetTextColor`'d to its difficulty still turns white under the cursor, and
`LockHighlight()` on the selected row holds it white with the cursor elsewhere. It was a
`$parentName` FontString in ARTWORK until 1605, back when this file's deviation 7 recorded
`Button:SetTextColor`/`LockHighlight` as having no engine method — long since untrue.
Kept at this file's own LEFT+22 / 267×16 rather than the base's `$parentHighlight`-relative
`x=0,y=13` auto-size, so the switch moved no pixel. -->
<ButtonText name="$parentText">
<Size><AbsDimension x="267" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="22" y="0"/></Offset></Anchor></Anchors>
</ButtonText>
<NormalFont inherits="GameFontNormal" justifyH="LEFT"/>
<HighlightFont inherits="GameFontHighlight" justifyH="LEFT"/>
<DisabledFont inherits="GameFontDisable" justifyH="LEFT"/>
<Scripts>
<OnClick>BenillaTradeSkillSkillButton_OnClick(self, arg1)</OnClick>
</Scripts>
</Button>
<!--
One reagent slot. The ref's `TradeSkillItemTemplate` (l.11-35) inherits `QuestItemTemplate` and
overrides **only scripts** — so the slot's whole visual IS QuestItemTemplate: a 39×39 icon, the
`UI-QuestItemNameFrame` plate riding its right edge, the name centred ON the plate, and the
"have /need" count on the icon's own BOTTOMRIGHT. Kept in sync by hand with QuestFrame.xml's
`QuestItemTemplate` and CraftFrame.xml's own copy (per-window duplication, StackSplit.xml's
"not a cross-file inherits" note).
This shape REPLACES a hand-rolled 28×28-icon / name-above / count-below slot that was written
when the session that built this window could not find the base QuestItemTemplate (bug B250,
decision 1595): TradeSkillFrame/CraftFrame are load-on-demand Blizzard ADDONS, so the ref lives
at `Interface\AddOns\Blizzard_TradeSkillUI\`, not in `Interface\FrameXML\`.
Gray-out + the SetTradeSkillItem(sel, index) hover are the ref's own SetSelection reagent loop
+ OnEnter.
-->
<Button name="BenillaTradeSkillReagentTemplate" virtual="true" hidden="true">
<Size><AbsDimension x="147" y="41"/></Size>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentIcon">
<Size><AbsDimension x="39" y="39"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
</Texture>
<!-- Declared AFTER the icon (same parent+level ⇒ z-tiebreak, this file's own note):
the plate's 128×64 texture starts 10px INSIDE the icon's right edge, but its art
only begins 11 texels in, so the visible plate opens 1px clear of the icon — the
small gap the reference renders. -->
<Texture name="$parentNameFrame" file="Interface\QuestFrame\UI-QuestItemNameFrame">
<Size><AbsDimension x="128" y="64"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="$parentIcon" relativePoint="RIGHT"><Offset><AbsDimension x="-10" y="0"/></Offset></Anchor></Anchors>
</Texture>
<FontString name="$parentName" inherits="GameFontHighlight" justifyH="LEFT">
<Size><AbsDimension x="90" y="36"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="$parentNameFrame"><Offset><AbsDimension x="15" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
<Layer level="ARTWORK">
<FontString name="$parentCount" inherits="NumberFontNormal" justifyH="RIGHT">
<Anchors><Anchor point="BOTTOMRIGHT" relativeTo="$parentIcon"><Offset><AbsDimension x="-4" y="1"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnEnter>BenillaTradeSkillReagent_OnEnter(self)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</Button>
<!-- A panel action button (Create/Create All/Exit): the UIPanelButtonTemplate face locally
reconstructed (this house dialect ships no such template — TalentFrame.xml's own ENGINE API
note), TrainerFrame.xml's identical-shaped BenillaTrainerPanelButtonTemplate. -->
<Button name="BenillaTradeSkillPanelButtonTemplate" virtual="true">
<Size><AbsDimension x="80" y="22"/></Size>
<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>
</Button>
<Frame name="BenillaTradeSkillFrame" hidden="true">
<!-- The real 384×512 window. No <Anchors> — ShowUIPanel/SetLeftFrame SetPoints it to the left
slot (MerchantFrame.xml/TrainerFrame.xml's own convention). -->
<Size><AbsDimension x="384" y="512"/></Size>
<!-- Transcribed for fidelity (ref l.47-49); no loader wiring exists yet for the hit-rect
shrink — TalentFrame.xml's own "transcribe the hook even if nothing drives it yet"
posture. -->
<HitRectInsets><AbsInset left="0" right="34" top="0" bottom="75"/></HitRectInsets>
<Layers>
<!-- "player" portrait 60×60 TOPLEFT (7,-6) (ref l.51-63), BACKGROUND so the window art's
ring frames it. Bound via SetPortraitTexture(region, "player") in Show/OnEvent —
CharacterFrame.xml/TalentFrame.xml's own player-token precedent (this is not an NPC
session, so it is NOT the "npc" token Merchant/Trainer/Gossip use). -->
<Layer level="BACKGROUND">
<Texture name="BenillaTradeSkillFramePortrait">
<Size><AbsDimension x="60" y="60"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
</Texture>
</Layer>
<!-- The four quadrant slabs that ARE the window art (ref l.65-97): three from
ClassTrainerFrame's shared art, ONE (BotLeft) from TradeSkillFrame's own art — a real
mixed art set in the reference itself, transcribed exactly as given, not "fixed" to be
uniform. -->
<Layer level="BORDER">
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopLeft"><Size><AbsDimension x="256" y="256"/></Size><Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors></Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopRight"><Size><AbsDimension x="128" y="256"/></Size><Anchors><Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"/></Anchors></Texture>
<Texture file="Interface\TradeSkillFrame\UI-TradeSkill-BotLeft"><Size><AbsDimension x="256" y="256"/></Size><Anchors><Anchor point="BOTTOMLEFT" relativePoint="BOTTOMLEFT"/></Anchors></Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-BotRight"><Size><AbsDimension x="128" y="256"/></Size><Anchors><Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"/></Anchors></Texture>
<!-- Title: TRADE_SKILL_TITLE ("%s") formatted with the skill line name — set in
Show/SetSelection/HideDetails; "Trade Skills" (ref's own static default, l.98) is
the placeholder until the first paint. -->
<FontString name="BenillaTradeSkillFrameTitleText" inherits="GameFontNormal" justifyH="CENTER" text="Trade Skills">
<Anchors><Anchor point="TOP" relativePoint="TOP"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
<!-- The skill-border + horizontal-divider decorative bars (ref l.108-156), sampled from the
same two shared ClassTrainer/TradeSkill textures by TexCoord. -->
<Layer level="ARTWORK">
<Texture name="BenillaTradeSkillSkillBorderLeft" file="Interface\TradeSkillFrame\UI-TradeSkill-SkillBorder">
<Size><AbsDimension x="256" y="8"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="63" y="-50"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0" bottom="0.25"/>
</Texture>
<Texture file="Interface\TradeSkillFrame\UI-TradeSkill-SkillBorder">
<Size><AbsDimension x="28" y="8"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillSkillBorderLeft" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0" right="0.109375" top="0.25" bottom="0.5"/>
</Texture>
<Texture name="BenillaTradeSkillHorizontalBarLeft" file="Interface\ClassTrainerFrame\UI-ClassTrainer-HorizontalBar">
<Size><AbsDimension x="256" y="16"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="15" y="-221"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="1.0" top="0" bottom="0.25"/>
</Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-HorizontalBar">
<Size><AbsDimension x="75" y="16"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillHorizontalBarLeft" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0" right="0.29296875" top="0.25" bottom="0.5"/>
</Texture>
</Layer>
<!-- The FIXED detail-pane FontStrings + header art (ref TradeSkillDetailScrollChildFrame,
l.444-537), flattened onto this window's OWN Layers at OVERLAY (the flat-layout gap,
this file's header comment) — computed from the ref's ScrollFrame anchor (TOPLEFT
20,-234) + each region's own ScrollChild-relative offset, then chained by name like the
reference chains them. Declaration order matters (same parent+level ⇒ z-tiebreak,
TrainerFrame.xml's own note): the header art is declared BEFORE ReagentLabel so the
"Reagents: " banner text paints over its lower edge, exactly as the reference does. -->
<Layer level="OVERLAY">
<FontString name="BenillaTradeSkillSkillName" inherits="GameFontNormal" justifyH="LEFT">
<Size><AbsDimension x="244" y="0"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="70" y="-239"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="BenillaTradeSkillRequirementLabel" inherits="GameFontHighlightSmall">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkillName" relativePoint="BOTTOMLEFT"/></Anchors>
</FontString>
<FontString name="BenillaTradeSkillRequirementText" inherits="GameFontHighlightSmall" justifyV="TOP" justifyH="LEFT">
<Size><AbsDimension x="180" y="0"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillRequirementLabel" relativePoint="TOPRIGHT"><Offset><AbsDimension x="4" y="0"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="BenillaTradeSkillSkillCooldown" inherits="GameFontRedSmall">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillRequirementLabel" relativePoint="BOTTOMLEFT"/></Anchors>
</FontString>
<Texture name="BenillaTradeSkillDetailHeaderLeft" file="Interface\ClassTrainerFrame\UI-ClassTrainer-DetailHeaderLeft">
<Size><AbsDimension x="256" y="64"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="20" y="-231"/></Offset></Anchor></Anchors>
</Texture>
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-DetailHeaderRight">
<Size><AbsDimension x="64" y="64"/></Size>
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillDetailHeaderLeft" relativePoint="TOPRIGHT"/></Anchors>
</Texture>
<FontString name="BenillaTradeSkillReagentLabel" inherits="GameFontNormalSmall" text="Reagents: ">
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="28" y="-281"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Frames>
<!-- The scroll-wheel catcher (TrainerFrame.xml's own note) — MUST be first so every real
row below out-ranks it in hit-testing. -->
<Button name="BenillaTradeSkillWheelCatcher">
<Anchors>
<Anchor point="TOPLEFT" relativePoint="TOPLEFT"/>
<Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"/>
</Anchors>
<Scripts><OnMouseWheel>BenillaTradeSkillFrame_OnMouseWheel(arg1)</OnMouseWheel></Scripts>
</Button>
<!-- The top-right close X (ref a UIPanelCloseButton at TOPRIGHT -29,-8, l.838-846; the
minimize-button art stands in — this house dialect ships no such template,
TrainerFrame.xml's own precedent). -->
<Button name="BenillaTradeSkillFrameCloseButton">
<Size><AbsDimension x="32" y="32"/></Size>
<Anchors><Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-29" y="-8"/></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>BenillaTradeSkillFrameCloseButton_OnClick()</OnClick></Scripts>
</Button>
<!-- The skill-rank StatusBar (ref TradeSkillRankFrame, l.160-228): blue fill, "name
rank/max" text, the UI-Character-Skills-Bar[Border] art. SKILL_LINES_CHANGED
self-listen dropped (deviation 4). -->
<StatusBar name="BenillaTradeSkillRankFrame" drawLayer="BACKGROUND" minValue="0" maxValue="1" defaultValue="0">
<Size><AbsDimension x="268" y="15"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="73" y="-37"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="ARTWORK">
<FontString name="BenillaTradeSkillRankFrameSkillName" inherits="GameFontNormalSmall">
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="6" y="1"/></Offset></Anchor></Anchors>
</FontString>
<FontString name="BenillaTradeSkillRankFrameSkillRank" inherits="GameFontHighlightSmall" justifyH="LEFT">
<Size><AbsDimension x="128" y="0"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillRankFrameSkillName" relativePoint="RIGHT"><Offset><AbsDimension x="13" y="0"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
<Layer level="BACKGROUND">
<Texture name="BenillaTradeSkillRankFrameBackground"><Color r="1.0" g="1.0" b="1.0" a="0.2"/></Texture>
</Layer>
</Layers>
<Frames>
<Button name="BenillaTradeSkillRankFrameBorder">
<Size><AbsDimension x="281" y="32"/></Size>
<Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
<NormalTexture file="Interface\PaperDollInfoFrame\UI-Character-Skills-BarBorder"/>
</Button>
</Frames>
<BarTexture name="BenillaTradeSkillRankFrameBar" file="Interface\PaperDollInfoFrame\UI-Character-Skills-Bar"/>
<BarColor r="0.25" g="0.25" b="0.75"/>
</StatusBar>
<!-- The expand/collapse-all tab (ref TradeSkillExpandButtonFrame, l.229-294): a tab-shaped
frame carrying the CollapseAll button, verbatim geometry/art. The button is a
STANDALONE Button — not this file's row template (header comment, deviation 1's own
note on why) — TrainerFrame.xml's own BenillaTrainerCollapseAllButton is the precedent
this mirrors byte-for-byte: its own NormalTexture/HighlightTexture/ButtonText
reconstructing the un-extracted ClassTrainerSkillButtonTemplate base. -->
<Frame name="BenillaTradeSkillExpandButtonFrame">
<Size><AbsDimension x="54" y="32"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="15" y="-71"/></Offset></Anchor></Anchors>
<Layers><Layer level="BACKGROUND">
<Texture name="BenillaTradeSkillExpandTabLeft" file="Interface\ClassTrainerFrame\UI-ClassTrainer-ExpandTab-Left"><Size><AbsDimension x="8" y="32"/></Size><Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors></Texture>
<Texture name="BenillaTradeSkillExpandTabMiddle" file="Interface\QuestFrame\UI-QuestLogSortTab-Middle"><Size><AbsDimension x="38" y="32"/></Size><Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillExpandTabLeft" relativePoint="RIGHT"><Offset><AbsDimension x="0" y="6"/></Offset></Anchor></Anchors></Texture>
<Texture file="Interface\QuestFrame\UI-QuestLogSortTab-Right"><Size><AbsDimension x="8" y="32"/></Size><Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillExpandTabMiddle" relativePoint="RIGHT"/></Anchors></Texture>
</Layer></Layers>
<Frames>
<Button name="BenillaTradeSkillCollapseAllButton" text="All">
<Size><AbsDimension x="40" y="22"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillExpandTabLeft" relativePoint="RIGHT"><Offset><AbsDimension x="0" y="3"/></Offset></Anchor></Anchors>
<NormalTexture file="Interface\Buttons\UI-MinusButton-Up"><Size><AbsDimension x="16" y="16"/></Size><Anchors><Anchor point="LEFT" relativePoint="LEFT"/></Anchors></NormalTexture>
<HighlightTexture file="Interface\Buttons\UI-PlusButton-Hilight" alphaMode="ADD"><Size><AbsDimension x="16" y="16"/></Size><Anchors><Anchor point="LEFT" relativePoint="LEFT"/></Anchors></HighlightTexture>
<ButtonText><Anchors><Anchor point="LEFT" relativePoint="LEFT"><Offset><AbsDimension x="18" y="0"/></Offset></Anchor></Anchors></ButtonText>
<NormalFont inherits="GameFontNormalSmall" justifyH="LEFT"/>
<!-- text="All" (ALL, GlobalStrings.lua:45) rides the XML attribute — an
inline OnLoad SetText(chunk_local) reads nil across the chunk boundary
(TrainerFrame.xml's own BenillaTrainerCollapseAllButton precedent; the
ref's own inline SetText(ALL) works only because ITS ALL is a global). -->
<Scripts>
<OnClick>BenillaTradeSkillCollapseAllButton_OnClick(self)</OnClick>
</Scripts>
</Button>
</Frames>
</Frame>
<!-- The two filter dropdowns (ref l.295-328): the UIDropDownMenuTemplate capsule
(UIDropDownMenu.xml). InvSlot is id 2, TOPRIGHT (-25,-66); SubClass is id 1, RIGHT of
InvSlot (35,0) — the ref's own ordering/ids, transcribed verbatim. -->
<Frame name="BenillaTradeSkillInvSlotDropDown" inherits="UIDropDownMenuTemplate" id="2">
<Anchors><Anchor point="TOPRIGHT" relativePoint="TOPRIGHT"><Offset><AbsDimension x="-25" y="-66"/></Offset></Anchor></Anchors>
<Scripts>
<OnLoad>BenillaTradeSkillInvSlotDropDown_OnLoad()</OnLoad>
<OnShow>BenillaTradeSkillInvSlotDropDown_OnShow()</OnShow>
</Scripts>
</Frame>
<Frame name="BenillaTradeSkillSubClassDropDown" inherits="UIDropDownMenuTemplate" id="1">
<Anchors><Anchor point="RIGHT" relativeTo="BenillaTradeSkillInvSlotDropDown" relativePoint="LEFT"><Offset><AbsDimension x="35" y="0"/></Offset></Anchor></Anchors>
<Scripts>
<OnLoad>BenillaTradeSkillSubClassDropDown_OnLoad()</OnLoad>
<OnShow>BenillaTradeSkillSubClassDropDown_OnShow()</OnShow>
</Scripts>
</Frame>
<!-- The persistent selection highlight (ref TradeSkillHighlightFrame, l.330-342): 293×16,
ARTWORK-layer UI-Listbox-Highlight2. Declared BEFORE the rows so the row text paints
OVER the glow (TrainerFrame.xml's own z-tiebreak note). Vertex-colored by the selected
skill's TradeSkillTypeColor (SetSelection), positioned onto the selected visible row
(Update). -->
<Frame name="BenillaTradeSkillHighlightFrame" hidden="true">
<Size><AbsDimension x="293" y="16"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
<Layers><Layer level="ARTWORK">
<Texture name="BenillaTradeSkillHighlight" file="Interface\Buttons\UI-Listbox-Highlight2">
<Anchors><Anchor point="TOPLEFT"/><Anchor point="BOTTOMRIGHT"/></Anchors>
</Texture>
</Layer></Layers>
</Frame>
<!-- 8 list rows (ref TradeSkillSkill1..8, l.343-414): 293×16, row1 TOPLEFT (22,-96); rows
2-8 chain TOPLEFT→prior BOTTOMLEFT with no gap. -->
<Button name="BenillaTradeSkillSkill1" inherits="BenillaTradeSkillSkillButtonTemplate">
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-96"/></Offset></Anchor></Anchors>
</Button>
<Button name="BenillaTradeSkillSkill2" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill1" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill3" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill2" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill4" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill3" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill5" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill4" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill6" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill5" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill7" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill6" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<Button name="BenillaTradeSkillSkill8" inherits="BenillaTradeSkillSkillButtonTemplate"><Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillSkill7" relativePoint="BOTTOMLEFT"/></Anchors></Button>
<!-- The faux-scroll frame (FauxScrollFrameTemplate, ScrollTemplates.xml): invisible
container over the 8 rows; only its right-hand Slider scroll bar renders. Sized to the
8-row span (TRADE_SKILLS_DISPLAYED × TRADE_SKILL_HEIGHT), TrainerFrame.xml's own
same-TOPLEFT-as-row1 convention. updateFunc set in BenillaTradeSkillFrame_OnLoad. -->
<Frame name="BenillaTradeSkillListScrollFrame" inherits="FauxScrollFrameTemplate">
<Size><AbsDimension x="293" y="128"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-96"/></Offset></Anchor></Anchors>
</Frame>
<!-- The detail product icon (ref TradeSkillSkillIcon, l.539-591): a 37×37 button over a
64×64 UI-EmptySlot ring (TrainerFrame.xml's own BenillaTrainerSkillIcon shape) at the
flattened header art's top-left. Icon via SetNormalTexture; the made-count FontString
rides its BOTTOMRIGHT. -->
<Button name="BenillaTradeSkillSkillIcon" hidden="true">
<Size><AbsDimension x="37" y="37"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="28" y="-237"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture file="Interface\Buttons\UI-EmptySlot"><Size><AbsDimension x="64" y="64"/></Size><Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="-13" y="13"/></Offset></Anchor></Anchors></Texture>
</Layer>
<Layer level="ARTWORK">
<FontString name="BenillaTradeSkillSkillIconCount" inherits="NumberFontNormal" justifyH="RIGHT">
<Anchors><Anchor point="BOTTOMRIGHT" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="2"/></Offset></Anchor></Anchors>
</FontString>
</Layer>
</Layers>
<Scripts>
<OnEnter>BenillaTradeSkillSkillIcon_OnEnter(self)</OnEnter>
<OnLeave>GameTooltip:Hide()</OnLeave>
</Scripts>
</Button>
<!-- Up to 8 reagents, the ref's own 2-column grid + anchor chain (l.592-663): odd slots
chain down the left column (TOPLEFT→prior BOTTOMLEFT, -2 gap), even slots sit LEFT of
the odd slot in the same row. Reagent7 chains off Reagent6 (not Reagent5) — a literal
ref quirk (this file's header comment, deviation 6's sibling note): harmless, since 6's
row-mate LEFT anchor to 5 already puts it at 5's own row height. -->
<Button name="BenillaTradeSkillReagent1" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillReagentLabel" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="-5" y="-3"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 1)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent2" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillReagent1" relativePoint="RIGHT"/></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 2)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent3" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillReagent1" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 3)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent4" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillReagent3" relativePoint="RIGHT"/></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 4)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent5" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillReagent3" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 5)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent6" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillReagent5" relativePoint="RIGHT"/></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 6)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent7" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="TOPLEFT" relativeTo="BenillaTradeSkillReagent6" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 7)</OnLoad></Scripts>
</Button>
<Button name="BenillaTradeSkillReagent8" inherits="BenillaTradeSkillReagentTemplate">
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillReagent7" relativePoint="RIGHT"/></Anchors>
<Scripts><OnLoad>BenillaTradeSkillReagent_OnLoad(self, 8)</OnLoad></Scripts>
</Button>
<!-- The action row (ref l.668-837): Create / Create All / -/+ / the repeat-count input /
Exit, all anchored off CreateButton like the reference. -->
<Button name="BenillaTradeSkillCreateButton" inherits="BenillaTradeSkillPanelButtonTemplate" text="Create">
<Anchors><Anchor point="CENTER" relativeTo="BenillaTradeSkillFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="224" y="-422"/></Offset></Anchor></Anchors>
<Scripts><OnClick>BenillaTradeSkillCreateButton_OnClick()</OnClick></Scripts>
</Button>
<Button name="BenillaTradeSkillCancelButton" inherits="BenillaTradeSkillPanelButtonTemplate" text="Exit">
<Anchors><Anchor point="CENTER" relativeTo="BenillaTradeSkillFrame" relativePoint="TOPLEFT"><Offset><AbsDimension x="305" y="-422"/></Offset></Anchor></Anchors>
<Scripts><OnClick>BenillaTradeSkillCancelButton_OnClick()</OnClick></Scripts>
</Button>
<Button name="BenillaTradeSkillCreateAllButton" inherits="BenillaTradeSkillPanelButtonTemplate" text="Create All">
<Anchors><Anchor point="RIGHT" relativeTo="BenillaTradeSkillCreateButton" relativePoint="LEFT"><Offset><AbsDimension x="-86" y="0"/></Offset></Anchor></Anchors>
<Scripts><OnClick>BenillaTradeSkillCreateAllButton_OnClick()</OnClick></Scripts>
</Button>
<Button name="BenillaTradeSkillDecrementButton">
<Size><AbsDimension x="23" y="22"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillCreateAllButton" relativePoint="RIGHT"><Offset><AbsDimension x="3" y="0"/></Offset></Anchor></Anchors>
<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"/>
<Scripts><OnClick>BenillaTradeSkillFrameDecrement_OnClick()</OnClick></Scripts>
</Button>
<EditBox name="BenillaTradeSkillInputBox" letters="3" historyLines="0" numeric="true" autoFocus="false">
<Size><AbsDimension x="30" y="20"/></Size>
<Anchors><Anchor point="LEFT" relativeTo="BenillaTradeSkillDecrementButton" relativePoint="RIGHT"><Offset><AbsDimension x="4" y="0"/></Offset></Anchor></Anchors>
<Layers>
<Layer level="BACKGROUND">
<Texture name="$parentLeft" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="8" y="20"/></Size>
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
<TexCoords left="0" right="0.0625" top="0" bottom="0.625"/>
</Texture>
<Texture name="$parentRight" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="8" y="20"/></Size>
<Anchors><Anchor point="RIGHT" relativePoint="RIGHT"/></Anchors>
<TexCoords left="0.9375" right="1.0" top="0" bottom="0.625"/>
</Texture>
<Texture name="$parentMiddle" file="Interface\Common\Common-Input-Border">
<Size><AbsDimension x="10" y="20"/></Size>
<Anchors>
<Anchor point="LEFT" relativeTo="$parentLeft" relativePoint="RIGHT"/>
<Anchor point="RIGHT" relativeTo="$parentRight" relativePoint="LEFT"/>
</Anchors>
<TexCoords left="0.0625" right="0.9375" top="0" bottom="0.625"/>
</Texture>
</Layer>
</Layers>
<Scripts>
<OnEnterPressed>self:ClearFocus()</OnEnterPressed>
<OnEscapePressed>self:ClearFocus()</OnEscapePressed>
<OnTextChanged>
if self:GetText() == "0" then
self:SetText("1")
end
</OnTextChanged>
<OnEditFocusLost>self:HighlightText(0, 0)</OnEditFocusLost>
<OnEditFocusGained>self:HighlightText()</OnEditFocusGained>
</Scripts>
<FontString inherits="ChatFontNormal"/>
</EditBox>
<Button name="BenillaTradeSkillIncrementButton">
<Size><AbsDimension x="23" y="22"/></Size>
<Anchors><Anchor point="RIGHT" relativeTo="BenillaTradeSkillCreateButton" relativePoint="LEFT"><Offset><AbsDimension x="-3" y="0"/></Offset></Anchor></Anchors>
<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"/>
<Scripts><OnClick>BenillaTradeSkillFrameIncrement_OnClick()</OnClick></Scripts>
</Button>
</Frames>
<!-- The window lifecycle (MerchantFrame.xml/TrainerFrame.xml's own shape): OnHide calls
CloseTradeSkill() for ANY hide reason. The list wheel lives on the WINDOW (the engine
bubbles a spin up the parent chain). -->
<Scripts>
<OnLoad>BenillaTradeSkillFrame_OnLoad(self)</OnLoad>
<OnEvent>BenillaTradeSkillFrame_OnEvent(self)</OnEvent>
<OnMouseWheel>BenillaTradeSkillFrame_OnMouseWheel(arg1)</OnMouseWheel>
<OnShow>PlaySound("igCharacterInfoOpen")</OnShow>
<OnHide>CloseTradeSkill(); PlaySound("igCharacterInfoClose")</OnHide>
</Scripts>
</Frame>
</Ui>