ca2b78e217
/fui keeps edit mode, reset and list. The thirty diagnostic and tuning functions behind the other subcommands (bar, bags, character, tabs, close, model, minimap, mouse, map, questlog, chat, chatlines, tabard...) go with them, with Panes.Report, the bags read-back and the 205 texts they alone used. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
613 lines
18 KiB
Lua
613 lines
18 KiB
Lua
-- Skills tab of the character frame (camelot SkillsFrame.xml / skillsframe.lua), the same
|
|
-- screen as the reputation tab. Headers are 26 high (not 28), entries 30. The entry bar
|
|
-- (SkillsBarTemplate 160 x 29) is blue through its sprite, not a tint, and always shows
|
|
-- the progress. Right pane: SkillDetailFrameMixin with a 180 x 29 RankBar.
|
|
-- 3.3.5 differences: GetSkillLineInfo has one level, so there are no sub-headers; weapon
|
|
-- lines get no hit / crit detail (no WEAPON_SKILL_DETAIL_* strings); the description does
|
|
-- not scroll (no ScrollingFontTemplate); the scroll bar is MinimalScrollBar from ScrollBar.lua.
|
|
|
|
local ForeverUI = ForeverUI or {}
|
|
_G.ForeverUI = ForeverUI
|
|
local L = ForeverUI.L
|
|
|
|
local LIST_X, LIST_Y = 10, -40
|
|
local LIST_X2, LIST_Y2 = -25, 15
|
|
|
|
local ENTRY_H = 30 -- SkillsEntryTemplate
|
|
local HEADER_H = 26 -- SkillsHeaderTemplate
|
|
local MARGIN = 10 -- SetPadding
|
|
local GAP = 3 -- elementSpacing
|
|
local HEADER_INDENT = 0
|
|
local OTHER_INDENT = 2
|
|
|
|
local BAR_W, BAR_H = 160, 29
|
|
local BAR_X = -3
|
|
local FILL_H = 15
|
|
local NAME_H = 15
|
|
local NAME_X = 2 -- SkillsEntryTemplate
|
|
local NAME_GAP = -10
|
|
local HEADER_NAME_X = 10
|
|
local ARROW_X, ARROW_Y = -8, -1
|
|
local ARROW_SPACE = 16
|
|
local SIDE = 6
|
|
|
|
local PLATE_CORNER = 12 -- same as the reputation tab
|
|
local GAUGE_CORNER = 10
|
|
|
|
local ATLAS_BAR_BACKGROUND = "common-stat-bar-bg"
|
|
local ATLAS_HEADER = "common-button-list-collapseexpand"
|
|
local ATLAS_PLUS = "common-button-list-plus"
|
|
local ATLAS_MINUS = "common-button-list-minus"
|
|
local ATLAS_LINE = "ui-character-info-scrollline-long"
|
|
local ATLAS_HOVER_SIDE = "charactercreate-customize-dropdown-linemouseover-side"
|
|
local ATLAS_HOVER_MIDDLE = "charactercreate-customize-dropdown-linemouseover-middle"
|
|
local ATLAS_SEPARATOR = "ui-character-info-scrollline"
|
|
|
|
-- Blue fill, baked with the mask like the reputation's white one: same slicing as the
|
|
-- background, so the outlines overlap.
|
|
local FILL_PATH = "Interface\\ForeverUI\\Bars\\statbarfillblue"
|
|
|
|
-- Right pane: CharacterFrameSidePaneTemplate.
|
|
local RIGHT_PANE_X, RIGHT_PANE_Y = 16, -14
|
|
local RIGHT_PANE_X2, RIGHT_PANE_Y2 = -12, 14
|
|
local TITLE_W = 195
|
|
local SUBTITLE_Y = -3
|
|
local SEPARATOR_Y = -4
|
|
local RANK_W, RANK_H = 180, 29
|
|
local RANK_Y = -6
|
|
local DESCRIPTION_Y = -8 -- not -6: SkillDetailFrameMixin
|
|
local DESCRIPTION_X2 = -14
|
|
local DESCRIPTION_Y2 = 6
|
|
|
|
-- Pane size, used while the host has no size yet.
|
|
local PANE_W, PANE_H = 398, 464
|
|
|
|
local rows = {}
|
|
local panel, offset = nil, 0
|
|
local visibleCount = 0
|
|
local selectedItem -- the NAME, the only stable key
|
|
local detail, lastData
|
|
|
|
-- --------------------------------------------------------------- Data
|
|
|
|
-- GetSkillLineInfo returns, in order: name, header, expanded, rank, temporary points, bonus,
|
|
-- max rank, abandonable, step cost, rank cost, min level, cost type, description.
|
|
local function readSkill(rank)
|
|
local name, header, expanded, value, temporary, bonus, maximum, _, _, _,
|
|
_, _, description = GetSkillLineInfo(rank)
|
|
if not name or name == "" then
|
|
return nil
|
|
end
|
|
|
|
value = (value or 0) + (temporary or 0)
|
|
bonus = bonus or 0
|
|
maximum = maximum or 0
|
|
|
|
-- InitializeBarForStandardSkill: "rank / max", or "rank (+bonus) / max" with the bonus
|
|
-- in green.
|
|
local text
|
|
if bonus == 0 then
|
|
text = tostring(value) .. " / " .. tostring(maximum)
|
|
else
|
|
text = tostring(value) .. " |cff00ff00(+" .. tostring(bonus)
|
|
.. ")|r / " .. tostring(maximum)
|
|
end
|
|
|
|
return {
|
|
index = rank,
|
|
name = name,
|
|
header = header,
|
|
collapsed = (header and not expanded) or false,
|
|
value = value,
|
|
maximum = maximum,
|
|
text = text,
|
|
description = description or "",
|
|
}
|
|
end
|
|
|
|
-- Skill line index of a skill name, or nil
|
|
local function indexOf(name)
|
|
if not name then
|
|
return nil
|
|
end
|
|
for rank = 1, (GetNumSkillLines and GetNumSkillLines()) or 0 do
|
|
if GetSkillLineInfo(rank) == name then
|
|
return rank
|
|
end
|
|
end
|
|
return nil
|
|
end
|
|
|
|
-- --------------------------------------------------------------- Row
|
|
|
|
local function createRow(index, width)
|
|
local row = CreateFrame("Button", "ForeverUISkillRow" .. index, panel)
|
|
row:SetWidth(width)
|
|
row:SetHeight(ENTRY_H)
|
|
|
|
row.plate = ForeverUI.CreateNineSlice(row, ATLAS_HEADER, PLATE_CORNER,
|
|
{ 0, 0, 0, 0 }, "BACKGROUND") or {}
|
|
for _, slice in ipairs(row.plate) do
|
|
slice:Hide()
|
|
end
|
|
|
|
local hover = CreateFrame("Frame", nil, row)
|
|
hover:SetAllPoints(row)
|
|
hover:SetAlpha(0)
|
|
row.hover = hover
|
|
|
|
local left = hover:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(left, ATLAS_HOVER_SIDE, true)
|
|
left:SetWidth(SIDE)
|
|
left:SetPoint("TOPLEFT", hover, "TOPLEFT", 0, 0)
|
|
left:SetPoint("BOTTOMLEFT", hover, "BOTTOMLEFT", 0, 0)
|
|
|
|
local right = hover:CreateTexture(nil, "BACKGROUND")
|
|
if ForeverUI.SetAtlas(right, ATLAS_HOVER_SIDE, true) then
|
|
local e = ForeverUI.AtlasEntry(ATLAS_HOVER_SIDE)
|
|
right:SetTexCoord(e[3], e[2], e[4], e[5])
|
|
end
|
|
right:SetWidth(SIDE)
|
|
right:SetPoint("TOPRIGHT", hover, "TOPRIGHT", 0, 0)
|
|
right:SetPoint("BOTTOMRIGHT", hover, "BOTTOMRIGHT", 0, 0)
|
|
|
|
local middle = hover:CreateTexture(nil, "BACKGROUND")
|
|
ForeverUI.SetAtlas(middle, ATLAS_HOVER_MIDDLE, true)
|
|
middle:SetPoint("TOPLEFT", left, "TOPRIGHT", 0, 0)
|
|
middle:SetPoint("BOTTOMRIGHT", right, "BOTTOMLEFT", 0, 0)
|
|
|
|
local bar = CreateFrame("Frame", nil, row)
|
|
bar:SetWidth(BAR_W)
|
|
bar:SetHeight(BAR_H)
|
|
bar:SetPoint("RIGHT", row, "RIGHT", BAR_X, 0)
|
|
row.bar = bar
|
|
|
|
ForeverUI.CreateNineSlice(bar, ATLAS_BAR_BACKGROUND, GAUGE_CORNER,
|
|
{ 0, 0, 0, 0 }, "BACKGROUND")
|
|
|
|
bar.fill = bar:CreateTexture(nil, "BORDER")
|
|
bar.fill:SetPoint("LEFT", bar, "LEFT", 0, 0)
|
|
|
|
bar.text = bar:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
bar.text:SetPoint("LEFT", bar, "LEFT", 0, 0)
|
|
bar.text:SetPoint("RIGHT", bar, "RIGHT", 0, 0)
|
|
bar.text:SetJustifyH("CENTER")
|
|
|
|
local name = row:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
|
|
name:SetHeight(NAME_H)
|
|
name:SetJustifyH("LEFT")
|
|
row.name = name
|
|
|
|
local arrow = row:CreateTexture(nil, "OVERLAY")
|
|
arrow:SetPoint("RIGHT", row, "RIGHT", ARROW_X, ARROW_Y)
|
|
arrow:Hide()
|
|
row.arrow = arrow
|
|
|
|
row:RegisterForClicks("LeftButtonUp")
|
|
return row
|
|
end
|
|
|
|
-- ----------------------------------------------------------- Fill
|
|
|
|
-- SetFillPercent: width = fraction x bar width, texture cropped the same.
|
|
local function placeBarIn(bar, data, width)
|
|
local fraction = 0
|
|
if data.maximum and data.maximum > 0 then
|
|
fraction = data.value / data.maximum
|
|
end
|
|
if fraction < 0 then
|
|
fraction = 0
|
|
elseif fraction > 1 then
|
|
fraction = 1
|
|
end
|
|
|
|
if fraction * width < 1 then
|
|
bar.fill:Hide()
|
|
else
|
|
bar.fill:SetTexture(FILL_PATH)
|
|
bar.fill:SetTexCoord(0, fraction, 0, 1)
|
|
bar.fill:SetWidth(width * fraction)
|
|
bar.fill:SetHeight(FILL_H)
|
|
bar.fill:Show()
|
|
end
|
|
|
|
bar.text:SetText(data.text or "")
|
|
end
|
|
|
|
-- RefreshBackgroundHighlightOpacity, without the "at war" case; the tint is always white.
|
|
local function placeHover(row)
|
|
if row.header then
|
|
row.hover:SetAlpha(0)
|
|
return
|
|
end
|
|
|
|
local hovered = row:IsMouseOver()
|
|
row.hover:SetAlpha((row.selectedItem and 0.20) or (hovered and 0.10) or 0)
|
|
end
|
|
|
|
-- SetFontObject resets the justification to the font's own (GameFontHighlight is centered
|
|
-- in the client's FontStyles.xml), so SetJustifyH must come after it, as camelot sets
|
|
-- justifyH="LEFT" on top of the font object.
|
|
local function populateRow(row, data)
|
|
row.skillIndex = data.index
|
|
row.skillName = data.name
|
|
row.header = data.header
|
|
row.collapsed = data.collapsed
|
|
|
|
row.name:SetText(data.name or "")
|
|
row.name:ClearAllPoints()
|
|
|
|
for _, slice in ipairs(row.plate) do
|
|
if data.header then slice:Show() else slice:Hide() end
|
|
end
|
|
|
|
if data.header then
|
|
row:SetHeight(HEADER_H)
|
|
row.bar:Hide()
|
|
row.name:SetFontObject(GameFontNormalLeft or GameFontNormal)
|
|
row.name:SetJustifyH("LEFT")
|
|
row.name:SetPoint("LEFT", row, "LEFT", HEADER_NAME_X, 0)
|
|
row.name:SetPoint("RIGHT", row, "RIGHT", ARROW_X - ARROW_SPACE, 0)
|
|
|
|
ForeverUI.SetAtlas(row.arrow, data.collapsed and ATLAS_PLUS or ATLAS_MINUS)
|
|
row.arrow:Show()
|
|
else
|
|
row:SetHeight(ENTRY_H)
|
|
row.bar:Show()
|
|
row.arrow:Hide()
|
|
row.name:SetFontObject(GameFontHighlight or GameFontNormal)
|
|
row.name:SetJustifyH("LEFT")
|
|
row.name:SetPoint("LEFT", row, "LEFT", NAME_X, 0)
|
|
row.name:SetPoint("RIGHT", row.bar, "LEFT", NAME_GAP, 0)
|
|
placeBarIn(row.bar, data, BAR_W)
|
|
end
|
|
|
|
row.selectedItem = (selectedItem ~= nil and selectedItem == data.name)
|
|
placeHover(row)
|
|
row:Show()
|
|
end
|
|
|
|
-- ------------------------------------------------------------- Layout
|
|
|
|
local function indentOf(data)
|
|
return data.header and HEADER_INDENT or OTHER_INDENT
|
|
end
|
|
|
|
local function heightOf(data)
|
|
return data.header and HEADER_H or ENTRY_H
|
|
end
|
|
|
|
-- Hides everything the client's SkillFrame holds except our panel: its regions
|
|
-- (GetRegions) and child frames (GetChildren). Runs on every pass, since
|
|
-- SkillFrame_UpdateSkills shows them again.
|
|
local function suppressClientScreen()
|
|
local frame = _G["SkillFrame"]
|
|
if not frame then
|
|
return
|
|
end
|
|
|
|
for _, region in ipairs({ frame:GetRegions() }) do
|
|
if region.Hide then
|
|
region:Hide()
|
|
end
|
|
end
|
|
|
|
if frame.GetChildren then
|
|
for _, childFrame in ipairs({ frame:GetChildren() }) do
|
|
if childFrame ~= panel and childFrame.Hide then
|
|
childFrame:Hide()
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local function layout()
|
|
local total = (GetNumSkillLines and GetNumSkillLines()) or 0
|
|
|
|
local usableHeight = (panel:GetHeight() or 0)
|
|
if usableHeight < 50 then
|
|
usableHeight = PANE_H + LIST_Y - LIST_Y2
|
|
end
|
|
local usableWidth = (panel:GetWidth() or 0)
|
|
if usableWidth < 50 then
|
|
usableWidth = PANE_W + LIST_X2 - LIST_X
|
|
end
|
|
|
|
local y = MARGIN
|
|
local placedCount = 0
|
|
local full = false
|
|
for rank, row in ipairs(rows) do
|
|
local index = offset + rank
|
|
local data = (not full and index <= total) and readSkill(index) or nil
|
|
local height = data and heightOf(data) or 0
|
|
if data and y + height <= usableHeight - MARGIN then
|
|
local indent = indentOf(data)
|
|
row:SetWidth(usableWidth - 2 * MARGIN - indent)
|
|
populateRow(row, data)
|
|
row:ClearAllPoints()
|
|
row:SetPoint("TOPLEFT", panel, "TOPLEFT", MARGIN + indent, -y)
|
|
y = y + height + GAP
|
|
placedCount = placedCount + 1
|
|
else
|
|
-- Past the first row that does not fit nothing is placed: a shorter row further down
|
|
-- (a header after an entry) would otherwise take its slot, out of order
|
|
full = full or data ~= nil
|
|
row:Hide()
|
|
end
|
|
end
|
|
return placedCount
|
|
end
|
|
|
|
local function layoutList()
|
|
if not panel then
|
|
return
|
|
end
|
|
|
|
suppressClientScreen()
|
|
|
|
local total = (GetNumSkillLines and GetNumSkillLines()) or 0
|
|
local placedCount = layout()
|
|
|
|
if offset > 0 and offset + placedCount > total then
|
|
offset = math.max(0, total - placedCount)
|
|
placedCount = layout()
|
|
end
|
|
|
|
visibleCount = placedCount
|
|
|
|
if panel.bar then
|
|
panel.bar:Configure(total, placedCount, offset)
|
|
end
|
|
|
|
if ForeverUI.SkillsDetail then
|
|
ForeverUI.SkillsDetail()
|
|
end
|
|
end
|
|
ForeverUI.SkillsLayout = layoutList
|
|
|
|
-- --------------------------------------------------------------- Detail
|
|
|
|
local function clearDetail()
|
|
detail.title:SetText("")
|
|
detail.subtitle:SetText("")
|
|
-- SKILL_DETAIL_SELECT_PROMPT does not exist in 3.3.5: left empty.
|
|
detail.description:SetText("")
|
|
detail.separator:Hide()
|
|
detail.gauge:Hide()
|
|
end
|
|
|
|
local function updateDetail()
|
|
if not detail then
|
|
return
|
|
end
|
|
|
|
if not selectedItem then
|
|
lastData = nil
|
|
clearDetail()
|
|
return
|
|
end
|
|
|
|
local index = indexOf(selectedItem)
|
|
local data
|
|
if index then
|
|
if SetSelectedSkill and GetSelectedSkill and GetSelectedSkill() ~= index then
|
|
SetSelectedSkill(index)
|
|
end
|
|
data = readSkill(index)
|
|
lastData = data
|
|
else
|
|
data = lastData
|
|
end
|
|
|
|
if not data or data.header then
|
|
clearDetail()
|
|
return
|
|
end
|
|
|
|
detail.title:SetText(data.name or "")
|
|
detail.subtitle:SetText("")
|
|
detail.separator:Show()
|
|
detail.gauge:Show()
|
|
placeBarIn(detail.gauge, data, RANK_W)
|
|
detail.description:SetText(data.description or "")
|
|
end
|
|
ForeverUI.SkillsDetail = updateDetail
|
|
|
|
local function choose(name)
|
|
selectedItem = name
|
|
local index = indexOf(name)
|
|
if index and SetSelectedSkill then
|
|
SetSelectedSkill(index)
|
|
end
|
|
layoutList()
|
|
end
|
|
ForeverUI.SkillsSelect = choose
|
|
|
|
-- SelectFirstSkillIfNoneSelected: on opening, select the first non-header skill so the
|
|
-- right pane is not empty.
|
|
local function selectFirst()
|
|
if selectedItem then
|
|
return
|
|
end
|
|
for rank = 1, (GetNumSkillLines and GetNumSkillLines()) or 0 do
|
|
local data = readSkill(rank)
|
|
if data and not data.header then
|
|
choose(data.name)
|
|
return
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Builds the right pane (skill detail) in host
|
|
local function buildDetail(host)
|
|
if detail then
|
|
return detail, {}
|
|
end
|
|
|
|
local frame = CreateFrame("Frame", "ForeverUISkillDetail", host)
|
|
frame:SetPoint("TOPLEFT", host, "TOPLEFT", RIGHT_PANE_X, RIGHT_PANE_Y)
|
|
frame:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", RIGHT_PANE_X2, RIGHT_PANE_Y2)
|
|
detail = frame
|
|
|
|
detail.title = frame:CreateFontString(nil, "ARTWORK", "GameFontNormalLarge")
|
|
detail.title:SetWidth(TITLE_W)
|
|
detail.title:SetJustifyH("CENTER")
|
|
detail.title:SetPoint("TOP", frame, "TOP", 0, 0)
|
|
|
|
detail.subtitle = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
detail.subtitle:SetWidth(TITLE_W)
|
|
detail.subtitle:SetJustifyH("CENTER")
|
|
detail.subtitle:SetPoint("TOP", detail.title, "BOTTOM", 0, SUBTITLE_Y)
|
|
|
|
detail.separator = frame:CreateTexture(nil, "BORDER")
|
|
ForeverUI.SetAtlas(detail.separator, ATLAS_SEPARATOR)
|
|
detail.separator:SetPoint("TOP", detail.subtitle, "BOTTOM", 0, SEPARATOR_Y)
|
|
|
|
local gauge = CreateFrame("Frame", "ForeverUISkillRank", frame)
|
|
gauge:SetWidth(RANK_W)
|
|
gauge:SetHeight(RANK_H)
|
|
gauge:SetPoint("TOP", detail.separator, "BOTTOM", 0, RANK_Y)
|
|
ForeverUI.CreateNineSlice(gauge, ATLAS_BAR_BACKGROUND, GAUGE_CORNER,
|
|
{ 0, 0, 0, 0 }, "BACKGROUND")
|
|
gauge.fill = gauge:CreateTexture(nil, "BORDER")
|
|
gauge.fill:SetPoint("LEFT", gauge, "LEFT", 0, 0)
|
|
gauge.text = gauge:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
gauge.text:SetPoint("LEFT", gauge, "LEFT", 0, 0)
|
|
gauge.text:SetPoint("RIGHT", gauge, "RIGHT", 0, 0)
|
|
gauge.text:SetJustifyH("CENTER")
|
|
detail.gauge = gauge
|
|
|
|
-- Description in a box: without a bottom anchor, a long text disappears.
|
|
detail.description = frame:CreateFontString(nil, "ARTWORK", "GameFontHighlight")
|
|
detail.description:SetPoint("TOPLEFT", gauge, "BOTTOMLEFT", 0, DESCRIPTION_Y)
|
|
detail.description:SetPoint("TOPRIGHT", gauge, "BOTTOMRIGHT", 0, DESCRIPTION_Y)
|
|
detail.description:SetPoint("BOTTOMLEFT", frame, "BOTTOMLEFT", 0, DESCRIPTION_Y2)
|
|
detail.description:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT",
|
|
DESCRIPTION_X2, DESCRIPTION_Y2)
|
|
detail.description:SetJustifyH("LEFT")
|
|
detail.description:SetJustifyV("TOP")
|
|
if detail.description.SetWordWrap then
|
|
detail.description:SetWordWrap(true)
|
|
end
|
|
|
|
updateDetail()
|
|
return frame, {}
|
|
end
|
|
|
|
-- --------------------------------------------------------- Build
|
|
|
|
local function trackHover()
|
|
for _, row in ipairs(rows) do
|
|
if row:IsShown() and not row.header then
|
|
placeHover(row)
|
|
end
|
|
end
|
|
end
|
|
|
|
-- Builds the skill list in host over the client's SkillFrame
|
|
local function build(host)
|
|
local frame = _G["SkillFrame"]
|
|
if not frame or not host then
|
|
return nil, {}
|
|
end
|
|
|
|
frame:ClearAllPoints()
|
|
frame:SetPoint("TOPLEFT", host, "TOPLEFT", 0, 0)
|
|
frame:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", 0, 0)
|
|
|
|
if panel then
|
|
selectFirst()
|
|
return nil, { frame }
|
|
end
|
|
|
|
-- The 3.3.5 art and rows go away; only the frame is kept.
|
|
if frame.SetBackdrop then
|
|
frame:SetBackdrop(nil)
|
|
end
|
|
|
|
local height = host:GetHeight() or 0
|
|
if height < 100 then
|
|
height = PANE_H
|
|
end
|
|
local width = host:GetWidth() or 0
|
|
if width < 100 then
|
|
width = PANE_W
|
|
end
|
|
width = width + LIST_X2 - LIST_X
|
|
|
|
panel = CreateFrame("Frame", "ForeverUISkillList", frame)
|
|
panel:SetPoint("TOPLEFT", host, "TOPLEFT", LIST_X, LIST_Y)
|
|
panel:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", LIST_X2, LIST_Y2)
|
|
panel:SetWidth(width)
|
|
|
|
local position = math.floor((height + LIST_Y - LIST_Y2) / (HEADER_H + GAP))
|
|
if position < 1 then
|
|
position = 1
|
|
end
|
|
for index = 1, position do
|
|
local row = createRow(index, width)
|
|
row:SetScript("OnClick", function(self)
|
|
if not self.skillIndex then
|
|
return
|
|
end
|
|
if self.header then
|
|
if self.collapsed then
|
|
ExpandSkillHeader(self.skillIndex)
|
|
else
|
|
CollapseSkillHeader(self.skillIndex)
|
|
end
|
|
layoutList()
|
|
else
|
|
ForeverUI.SkillsSelect(self.skillName)
|
|
end
|
|
end)
|
|
rows[index] = row
|
|
end
|
|
|
|
-- Both lines live on our panel, not on the client's frame: its regions are all hidden
|
|
-- on every pass.
|
|
local top = panel:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(top, ATLAS_LINE)
|
|
top:SetPoint("CENTER", panel, "TOP", 0, 0)
|
|
|
|
local down = panel:CreateTexture(nil, "ARTWORK")
|
|
ForeverUI.SetAtlas(down, ATLAS_LINE)
|
|
down:SetPoint("CENTER", panel, "BOTTOM", 0, 0)
|
|
|
|
-- Camelot's scroll bar, right of the list, as in the reputation tab. It does not know the
|
|
-- list: it gets three numbers and returns the new offset.
|
|
panel.bar = ForeverUI.CreateScrollBar("ForeverUISkillsScrollBar",
|
|
frame, panel)
|
|
panel.bar.onScroll = function(new)
|
|
offset = new
|
|
layoutList()
|
|
end
|
|
-- Without a bar the list takes its place, as in the reputation tab: right edge from -25
|
|
-- to -10, rows laid out again.
|
|
panel.bar.onVisibility = function(hasBar)
|
|
local x2 = hasBar and LIST_X2 or -LIST_X
|
|
panel:SetPoint("BOTTOMRIGHT", host, "BOTTOMRIGHT", x2, LIST_Y2)
|
|
panel:SetWidth(width + x2 - LIST_X2)
|
|
layout()
|
|
end
|
|
|
|
panel:SetScript("OnUpdate", trackHover)
|
|
panel:EnableMouseWheel(true)
|
|
panel:SetScript("OnMouseWheel", function(self, direction)
|
|
local total = (GetNumSkillLines and GetNumSkillLines()) or 0
|
|
offset = math.max(0, math.min(offset - direction, total - visibleCount))
|
|
layoutList()
|
|
end)
|
|
|
|
layoutList()
|
|
selectFirst()
|
|
return nil, { frame }
|
|
end
|
|
|
|
ForeverUI.SkillsTab = { Build = build, BuildRight = buildDetail, Rows = rows }
|
|
|
|
-- The client rebuilds its list in SkillFrame_UpdateSkills: run after it.
|
|
if hooksecurefunc and type(_G["SkillFrame_UpdateSkills"]) == "function" then
|
|
hooksecurefunc("SkillFrame_UpdateSkills", layoutList)
|
|
end
|