-- 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 -- ─── 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 = CreateFrame("Button", "AltSystemSaveSkillsButton", parentFrame, "UIPanelButtonTemplate") saveButton:SetSize(180, 30) saveButton:SetPoint("BOTTOM", parentFrame, "BOTTOM", 0, PADDING) saveButton:SetText("Save Skills to TRP") -- Style the save button with a dark red tint local saveBg = saveButton:CreateTexture(nil, "BACKGROUND") saveBg:SetAllPoints() saveBg:SetColorTexture(0.4, 0.08, 0.08, 0.9) 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