mirror of
https://codeberg.org/Duvelcorp/MetaHunt.git
synced 2026-09-22 07:36:58 +00:00
404 lines
14 KiB
Lua
404 lines
14 KiB
Lua
------------------------------------------------------
|
|
-- MetaHunt Profile Management Options Panel
|
|
------------------------------------------------------
|
|
|
|
local MAX_PROFILE_ROWS = 15
|
|
local MAX_CHAR_ROWS = 10
|
|
local ROW_H = 26
|
|
|
|
-- Row pools — anonymous frames created once against scroll content, reused each refresh
|
|
local gProfileRows = {} -- [i] = { frame, nameText, infoText, loadBtn, delBtn }
|
|
local gCharRows = {} -- [i] = { frame, nameText, infoText, copyBtn }
|
|
|
|
-- Static element refs created once
|
|
local gActiveLabel = nil
|
|
local gSetupDone = false
|
|
|
|
-- Pending confirmations — globals so StaticPopup OnAccept can read them
|
|
MTH_PROFILE_PENDING_LOAD = nil
|
|
MTH_PROFILE_PENDING_DELETE = nil
|
|
MTH_PROFILE_PENDING_SAVE = nil
|
|
|
|
-- ── Static popup dialogs ───────────────────────────────────────────────────
|
|
|
|
StaticPopupDialogs["MTH_PROFILE_LOAD_CONFIRM"] = {
|
|
text = "Load profile \"%s\"?\n\nCurrent settings will be replaced.\nThe UI will reload to apply all changes.",
|
|
button1 = "Load & Reload",
|
|
button2 = "Cancel",
|
|
OnAccept = function()
|
|
if MTH_PROFILE_PENDING_LOAD then
|
|
local ok, err = MTH_Profile_Load(MTH_PROFILE_PENDING_LOAD)
|
|
MTH_PROFILE_PENDING_LOAD = nil
|
|
if ok then
|
|
ReloadUI()
|
|
elseif MTH and MTH.Print then
|
|
MTH:Print("Profile load failed: " .. tostring(err), "error")
|
|
end
|
|
end
|
|
end,
|
|
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
|
}
|
|
|
|
StaticPopupDialogs["MTH_PROFILE_DELETE_CONFIRM"] = {
|
|
text = "Delete profile \"%s\"?\n\nThis cannot be undone.",
|
|
button1 = "Delete",
|
|
button2 = "Cancel",
|
|
OnAccept = function()
|
|
if MTH_PROFILE_PENDING_DELETE then
|
|
MTH_Profile_Delete(MTH_PROFILE_PENDING_DELETE)
|
|
MTH_PROFILE_PENDING_DELETE = nil
|
|
MTH_SetupProfilesOptions()
|
|
end
|
|
end,
|
|
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
|
}
|
|
|
|
StaticPopupDialogs["MTH_PROFILE_SAVE_CONFIRM"] = {
|
|
text = "Profile \"%s\" already exists.\nOverwrite with current settings?",
|
|
button1 = "Overwrite",
|
|
button2 = "Cancel",
|
|
OnAccept = function()
|
|
if MTH_PROFILE_PENDING_SAVE then
|
|
MTH_Profile_Save(MTH_PROFILE_PENDING_SAVE)
|
|
MTH_PROFILE_PENDING_SAVE = nil
|
|
MTH_SetupProfilesOptions()
|
|
end
|
|
end,
|
|
timeout = 0, whileDead = 0, hideOnEscape = 1,
|
|
}
|
|
|
|
-- ── internal helpers ───────────────────────────────────────────────────────
|
|
|
|
local function PROF_TrimString(s)
|
|
if type(s) ~= "string" then return "" end
|
|
return string.gsub(s, "^%s*(.-)%s*$", "%1")
|
|
end
|
|
|
|
local function PROF_DoSave(rawName)
|
|
local name = PROF_TrimString(rawName)
|
|
if string.len(name) == 0 then
|
|
if MTH and MTH.Print then MTH:Print("Enter a profile name first.", "error") end
|
|
return
|
|
end
|
|
if string.len(name) > 50 then
|
|
if MTH and MTH.Print then MTH:Print("Profile name too long (50 chars max).", "error") end
|
|
return
|
|
end
|
|
local existing = type(MTH_SavedVariables) == "table"
|
|
and type(MTH_SavedVariables.profiles) == "table"
|
|
and MTH_SavedVariables.profiles[name] ~= nil
|
|
if existing then
|
|
MTH_PROFILE_PENDING_SAVE = name
|
|
StaticPopup_Show("MTH_PROFILE_SAVE_CONFIRM", name)
|
|
else
|
|
MTH_Profile_Save(name)
|
|
if MTH and MTH.Print then MTH:Print("Profile saved: " .. name) end
|
|
MTH_SetupProfilesOptions()
|
|
end
|
|
end
|
|
|
|
-- ── scroll frames + row pool creation (idempotent) ────────────────────────
|
|
|
|
local function PROF_EnsureScrollFrames(container)
|
|
if not MTH_GetFrame("MetaHuntOptionsProfilesScroll") then
|
|
local sf = CreateFrame("ScrollFrame", "MetaHuntOptionsProfilesScroll", container, "UIPanelScrollFrameTemplate")
|
|
local sc = CreateFrame("Frame", "MetaHuntOptionsProfilesContent", sf)
|
|
sc:SetWidth(510)
|
|
sc:SetHeight(MAX_PROFILE_ROWS * ROW_H + 4)
|
|
sf:SetScrollChild(sc)
|
|
end
|
|
if not MTH_GetFrame("MetaHuntOptionsCharScroll") then
|
|
local csf = CreateFrame("ScrollFrame", "MetaHuntOptionsCharScroll", container, "UIPanelScrollFrameTemplate")
|
|
local csc = CreateFrame("Frame", "MetaHuntOptionsCharContent", csf)
|
|
csc:SetWidth(510)
|
|
csc:SetHeight(MAX_CHAR_ROWS * ROW_H + 4)
|
|
csf:SetScrollChild(csc)
|
|
end
|
|
end
|
|
|
|
local function PROF_EnsureProfileRows()
|
|
local sc = MTH_GetFrame("MetaHuntOptionsProfilesContent")
|
|
if not sc then return end
|
|
for i = 1, MAX_PROFILE_ROWS do
|
|
if not gProfileRows[i] then
|
|
local row = CreateFrame("Frame", nil, sc)
|
|
row:SetHeight(ROW_H)
|
|
row:SetWidth(510)
|
|
row:SetPoint("TOPLEFT", sc, "TOPLEFT", 0, -(i - 1) * ROW_H)
|
|
|
|
local nameText = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
|
nameText:SetPoint("LEFT", row, "LEFT", 4, 2)
|
|
nameText:SetWidth(210)
|
|
nameText:SetJustifyH("LEFT")
|
|
|
|
local infoText = row:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
|
infoText:SetPoint("LEFT", row, "LEFT", 218, 2)
|
|
infoText:SetWidth(155)
|
|
infoText:SetJustifyH("LEFT")
|
|
infoText:SetTextColor(0.55, 0.55, 0.55)
|
|
|
|
local loadBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
|
loadBtn:SetWidth(58)
|
|
loadBtn:SetHeight(20)
|
|
loadBtn:SetPoint("RIGHT", row, "RIGHT", -62, 0)
|
|
loadBtn:SetText("Load")
|
|
|
|
local delBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
|
delBtn:SetWidth(58)
|
|
delBtn:SetHeight(20)
|
|
delBtn:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
|
delBtn:SetText("Delete")
|
|
|
|
gProfileRows[i] = { frame = row, nameText = nameText, infoText = infoText, loadBtn = loadBtn, delBtn = delBtn }
|
|
end
|
|
end
|
|
end
|
|
|
|
local function PROF_EnsureCharRows()
|
|
local csc = MTH_GetFrame("MetaHuntOptionsCharContent")
|
|
if not csc then return end
|
|
for i = 1, MAX_CHAR_ROWS do
|
|
if not gCharRows[i] then
|
|
local row = CreateFrame("Frame", nil, csc)
|
|
row:SetHeight(ROW_H)
|
|
row:SetWidth(510)
|
|
row:SetPoint("TOPLEFT", csc, "TOPLEFT", 0, -(i - 1) * ROW_H)
|
|
|
|
local nameText = row:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall")
|
|
nameText:SetPoint("LEFT", row, "LEFT", 4, 2)
|
|
nameText:SetWidth(250)
|
|
nameText:SetJustifyH("LEFT")
|
|
nameText:SetTextColor(0.70, 0.85, 1.00)
|
|
|
|
local infoText = row:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
|
infoText:SetPoint("LEFT", row, "LEFT", 258, 2)
|
|
infoText:SetWidth(120)
|
|
infoText:SetJustifyH("LEFT")
|
|
infoText:SetTextColor(0.55, 0.55, 0.55)
|
|
|
|
local copyBtn = CreateFrame("Button", nil, row, "UIPanelButtonTemplate")
|
|
copyBtn:SetWidth(80)
|
|
copyBtn:SetHeight(20)
|
|
copyBtn:SetPoint("RIGHT", row, "RIGHT", 0, 0)
|
|
copyBtn:SetText("Import")
|
|
|
|
gCharRows[i] = { frame = row, nameText = nameText, infoText = infoText, copyBtn = copyBtn }
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ── dynamic refresh functions ──────────────────────────────────────────────
|
|
|
|
local function PROF_RefreshActive()
|
|
if not gActiveLabel then return end
|
|
local active = type(MTH_Profile_GetActive) == "function" and MTH_Profile_GetActive()
|
|
if active then
|
|
gActiveLabel:SetText("Active: |cffffd200" .. active .. "|r")
|
|
gActiveLabel:SetTextColor(0.90, 0.90, 0.90)
|
|
else
|
|
gActiveLabel:SetText("Active: |cff888888Custom (unsaved)|r")
|
|
gActiveLabel:SetTextColor(0.90, 0.90, 0.90)
|
|
end
|
|
gActiveLabel:Show()
|
|
end
|
|
|
|
local function PROF_RefreshProfileList()
|
|
PROF_EnsureProfileRows()
|
|
local profiles = type(MTH_Profile_GetList) == "function" and MTH_Profile_GetList() or {}
|
|
local active = type(MTH_Profile_GetActive) == "function" and MTH_Profile_GetActive()
|
|
local n = table.getn(profiles)
|
|
|
|
local sc = MTH_GetFrame("MetaHuntOptionsProfilesContent")
|
|
if sc then
|
|
sc:SetHeight(math.max(n * ROW_H + 4, ROW_H))
|
|
end
|
|
|
|
for i = 1, MAX_PROFILE_ROWS do
|
|
local row = gProfileRows[i]
|
|
if not row then break end
|
|
local name = profiles[i]
|
|
if name then
|
|
-- Name (with active indicator)
|
|
if name == active then
|
|
row.nameText:SetText(name .. " |cff00cc44[active]|r")
|
|
else
|
|
row.nameText:SetText(name)
|
|
end
|
|
-- Info: date saved by who
|
|
local p = type(MTH_SavedVariables) == "table"
|
|
and type(MTH_SavedVariables.profiles) == "table"
|
|
and MTH_SavedVariables.profiles[name]
|
|
local info = ""
|
|
if type(p) == "table" then
|
|
if p.savedAt and p.savedAt ~= "" then
|
|
info = p.savedAt
|
|
end
|
|
end
|
|
row.infoText:SetText(info)
|
|
-- Wire buttons (SetScript replaces previous handler each refresh)
|
|
local rowName = name
|
|
row.loadBtn:SetScript("OnClick", function()
|
|
MTH_PROFILE_PENDING_LOAD = rowName
|
|
StaticPopup_Show("MTH_PROFILE_LOAD_CONFIRM", rowName)
|
|
end)
|
|
row.delBtn:SetScript("OnClick", function()
|
|
MTH_PROFILE_PENDING_DELETE = rowName
|
|
StaticPopup_Show("MTH_PROFILE_DELETE_CONFIRM", rowName)
|
|
end)
|
|
row.frame:Show()
|
|
row.loadBtn:Show()
|
|
row.delBtn:Show()
|
|
else
|
|
row.frame:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
local function PROF_RefreshCharList()
|
|
PROF_EnsureCharRows()
|
|
local chars = type(MTH_Profile_GetCharList) == "function" and MTH_Profile_GetCharList() or {}
|
|
local n = table.getn(chars)
|
|
|
|
local csc = MTH_GetFrame("MetaHuntOptionsCharContent")
|
|
if csc then
|
|
csc:SetHeight(math.max(n * ROW_H + 4, ROW_H))
|
|
end
|
|
|
|
for i = 1, MAX_CHAR_ROWS do
|
|
local row = gCharRows[i]
|
|
if not row then break end
|
|
local charKey = chars[i]
|
|
if charKey then
|
|
local snap = type(MTH_SavedVariables) == "table"
|
|
and type(MTH_SavedVariables.charSnapshots) == "table"
|
|
and MTH_SavedVariables.charSnapshots[charKey]
|
|
local info = (type(snap) == "table" and snap.savedAt) and snap.savedAt or ""
|
|
row.nameText:SetText(charKey)
|
|
row.infoText:SetText(info)
|
|
local ck = charKey
|
|
row.copyBtn:SetScript("OnClick", function()
|
|
local ok, pName = MTH_Profile_CopyFromChar(ck)
|
|
if ok then
|
|
MTH_SetupProfilesOptions()
|
|
if MTH and MTH.Print then
|
|
MTH:Print('Imported "' .. ck .. '" as profile "' .. tostring(pName) .. '".')
|
|
end
|
|
end
|
|
end)
|
|
row.frame:Show()
|
|
row.copyBtn:Show()
|
|
else
|
|
row.frame:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
-- ── main setup (static elements created once, dynamic lists refreshed every call) ──
|
|
|
|
function MTH_SetupProfilesOptions()
|
|
local container = MTH_GetFrame("MetaHuntOptionsProfiles")
|
|
if not container then return end
|
|
|
|
if not gSetupDone then
|
|
-- Title
|
|
local title = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
title:SetPoint("TOPLEFT", container, "TOPLEFT", 6, -6)
|
|
title:SetText("Profiles")
|
|
title:SetTextColor(1.00, 0.82, 0.00)
|
|
|
|
-- Active profile indicator
|
|
gActiveLabel = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
|
gActiveLabel:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -4)
|
|
gActiveLabel:SetWidth(540)
|
|
gActiveLabel:SetJustifyH("LEFT")
|
|
|
|
-- ── Save As section ───────────────────────────────────────────────
|
|
local saveHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
saveHdr:SetPoint("TOPLEFT", gActiveLabel, "BOTTOMLEFT", 0, -10)
|
|
saveHdr:SetText("Save current settings as:")
|
|
saveHdr:SetTextColor(0.90, 0.90, 0.90)
|
|
|
|
local eb = CreateFrame("EditBox", "MTH_ProfileNameInput", container, "InputBoxTemplate")
|
|
eb:SetWidth(240)
|
|
eb:SetHeight(20)
|
|
eb:SetMaxLetters(50)
|
|
eb:SetAutoFocus(false)
|
|
eb:SetPoint("TOPLEFT", saveHdr, "BOTTOMLEFT", 2, -6)
|
|
eb:SetScript("OnEnterPressed", function()
|
|
PROF_DoSave(this:GetText())
|
|
this:SetText("")
|
|
this:ClearFocus()
|
|
end)
|
|
eb:SetScript("OnEscapePressed", function()
|
|
this:SetText("")
|
|
this:ClearFocus()
|
|
end)
|
|
|
|
local saveBtn = CreateFrame("Button", "MTH_ProfileSaveBtn", container, "UIPanelButtonTemplate")
|
|
saveBtn:SetWidth(70)
|
|
saveBtn:SetHeight(20)
|
|
saveBtn:SetPoint("LEFT", eb, "RIGHT", 6, 0)
|
|
saveBtn:SetText("Save")
|
|
saveBtn:SetScript("OnClick", function()
|
|
local box = MTH_GetFrame("MTH_ProfileNameInput")
|
|
if box then
|
|
PROF_DoSave(box:GetText())
|
|
box:SetText("")
|
|
box:ClearFocus()
|
|
end
|
|
end)
|
|
|
|
-- ── Saved Profiles section ────────────────────────────────────────
|
|
local profHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
profHdr:SetPoint("TOPLEFT", eb, "BOTTOMLEFT", -2, -12)
|
|
profHdr:SetText("Saved Profiles")
|
|
profHdr:SetTextColor(1.00, 0.82, 0.00)
|
|
|
|
local rule1 = container:CreateTexture(nil, "ARTWORK")
|
|
rule1:SetPoint("TOPLEFT", profHdr, "BOTTOMLEFT", 0, -3)
|
|
rule1:SetWidth(540)
|
|
rule1:SetHeight(1)
|
|
rule1:SetTexture(0.30, 0.30, 0.30, 0.80)
|
|
|
|
PROF_EnsureScrollFrames(container)
|
|
|
|
local sf = MTH_GetFrame("MetaHuntOptionsProfilesScroll")
|
|
sf:SetPoint("TOPLEFT", rule1, "BOTTOMLEFT", 0, -4)
|
|
sf:SetWidth(530)
|
|
sf:SetHeight(170)
|
|
sf:Show()
|
|
|
|
-- ── Copy from Character section ───────────────────────────────────
|
|
local charHdr = container:CreateFontString(nil, "ARTWORK", "GameFontNormal")
|
|
charHdr:SetPoint("TOPLEFT", sf, "BOTTOMLEFT", 0, -10)
|
|
charHdr:SetText("Copy from Character")
|
|
charHdr:SetTextColor(1.00, 0.82, 0.00)
|
|
|
|
local charNote = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall")
|
|
charNote:SetPoint("TOPLEFT", charHdr, "BOTTOMLEFT", 0, -2)
|
|
charNote:SetWidth(540)
|
|
charNote:SetJustifyH("LEFT")
|
|
charNote:SetText("Snapshots are saved automatically on logout. Click Import to create a profile from one.")
|
|
charNote:SetTextColor(0.60, 0.60, 0.60)
|
|
|
|
local rule2 = container:CreateTexture(nil, "ARTWORK")
|
|
rule2:SetPoint("TOPLEFT", charNote, "BOTTOMLEFT", 0, -3)
|
|
rule2:SetWidth(540)
|
|
rule2:SetHeight(1)
|
|
rule2:SetTexture(0.30, 0.30, 0.30, 0.80)
|
|
|
|
local csf = MTH_GetFrame("MetaHuntOptionsCharScroll")
|
|
csf:SetPoint("TOPLEFT", rule2, "BOTTOMLEFT", 0, -4)
|
|
csf:SetWidth(530)
|
|
csf:SetHeight(140)
|
|
csf:Show()
|
|
|
|
gSetupDone = true
|
|
end
|
|
|
|
-- Refresh dynamic content every time the panel is opened
|
|
PROF_RefreshActive()
|
|
PROF_RefreshProfileList()
|
|
PROF_RefreshCharList()
|
|
end
|