AltSystem/BuildSkillsUI.lua
2026-05-15 14:46:30 +01:00

430 lines
16 KiB
Lua
Executable file

-- AltSystem Build Skills UI
-- Creates the Build Skills tab content with editable skill rows, add/delete functionality, and save to TRP.
AltSystem = AltSystem or {}
local PADDING = 12
local ROW_HEIGHT = 36
local ROW_SPACING = 8
local NAME_WIDTH = 340
local LEVEL_WIDTH = 140
local VALUE_WIDTH = 80
local DELETE_WIDTH = 30
-- Working copy of skills being edited (not saved until user clicks Save)
local editableSkills = {}
-- Pool of created row frames for reuse
local skillRowFrames = {}
-- References set during creation
local scrollFrame, scrollChild, addRowButton
-- ─── Helper: Create a flat dark input box matching the blocky design ────────
local function CreateFlatEditBox(name, parent, width)
local container = CreateFrame("Frame", name .. "Container", parent, "BackdropTemplate")
container:SetSize(width, 28)
container:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeSize = 1,
})
container:SetBackdropColor(0.12, 0.12, 0.12, 1)
container:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
local editBox = CreateFrame("EditBox", name, container)
editBox:SetPoint("LEFT", 8, 0)
editBox:SetPoint("RIGHT", -8, 0)
editBox:SetHeight(28)
editBox:SetAutoFocus(false)
editBox:SetFontObject("GameFontHighlight")
return container, editBox
end
-- ─── Helper: Create a flat dark dropdown button matching the blocky design ──
-- Shared globally so other files (UI.lua) can reuse it.
function AltSystem.CreateFlatDropdown(name, parent, width)
local btn = CreateFrame("Button", name, parent, "BackdropTemplate")
btn:SetSize(width, 28)
btn:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeSize = 1,
})
btn:SetBackdropColor(0.12, 0.12, 0.12, 1)
btn:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
-- Text label (left-aligned)
local text = btn:CreateFontString(nil, "OVERLAY", "GameFontHighlight")
text:SetPoint("LEFT", 8, 0)
text:SetPoint("RIGHT", -24, 0)
text:SetJustifyH("LEFT")
btn.label = text
-- Gold arrow icon (right side)
local arrow = btn:CreateTexture(nil, "OVERLAY")
arrow:SetSize(12, 12)
arrow:SetPoint("RIGHT", -6, 0)
arrow:SetTexture("Interface\\ChatFrame\\ChatFrameExpandArrow")
arrow:SetVertexColor(0.9, 0.75, 0.2, 1)
btn.arrow = arrow
-- Hover highlight
btn:SetScript("OnEnter", function(self)
self:SetBackdropColor(0.18, 0.18, 0.18, 1)
end)
btn:SetScript("OnLeave", function(self)
self:SetBackdropColor(0.12, 0.12, 0.12, 1)
end)
-- Menu storage
btn.menuSetup = nil
btn:SetScript("OnClick", function(self)
if self.menuSetup then
MenuUtil.CreateContextMenu(self, self.menuSetup)
end
end)
function btn:SetupMenu(setupFunc)
self.menuSetup = setupFunc
end
return btn
end
-- ─── Helper: Create a flat dark button matching the blocky design ────────────
-- Shared globally so other files (UI.lua) can reuse it.
function AltSystem.CreateFlatButton(name, parent, width, height, text)
local btn = CreateFrame("Button", name, parent, "BackdropTemplate")
btn:SetSize(width, height)
btn:SetBackdrop({
bgFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeFile = "Interface\\ChatFrame\\ChatFrameBackground",
edgeSize = 1,
})
btn:SetBackdropColor(0.4, 0.08, 0.08, 0.9)
btn:SetBackdropBorderColor(0.25, 0.25, 0.25, 1)
local label = btn:CreateFontString(nil, "OVERLAY", "GameFontNormal")
label:SetPoint("CENTER")
label:SetText(text or "")
btn.label = label
btn:SetScript("OnEnter", function(self)
self:SetBackdropColor(0.55, 0.12, 0.12, 0.9)
end)
btn:SetScript("OnLeave", function(self)
self:SetBackdropColor(0.4, 0.08, 0.08, 0.9)
end)
function btn:SetText(newText)
self.label:SetText(newText)
end
function btn:GetText()
return self.label:GetText()
end
return btn
end
-- ─── Create a single skill row frame ────────────────────────────────────────
local function CreateSkillRowFrame(index)
local row = CreateFrame("Frame", "AltSystemSkillRow" .. index, scrollChild)
row:SetHeight(ROW_HEIGHT)
-- Name EditBox (flat dark style)
local nameContainer, nameBox = CreateFlatEditBox("AltSystemSkillName" .. index, row, NAME_WIDTH)
nameContainer:SetPoint("LEFT", row, "LEFT", 8, 0)
row.nameBox = nameBox
row.nameContainer = nameContainer
-- Level Dropdown (flat dark style)
local levelDropdown = AltSystem.CreateFlatDropdown("AltSystemSkillLevel" .. index, row, LEVEL_WIDTH)
levelDropdown:SetPoint("LEFT", nameContainer, "RIGHT", 12, 0)
row.levelDropdown = levelDropdown
-- Value Dropdown (flat dark style)
local valueDropdown = AltSystem.CreateFlatDropdown("AltSystemSkillValue" .. index, row, VALUE_WIDTH)
valueDropdown:SetPoint("LEFT", levelDropdown, "RIGHT", 8, 0)
row.valueDropdown = valueDropdown
-- Delete Button (trash can icon with dark red background, matching design)
local deleteBtn = CreateFrame("Button", "AltSystemSkillDelete" .. index, row)
deleteBtn:SetSize(DELETE_WIDTH, DELETE_WIDTH)
deleteBtn:SetPoint("LEFT", valueDropdown, "RIGHT", 8, 0)
local deleteIcon = deleteBtn:CreateTexture(nil, "ARTWORK")
deleteIcon:SetSize(DELETE_WIDTH - 4, DELETE_WIDTH - 4)
deleteIcon:SetPoint("CENTER")
deleteIcon:SetTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
deleteBtn.icon = deleteIcon
-- Shimmer animation on hover
local shimmer = deleteBtn:CreateTexture(nil, "OVERLAY")
shimmer:SetSize(DELETE_WIDTH - 4, DELETE_WIDTH - 4)
shimmer:SetPoint("CENTER")
shimmer:SetTexture("Interface\\Buttons\\UI-GroupLoot-Pass-Up")
shimmer:SetBlendMode("ADD")
shimmer:SetAlpha(0)
deleteBtn.shimmer = shimmer
local shimmerAnim = shimmer:CreateAnimationGroup()
shimmerAnim:SetLooping("REPEAT")
local fadeIn = shimmerAnim:CreateAnimation("Alpha")
fadeIn:SetFromAlpha(0)
fadeIn:SetToAlpha(0.5)
fadeIn:SetDuration(0.5)
fadeIn:SetOrder(1)
local fadeOut = shimmerAnim:CreateAnimation("Alpha")
fadeOut:SetFromAlpha(0.5)
fadeOut:SetToAlpha(0)
fadeOut:SetDuration(0.5)
fadeOut:SetOrder(2)
deleteBtn.shimmerAnim = shimmerAnim
deleteBtn:SetScript("OnEnter", function(self)
self.shimmer:SetAlpha(0)
self.shimmerAnim:Play()
end)
deleteBtn:SetScript("OnLeave", function(self)
self.shimmerAnim:Stop()
self.shimmer:SetAlpha(0)
end)
row.deleteBtn = deleteBtn
return row
end
-- ─── Refresh all skill rows ─────────────────────────────────────────────────
local function RefreshSkillRows()
-- Hide all existing row frames
for _, row in ipairs(skillRowFrames) do
row:Hide()
end
local yPos = 0
for i, skillData in ipairs(editableSkills) do
local row = skillRowFrames[i]
if not row then
row = CreateSkillRowFrame(i)
skillRowFrames[i] = row
end
-- Clear previous anchor points before repositioning
row:ClearAllPoints()
row:SetPoint("TOPLEFT", scrollChild, "TOPLEFT", 0, -yPos)
row:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", 0, -yPos)
-- Populate name
row.nameBox:SetText(skillData.name or "")
row.nameBox:SetScript("OnTextChanged", function(self)
local idx = nil
for j, s in ipairs(editableSkills) do
if skillRowFrames[j] == row then
idx = j
break
end
end
if idx then
editableSkills[idx].name = self:GetText()
end
end)
-- Setup level dropdown
local currentRowIndex = i
row.levelDropdown.label:SetText(skillData.level or "Novice")
row.levelDropdown:SetupMenu(function(owner, rootDescription)
for _, levelName in ipairs(AltSystem.Data.SkillLevelOrder) do
rootDescription:CreateRadio(
levelName,
function()
return editableSkills[currentRowIndex] and editableSkills[currentRowIndex].level == levelName
end,
function()
if editableSkills[currentRowIndex] then
editableSkills[currentRowIndex].level = levelName
editableSkills[currentRowIndex].value = AltSystem.Data:GetDefaultValueForLevel(levelName)
RefreshSkillRows()
end
end
)
end
end)
-- Setup value dropdown based on current level
row.valueDropdown.label:SetText(tostring(skillData.value or 1))
local range = AltSystem.Data.SkillValueRanges[skillData.level]
if range then
row.valueDropdown:SetupMenu(function(owner, rootDescription)
for v = range.min, range.max do
rootDescription:CreateRadio(
tostring(v),
function()
return editableSkills[currentRowIndex] and editableSkills[currentRowIndex].value == v
end,
function()
if editableSkills[currentRowIndex] then
editableSkills[currentRowIndex].value = v
row.valueDropdown.label:SetText(tostring(v))
end
end
)
end
end)
end
-- Bind delete button for current index
row.deleteBtn:SetScript("OnClick", function()
table.remove(editableSkills, currentRowIndex)
RefreshSkillRows()
end)
row:Show()
yPos = yPos + ROW_HEIGHT + ROW_SPACING
end
-- Reposition Add A Row button
if addRowButton then
addRowButton:ClearAllPoints()
addRowButton:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", -PADDING, -(yPos + 4))
end
-- Update scroll child height
local totalHeight = yPos + ROW_HEIGHT + 20 -- extra space for Add A Row button
if scrollChild then
scrollChild:SetHeight(math.max(totalHeight, 1))
end
end
-- ─── Create Build Skills Content ────────────────────────────────────────────
function AltSystem:CreateBuildSkillsContent(parentFrame)
local contentWidth = parentFrame:GetWidth() or 692
local contentHeight = parentFrame:GetHeight() or 444
local yPos = -PADDING
-- Info text paragraph 1
local info1 = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
info1:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos)
info1:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -PADDING, yPos)
info1:SetJustifyH("LEFT")
info1:SetText("All skills below are directly extracted from your TRP's characteristics sheet. You can view and edit them there at any time.")
info1:SetTextColor(0.9, 0.75, 0.2)
info1:SetWordWrap(true)
yPos = yPos - (info1:GetStringHeight() or 16) - 10
-- Info text paragraph 2
local info2 = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
info2:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos)
info2:SetPoint("TOPRIGHT", parentFrame, "TOPRIGHT", -PADDING, yPos)
info2:SetJustifyH("LEFT")
info2:SetText("This menu serves as an easy alternative for if you want to use this system fast without diving deep into understanding it and/or styling your TRP sheet at this point in time. All your changes made will not be saved to TRP up until you hit the specific button to do so.")
info2:SetTextColor(0.9, 0.75, 0.2)
info2:SetWordWrap(true)
yPos = yPos - (info2:GetStringHeight() or 32) - 16
-- "Skill list" section header
local sectionHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormalLarge")
sectionHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos)
sectionHeader:SetText("Skill list")
--sectionHeader:SetTextColor(1, 1, 1)
yPos = yPos - 24
-- Column headers
local nameHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
nameHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8, yPos)
nameHeader:SetText("Name")
nameHeader:SetTextColor(0.9, 0.75, 0.2)
local levelHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
levelHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8 + NAME_WIDTH + 12, yPos)
levelHeader:SetText("Level")
levelHeader:SetTextColor(0.9, 0.75, 0.2)
local valueHeader = parentFrame:CreateFontString(nil, "OVERLAY", "GameFontNormal")
valueHeader:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING + 8 + NAME_WIDTH + 12 + LEVEL_WIDTH + 8, yPos)
valueHeader:SetText("Value")
valueHeader:SetTextColor(0.9, 0.75, 0.2)
yPos = yPos - 20
-- Save button (pinned to bottom, outside scroll frame)
local saveButton = AltSystem.CreateFlatButton("AltSystemSaveSkillsButton", parentFrame, 180, 30, "Save Skills to TRP")
saveButton:SetPoint("BOTTOM", parentFrame, "BOTTOM", 0, PADDING)
saveButton:SetScript("OnClick", function()
local success = AltSystem.Data:SaveSkills(editableSkills)
if success and AltSystem.RefreshSkillDropdown then
AltSystem:RefreshSkillDropdown()
end
end)
-- Scrollable skill list area (between column headers and save button)
scrollFrame = CreateFrame("ScrollFrame", "AltSystemBuildSkillsScrollFrame", parentFrame, "UIPanelScrollFrameTemplate")
scrollFrame:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", PADDING, yPos)
scrollFrame:SetPoint("BOTTOMRIGHT", parentFrame, "BOTTOMRIGHT", -PADDING - 22, saveButton:GetHeight() + PADDING + 8)
scrollChild = CreateFrame("Frame", "AltSystemBuildSkillsScrollChild", scrollFrame)
scrollChild:SetWidth(scrollFrame:GetWidth() or (contentWidth - PADDING * 2 - 22))
scrollChild:SetHeight(1)
scrollFrame:SetScrollChild(scrollChild)
-- Update scroll child width when frame resizes
scrollFrame:SetScript("OnSizeChanged", function(self)
scrollChild:SetWidth(self:GetWidth())
end)
-- "Add A Row" button (inside scroll child, below last row)
addRowButton = CreateFrame("Button", "AltSystemAddRowButton", scrollChild, "UIPanelButtonTemplate")
addRowButton:SetSize(130, 26)
addRowButton:SetText("+ Add A Row")
addRowButton:SetPoint("TOPRIGHT", scrollChild, "TOPRIGHT", -PADDING, 0)
-- Style add row button
local addBg = addRowButton:CreateTexture(nil, "BACKGROUND")
addBg:SetAllPoints()
addBg:SetColorTexture(0.4, 0.08, 0.08, 0.9)
addRowButton:SetScript("OnClick", function()
table.insert(editableSkills, {
name = "Skillname",
level = "Novice",
value = 1,
icon = "inv_misc_questionmark",
isNew = true,
})
RefreshSkillRows()
-- Auto-scroll to bottom to show the new row
C_Timer.After(0.05, function()
if scrollFrame then
scrollFrame:SetVerticalScroll(scrollFrame:GetVerticalScrollRange())
end
end)
end)
AltSystem.BuildSkillsScrollFrame = scrollFrame
AltSystem.BuildSkillsScrollChild = scrollChild
end
-- ─── Refresh Build Skills list (called on tab switch) ───────────────────────
function AltSystem:RefreshBuildSkillsList()
-- Reload skills from TRP3 into working copy, discarding any unsaved edits
editableSkills = AltSystem.Data:GetEditableSkills()
RefreshSkillRows()
end