diff --git a/CHANGELOG.md b/CHANGELOG.md index b94b21a..509e5b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,9 +9,9 @@ Resurrecting on Octowow. ### Added -- **Pet Auto-Teach — Growl**: When you tame a new beast, MetaHunt now offers to teach it Growl at the best rank for the pet's level. Click **Yes** and it opens the Beast Training window, teaches the ability and closes the window for you — no manual trip to the pet trainer needed. Toggle it in **Options → General → Pet Auto-Teach** ("Activate Auto-Teach of Growl (Max rank)", on by default) or with `/mth growl on|off`. +- **Pet Auto-Teach — Growl**: When you tame a new beast, MetaHunt now offers to teach it Growl at the best rank for the pet's level. Click **Yes** and it opens the Beast Training window, teaches the ability and closes the window automatically. -- **Animations**: MetaHunt now plays optional animations that make the interface feel alive. A firework-and-banner celebration bursts on screen when your pet learns a new ability or a new rank, and a red shaking banner warns you when a pet runs away for good. Your pet button flashes when its happiness changes, it gains a loyalty level, or you feed it; the ammo button flashes when Smart Ammo swaps your bullets or auto-equips after a weapon swap; and the MM Widget pulses on a Lock & Load proc. The Great Book slides up and fades in when you open it, gently fades its list when you switch tabs or run a search, fades beast models into view, and fades out when closed; stable slots flash when a pet is deposited, withdrawn or swapped. The options window slides in when opened, fades its tabs as you switch them, and fades out when closed. The minimap button pulses when clicked, minimap markers ping as beasts come into range, collapsible zButton bars fade their buttons in as they expand, the zBar fades in when enabled, and world-map markers fade in when you locate a beast, vendor or master. Everything is optional and off-switchable: a new **Animations** tab (Options → Animations) lets you toggle each effect on its own — or disable them all at once. Powered by the PizzaSauce animation library. +- **Animations**: MetaHunt now plays optional animations that make the interface feel alive. Powered by the PizzaSauce library of [Pizzahawaii](https://codeberg.org/Pizzahawaii/) ! - **zTrack — Find Fish**: Added new spell Find Fish to the zTrack list of trackings. @@ -19,8 +19,6 @@ Resurrecting on Octowow. ### Changed -- **zButton bars — expand animation**: Collapsible zButton flyout bars and the zBar now fade in together when they open, instead of stacking their buttons one after another, so expanding them feels smoother. - - **Complete revamp of SavedVariables**: All your settings now live in one tidy place instead of being scattered around. A migration happens the first time you log in, and you shouldn't lose any settings. diff --git a/api/options-animations.lua b/api/options-animations.lua index 9513d7a..0bba53f 100644 --- a/api/options-animations.lua +++ b/api/options-animations.lua @@ -80,102 +80,71 @@ function MTH_SetupAnimationsOptions() return end - MTH_ClearContainer(container) + local panel = MTH_Layout.Panel(container) + panel:Title(MTH_ANIM_L("ANIM_TITLE", "Animations")) + panel:Note(MTH_ANIM_L("ANIM_CREDIT", + "Animations are powered by PizzaSauce, a WoW 1.12 animation library kindly made and shared by Pizzahawaii. Thank you!"), + { color = MTH_Layout.Theme.colText, lines = 2 }) + panel:Link("MetaHuntOptionsAnimLink", MTH_ANIM_LINK, function() MTH_ANIM_InsertLink(MTH_ANIM_LINK) end) + MTH_ANIM_CTRL = {} - local title = container:CreateFontString("MetaHuntOptionsAnimationsTitle", "ARTWORK", "GameFontNormal") - title:SetPoint("TOPLEFT", container, "TOPLEFT", 10, -10) - title:SetText(MTH_ANIM_L("ANIM_TITLE", "Animations")) - title:SetTextColor(1.00, 0.82, 0.00) - - local credit = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") - credit:SetPoint("TOPLEFT", title, "BOTTOMLEFT", 0, -8) - credit:SetWidth(460) - credit:SetJustifyH("LEFT") - credit:SetText(MTH_ANIM_L("ANIM_CREDIT", - "Animations are powered by PizzaSauce, a WoW 1.12 animation library kindly made and shared by Pizzahawaii. Thank you!")) - credit:SetTextColor(0.93, 0.93, 0.93) - - local link = CreateFrame("Button", nil, container) - link:SetPoint("TOPLEFT", credit, "BOTTOMLEFT", 0, -4) - link:SetHeight(16) - link:SetWidth(460) - local linkText = link:CreateFontString(nil, "ARTWORK", "GameFontHighlightSmall") - linkText:SetPoint("LEFT", link, "LEFT", 0, 0) - linkText:SetJustifyH("LEFT") - linkText:SetText(MTH_ANIM_LINK) - linkText:SetTextColor(0.40, 0.75, 1.00) - link:SetScript("OnClick", function() MTH_ANIM_InsertLink(MTH_ANIM_LINK) end) - link:SetScript("OnEnter", function() linkText:SetTextColor(0.60, 0.90, 1.00) end) - link:SetScript("OnLeave", function() linkText:SetTextColor(0.40, 0.75, 1.00) end) - - -- Global master toggle. - local master = MTH_CreateCheckbox(container, "MetaHuntOptionsAnimMaster", - MTH_ANIM_L("ANIM_ENABLE_ALL", "Enable animations"), -70) - if master then - master:SetChecked(MTH_FX_AnimationsEnabled()) - master:SetScript("OnClick", function() - MTH_FX_SetAnimationsEnabled(master:GetChecked()) + -- Global master toggle (full width, above the columns). + panel:TopCheckbox("MetaHuntOptionsAnimMaster", + MTH_ANIM_L("ANIM_ENABLE_ALL", "Enable animations"), { + checked = MTH_FX_AnimationsEnabled, + onClick = function() + local cb = getglobal("MetaHuntOptionsAnimMaster") + MTH_FX_SetAnimationsEnabled(cb:GetChecked()) MTH_ANIM_UpdateChildStates() - end) - end + end, + }) - -- Per-animation checkboxes, grouped by section, laid out in two columns. - -- Each whole section is placed into whichever column is currently shorter - -- (largest remaining Y), so the two columns stay balanced as defs grow. - local rowStep = 26 - local sectionGap = 12 - local columns = { - { headerX = 15, cbX = 28, y = -104 }, - { headerX = 300, cbX = 313, y = -104 }, - } + -- Per-animation checkboxes: each section is a self-sizing box, dropped into + -- whichever column currently has the most room so the two stay balanced. local s = 1 while MTH_FX_ANIM_SECTIONS and s <= table.getn(MTH_FX_ANIM_SECTIONS) do local sec = MTH_FX_ANIM_SECTIONS[s] - -- Pick the shorter column (the one whose cursor is nearest the top). - local col = columns[1] - if columns[2].y > columns[1].y then - col = columns[2] - end - - local header = container:CreateFontString(nil, "ARTWORK", "GameFontNormal") - header:SetPoint("TOPLEFT", container, "TOPLEFT", col.headerX, col.y) - header:SetText(MTH_ANIM_L(sec.titleKey, sec.title)) - header:SetTextColor(1.00, 0.82, 0.00) - col.y = col.y - 22 - + -- Collect this section's defs in order. + local secDefs = {} local d = 1 while MTH_FX_ANIM_DEFS and d <= table.getn(MTH_FX_ANIM_DEFS) do local def = MTH_FX_ANIM_DEFS[d] if def.section == sec.id then - local cbName = "MetaHuntOptionsAnim_" .. string.gsub(def.key, "%.", "_") - local cb = MTH_CreateCheckbox(container, cbName, MTH_ANIM_L(def.labelKey, def.label), col.y, col.cbX) - if cb then - cb.mthKey = def.key - cb:SetChecked(MTH_FX_GetFlag(def.key)) - cb:SetScript("OnClick", function() - MTH_FX_SetFlag(cb.mthKey, cb:GetChecked()) - end) - if def.tooltip and def.tooltip ~= "" then - cb.mthTooltip = MTH_ANIM_L(def.tooltipKey, def.tooltip) - cb:SetScript("OnEnter", function() - GameTooltip:SetOwner(cb, "ANCHOR_RIGHT") - GameTooltip:SetText(cb.mthTooltip, 1, 1, 1, 1, true) - GameTooltip:Show() - end) - cb:SetScript("OnLeave", function() GameTooltip:Hide() end) - end - table.insert(MTH_ANIM_CTRL, cb) - col.y = col.y - rowStep - end + table.insert(secDefs, def) end d = d + 1 end - col.y = col.y - sectionGap + local count = table.getn(secDefs) + + if count > 0 then + local box = panel:Box(panel:ShorterSide(), + "MetaHuntOptionsAnimBox_" .. tostring(sec.id), + MTH_ANIM_L(sec.titleKey, sec.title)) + + local di = 1 + while di <= count do + local def = secDefs[di] + local cbName = "MetaHuntOptionsAnim_" .. string.gsub(def.key, "%.", "_") + local cb = box:Checkbox(cbName, MTH_ANIM_L(def.labelKey, def.label), { + checked = function() return MTH_FX_GetFlag(def.key) end, + tooltip = (def.tooltip and def.tooltip ~= "") and MTH_ANIM_L(def.tooltipKey, def.tooltip) or nil, + }) + if cb then + cb.mthKey = def.key + cb:SetScript("OnClick", function() + MTH_FX_SetFlag(cb.mthKey, cb:GetChecked()) + end) + table.insert(MTH_ANIM_CTRL, cb) + end + di = di + 1 + end + end s = s + 1 end + panel:Finish() MTH_ANIM_UpdateChildStates() MTH_ANIM_DBG.setupResult = "ok" end diff --git a/api/options-autobuy.lua b/api/options-autobuy.lua index d95c5be..21ae22d 100644 --- a/api/options-autobuy.lua +++ b/api/options-autobuy.lua @@ -589,7 +589,9 @@ local function MTH_AB_RefreshOptionsUI() end end -local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset) +local function MTH_AB_CreateRow(box, subtype, index) + local f, y = box:Row(38) + local padX = box:PadX() local row = { subtype = subtype, index = index, @@ -597,14 +599,15 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset) } local nameBase = "MetaHuntOptionsAutoBuy_" .. subtype .. "_" .. tostring(index) - row.check = MTH_CreateCheckbox(container, nameBase .. "Check", "", yOffset, xBase + MTH_AB_LAYOUT.ROW_CHECK_X) - row.label = container:CreateFontString(nameBase .. "Label", "ARTWORK", "GameFontNormalSmall") - row.label:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_CHECK_LABEL_X, yOffset - 5) + row.check = MTH_CreateCheckbox(f, nameBase .. "Check", "", y, padX) + row.label = getglobal(nameBase .. "Label") or f:CreateFontString(nameBase .. "Label", "ARTWORK", "GameFontNormalSmall") + row.label:ClearAllPoints() + row.label:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 27, y - 5) row.label:SetText("On?") - row.dropdown = CreateFrame("Frame", nameBase .. "Drop", container, "UIDropDownMenuTemplate") + row.dropdown = getglobal(nameBase .. "Drop") or CreateFrame("Frame", nameBase .. "Drop", f, "UIDropDownMenuTemplate") row.dropdown:ClearAllPoints() - row.dropdown:SetPoint("TOPLEFT", container, "TOPLEFT", xBase + MTH_AB_LAYOUT.ROW_DROPDOWN_X, yOffset + 3) + row.dropdown:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 33, y + 3) if UIDropDownMenu_SetWidth then UIDropDownMenu_SetWidth(MTH_AB_LAYOUT.ROW_DROPDOWN_WIDTH, row.dropdown) end @@ -640,7 +643,8 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset) end) end - row.qty = CreateFrame("EditBox", nameBase .. "Qty", container, "InputBoxTemplate") + row.qty = getglobal(nameBase .. "Qty") or CreateFrame("EditBox", nameBase .. "Qty", f, "InputBoxTemplate") + row.qty:ClearAllPoints() local dropButton = getglobal(nameBase .. "DropButton") if dropButton then row.qty:SetPoint("LEFT", dropButton, "RIGHT", MTH_AB_LAYOUT.ROW_QTY_GAP, -1) @@ -652,7 +656,8 @@ local function MTH_AB_CreateRow(container, subtype, index, xBase, yOffset) row.qty:SetNumeric(true) row.qty:SetAutoFocus(false) - row.suffix = container:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall") + row.suffix = getglobal(nameBase .. "Suffix") or f:CreateFontString(nameBase .. "Suffix", "ARTWORK", "GameFontNormalSmall") + row.suffix:ClearAllPoints() row.suffix:SetPoint("LEFT", row.qty, "RIGHT", MTH_AB_LAYOUT.ROW_SUFFIX_GAP, 0) row.suffix:SetText("Stacks") @@ -687,32 +692,18 @@ local function MTH_AB_BuildUI(container) return end - MTH_ClearContainer(container) + local panel = MTH_Layout.Panel(container) MTH_AB_STATE.container = container MTH_AB_STATE.built = true local controls = MTH_AB_STATE.controls - controls.title = container:CreateFontString("MetaHuntOptionsAutoBuyTitle", "ARTWORK", "GameFontHighlight") - if controls.title then - if controls.title.SetPoint then controls.title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) end - if controls.title.SetText then controls.title:SetText("Auto Buy") end - end + panel:Title("Auto Buy") + panel:Note("Configure automatic buying rules that trigger when you open a vendor. Quantity is how many STACKS of the item you want in your bags after purchase.", + { lines = 2, color = MTH_Layout.Theme.colText }) - controls.body = container:CreateFontString("MetaHuntOptionsAutoBuyBody", "ARTWORK", "GameFontNormalSmall") - if controls.body then - if controls.body.SetPoint then controls.body:SetPoint("TOPLEFT", container, "TOPLEFT", 176, -30) end - if controls.body.SetWidth then controls.body:SetWidth(400) end - if controls.body.SetJustifyH then controls.body:SetJustifyH("LEFT") end - if controls.body.SetTextColor then controls.body:SetTextColor(1, 1, 1) end - if controls.body.SetText then - controls.body:SetText("Configure automatic buying rules that triggers when you open a vendor.\nThe quantity refers to how many STACKS of the item you want to have in your bags after your purchase") - end - end - - controls.moduleEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyModuleEnabled", "Enable Auto Buy module", -30) - if controls.moduleEnabled then - controls.moduleEnabled:SetScript("OnClick", function() + controls.moduleEnabled = panel:TopCheckbox("MetaHuntOptionsAutoBuyModuleEnabled", "Enable Auto Buy module", { + onClick = function() if not this or MTH_AB_STATE.syncing then return end if MTH and MTH.SetModuleEnabled then local ok, err = MTH:SetModuleEnabled("autobuy", MTH_AB_IsChecked(this)) @@ -721,70 +712,44 @@ local function MTH_AB_BuildUI(container) end end MTH_AB_RefreshOptionsUI() - end) - end + end, + }) - controls.projectilesEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyProjectilesEnabled", "Enable for Projectiles", -74) - if controls.projectilesEnabled then - controls.projectilesEnabled:SetScript("OnClick", function() + controls.projectilesEnabled = panel:TopCheckbox("MetaHuntOptionsAutoBuyProjectilesEnabled", "Enable for Projectiles (arrows & bullets)", { + onClick = function() if not this or MTH_AB_STATE.syncing then return end local store = MTH_AB_EnsureConfig() store.projectiles.enabled = MTH_AB_IsChecked(this) MTH_AB_RefreshOptionsUI() - end) - end - - controls.sectionArrowTitle = container:CreateFontString("MetaHuntOptionsAutoBuyArrowsTitle", "ARTWORK", "GameFontHighlight") - if controls.sectionArrowTitle then - if controls.sectionArrowTitle.SetPoint then - controls.sectionArrowTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y) - end - if controls.sectionArrowTitle.SetText then controls.sectionArrowTitle:SetText("Arrows") end - end - - controls.sectionBulletTitle = container:CreateFontString("MetaHuntOptionsAutoBuyBulletsTitle", "ARTWORK", "GameFontHighlight") - if controls.sectionBulletTitle then - if controls.sectionBulletTitle.SetPoint then - controls.sectionBulletTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.RIGHT_SECTION_X, MTH_AB_LAYOUT.SECTION_TITLE_Y) - end - if controls.sectionBulletTitle.SetText then controls.sectionBulletTitle:SetText("Bullets") end - end + end, + }) + -- ===== LEFT: Arrows / RIGHT: Bullets (3 rules each) ===== + local arrowBox = panel:Box("left", "MetaHuntOptionsAutoBuyArrowsBox", "Arrows") for i = 1, 3 do - local rowY = MTH_AB_LAYOUT.SECTION_ROWS_Y0 - ((i - 1) * MTH_AB_LAYOUT.ROW_STEP) - controls.rows.arrow[i] = MTH_AB_CreateRow(container, "arrow", i, MTH_AB_LAYOUT.LEFT_SECTION_X, rowY) - controls.rows.bullet[i] = MTH_AB_CreateRow(container, "bullet", i, MTH_AB_LAYOUT.RIGHT_SECTION_X, rowY) + controls.rows.arrow[i] = MTH_AB_CreateRow(arrowBox, "arrow", i) end - controls.petFoodTitle = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodTitle", "ARTWORK", "GameFontHighlight") - if controls.petFoodTitle then - controls.petFoodTitle:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X, -300) - controls.petFoodTitle:SetText("Pet Food") + local bulletBox = panel:Box("right", "MetaHuntOptionsAutoBuyBulletsBox", "Bullets") + for i = 1, 3 do + controls.rows.bullet[i] = MTH_AB_CreateRow(bulletBox, "bullet", i) end - controls.petFoodEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodEnabled", "Enable for Pet Food", -322, MTH_AB_LAYOUT.LEFT_SECTION_X) - if controls.petFoodEnabled then - controls.petFoodEnabled:SetScript("OnClick", function() + -- ===== Pet Food ===== + local petBox = panel:Box(panel:ShorterSide(), "MetaHuntOptionsAutoBuyPetFoodBox", "Pet Food") + + controls.petFoodEnabled = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodEnabled", "Enable for Pet Food", { + onClick = function() if not this or MTH_AB_STATE.syncing then return end local store = MTH_AB_EnsureConfig() store.petFood.enabled = MTH_AB_IsChecked(this) MTH_AB_RefreshOptionsUI() - end) - end + end, + }) - controls.petFoodQtyLabel = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtyLabel", "ARTWORK", "GameFontNormalSmall") - if controls.petFoodQtyLabel then - controls.petFoodQtyLabel:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, -350) - controls.petFoodQtyLabel:SetText("Quantity :") - end - - controls.petFoodQty = CreateFrame("EditBox", "MetaHuntOptionsAutoBuyPetFoodQty", container, "InputBoxTemplate") + controls.petFoodQty = petBox:EditRow("MetaHuntOptionsAutoBuyPetFoodQtyLabel", "Quantity:", + "MetaHuntOptionsAutoBuyPetFoodQty", 40, { numeric = true, height = 20 }) if controls.petFoodQty then - controls.petFoodQty:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 95, -347) - controls.petFoodQty:SetWidth(40) - controls.petFoodQty:SetHeight(20) - controls.petFoodQty:SetNumeric(true) - controls.petFoodQty:SetAutoFocus(false) controls.petFoodQty:SetScript("OnEnterPressed", function() if not this then return end local store = MTH_AB_EnsureConfig() @@ -798,43 +763,52 @@ local function MTH_AB_BuildUI(container) MTH_AB_SetPetFoodStacks(store, this:GetText()) MTH_AB_RefreshOptionsUI() end) + local suffix = getglobal("MetaHuntOptionsAutoBuyPetFoodQtySuffix") + if not suffix then + suffix = petBox:Frame():CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtySuffix", "ARTWORK", "GameFontNormalSmall") + end + suffix:ClearAllPoints() + suffix:SetPoint("LEFT", controls.petFoodQty, "RIGHT", 8, 0) + suffix:SetText("stacks") + controls.petFoodQtySuffix = suffix end - controls.petFoodQtySuffix = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodQtySuffix", "ARTWORK", "GameFontNormalSmall") - if controls.petFoodQtySuffix then - controls.petFoodQtySuffix:SetPoint("LEFT", controls.petFoodQty, "RIGHT", 8, 0) - controls.petFoodQtySuffix:SetText("stacks") - end - - controls.petFoodScopeCurrent = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeCurrent", "Buy only for my current pet", -376, MTH_AB_LAYOUT.LEFT_SECTION_X + 28) - if controls.petFoodScopeCurrent then - controls.petFoodScopeCurrent:SetScript("OnClick", function() + controls.petFoodScopeCurrent = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodScopeCurrent", "Buy only for my current pet", { + onClick = function() if not this or MTH_AB_STATE.syncing then return end local store = MTH_AB_EnsureConfig() store.petFood.scope = "current" MTH_AB_RefreshOptionsUI() - end) - end + end, + }) - controls.petFoodScopeAll = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoBuyPetFoodScopeAll", "Buy for all my pets", -402, MTH_AB_LAYOUT.LEFT_SECTION_X + 28) - if controls.petFoodScopeAll then - controls.petFoodScopeAll:SetScript("OnClick", function() + controls.petFoodScopeAll = petBox:Checkbox("MetaHuntOptionsAutoBuyPetFoodScopeAll", "Buy for all my pets", { + onClick = function() if not this or MTH_AB_STATE.syncing then return end local store = MTH_AB_EnsureConfig() store.petFood.scope = "all" MTH_AB_RefreshOptionsUI() - end) - end + end, + }) controls.petFoodPetLines = {} for i = 1, 5 do - local rowY = -434 - ((i - 1) * 22) - local icon = container:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK") + local f, y = petBox:Row(22) + local padX = petBox:PadX() + local icon = getglobal("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i)) + if not icon then + icon = f:CreateTexture("MetaHuntOptionsAutoBuyPetFoodPetIcon" .. tostring(i), "ARTWORK") + end icon:SetWidth(16) icon:SetHeight(16) - icon:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_AB_LAYOUT.LEFT_SECTION_X + 28, rowY) + icon:ClearAllPoints() + icon:SetPoint("TOPLEFT", f, "TOPLEFT", padX + 4, y) icon:SetTexture("Interface\\Icons\\INV_Misc_QuestionMark") - local text = container:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall") + local text = getglobal("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i)) + if not text then + text = f:CreateFontString("MetaHuntOptionsAutoBuyPetFoodPetText" .. tostring(i), "ARTWORK", "GameFontNormalSmall") + end + text:ClearAllPoints() text:SetPoint("LEFT", icon, "RIGHT", 6, 0) text:SetJustifyH("LEFT") text:SetTextColor(1, 1, 1) @@ -842,6 +816,7 @@ local function MTH_AB_BuildUI(container) controls.petFoodPetLines[i] = { icon = icon, text = text } end + panel:Finish() end function MTH_SetupAutoBuyOptions() diff --git a/api/options-autoquest.lua b/api/options-autoquest.lua index 13c7a5f..21f4288 100644 --- a/api/options-autoquest.lua +++ b/api/options-autoquest.lua @@ -48,19 +48,10 @@ function MTH_SetupAutoQuestOptions() local container = MTH_GetFrame("MetaHuntOptionsAutoQuest") if not container then return end - MTH_ClearContainer(container) - - local title = container:CreateFontString("MetaHuntOptionsAutoQuestTitle", "ARTWORK", "GameFontHighlight") - title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) - title:SetText("Auto Quest") - - local body = container:CreateFontString("MetaHuntOptionsAutoQuestBody", "ARTWORK", "GameFontNormalSmall") - body:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -30) - body:SetWidth(560) - body:SetJustifyH("LEFT") - body:SetJustifyV("TOP") - body:SetTextColor(0.45, 0.75, 1) - body:SetText("Automation for specific quest interactions.") + local store = MTH_AQ_EnsureStore() + if store.scorpokTooltip == nil then + store.scorpokTooltip = false + end local moduleEnabled = true if MTH and MTH.GetModule then @@ -73,10 +64,14 @@ function MTH_SetupAutoQuestOptions() elseif MTH and MTH.IsModuleEnabled then moduleEnabled = MTH:IsModuleEnabled("autoquest", false) and true or false end - local moduleToggle = MTH_CreateCheckbox(container, "MetaHuntOptionsAutoQuestModuleToggle", "Enable Auto Quest module", -70, 20) - if moduleToggle then - moduleToggle:SetChecked(moduleEnabled and true or false) - moduleToggle:SetScript("OnClick", function() + + local panel = MTH_Layout.Panel(container) + panel:Title("Auto Quest") + panel:Note("Automation for specific quest interactions.") + + panel:TopCheckbox("MetaHuntOptionsAutoQuestModuleToggle", "Enable Auto Quest module", { + checked = moduleEnabled, + onClick = function() if not this then return end local enabled = MTH_AQ_IsChecked(this) if MTH and MTH.SetModuleEnabled then @@ -88,145 +83,66 @@ function MTH_SetupAutoQuestOptions() local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") or nil local actual = (type(module) == "table" and module.enabled ~= nil) and (module.enabled and true or false) or enabled this:SetChecked(actual and true or false) - end) - end + end, + }) - local store = MTH_AQ_EnsureStore() - if store.scorpokTooltip == nil then - store.scorpokTooltip = false - end - - local sectionWidth = (container:GetWidth() or 600) - 40 - if sectionWidth < 360 then - sectionWidth = 360 - end - - local function ensureSection(name, sectionTitle, yOffset, height) - local section = getglobal(name) - if not section then - section = CreateFrame("Frame", name, container, "OptionFrameBoxTemplate") - end - if not section then - return nil - end - section:SetParent(container) - section:ClearAllPoints() - section:SetPoint("TOPLEFT", container, "TOPLEFT", 20, yOffset) - section:SetWidth(sectionWidth) - section:SetHeight(height) - section:Show() - - local titleFrame = getglobal(name .. "Title") - if titleFrame then - titleFrame:SetText(sectionTitle) - end - return section - end - - local function ensureCheckbox(section, name, label, yOffset, checked) - local check = getglobal(name) - if not check then - check = CreateFrame("CheckButton", name, section, "OptionsCheckButtonTemplate") - end - if not check then - return nil - end - check:SetParent(section) - check:ClearAllPoints() - check:SetPoint("TOPLEFT", section, "TOPLEFT", 12, yOffset) - check:SetChecked(checked and true or false) - check:Show() - - local text = getglobal(name .. "Text") - if text then - text:SetText(label) - end - return check - end - - local scorpokSection = ensureSection("MetaHuntAutoQuestScorpokBox", "Salt of the Scorpok", -130, 112) - if scorpokSection then - local scorpokToggle = ensureCheckbox( - scorpokSection, - "MetaHuntOptionsAutoQuestScorpokToggle", - "Enable SHIFT-rightclick for Bloodmage Drazial", - -10, - store.scorpokDrazial and true or false - ) - if scorpokToggle then - scorpokToggle:SetScript("OnClick", function() - local enabled = MTH_AQ_IsChecked(this) - store.scorpokDrazial = enabled and true or false - if MTH and MTH.GetModuleCharSavedVariables then - local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") - if type(moduleStore) == "table" then - moduleStore.scorpokDrazial = store.scorpokDrazial and true or false - end + -- Salt of the Scorpok (left column) + local scorpok = panel:Box("left", "MetaHuntAutoQuestScorpokBox", "Salt of the Scorpok") + scorpok:Checkbox("MetaHuntOptionsAutoQuestScorpokToggle", + "Enable SHIFT-rightclick for Bloodmage Drazial", { + checked = store.scorpokDrazial and true or false, + onClick = function() + local enabled = MTH_AQ_IsChecked(this) + store.scorpokDrazial = enabled and true or false + if MTH and MTH.GetModuleCharSavedVariables then + local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") + if type(moduleStore) == "table" then + moduleStore.scorpokDrazial = store.scorpokDrazial and true or false end - local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") - if module and module.SetScorpokDrazialEnabled then - module:SetScorpokDrazialEnabled(enabled) + end + local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") + if module and module.SetScorpokDrazialEnabled then + module:SetScorpokDrazialEnabled(enabled) + end + end, + }) + scorpok:Checkbox("MetaHuntOptionsAutoQuestScorpokTooltipToggle", + "Enhance Tooltip on Drazial and related beasts (requires Tooltips module enabled)", { + checked = store.scorpokTooltip and true or false, + onClick = function() + local enabled = MTH_AQ_IsChecked(this) + store.scorpokTooltip = enabled and true or false + if MTH and MTH.GetModuleCharSavedVariables then + local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") + if type(moduleStore) == "table" then + moduleStore.scorpokTooltip = store.scorpokTooltip and true or false end - end) - end + end + end, + }) - local tooltipToggle = ensureCheckbox( - scorpokSection, - "MetaHuntOptionsAutoQuestScorpokTooltipToggle", - "Enhance Tooltip on Drazial and related beasts (requires Tooltips module enabled)", - -42, - store.scorpokTooltip and true or false - ) - if tooltipToggle then - tooltipToggle:SetScript("OnClick", function() - local enabled = MTH_AQ_IsChecked(this) - store.scorpokTooltip = enabled and true or false - if MTH and MTH.GetModuleCharSavedVariables then - local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") - if type(moduleStore) == "table" then - moduleStore.scorpokTooltip = store.scorpokTooltip and true or false - end + -- Arrows Are For Sissies (right column) + local sissies = panel:Box("right", "MetaHuntAutoQuestSissiesBox", "Arrows Are For Sissies") + sissies:Note("MetaHuntOptionsAutoQuestSissiesNote", + "Note that you should not activate this if you are using LazyPig", { lines = 2 }) + sissies:Checkbox("MetaHuntOptionsAutoQuestSissiesToggle", + "Enable SHIFT-rightclick for Artilleryman Sheldonore", { + checked = store.arrowsForSissies and true or false, + onClick = function() + local enabled = MTH_AQ_IsChecked(this) + store.arrowsForSissies = enabled and true or false + if MTH and MTH.GetModuleCharSavedVariables then + local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") + if type(moduleStore) == "table" then + moduleStore.arrowsForSissies = store.arrowsForSissies and true or false end - end) - end - end + end + local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") + if module and module.SetArrowsForSissiesEnabled then + module:SetArrowsForSissiesEnabled(enabled) + end + end, + }) - local sissiesSection = ensureSection("MetaHuntAutoQuestSissiesBox", "Arrows Are For Sissies", -255, 108) - if sissiesSection then - local sissiesNote = getglobal("MetaHuntOptionsAutoQuestSissiesNote") - if not sissiesNote then - sissiesNote = sissiesSection:CreateFontString("MetaHuntOptionsAutoQuestSissiesNote", "ARTWORK", "GameFontNormalSmall") - end - sissiesNote:ClearAllPoints() - sissiesNote:SetPoint("TOPLEFT", sissiesSection, "TOPLEFT", 14, -10) - sissiesNote:SetWidth(sectionWidth - 28) - sissiesNote:SetJustifyH("LEFT") - sissiesNote:SetTextColor(0.45, 0.75, 1) - sissiesNote:SetText("Note that you should not activate this if you are using LazyPig") - sissiesNote:Show() - - local sissiesToggle = ensureCheckbox( - sissiesSection, - "MetaHuntOptionsAutoQuestSissiesToggle", - "Enable SHIFT-rightclick for Artilleryman Sheldonore", - -40, - store.arrowsForSissies and true or false - ) - if sissiesToggle then - sissiesToggle:SetScript("OnClick", function() - local enabled = MTH_AQ_IsChecked(this) - store.arrowsForSissies = enabled and true or false - if MTH and MTH.GetModuleCharSavedVariables then - local moduleStore = MTH:GetModuleCharSavedVariables("autoquest") - if type(moduleStore) == "table" then - moduleStore.arrowsForSissies = store.arrowsForSissies and true or false - end - end - local module = MTH and MTH.GetModule and MTH:GetModule("autoquest") - if module and module.SetArrowsForSissiesEnabled then - module:SetArrowsForSissiesEnabled(enabled) - end - end) - end - end + panel:Finish() end diff --git a/api/options-chronometer.lua b/api/options-chronometer.lua index 446c3cf..2a5c097 100644 --- a/api/options-chronometer.lua +++ b/api/options-chronometer.lua @@ -358,25 +358,14 @@ local function MTH_CHRON_NormalizeSection(tabKey) return "general" end -local function MTH_CHRON_RenderTimerSection(container, profile, title, items, bucket, opts) - opts = opts or {} - local timerX = opts.x or 20 - local timerY = opts.y or -84 - local prefix = tostring(opts.prefix or "Main") - - MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTimersHeader" .. prefix, title, timerX, timerY) - timerY = timerY - 24 - +local function MTH_CHRON_RenderTimerBox(box, profile, items, bucket, prefix) + prefix = tostring(prefix or "Main") if table.getn(items) == 0 then - MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerSectionEmpty" .. prefix, "(none)", timerX + 8, timerY) + box:Text("MetaHuntOptionsChronometerSectionEmpty" .. prefix, "(none)") return end for i = 1, table.getn(items) do - if timerY < -560 then - MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerOverflow" .. prefix, "...more", timerX + 8, timerY) - break - end local row = items[i] local timerName = row local label = row @@ -387,11 +376,9 @@ local function MTH_CHRON_RenderTimerSection(container, profile, title, items, bu timerName = tostring(timerName or "") label = tostring(label or timerName) local controlName = "MetaHuntOptionsChronometerTimer" .. prefix .. tostring(bucket) .. tostring(i) - local check = MTH_CreateCheckbox(container, controlName, label, timerY, timerX) - if check then - check:SetChecked(profile.disabledSpells[bucket][timerName] == nil and true or false) - check:SetScript("OnClick", function() - if not this then return end + box:Checkbox(controlName, label, { + checked = profile.disabledSpells[bucket][timerName] == nil, + onClick = function() local currentlyDisabled = profile.disabledSpells[bucket][timerName] ~= nil if currentlyDisabled then profile.disabledSpells[bucket][timerName] = nil @@ -402,9 +389,8 @@ local function MTH_CHRON_RenderTimerSection(container, profile, title, items, bu end MTH_CHRON_ApplyLiveProfile(profile) - end) - end - timerY = timerY - 20 + end, + }) end end @@ -461,210 +447,185 @@ function MTH_SetupChronometerOptions(tabKey) local container = MTH_GetFrame("MetaHuntOptionsChronometer") if not container then return end - MTH_ClearContainer(container) MTH_CHRON_STATE.controls = {} local profile = MTH_CHRON_GetProfile() local _, class = UnitClass("player") class = class or "HUNTER" - MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerTitle", "Chronometer", 20, -10) + local panel = MTH_Layout.Panel(container) + panel:Title("Chronometer") - local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", -54) - if moduleCheck then + local moduleEnabled = false + do local module = MTH and MTH:GetModule("chronometer") - moduleCheck:SetChecked(module and module.enabled and true or false) - moduleCheck:SetScript("OnClick", function() - if not this then return end - local enabled = MTH_CHRON_IsChecked(this) + moduleEnabled = module and module.enabled and true or false + end + panel:TopCheckbox("MetaHuntOptionsChronometerEnabled", "Enable Chronometer module", { + checked = moduleEnabled, + onClick = function() + local enabled = this:GetChecked() == 1 if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("chronometer", enabled) end - end) - end + end, + }) if section == "general" then - MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerGeneralHeader", "General", 20, -84) - - local ghostSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerGhost", "Ghost Duration", 0, 30, 1, -112) - if ghostSlider then - ghostSlider:SetValue(tonumber(profile.ghost) or 0) - ghostSlider.onChange = function(value) + local genBox = panel:Box("left", "MetaHuntOptionsChronometerGeneralBox", "General") + genBox:Slider("MetaHuntOptionsChronometerGhost", { + label = "Ghost Duration", min = 0, max = 30, step = 1, + value = tonumber(profile.ghost) or 0, + onChange = function(value) profile.ghost = tonumber(value) or 0 MTH_CHRON_ApplyLiveProfile(profile) - end - end - - local killCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnKill", "Fade on kill", -154) - if killCheck then - killCheck:SetChecked(profile.fadeonkill and true or false) - killCheck:SetScript("OnClick", function() - if not this then return end - profile.fadeonkill = MTH_CHRON_IsChecked(this) - end) - end - - local fadeCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerFadeOnFade", "Fade on aura fade", -179) - if fadeCheck then - fadeCheck:SetChecked(profile.fadeonfade and true or false) - fadeCheck:SetScript("OnClick", function() - if not this then return end - profile.fadeonfade = MTH_CHRON_IsChecked(this) - end) - end - - local selfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerSelfBars", "Show self bars", -204) - if selfCheck then - selfCheck:SetChecked(profile.selfbars and true or false) - selfCheck:SetScript("OnClick", function() - if not this then return end - profile.selfbars = MTH_CHRON_IsChecked(this) - end) - end - - local onlySelfCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerOnlySelf", "Only self target", -229) - if onlySelfCheck then - onlySelfCheck:SetChecked(profile.onlyself and true or false) - onlySelfCheck:SetScript("OnClick", function() - if not this then return end - profile.onlyself = MTH_CHRON_IsChecked(this) - end) - end + end, + }) + genBox:Checkbox("MetaHuntOptionsChronometerFadeOnKill", "Fade on kill", { + checked = profile.fadeonkill and true or false, + onClick = function() profile.fadeonkill = this:GetChecked() == 1 end, + }) + genBox:Checkbox("MetaHuntOptionsChronometerFadeOnFade", "Fade on aura fade", { + checked = profile.fadeonfade and true or false, + onClick = function() profile.fadeonfade = this:GetChecked() == 1 end, + }) + genBox:Checkbox("MetaHuntOptionsChronometerSelfBars", "Show self bars", { + checked = profile.selfbars and true or false, + onClick = function() profile.selfbars = this:GetChecked() == 1 end, + }) + genBox:Checkbox("MetaHuntOptionsChronometerOnlySelf", "Only self target", { + checked = profile.onlyself and true or false, + onClick = function() profile.onlyself = this:GetChecked() == 1 end, + }) elseif section == "bar" then - MTH_CHRON_MakeTitle(container, "MetaHuntOptionsChronometerBarHeader", "Bars", 20, -84) - - local runTestButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerRunTest", "Run Test", 20, -112, 130, 22, function() - local engine = MTH_ChronometerHunter - if engine and engine.RunTest then - engine:RunTest() - end - end) - local anchorButton = MTH_CreateActionButton(container, "MetaHuntOptionsChronometerAnchor", "Toggle Anchor", 165, -112, 130, 22, function() - local engine = MTH_ChronometerHunter - if engine and engine.ToggleAnchor then - engine:ToggleAnchor() - end - end) - if runTestButton then runTestButton:Show() end - if anchorButton then anchorButton:Show() end - + local ctrlBox = panel:Box("left", "MetaHuntOptionsChronometerControlsBox", "Controls") + ctrlBox:Buttons({ + { name = "MetaHuntOptionsChronometerRunTest", label = "Run Test", width = 120, + onClick = function() + local engine = MTH_ChronometerHunter + if engine and engine.RunTest then engine:RunTest() end + end }, + { name = "MetaHuntOptionsChronometerAnchor", label = "Toggle Anchor", width = 120, + onClick = function() + local engine = MTH_ChronometerHunter + if engine and engine.ToggleAnchor then engine:ToggleAnchor() end + end }, + }) local anchorX = tonumber(profile.barposition and profile.barposition.x) or 0 local anchorY = tonumber(profile.barposition and profile.barposition.y) or 0 - MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerAnchorPos", "Anchor: X " .. tostring(anchorX) .. " Y " .. tostring(anchorY), 20, -142) + ctrlBox:Note("MetaHuntOptionsChronometerAnchorPos", + "Anchor: X " .. tostring(anchorX) .. " Y " .. tostring(anchorY)) - local widthSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarWidth", "Bar Width", 80, 320, 1, -180) - if widthSlider then - widthSlider:SetValue(tonumber(profile.barwidth) or 220) - widthSlider.onChange = function(value) + local dimBox = panel:Box("left", "MetaHuntOptionsChronometerDimBox", "Dimensions") + dimBox:Slider("MetaHuntOptionsChronometerBarWidth", { + label = "Bar Width", min = 80, max = 320, step = 1, + value = tonumber(profile.barwidth) or 220, + onChange = function(value) profile.barwidth = tonumber(value) or 220 MTH_CHRON_ApplyLiveProfile(profile) - end - end - - local scaleSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarScale", "Bar Scale", 0.5, 1.5, 0.1, -222) - if scaleSlider then - scaleSlider:SetValue(tonumber(profile.barscale) or 1) - scaleSlider.onChange = function(value) + end, + }) + dimBox:Slider("MetaHuntOptionsChronometerBarScale", { + label = "Bar Scale", min = 0.5, max = 1.5, step = 0.1, + value = tonumber(profile.barscale) or 1, + format = function(v) return string.format("%.1f", v) end, + onChange = function(value) profile.barscale = tonumber(value) or 1 MTH_CHRON_ApplyLiveProfile(profile) - end - end - - local heightSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarHeight", "Bar Height", 8, 30, 1, -264) - if heightSlider then - heightSlider:SetValue(tonumber(profile.barheight) or 16) - heightSlider.onChange = function(value) + end, + }) + dimBox:Slider("MetaHuntOptionsChronometerBarHeight", { + label = "Bar Height", min = 8, max = 30, step = 1, + value = tonumber(profile.barheight) or 16, + onChange = function(value) profile.barheight = tonumber(value) or 16 MTH_CHRON_ApplyLiveProfile(profile) - end - end - - local spacingSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerBarSpacing", "Bar Spacing", 0, 15, 1, -306) - if spacingSlider then - spacingSlider:SetValue(tonumber(profile.spacing) or 0) - spacingSlider.onChange = function(value) + end, + }) + dimBox:Slider("MetaHuntOptionsChronometerBarSpacing", { + label = "Bar Spacing", min = 0, max = 15, step = 1, + value = tonumber(profile.spacing) or 0, + onChange = function(value) profile.spacing = tonumber(value) or 0 MTH_CHRON_ApplyLiveProfile(profile) - end - end - - local textSizeSlider = MTH_CreateSlider(container, "MetaHuntOptionsChronometerTextSize", "Text Size", 8, 20, 1, -348) - if textSizeSlider then - textSizeSlider:SetValue(tonumber(profile.textsize) or 10) - textSizeSlider.onChange = function(value) + end, + }) + dimBox:Slider("MetaHuntOptionsChronometerTextSize", { + label = "Text Size", min = 8, max = 20, step = 1, + value = tonumber(profile.textsize) or 10, + onChange = function(value) profile.textsize = tonumber(value) or 10 MTH_CHRON_ApplyLiveProfile(profile) - end - end + end, + }) - MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerTextPatternLabel", "Bar Text ($s spell, $t target)", 20, -390) - MTH_CHRON_MakeInput(container, "MetaHuntOptionsChronometerTextPattern", 20, -408, 220, 20, profile.text or "$t", function(newText) - if not newText or newText == "" then - newText = "$t" - end - profile.text = tostring(newText) - MTH_CHRON_ApplyLiveProfile(profile) - end) - - local growCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerGrowUp", "Grow bars upward", -438) - if growCheck then - growCheck:SetChecked(profile.growup and true or false) - growCheck:SetScript("OnClick", function() - if not this then return end - profile.growup = MTH_CHRON_IsChecked(this) + local textBox = panel:Box("right", "MetaHuntOptionsChronometerTextBox", "Text & Growth") + textBox:Note("MetaHuntOptionsChronometerTextPatternHint", + "Bar text tokens: $s = spell, $t = target", { lines = 2 }) + local patternEdit = textBox:EditRow("MetaHuntOptionsChronometerTextPatternLabel", "Bar Text", + "MetaHuntOptionsChronometerTextPattern", 150, { height = 20 }) + if patternEdit then + patternEdit:SetText(tostring(profile.text or "$t")) + patternEdit:SetScript("OnEnterPressed", function() + local newText = this:GetText() or "" + if newText == "" then newText = "$t" end + profile.text = tostring(newText) MTH_CHRON_ApplyLiveProfile(profile) + this:ClearFocus() end) end - - local reverseCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerReverse", "Reverse bars", -463) - if reverseCheck then - reverseCheck:SetChecked(profile.reverse and true or false) - reverseCheck:SetScript("OnClick", function() - if not this then return end - profile.reverse = MTH_CHRON_IsChecked(this) + textBox:Checkbox("MetaHuntOptionsChronometerGrowUp", "Grow bars upward", { + checked = profile.growup and true or false, + onClick = function() + profile.growup = this:GetChecked() == 1 MTH_CHRON_ApplyLiveProfile(profile) - end) - end + end, + }) + textBox:Checkbox("MetaHuntOptionsChronometerReverse", "Reverse bars", { + checked = profile.reverse and true or false, + onClick = function() + profile.reverse = this:GetChecked() == 1 + MTH_CHRON_ApplyLiveProfile(profile) + end, + }) - MTH_CHRON_MakeSmall(container, "MetaHuntOptionsChronometerIconPosLabel", "Icon Position", 20, -494) - local iconLeft = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconLeft", "Left", -514) - local iconRight = MTH_CreateCheckbox(container, "MetaHuntOptionsChronometerIconRight", "Right", -538) - if iconLeft and iconRight then - iconLeft:SetChecked(profile.iconposition ~= "RIGHT") - iconRight:SetChecked(profile.iconposition == "RIGHT") - iconLeft:SetScript("OnClick", function() + local iconBox = panel:Box("right", "MetaHuntOptionsChronometerIconBox", "Icon Position") + local iconLeft, iconRight + iconLeft = iconBox:Checkbox("MetaHuntOptionsChronometerIconLeft", "Left", { + checked = profile.iconposition ~= "RIGHT", + onClick = function() profile.iconposition = "LEFT" iconLeft:SetChecked(true) iconRight:SetChecked(nil) MTH_CHRON_ApplyLiveProfile(profile) - end) - iconRight:SetScript("OnClick", function() + end, + }) + iconRight = iconBox:Checkbox("MetaHuntOptionsChronometerIconRight", "Right", { + checked = profile.iconposition == "RIGHT", + onClick = function() profile.iconposition = "RIGHT" iconRight:SetChecked(true) iconLeft:SetChecked(nil) MTH_CHRON_ApplyLiveProfile(profile) - end) - end - + end, + }) else local classSpells, classEvents, racial = MTH_CHRON_BuildTimerLists() if section == "classspells" then - MTH_CHRON_RenderTimerSection(container, profile, "Hunter Spells", classSpells, class, { - x = 20, - y = -84, - prefix = "ClassSpells", - }) - MTH_CHRON_RenderTimerSection(container, profile, "Hunter Trinkets", MTH_CHRON_BuildHunterTrinketItems(classEvents), class, { - x = 300, - y = -84, - prefix = "ClassTrinkets", - }) + local spellsBox = panel:Box("left", "MetaHuntOptionsChronometerSpellsBox", "Hunter Spells") + MTH_CHRON_RenderTimerBox(spellsBox, profile, classSpells, class, "ClassSpells") + local trinketBox = panel:Box("right", "MetaHuntOptionsChronometerTrinketsBox", "Hunter Trinkets") + MTH_CHRON_RenderTimerBox(trinketBox, profile, MTH_CHRON_BuildHunterTrinketItems(classEvents), class, "ClassTrinkets") elseif section == "classevents" then - MTH_CHRON_RenderTimerSection(container, profile, "Hunter Events", classEvents, class, { prefix = "ClassEvents" }) + local eventsBox = panel:Box("left", "MetaHuntOptionsChronometerEventsBox", "Hunter Events") + MTH_CHRON_RenderTimerBox(eventsBox, profile, classEvents, class, "ClassEvents") elseif section == "racial" then - MTH_CHRON_RenderTimerSection(container, profile, "Racial", racial, "RACIAL", { prefix = "Racial" }) + local racialBox = panel:Box("left", "MetaHuntOptionsChronometerRacialBox", "Racial") + MTH_CHRON_RenderTimerBox(racialBox, profile, racial, "RACIAL", "Racial") end end + + panel:Finish() end if _G then diff --git a/api/options-credits.lua b/api/options-credits.lua index 25603ac..f729e2d 100644 --- a/api/options-credits.lua +++ b/api/options-credits.lua @@ -96,12 +96,8 @@ function MTH_SetupCreditsOptions() local container = MTH_GetFrame("MetaHuntOptionsCredits") if not container then return end - MTH_ClearContainer(container) - - local title = container:CreateFontString("MetaHuntOptionsCreditsTitle", "ARTWORK", "GameFontNormal") - title:SetPoint("TOPLEFT", container, "TOPLEFT", 10, -10) - title:SetText(MTH_CR_L("CREDITS_TITLE", "Credits")) - title:SetTextColor(1.00, 0.82, 0.00) + local panel = MTH_Layout.Panel(container) + local title = panel:Title(MTH_CR_L("CREDITS_TITLE", "Credits")) local scrollFrame = MTH_GetFrame("MetaHuntOptionsCreditsScroll") if not scrollFrame then @@ -124,12 +120,12 @@ function MTH_SetupCreditsOptions() cursor = MTH_CreditsCreateText(content, cursor, MTH_CR_L("CREDITS_ABOUT_CODE_INTRO", "This addon is, in others, a compilation of old vanilla addons that I loved and played with during years. But most of them had become very buggy with TwoW as new contain was added to the server. \n I fixed, reworked, enhanced, and melted them within a modern modular framework that I created.\n" - .. "I want to make it crystal clear about what work is mine, and what is not and respectfully credit the original authors for the great stuff I've taken from them:"), + .. "I want to make it clear about what work is mine, and what is not and respectfully credit the original authors for the great stuff I've taken from them:"), "GameFontNormalSmall", -10) cursor = MTH_CreditsCreateText(content, cursor, MTH_CR_L("CREDITS_ABOUT_ZHUNTER", - "- zBars, Antidaze and Autostrip were core functionalities of Vanilla zHunterMod addon. And I kept the \"z\" naming of bars/buttons to always remember it. On top of this I have created myself zBars, zAmmo, zCompanions, zMount, zToys, zCraft.\nAlso the SmartAmmo feature idea is coming from Zhuntermod: there was a file in it with that functionality, but was like a work-in-progress, totally unfunctional and even \"dangerous\" in its current state, and was not active in the addon. Since it was a fucking great idea, I recoded it mostly from scratch and made it work reliably and safely."), + "- zBars, Antidaze and Autostrip were core functionalities of Vanilla zHunterMod addon. And I kept the \"z\" naming of bars/buttons to always remember it. I improve it and added zBar, zAmmo, zCompanions, zMount, zToys, zCraft.\nAlso the SmartAmmo feature idea is coming from Zhuntermod: there was a file in it with that functionality, but was like a work-in-progress, totally unfunctional and even \"dangerous\" in its current state, and was not active in the addon. Since it was a fucking great idea, I recoded it mostly from scratch and made it work reliably and safely."), "GameFontNormalSmall", -10) cursor = MTH_CreditsCreateText(content, cursor, @@ -189,7 +185,7 @@ function MTH_SetupCreditsOptions() "GameFontNormalSmall", -10) cursor = MTH_CreditsCreateText(content, cursor, - MTH_CR_L("CREDITS_CLOSING", "With that being said, go back hunting fellas! Preferably on Octowow."), + MTH_CR_L("CREDITS_CLOSING", ""), "GameFontNormalSmall", -10, 1.00, 0.82, 0.00) cursor = MTH_CreditsCreateText(content, cursor, diff --git a/api/options-expammo.lua b/api/options-expammo.lua index 69b0651..75fc179 100644 --- a/api/options-expammo.lua +++ b/api/options-expammo.lua @@ -15,16 +15,7 @@ local function MTH_EA_OPT_GetCfg() return MTH_SavedVariables.modules["expammo"] or {} end -local MTH_EA_OPT_built = false local MTH_EA_OPT_STATE = { bindStatus = nil, bindButton = nil, captureFrame = nil } - --- ── Layout constants ────────────────────────────────────────── --- Two equal columns separated by a gutter. Values are offsets from the --- container TOPLEFT corner (positive X right, negative Y down). -local OPT_L = 20 -- left column X -local OPT_R = 300 -- right column X -local OPT_CW = 255 -- column width (used for text wrapping and sliders) - local function MTH_EA_OPT_GetBindText() if not GetBindingKey then return "Unbound" end local k1, k2 = GetBindingKey("EXPAMMO ACTION") @@ -119,271 +110,168 @@ local function MTH_EA_OPT_StartCapture() if bb then bb:SetText("Press key...") end end --- Helper: checkbox anchored to a given X column -local function MTH_EA_OPT_CB(container, name, label, yOff, xOff, isChecked, onClick) - local cb = MTH_CreateCheckbox(container, name, label, yOff, xOff or OPT_L) - if cb then - cb:SetChecked(isChecked and 1 or 0) - cb:SetScript("OnClick", function() - local v = this:GetChecked() - onClick(v == 1 or v == true) - end) - end - return cb -end - --- Helper: section header label -local function MTH_EA_OPT_Header(container, text, yOff, xOff) - local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormal") - fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff) - fs:SetText("|cffff9900" .. text .. "|r") - return fs -end - --- Helper: small descriptive text -local function MTH_EA_OPT_Tip(container, text, yOff, xOff, r, g, b) - local fs = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") - fs:SetPoint("TOPLEFT", container, "TOPLEFT", xOff or OPT_L, yOff) - fs:SetWidth(OPT_CW) - fs:SetJustifyH("LEFT") - fs:SetTextColor(r or 0.6, g or 0.6, b or 0.6) - fs:SetText(text) - return fs -end - --- Enable or disable a list of widgets (supports Frames/Buttons and FontStrings) -local function MTH_EA_OPT_SetGroupEnabled(widgets, enabled) - for _, w in ipairs(widgets) do - if w then - if w.Enable and w.Disable then - -- Frame / Button / CheckButton - if enabled then w:Enable() else w:Disable() end - end - -- Dim all (including FontStrings and frames) via alpha - if w.SetAlpha then - w:SetAlpha(enabled and 1.0 or 0.35) - end - end - end -end - local function MTH_EA_OPT_BuildUI(container) - if MTH_EA_OPT_built then return end - MTH_EA_OPT_built = true - local cfg = MTH_EA_OPT_GetCfg() local showHint = cfg.showHint ~= false - --------------------------------------------------------------------------- - -- Title - --------------------------------------------------------------------------- - local title = container:CreateFontString(nil, "ARTWORK", "GameFontHighlightLarge") - title:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L, -16) - title:SetText("MM Widget — Experimental Ammunition Tracker") + local panel = MTH_Layout.Panel(container) + panel:Title("MM Widget — Experimental Ammunition Tracker") - --------------------------------------------------------------------------- - -- LEFT COLUMN - --------------------------------------------------------------------------- - - -- ── Module ────────────────────────────────────────────── - MTH_EA_OPT_Header(container, "Module", -46) + -- Right-column gating: dim the box frames and disable their interactive + -- controls when the "Show action hint cell" checkbox is off. + local rightBoxes = {} + local rightControls = {} + local function setRightEnabled(enabled) + local i + for i = 1, table.getn(rightBoxes) do + local f = rightBoxes[i] + if f and f.SetAlpha then f:SetAlpha(enabled and 1.0 or 0.35) end + end + for i = 1, table.getn(rightControls) do + local c = rightControls[i] + if c and c.Enable and c.Disable then + if enabled then c:Enable() else c:Disable() end + end + end + end + -- ===== LEFT: Module ===== + local moduleBox = panel:Box("left", "MTH_ExpAmmoModuleBox", "Module") local moduleEnabled = true if MTH and MTH.IsModuleEnabled then moduleEnabled = MTH:IsModuleEnabled("expammo", true) and true or false end - MTH_EA_OPT_CB(container, "MTH_ExpAmmoModuleCB", - "Enable MM Widget module", - -66, OPT_L, moduleEnabled, - function(v) + moduleBox:Checkbox("MTH_ExpAmmoModuleCB", "Enable MM Widget module", { + checked = moduleEnabled, + onClick = function() + local v = this:GetChecked() == 1 if MTH and MTH.SetModuleEnabled then MTH:SetModuleEnabled("expammo", v) end if MTH_ExpAmmo then MTH_ExpAmmo.SetEnabled(v) end - end) + end, + }) - -- ── Tracker Visibility ─────────────────────────────────── - MTH_EA_OPT_Header(container, "Tracker Visibility", -100) - - MTH_EA_OPT_CB(container, "MTH_ExpAmmoShowLnLCB", - "Show Lock and Load cell (top)", - -120, OPT_L, cfg.showLnL ~= false, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetShowLnL(v) end end) - - -- "Show action hint cell" also gates the right column - local rightGroup = {} -- filled below; toggled when this CB changes - local showHintCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoShowHintCB", - "Show action hint cell (bottom)", - -144, OPT_L, showHint, - function(v) + -- ===== LEFT: Tracker Visibility ===== + local visBox = panel:Box("left", "MTH_ExpAmmoVisBox", "Tracker Visibility") + visBox:Checkbox("MTH_ExpAmmoShowLnLCB", "Show Lock and Load cell (top)", { + checked = cfg.showLnL ~= false, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetShowLnL(this:GetChecked() == 1) end end, + }) + visBox:Checkbox("MTH_ExpAmmoShowHintCB", "Show action hint cell (bottom)", { + checked = showHint, + onClick = function() + local v = this:GetChecked() == 1 if MTH_ExpAmmo then MTH_ExpAmmo.SetShowHint(v) end - MTH_EA_OPT_SetGroupEnabled(rightGroup, v) - end) + setRightEnabled(v) + end, + }) + visBox:Checkbox("MTH_ExpAmmoHideOOCCB", "Hide widget when out of combat", { + checked = cfg.hideOOC == true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetHideOOC(this:GetChecked() == 1) end end, + }) - MTH_EA_OPT_CB(container, "MTH_ExpAmmoHideOOCCB", - "Hide widget when out of combat", - -168, OPT_L, cfg.hideOOC == true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetHideOOC(v) end end) + -- ===== LEFT: Appearance ===== + local appBox = panel:Box("left", "MTH_ExpAmmoAppBox", "Appearance") + appBox:Slider("MTH_ExpAmmoCellSizeSlider", { + label = "Cell Size (px)", min = 20, max = 80, step = 4, + value = math.max(20, math.min(80, cfg.cellSize or 40)), + onChange = function(val) if MTH_ExpAmmo then MTH_ExpAmmo.SetCellSize(val) end end, + }) + appBox:Slider("MTH_ExpAmmoCellGapSlider", { + label = "Cell Spacing (px)", min = 0, max = 20, step = 1, + value = math.max(0, math.min(20, cfg.cellGap or 2)), + onChange = function(val) if MTH_ExpAmmo then MTH_ExpAmmo.SetCellGap(val) end end, + }) + appBox:Checkbox("MTH_ExpAmmoBigCDCB", "Large cooldown numbers (centered in cell)", { + checked = cfg.bigCD == true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBigCD(this:GetChecked() == 1) end end, + }) - -- ── Appearance ─────────────────────────────────────────── - MTH_EA_OPT_Header(container, "Appearance", -202) - - local sizeSlider = MTH_CreateSlider(container, "MTH_ExpAmmoCellSizeSlider", - "Cell Size (px)", 20, 80, 4, -222) - if sizeSlider then - sizeSlider:ClearAllPoints() - sizeSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 5, -222) - sizeSlider:SetWidth(OPT_CW - 10) - sizeSlider:SetValue(math.max(20, math.min(80, cfg.cellSize or 40))) - sizeSlider.onChange = function(val) - if MTH_ExpAmmo then MTH_ExpAmmo.SetCellSize(val) end - end - end - - local gapSlider = MTH_CreateSlider(container, "MTH_ExpAmmoCellGapSlider", - "Cell Spacing (px)", 0, 20, 1, -286) - if gapSlider then - gapSlider:ClearAllPoints() - gapSlider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_L + 5, -286) - gapSlider:SetWidth(OPT_CW - 10) - gapSlider:SetValue(math.max(0, math.min(20, cfg.cellGap or 2))) - gapSlider.onChange = function(val) - if MTH_ExpAmmo then MTH_ExpAmmo.SetCellGap(val) end - end - end - - MTH_EA_OPT_CB(container, "MTH_ExpAmmoBigCDCB", - "Large cooldown numbers (centered in cell)", - -350, OPT_L, cfg.bigCD == true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBigCD(v) end end) - - -- ── Position ───────────────────────────────────────────── - MTH_EA_OPT_Header(container, "Position", -390) - - local anchorButton = MTH_CreateActionButton(container, "MTH_ExpAmmoToggleAnchor", - "Toggle Anchor", OPT_L, -410, 130, 22, function() - if MTH_ExpAmmo then MTH_ExpAmmo.ToggleAnchor() end - end) - if anchorButton then anchorButton:Show() end - - MTH_EA_OPT_Tip(container, + -- ===== LEFT: Position ===== + local posBox = panel:Box("left", "MTH_ExpAmmoPosBox", "Position") + posBox:Button("MTH_ExpAmmoToggleAnchor", "Toggle Anchor", { + width = 130, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.ToggleAnchor() end end, + }) + posBox:Note("MTH_ExpAmmoPosNote", "Shows 4 drag handles around the widget (top / bottom / left / right). Drag any one to reposition it.", - -436, OPT_L) + { color = MTH_Layout.Theme.colText, lines = 3 }) - --------------------------------------------------------------------------- - -- RIGHT COLUMN — Action Hint Cell (keybind + casting) - -- All widgets here are greyed out when the bottom cell is hidden. - --------------------------------------------------------------------------- - - -- Vertical divider line - local divider = container:CreateTexture(nil, "BACKGROUND") - divider:SetTexture(0.3, 0.3, 0.3, 0.6) - divider:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R - 10, -46) - divider:SetPoint("BOTTOMLEFT", container, "TOPLEFT", OPT_R - 10, -530) - divider:SetWidth(1) - - -- Column title - local rTitle = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - rTitle:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -46) - rTitle:SetText("|cffff9900" .. "Action Hint Cell" .. "|r") - table.insert(rightGroup, rTitle) - - - local rSubtitle = MTH_EA_OPT_Tip(container, - "Options below apply to the bottom cell only.\n Disable \"Show action hint cell\" to hide the cell and these controls.", - -64, OPT_R, 1.00, 1.00, 1.00) - if rSubtitle then rSubtitle:SetWidth(290) end - table.insert(rightGroup, rSubtitle) - - local rNamPowerTip = MTH_EA_OPT_Tip(container, + -- ===== RIGHT: Smart Action Cell (helper + keybind + casting) ===== + -- One feature = one box. The intro notes explain the bottom cell, and the + -- Keybind / Casting controls that drive it live underneath as sub-sections. + local cellBox = panel:Box("right", "MTH_ExpAmmoCellBox", "Smart Action Cell") + table.insert(rightBoxes, cellBox:Frame()) + cellBox:Text("MTH_ExpAmmoHintSub", + "Options below apply to the bottom cell only. Disable \"Show action hint cell\" to hide the cell and these controls.", + { color = { 1, 1, 1 }, lines = 3 }) + cellBox:Note("MTH_ExpAmmoHintNam", "Notes:\n - You need NAMPOWER for this to work reliably.\n - Aimed/Steady/Multi/Arcane Shots SHOULD BE on your action bars.", - -100, OPT_R, 0.35, 0.65, 1.00) - if rNamPowerTip then rNamPowerTip:SetWidth(290) end - table.insert(rightGroup, rNamPowerTip) + { lines = 3 }) - -- ── Keybind ────────────────────────────────────────────── - table.insert(rightGroup, MTH_EA_OPT_Header(container, "Keybind", -182, OPT_R)) + -- Keybind sub-section + cellBox:Gap(4) + cellBox:Text("MTH_ExpAmmoKeyHead", "Keybind", + { font = MTH_Layout.Theme.fontBoxTitle, color = MTH_Layout.Theme.colPanelTitle }) + cellBox:Note("MTH_ExpAmmoBindTip", + "One key to rule them all.\n Aimed Shot when ready, Steady Shot while Aimed is on cooldown, consume-spell (Multi / Serpent / Arcane) when proc is up.", + { lines = 4 }) + MTH_EA_OPT_STATE.bindStatus = cellBox:Text("MTH_ExpAmmoBindStatus", + "Key: " .. MTH_EA_OPT_GetBindText()) + local keyButtons = cellBox:Buttons({ + { name = "MTH_ExpAmmoBindButton", label = "Set Key", width = 90, + onClick = function() + if MTH_EA_OPT_STATE.captureFrame and MTH_EA_OPT_STATE.captureFrame:IsShown() then + MTH_EA_OPT_StopCapture() + else + MTH_EA_OPT_StartCapture() + end + end }, + { name = "MTH_ExpAmmoBindClear", label = "Clear", width = 70, + onClick = function() MTH_EA_OPT_SaveBinding(nil) end }, + }) + MTH_EA_OPT_STATE.bindButton = keyButtons[1] + table.insert(rightControls, keyButtons[1]) + table.insert(rightControls, keyButtons[2]) - local bindTip = MTH_EA_OPT_Tip(container, - "One key to rule them all.\n Aimed Shot when ready, Steady Shot while Aimed is on cooldown,\n consume-spell (Multi / Serpent / Arcane) when proc is up.", - -200, OPT_R, 0.65, 0.65, 0.65) - if bindTip then bindTip:SetWidth(290) end - table.insert(rightGroup, bindTip) + -- Casting sub-section + cellBox:Gap(4) + cellBox:Text("MTH_ExpAmmoCastHead", "Casting", + { font = MTH_Layout.Theme.fontBoxTitle, color = MTH_Layout.Theme.colPanelTitle }) + local quiverCB = cellBox:Checkbox("MTH_ExpAmmoQuiverNoClipCB", "Use Quiver no-clip casting", { + checked = cfg.useQuiverNoClip == true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetUseQuiverNoClip(this:GetChecked() == 1) end end, + }) + table.insert(rightControls, quiverCB) + cellBox:Note("MTH_ExpAmmoQuiverTip", + "Requires the Quiver addon. Routes Aimed/Steady/Multi cast through Quiver.CastNoClip to avoid clipping your auto-shot swing timer.", + { lines = 3 }) - local bindStatus = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") - bindStatus:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -248) - bindStatus:SetText("Key: " .. MTH_EA_OPT_GetBindText()) - MTH_EA_OPT_STATE.bindStatus = bindStatus - table.insert(rightGroup, bindStatus) + -- ===== RIGHT: Rotation ===== + local rotBox = panel:Box("right", "MTH_ExpAmmoRotBox", "Rotation") + table.insert(rightBoxes, rotBox:Frame()) + rotBox:Note("MTH_ExpAmmoRotTip", + "Uncheck a shot to permanently skip it during its proc window. The keybind will cast Steady/Aimed instead.", + { lines = 2 }) + local multiCB = rotBox:Checkbox("MTH_ExpAmmoBlockMultiCB", "Multi-Shot (Explosive)", { + checked = cfg.blockExplosive ~= true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockExplosive(not (this:GetChecked() == 1)) end end, + }) + table.insert(rightControls, multiCB) + local serpentCB = rotBox:Checkbox("MTH_ExpAmmoBlockSerpentCB", "Serpent Sting (Poisonous)", { + checked = cfg.blockPoisonous ~= true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockPoisonous(not (this:GetChecked() == 1)) end end, + }) + table.insert(rightControls, serpentCB) + local arcaneCB = rotBox:Checkbox("MTH_ExpAmmoBlockArcaneCB", "Arcane Shot (Enchanted)", { + checked = cfg.blockEnchanted ~= true, + onClick = function() if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockEnchanted(not (this:GetChecked() == 1)) end end, + }) + table.insert(rightControls, arcaneCB) - local bindButton = CreateFrame("Button", "MTH_ExpAmmoBindButton", container, "UIPanelButtonTemplate") - bindButton:SetPoint("TOPLEFT", container, "TOPLEFT", OPT_R, -264) - bindButton:SetWidth(90) - bindButton:SetHeight(22) - bindButton:SetText("Set Key") - bindButton:EnableKeyboard(false) - bindButton:SetScript("OnClick", function() - if MTH_EA_OPT_STATE.captureFrame and MTH_EA_OPT_STATE.captureFrame:IsShown() then - MTH_EA_OPT_StopCapture() - else - MTH_EA_OPT_StartCapture() - end - end) - MTH_EA_OPT_STATE.bindButton = bindButton - table.insert(rightGroup, bindButton) + panel:Finish() - local clearButton = CreateFrame("Button", "MTH_ExpAmmoBindClear", container, "UIPanelButtonTemplate") - clearButton:SetPoint("LEFT", bindButton, "RIGHT", 6, 0) - clearButton:SetWidth(70) - clearButton:SetHeight(22) - clearButton:SetText("Clear") - clearButton:SetScript("OnClick", function() - MTH_EA_OPT_SaveBinding(nil) - end) - table.insert(rightGroup, clearButton) - - -- ── Casting ────────────────────────────────────────────── - table.insert(rightGroup, MTH_EA_OPT_Header(container, "Casting", -304, OPT_R)) - - local quiverCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoQuiverNoClipCB", - "Use Quiver no-clip casting", - -324, OPT_R, cfg.useQuiverNoClip == true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetUseQuiverNoClip(v) end end) - table.insert(rightGroup, quiverCB) - - local quiverTip = MTH_EA_OPT_Tip(container, - "|cff4d9cffRequires the Quiver addon.|r \n Routes Aimed/Steady/Multi cast through Quiver.CastNoClip \n to avoid clipping your auto-shot swing timer", - -350, OPT_R, 1, 1, 1) - if quiverTip then quiverTip:SetWidth(290) end - table.insert(rightGroup, quiverTip) - - -- ── Rotation ───────────────────────────────────────────── - table.insert(rightGroup, MTH_EA_OPT_Header(container, "Rotation", -400, OPT_R)) - - local rotTip = MTH_EA_OPT_Tip(container, - "Uncheck a shot to permanently skip it during its proc window.\n The keybind will cast Steady/Aimed instead.", - -418, OPT_R, 0.65, 0.65, 0.65) - if rotTip then rotTip:SetWidth(290) end - table.insert(rightGroup, rotTip) - - local multiCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockMultiCB", - "Multi-Shot (Explosive)", - -452, OPT_R, cfg.blockExplosive ~= true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockExplosive(not v) end end) - table.insert(rightGroup, multiCB) - - local serpentCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockSerpentCB", - "Serpent Sting (Poisonous)", - -476, OPT_R, cfg.blockPoisonous ~= true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockPoisonous(not v) end end) - table.insert(rightGroup, serpentCB) - - local arcaneCB = MTH_EA_OPT_CB(container, "MTH_ExpAmmoBlockArcaneCB", - "Arcane Shot (Enchanted)", - -500, OPT_R, cfg.blockEnchanted ~= true, - function(v) if MTH_ExpAmmo then MTH_ExpAmmo.SetBlockEnchanted(not v) end end) - table.insert(rightGroup, arcaneCB) - - -- Apply initial enabled state - MTH_EA_OPT_SetGroupEnabled(rightGroup, showHint) + -- Apply initial gating state. + setRightEnabled(showHint) end function MTH_SetupExpAmmoOptions() diff --git a/api/options-feedomatic.lua b/api/options-feedomatic.lua index 6c0c79c..a24bb2c 100644 --- a/api/options-feedomatic.lua +++ b/api/options-feedomatic.lua @@ -242,31 +242,25 @@ function MTH_RefreshFeedOMaticOptions() if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetChecked(FOM_Config.SaveForCookingLevel and FOM_Config.SaveForCookingLevel >= 4 and 1 or nil) end end +-- Small label helper: prefer Feed-O-Matic's own button text, else localized. +local function MTH_FOM_Label(key, lkey, fallback) + return (FOM_OptionsButtonText and FOM_OptionsButtonText[key]) or MTH_FOM_L(lkey, fallback) +end + function MTH_SetupFeedOMaticOptions() if not MTH_FOM_READY then return end local container = MTH_GetFrame("MetaHuntOptionsFeedOMatic") if not container then return end - local rightColumnX = 300 - local yAdjust = -64 - MTH_ClearContainer(container) MTH_FOM_STATE.ctrl = {} - local title = container:CreateFontString("MetaHuntOptionsFeedOMaticTitle", "ARTWORK", "GameFontHighlight") - title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) - title:SetText(MTH_FOM_L("FOM_TITLE", "Feed-O-Matic")) + local panel = MTH_Layout.Panel(container) + panel:Title(MTH_FOM_L("FOM_TITLE", "Feed-O-Matic")) + panel:Note(MTH_FOM_L("FOM_STATUS_NOTICE", "Feed-O-Matic, created by the great Fizzwidget, is not yet entirely ready for Twow because I am missing \na reliable list of all foods, mostly impossible to fetch from the DB.\n It still works much better in this version, but all stuff related to Food buff and Cooking isnt fully functional."), { lines = 4 }) - local statusNotice = container:CreateFontString("MetaHuntOptionsFeedOMaticStatusNotice", "ARTWORK", "GameFontNormalSmall") - statusNotice:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -34) - statusNotice:SetWidth(600) - statusNotice:SetJustifyH("LEFT") - statusNotice:SetJustifyV("TOP") - statusNotice:SetTextColor(0.35, 0.65, 1) - statusNotice:SetText(MTH_FOM_L("FOM_STATUS_NOTICE", "Feed-O-Matic, created by the great Fizzwidget, is not yet entirely ready for Twow because I am missing \na reliable list of all foods, mostly impossible to fetch from the DB.\n It still works much better in this version, but all stuff related to Food buff and Cooking isnt fully functional.")) - - local moduleCheck = MTH_CreateCheckbox(container, "MetaHuntOptionsFeedOMaticEnabled", MTH_FOM_L("FOM_ENABLE_MODULE", "Enable FeedOMatic module"), -32 + yAdjust) - if moduleCheck then - moduleCheck:SetScript("OnClick", function() + MTH_FOM_STATE.enableCheck = panel:TopCheckbox("MetaHuntOptionsFeedOMaticEnabled", + MTH_FOM_L("FOM_ENABLE_MODULE", "Enable FeedOMatic module"), { + onClick = function() if not this then return end local enabled = this:GetChecked() == 1 if MTH and MTH.SetModuleEnabled then @@ -276,125 +270,113 @@ function MTH_SetupFeedOMaticOptions() end end MTH_RefreshFeedOMaticOptions() - end) - MTH_FOM_STATE.enableCheck = moduleCheck - end + end, + }) - local leftHeader = container:CreateFontString("MetaHuntOptionsFeedOMaticGeneralHeader", "ARTWORK", "GameFontHighlight") - leftHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -62 + yAdjust) - leftHeader:SetText(MTH_FOM_L("FOM_HEADER_GENERAL", "General")) + -- ===== General (left column) ===== + local general = panel:Box("left", "MetaHuntOptionsFOMGeneralBox", MTH_FOM_L("FOM_HEADER_GENERAL", "General")) - local bindStatus = container:CreateFontString("MetaHuntOptionsFOMBindStatus", "ARTWORK", "GameFontNormalSmall") - bindStatus:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -80 + yAdjust) - bindStatus:SetText(string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText())) - MTH_FOM_STATE.bindStatus = bindStatus + MTH_FOM_STATE.bindStatus = general:Text("MetaHuntOptionsFOMBindStatus", + string.format(MTH_FOM_L("FOM_BIND_STATUS_VALUE", "Feed key: %s"), MTH_FOM_GetBindingDisplayText())) - local bindButton = CreateFrame("Button", "MetaHuntOptionsFOMBindButton", container, "UIPanelButtonTemplate") - bindButton:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -100 + yAdjust) - bindButton:SetWidth(90) - bindButton:SetHeight(22) - bindButton:SetText(MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key")) - bindButton:SetScript("OnClick", function() - if MTH_FOM_STATE.captureFrame and MTH_FOM_STATE.captureFrame:IsShown() then - MTH_FOM_StopBindingCapture() - else - MTH_FOM_StartBindingCapture() - end - end) - MTH_FOM_STATE.bindButton = bindButton + local bindButtons = general:Buttons({ + { name = "MetaHuntOptionsFOMBindButton", label = MTH_FOM_L("FOM_BIND_SET_KEY", "Set Key"), width = 90, + onClick = function() + if MTH_FOM_STATE.captureFrame and MTH_FOM_STATE.captureFrame:IsShown() then + MTH_FOM_StopBindingCapture() + else + MTH_FOM_StartBindingCapture() + end + end }, + { name = "MetaHuntOptionsFOMBindClearButton", label = MTH_FOM_L("FOM_BIND_CLEAR", "Clear"), width = 70, + onClick = function() MTH_FOM_SaveBinding(nil) end }, + }) + MTH_FOM_STATE.bindButton = bindButtons[1] + MTH_FOM_STATE.bindClearButton = bindButtons[2] - local bindClearButton = CreateFrame("Button", "MetaHuntOptionsFOMBindClearButton", container, "UIPanelButtonTemplate") - bindClearButton:SetPoint("LEFT", bindButton, "RIGHT", 8, 0) - bindClearButton:SetWidth(70) - bindClearButton:SetHeight(22) - bindClearButton:SetText(MTH_FOM_L("FOM_BIND_CLEAR", "Clear")) - bindClearButton:SetScript("OnClick", function() - MTH_FOM_SaveBinding(nil) - end) - MTH_FOM_STATE.bindClearButton = bindClearButton + general:Note("MetaHuntOptionsFOMBindHint", + MTH_FOM_L("FOM_BIND_HINT", "You should assign a key bind for feed-o-matic,\nin order to feed your pet automatically.\nThe key \"P\" is an excellent candidate !"), { lines = 3 }) - local bindHint = container:CreateFontString("MetaHuntOptionsFOMBindHint", "ARTWORK", "GameFontNormalSmall") - bindHint:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -128 + yAdjust) - bindHint:SetWidth(360) - bindHint:SetJustifyH("LEFT") - bindHint:SetJustifyV("TOP") - bindHint:SetTextColor(0.35, 0.65, 1) - bindHint:SetText(MTH_FOM_L("FOM_BIND_HINT", "You should assign a key bind for feed-o-matic,\nin order to feed your pet automatically.\nThe key \"P\" is an excellent candidate !")) + MTH_FOM_STATE.ctrl.AvoidQuestFood = general:Checkbox("MetaHuntOptionsFOMAvoidQuest", + MTH_FOM_Label("AvoidQuestFood", "FOM_LABEL_AVOID_QUEST_FOODS", "Avoid quest foods"), + { onClick = function() if not this then return end FOM_Config.AvoidQuestFood = this:GetChecked() == 1 end }) + MTH_FOM_STATE.ctrl.AvoidBonusFood = general:Checkbox("MetaHuntOptionsFOMAvoidBonus", + MTH_FOM_Label("AvoidBonusFood", "FOM_LABEL_AVOID_BONUS_FOODS", "Avoid bonus foods"), + { onClick = function() if not this then return end FOM_Config.AvoidBonusFood = this:GetChecked() == 1 end }) + MTH_FOM_STATE.ctrl.PreferHigherQuality = general:Checkbox("MetaHuntOptionsFOMPreferQuality", + MTH_FOM_Label("PreferHigherQuality", "FOM_LABEL_PREFER_HIGHER_QUALITY_FOOD", "Prefer higher quality food"), + { onClick = function() if not this then return end FOM_Config.PreferHigherQuality = this:GetChecked() == 1 end }) + MTH_FOM_STATE.ctrl.Fallback = general:Checkbox("MetaHuntOptionsFOMFallback", + MTH_FOM_Label("Fallback", "FOM_LABEL_FALLBACK_TO_AVOIDED_FOODS", "Fallback to avoided foods"), + { onClick = function() if not this then return end FOM_Config.Fallback = this:GetChecked() == 1 end }) - MTH_FOM_STATE.ctrl.AvoidQuestFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidQuest", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidQuestFood"] or MTH_FOM_L("FOM_LABEL_AVOID_QUEST_FOODS", "Avoid quest foods"), -170 + yAdjust) - MTH_FOM_STATE.ctrl.AvoidBonusFood = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAvoidBonus", FOM_OptionsButtonText and FOM_OptionsButtonText["AvoidBonusFood"] or MTH_FOM_L("FOM_LABEL_AVOID_BONUS_FOODS", "Avoid bonus foods"), -195 + yAdjust) - MTH_FOM_STATE.ctrl.PreferHigherQuality = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMPreferQuality", FOM_OptionsButtonText and FOM_OptionsButtonText["PreferHigherQuality"] or MTH_FOM_L("FOM_LABEL_PREFER_HIGHER_QUALITY_FOOD", "Prefer higher quality food"), -220 + yAdjust) - MTH_FOM_STATE.ctrl.Fallback = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMFallback", FOM_OptionsButtonText and FOM_OptionsButtonText["Fallback"] or MTH_FOM_L("FOM_LABEL_FALLBACK_TO_AVOIDED_FOODS", "Fallback to avoided foods"), -245 + yAdjust) + MTH_FOM_STATE.keepOpenEdit = general:EditRow("MetaHuntOptionsFOMKeepOpenLabel", + MTH_FOM_L("FOM_KEEP_OPEN_BAG_SLOTS", "Keep open bag slots:"), + "MetaHuntOptionsFOMKeepOpen", 40, { + numeric = true, + onChange = function() + if not this then return end + FOM_Config.KeepOpenSlots = tonumber(this:GetText()) or 0 + end, + }) - if MTH_FOM_STATE.ctrl.AvoidQuestFood then MTH_FOM_STATE.ctrl.AvoidQuestFood:SetScript("OnClick", function() if not this then return end FOM_Config.AvoidQuestFood = this:GetChecked() == 1 end) end - if MTH_FOM_STATE.ctrl.AvoidBonusFood then MTH_FOM_STATE.ctrl.AvoidBonusFood:SetScript("OnClick", function() if not this then return end FOM_Config.AvoidBonusFood = this:GetChecked() == 1 end) end - if MTH_FOM_STATE.ctrl.PreferHigherQuality then MTH_FOM_STATE.ctrl.PreferHigherQuality:SetScript("OnClick", function() if not this then return end FOM_Config.PreferHigherQuality = this:GetChecked() == 1 end) end - if MTH_FOM_STATE.ctrl.Fallback then MTH_FOM_STATE.ctrl.Fallback:SetScript("OnClick", function() if not this then return end FOM_Config.Fallback = this:GetChecked() == 1 end) end + -- ===== Notify when feeding (left column) ===== + local notify = panel:Box("left", "MetaHuntOptionsFOMNotifyBox", MTH_FOM_L("FOM_HEADER_NOTIFY_WHEN_FEEDING", "Notify when feeding")) + MTH_FOM_STATE.ctrl.AlertEmote = notify:Checkbox("MetaHuntOptionsFOMAlertEmote", + MTH_FOM_Label("AlertEmote", "FOM_LABEL_NOTIFY_VIA_EMOTE", "Via emote"), + { onClick = function() MTH_FOM_SetAlert("emote"); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.AlertChat = notify:Checkbox("MetaHuntOptionsFOMAlertChat", + MTH_FOM_Label("AlertChat", "FOM_LABEL_NOTIFY_IN_CHAT", "In chat"), + { onClick = function() MTH_FOM_SetAlert("chat"); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.AlertNone = notify:Checkbox("MetaHuntOptionsFOMAlertNone", + MTH_FOM_Label("AlertNone", "FOM_LABEL_NOTIFY_NONE", "Don't notify"), + { onClick = function() MTH_FOM_SetAlert("none"); MTH_RefreshFeedOMaticOptions() end }) - local keepOpenLabel = container:CreateFontString("MetaHuntOptionsFOMKeepOpenLabel", "ARTWORK", "GameFontNormalSmall") - keepOpenLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -302 + yAdjust) - keepOpenLabel:SetText(MTH_FOM_L("FOM_KEEP_OPEN_BAG_SLOTS", "Keep open bag slots:")) + -- ===== Avoid foods used in cooking (right column) ===== + local avoid = panel:Box("right", "MetaHuntOptionsFOMAvoidBox", MTH_FOM_L("FOM_HEADER_AVOID_FOODS_USED_IN_COOKING", "Avoid foods used in cooking")) + MTH_FOM_STATE.ctrl.SaveCookOrange = avoid:Checkbox("MetaHuntOptionsFOMSaveCookOrange", + MTH_FOM_Label("SaveForCook_Orange", "FOM_LABEL_ONLY_DIFFICULT_RECIPES", "Only difficult recipes"), + { onClick = function() MTH_FOM_SetCookingLevel(3); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.SaveCookYellow = avoid:Checkbox("MetaHuntOptionsFOMSaveCookYellow", + MTH_FOM_Label("SaveForCook_Yellow", "FOM_LABEL_MEDIUM_OR_BETTER", "Medium or better"), + { onClick = function() MTH_FOM_SetCookingLevel(2); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.SaveCookGreen = avoid:Checkbox("MetaHuntOptionsFOMSaveCookGreen", + MTH_FOM_Label("SaveForCook_Green", "FOM_LABEL_EASY_OR_BETTER", "Easy or better"), + { onClick = function() MTH_FOM_SetCookingLevel(1); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.SaveCookAll = avoid:Checkbox("MetaHuntOptionsFOMSaveCookAll", + MTH_FOM_Label("SaveForCook_All", "FOM_LABEL_ALL_FOODS", "All foods"), + { onClick = function() MTH_FOM_SetCookingLevel(0); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.SaveCookNone = avoid:Checkbox("MetaHuntOptionsFOMSaveCookNone", + MTH_FOM_Label("SaveForCook_None", "FOM_LABEL_DO_NOT_SAVE_COOKING_FOODS", "Do not save cooking foods"), + { onClick = function() MTH_FOM_SetCookingLevel(4); MTH_RefreshFeedOMaticOptions() end }) - local keepOpen = CreateFrame("EditBox", "MetaHuntOptionsFOMKeepOpen", container, "InputBoxTemplate") - keepOpen:SetPoint("LEFT", keepOpenLabel, "RIGHT", 8, 0) - keepOpen:SetWidth(40) - keepOpen:SetHeight(20) - keepOpen:SetNumeric(true) - keepOpen:SetAutoFocus(false) - keepOpen:SetScript("OnTextChanged", function() - if not this then return end - local value = tonumber(this:GetText()) or 0 - FOM_Config.KeepOpenSlots = value - end) - keepOpen:SetScript("OnEnterPressed", function() if this then this:ClearFocus() end end) - keepOpen:SetScript("OnEscapePressed", function() if this then this:ClearFocus() end end) - MTH_FOM_STATE.keepOpenEdit = keepOpen + -- ===== Warn when pet needs feeding (right column) ===== + local warn = panel:Box("right", "MetaHuntOptionsFOMWarnBox", MTH_FOM_L("FOM_HEADER_WARN_WHEN_PET_NEEDS_FEEDING", "Warn when pet needs feeding")) + MTH_FOM_STATE.ctrl.LevelContent = warn:Checkbox("MetaHuntOptionsFOMLevelContent", + MTH_FOM_Label("LevelContent", "FOM_LABEL_WARN_WHEN_CONTENT", "When content"), + { onClick = function() MTH_FOM_SetLevel("content"); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.LevelUnhappy = warn:Checkbox("MetaHuntOptionsFOMLevelUnhappy", + MTH_FOM_Label("LevelUnhappy", "FOM_LABEL_WARN_WHEN_UNHAPPY", "When unhappy"), + { onClick = function() MTH_FOM_SetLevel("unhappy"); MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.LevelOff = warn:Checkbox("MetaHuntOptionsFOMLevelOff", + MTH_FOM_Label("LevelOff", "FOM_LABEL_DONT_WARN", "Don't warn"), + { onClick = function() MTH_FOM_SetLevel("off"); MTH_RefreshFeedOMaticOptions() end }) - local notifyHeader = container:CreateFontString("MetaHuntOptionsFOMNotifyHeader", "ARTWORK", "GameFontHighlight") - notifyHeader:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -336 + yAdjust) - notifyHeader:SetText(MTH_FOM_L("FOM_HEADER_NOTIFY_WHEN_FEEDING", "Notify when feeding")) + warn:Gap(6) - MTH_FOM_STATE.ctrl.AlertEmote = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertEmote", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertEmote"] or MTH_FOM_L("FOM_LABEL_NOTIFY_VIA_EMOTE", "Via emote"), -360 + yAdjust) - MTH_FOM_STATE.ctrl.AlertChat = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertChat", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertChat"] or MTH_FOM_L("FOM_LABEL_NOTIFY_IN_CHAT", "In chat"), -385 + yAdjust) - MTH_FOM_STATE.ctrl.AlertNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAlertNone", FOM_OptionsButtonText and FOM_OptionsButtonText["AlertNone"] or MTH_FOM_L("FOM_LABEL_NOTIFY_NONE", "Don't notify"), -410 + yAdjust) - if MTH_FOM_STATE.ctrl.AlertEmote then MTH_FOM_STATE.ctrl.AlertEmote:SetScript("OnClick", function() MTH_FOM_SetAlert("emote"); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.AlertChat then MTH_FOM_STATE.ctrl.AlertChat:SetScript("OnClick", function() MTH_FOM_SetAlert("chat"); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.AlertNone then MTH_FOM_STATE.ctrl.AlertNone:SetScript("OnClick", function() MTH_FOM_SetAlert("none"); MTH_RefreshFeedOMaticOptions() end) end - - local warnHeader = container:CreateFontString("MetaHuntOptionsFOMWarnHeader", "ARTWORK", "GameFontHighlight") - warnHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -276 + yAdjust) - warnHeader:SetText(MTH_FOM_L("FOM_HEADER_WARN_WHEN_PET_NEEDS_FEEDING", "Warn when pet needs feeding")) - - MTH_FOM_STATE.ctrl.LevelContent = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelContent", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelContent"] or MTH_FOM_L("FOM_LABEL_WARN_WHEN_CONTENT", "When content"), -300 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.LevelUnhappy = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelUnhappy", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelUnhappy"] or MTH_FOM_L("FOM_LABEL_WARN_WHEN_UNHAPPY", "When unhappy"), -325 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.LevelOff = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMLevelOff", FOM_OptionsButtonText and FOM_OptionsButtonText["LevelOff"] or MTH_FOM_L("FOM_LABEL_DONT_WARN", "Don't warn"), -350 + yAdjust, rightColumnX) - if MTH_FOM_STATE.ctrl.LevelContent then MTH_FOM_STATE.ctrl.LevelContent:SetScript("OnClick", function() MTH_FOM_SetLevel("content"); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.LevelUnhappy then MTH_FOM_STATE.ctrl.LevelUnhappy:SetScript("OnClick", function() MTH_FOM_SetLevel("unhappy"); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.LevelOff then MTH_FOM_STATE.ctrl.LevelOff:SetScript("OnClick", function() MTH_FOM_SetLevel("off"); MTH_RefreshFeedOMaticOptions() end) end - - MTH_FOM_STATE.ctrl.AudioWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarning"] or MTH_FOM_L("FOM_LABEL_PLAY_SOUND", "Play sound"), -378 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.AudioWarningBell = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMAudioWarningBell", FOM_OptionsButtonText and FOM_OptionsButtonText["AudioWarningBell"] or MTH_FOM_L("FOM_LABEL_USE_BELL_SOUND", "Use bell sound"), -403 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.TextWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMTextWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["TextWarning"] or MTH_FOM_L("FOM_LABEL_SHOW_TEXT", "Show text"), -428 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.IconWarning = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMIconWarning", FOM_OptionsButtonText and FOM_OptionsButtonText["IconWarning"] or MTH_FOM_L("FOM_LABEL_FLASH_ICON", "Flash icon"), -453 + yAdjust, rightColumnX) - if MTH_FOM_STATE.ctrl.AudioWarning then MTH_FOM_STATE.ctrl.AudioWarning:SetScript("OnClick", function() if not this then return end if this:GetChecked() == 1 then if FOM_Config.AudioWarning ~= "bell" then FOM_Config.AudioWarning = true end else FOM_Config.AudioWarning = nil end MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.AudioWarningBell then MTH_FOM_STATE.ctrl.AudioWarningBell:SetScript("OnClick", function() if not this then return end if this:GetChecked() == 1 then FOM_Config.AudioWarning = "bell" else FOM_Config.AudioWarning = true end MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.TextWarning then MTH_FOM_STATE.ctrl.TextWarning:SetScript("OnClick", function() if not this then return end FOM_Config.TextWarning = this:GetChecked() == 1 end) end - if MTH_FOM_STATE.ctrl.IconWarning then MTH_FOM_STATE.ctrl.IconWarning:SetScript("OnClick", function() if not this then return end FOM_Config.IconWarning = this:GetChecked() == 1 end) end - - local cookHeader = container:CreateFontString("MetaHuntOptionsFOMCookingHeader", "ARTWORK", "GameFontHighlight") - cookHeader:SetPoint("TOPLEFT", container, "TOPLEFT", rightColumnX, -62 + yAdjust) - cookHeader:SetText(MTH_FOM_L("FOM_HEADER_AVOID_FOODS_USED_IN_COOKING", "Avoid foods used in cooking")) - - MTH_FOM_STATE.ctrl.SaveCookOrange = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookOrange", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Orange"] or MTH_FOM_L("FOM_LABEL_ONLY_DIFFICULT_RECIPES", "Only difficult recipes"), -86 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.SaveCookYellow = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookYellow", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Yellow"] or MTH_FOM_L("FOM_LABEL_MEDIUM_OR_BETTER", "Medium or better"), -111 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.SaveCookGreen = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookGreen", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_Green"] or MTH_FOM_L("FOM_LABEL_EASY_OR_BETTER", "Easy or better"), -136 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.SaveCookAll = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookAll", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_All"] or MTH_FOM_L("FOM_LABEL_ALL_FOODS", "All foods"), -161 + yAdjust, rightColumnX) - MTH_FOM_STATE.ctrl.SaveCookNone = MTH_CreateCheckbox(container, "MetaHuntOptionsFOMSaveCookNone", FOM_OptionsButtonText and FOM_OptionsButtonText["SaveForCook_None"] or MTH_FOM_L("FOM_LABEL_DO_NOT_SAVE_COOKING_FOODS", "Do not save cooking foods"), -186 + yAdjust, rightColumnX) - if MTH_FOM_STATE.ctrl.SaveCookOrange then MTH_FOM_STATE.ctrl.SaveCookOrange:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(3); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.SaveCookYellow then MTH_FOM_STATE.ctrl.SaveCookYellow:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(2); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.SaveCookGreen then MTH_FOM_STATE.ctrl.SaveCookGreen:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(1); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.SaveCookAll then MTH_FOM_STATE.ctrl.SaveCookAll:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(0); MTH_RefreshFeedOMaticOptions() end) end - if MTH_FOM_STATE.ctrl.SaveCookNone then MTH_FOM_STATE.ctrl.SaveCookNone:SetScript("OnClick", function() MTH_FOM_SetCookingLevel(4); MTH_RefreshFeedOMaticOptions() end) end + MTH_FOM_STATE.ctrl.AudioWarning = warn:Checkbox("MetaHuntOptionsFOMAudioWarning", + MTH_FOM_Label("AudioWarning", "FOM_LABEL_PLAY_SOUND", "Play sound"), + { onClick = function() if not this then return end if this:GetChecked() == 1 then if FOM_Config.AudioWarning ~= "bell" then FOM_Config.AudioWarning = true end else FOM_Config.AudioWarning = nil end MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.AudioWarningBell = warn:Checkbox("MetaHuntOptionsFOMAudioWarningBell", + MTH_FOM_Label("AudioWarningBell", "FOM_LABEL_USE_BELL_SOUND", "Use bell sound"), + { onClick = function() if not this then return end if this:GetChecked() == 1 then FOM_Config.AudioWarning = "bell" else FOM_Config.AudioWarning = true end MTH_RefreshFeedOMaticOptions() end }) + MTH_FOM_STATE.ctrl.TextWarning = warn:Checkbox("MetaHuntOptionsFOMTextWarning", + MTH_FOM_Label("TextWarning", "FOM_LABEL_SHOW_TEXT", "Show text"), + { onClick = function() if not this then return end FOM_Config.TextWarning = this:GetChecked() == 1 end }) + MTH_FOM_STATE.ctrl.IconWarning = warn:Checkbox("MetaHuntOptionsFOMIconWarning", + MTH_FOM_Label("IconWarning", "FOM_LABEL_FLASH_ICON", "Flash icon"), + { onClick = function() if not this then return end FOM_Config.IconWarning = this:GetChecked() == 1 end }) + panel:Finish() MTH_RefreshFeedOMaticOptions() end diff --git a/api/options-icu.lua b/api/options-icu.lua index aab7f9e..72c2278 100644 --- a/api/options-icu.lua +++ b/api/options-icu.lua @@ -197,31 +197,25 @@ local function MTH_ICU_OpenColorPicker(colorKey) ColorPickerFrame:Show() end -local function MTH_ICU_CreateOptionRow(container, rowName, label, key, y, labelX, dropdownX) - labelX = labelX or MTH_ICU_LAYOUT.LEFT_X - dropdownX = dropdownX or MTH_ICU_LAYOUT.LEFT_DROPDOWN_X +local function MTH_ICU_BuildUI(container) + MTH_ICU_OPT_STATE.controls = {} + MTH_ICU_OPT_STATE.container = container + MTH_ICU_OPT_STATE.built = true - local labelFs = container:CreateFontString(rowName .. "Label", "ARTWORK", "GameFontNormalSmall") - labelFs:SetPoint("TOPLEFT", container, "TOPLEFT", labelX, y) - labelFs:SetText(label) + local controls = MTH_ICU_OPT_STATE.controls - local dropdown = CreateFrame("Frame", rowName .. "Drop", container, "UIDropDownMenuTemplate") - dropdown:ClearAllPoints() - dropdown:SetPoint("TOPLEFT", container, "TOPLEFT", dropdownX, y + 12) - if UIDropDownMenu_SetWidth then - UIDropDownMenu_SetWidth(MTH_ICU_LAYOUT.DROPDOWN_WIDTH, dropdown) - end - if UIDropDownMenu_JustifyText then - UIDropDownMenu_JustifyText("LEFT", dropdown) - end + local panel = MTH_Layout.Panel(container) + panel:Title("MTH ICU") + panel:Note("ICU is a minimap targeting utility module for your Trackings. It provides enhanced target information and click actions for players and NPCs on the minimap.", + { lines = 2 }) - if UIDropDownMenu_Initialize then - UIDropDownMenu_Initialize(dropdown, function() + -- Standard UIDropDownMenu initialize factory for a stored option key. + local function makeInit(key) + return function() local values = MTH_ICU_GetOptionValues(key) local store = MTH_ICU_GetStore() local current = tostring(store[key] or "") local infoFactory = UIDropDownMenu_CreateInfo - for i = 1, table.getn(values) do local optionValue = tostring(values[i]) local info = type(infoFactory) == "function" and infoFactory() or {} @@ -229,76 +223,23 @@ local function MTH_ICU_CreateOptionRow(container, rowName, label, key, y, labelX info.checked = optionValue == current and 1 or nil info.func = function() MTH_ICU_SetValue(key, optionValue) - MTH_RefreshICUOptions() + -- ANCHOR toggles whether the Toggle Anchor / Expand Up + -- controls exist, so rebuild instead of a plain refresh. + if key == "ANCHOR" then + MTH_SetupICUOptions() + else + MTH_RefreshICUOptions() + end end if UIDropDownMenu_AddButton then UIDropDownMenu_AddButton(info) end end - end) + end end - MTH_ICU_OPT_STATE.controls[key] = { - label = labelFs, - dropdown = dropdown, - } -end - -local function MTH_ICU_CreateColorRow(container, rowName, label, colorKey, y, labelX, buttonX) - labelX = labelX or MTH_ICU_LAYOUT.RIGHT_X - buttonX = buttonX or MTH_ICU_LAYOUT.COLOR_BUTTON_X - - local labelFs = container:CreateFontString(rowName .. "Label", "ARTWORK", "GameFontNormalSmall") - labelFs:SetPoint("TOPLEFT", container, "TOPLEFT", labelX, y) - labelFs:SetText(label) - - local button = CreateFrame("Button", rowName .. "Button", container, "UIPanelButtonTemplate") - button:SetPoint("TOPLEFT", container, "TOPLEFT", buttonX, y + 8) - button:SetWidth(MTH_ICU_LAYOUT.COLOR_BUTTON_WIDTH) - button:SetHeight(22) - button:SetText("Pick") - button:SetScript("OnClick", function() - MTH_ICU_OpenColorPicker(colorKey) - end) - - local swatch = button:CreateTexture(rowName .. "Swatch", "ARTWORK") - swatch:SetTexture("Interface\\Buttons\\WHITE8X8") - swatch:SetPoint("LEFT", button, "LEFT", 6, 0) - swatch:SetWidth(14) - swatch:SetHeight(14) - - MTH_ICU_OPT_STATE.controls[colorKey] = { - label = labelFs, - button = button, - swatch = swatch, - isColor = true, - } -end - -local function MTH_ICU_BuildUI(container) - if MTH_ICU_OPT_STATE.built and MTH_ICU_OPT_STATE.container == container then - return - end - - MTH_ClearContainer(container) - MTH_ICU_OPT_STATE.controls = {} - MTH_ICU_OPT_STATE.container = container - MTH_ICU_OPT_STATE.built = true - - local title = container:CreateFontString("MetaHuntOptionsICUTitle", "ARTWORK", "GameFontHighlight") - title:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -10) - title:SetText("MTH ICU") - - local note = container:CreateFontString("MetaHuntOptionsICUNote", "ARTWORK", "GameFontNormalSmall") - note:SetPoint("TOPLEFT", container, "TOPLEFT", 20, -32) - note:SetWidth(560) - note:SetJustifyH("LEFT") - note:SetJustifyV("TOP") - note:SetTextColor(0.35, 0.65, 1) - note:SetText("ICU is a minimap targeting utility module for your Trackings.\nIt provides enhanced target information and click actions for players and NPCs on the minimap.") - - local moduleEnabled = MTH_CreateCheckbox(container, "MetaHuntOptionsICUModuleEnabled", "Enable ICU module", -72, MTH_ICU_LAYOUT.LEFT_X) - if moduleEnabled then - moduleEnabled:SetScript("OnClick", function() - if not this then return end + -- ===== LEFT: Module ===== + local moduleBox = panel:Box("left", "MTH_ICUModuleBox", "Module") + controls.moduleEnabled = moduleBox:Checkbox("MetaHuntOptionsICUModuleEnabled", "Enable ICU module", { + onClick = function() if MTH and MTH.SetModuleEnabled then local ok, err = MTH:SetModuleEnabled("icu", this:GetChecked() == 1) if not ok and MTH and MTH.Print then @@ -306,130 +247,97 @@ local function MTH_ICU_BuildUI(container) end end MTH_RefreshICUOptions() - end) - MTH_ICU_OPT_STATE.controls.moduleEnabled = moduleEnabled - end - - local mouseOver = MTH_CreateCheckbox(container, "MetaHuntOptionsICUMouseOver", "Enable CTRL mouseover scan", -98, MTH_ICU_LAYOUT.LEFT_X) - if mouseOver then - mouseOver:SetScript("OnClick", function() - if not this then return end + end, + }) + controls.mouseOver = moduleBox:Checkbox("MetaHuntOptionsICUMouseOver", "Enable CTRL mouseover scan", { + onClick = function() local store = MTH_ICU_GetStore() store.mouseOver = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.mouseOver = mouseOver + end, + }) + + -- ===== LEFT: General Settings ===== + local genBox = panel:Box("left", "MTH_ICUGeneralBox", "General Settings") + controls.ALERT = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAlertDrop", + { label = "ALERT PvP", width = 130, initialize = makeInit("ALERT") }) } + controls.ANNOUNCE = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAnnounceDrop", + { label = "ANNOUNCE on click", width = 130, initialize = makeInit("ANNOUNCE") }) } + controls.HEALTH_TEXT_MODE = { dropdown = genBox:Dropdown("MetaHuntOptionsICUHealthModeDrop", + { label = "Health text", width = 130, initialize = makeInit("HEALTH_TEXT_MODE") }) } + controls.POPUP_HIDE_DELAY = { dropdown = genBox:Dropdown("MetaHuntOptionsICUHideDelayDrop", + { label = "List hide delay", width = 130, initialize = makeInit("POPUP_HIDE_DELAY") }) } + controls.ANCHOR = { dropdown = genBox:Dropdown("MetaHuntOptionsICUAnchorDrop", + { label = "ANCHOR", width = 130, initialize = makeInit("ANCHOR") }) } + -- The manual-anchor controls only apply to a CUSTOM anchor. Build them + -- only in that case so the box never reserves empty space for them. + if tostring(MTH_ICU_GetStore().ANCHOR or "") == "CUSTOM" then + controls.customAnchorButton = genBox:Button("MetaHuntOptionsICUToggleAnchorButton", "Toggle Anchor", { + width = 116, + onClick = function() if type(ICU_ToggleAnchor) == "function" then ICU_ToggleAnchor() end end, + }) + controls.expandUp = genBox:Checkbox("MetaHuntOptionsICUExpandUp", "Expand Up", { + onClick = function() + local store = MTH_ICU_GetStore() + store.EXPAND_UP = this:GetChecked() == 1 and true or false + if type(ICU_SetPoints) == "function" then ICU_SetPoints() end + end, + }) end - local sectionHeader = container:CreateFontString("MetaHuntOptionsICUSectionHeader", "ARTWORK", "GameFontHighlight") - sectionHeader:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_ICU_LAYOUT.LEFT_X, -146) - sectionHeader:SetText("General Settings") - - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUAlert", "ALERT PVP", "ALERT", -172, MTH_ICU_LAYOUT.LEFT_X, MTH_ICU_LAYOUT.LEFT_DROPDOWN_X) - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUAnnounce", "ANNOUNCE on click", "ANNOUNCE", -198, MTH_ICU_LAYOUT.LEFT_X, MTH_ICU_LAYOUT.LEFT_DROPDOWN_X) - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUHealthMode", "Health text", "HEALTH_TEXT_MODE", -224, MTH_ICU_LAYOUT.LEFT_X, MTH_ICU_LAYOUT.LEFT_DROPDOWN_X) - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUHideDelay", "List hide delay", "POPUP_HIDE_DELAY", -250, MTH_ICU_LAYOUT.LEFT_X, MTH_ICU_LAYOUT.LEFT_DROPDOWN_X) - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUAnchor", "ANCHOR", "ANCHOR", -276, MTH_ICU_LAYOUT.LEFT_X, MTH_ICU_LAYOUT.LEFT_DROPDOWN_X) - - local toggleAnchorButton = CreateFrame("Button", "MetaHuntOptionsICUToggleAnchorButton", container, "UIPanelButtonTemplate") - toggleAnchorButton:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_ICU_LAYOUT.LEFT_X, -302) - toggleAnchorButton:SetWidth(116) - toggleAnchorButton:SetHeight(22) - toggleAnchorButton:SetText("Toggle Anchor") - toggleAnchorButton:SetScript("OnClick", function() - if type(ICU_ToggleAnchor) == "function" then - ICU_ToggleAnchor() - end - end) - MTH_ICU_OPT_STATE.controls.customAnchorButton = toggleAnchorButton - - local expandUp = MTH_CreateCheckbox(container, "MetaHuntOptionsICUExpandUp", "Expand Up", -301, MTH_ICU_LAYOUT.LEFT_X + 132) - if expandUp then - expandUp:SetScript("OnClick", function() - if not this then return end - local store = MTH_ICU_GetStore() - store.EXPAND_UP = this:GetChecked() == 1 and true or false - if type(ICU_SetPoints) == "function" then - ICU_SetPoints() - end - end) - MTH_ICU_OPT_STATE.controls.expandUp = expandUp - end - - local showGuild = MTH_CreateCheckbox(container, "MetaHuntOptionsICUShowGuild", "Show player guild names", -344, MTH_ICU_LAYOUT.LEFT_X) - if showGuild then - showGuild:SetScript("OnClick", function() - if not this then return end - local store = MTH_ICU_GetStore() - store.SHOW_GUILD_NAME = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.showGuildName = showGuild - end - - local showClass = MTH_CreateCheckbox(container, "MetaHuntOptionsICUShowClass", "Show player class", -368, MTH_ICU_LAYOUT.LEFT_X) - if showClass then - showClass:SetScript("OnClick", function() - if not this then return end - local store = MTH_ICU_GetStore() - store.SHOW_PLAYER_CLASS = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.showPlayerClass = showClass - end - - local showRace = MTH_CreateCheckbox(container, "MetaHuntOptionsICUShowRace", "Show player race", -392, MTH_ICU_LAYOUT.LEFT_X) - if showRace then - showRace:SetScript("OnClick", function() - if not this then return end - local store = MTH_ICU_GetStore() - store.SHOW_PLAYER_RACE = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.showPlayerRace = showRace - end - - local showTitles = MTH_CreateCheckbox(container, "MetaHuntOptionsICUShowTitles", "Show custom titles in alerts", -416, MTH_ICU_LAYOUT.LEFT_X) - if showTitles then - showTitles:SetScript("OnClick", function() - if not this then return end - local store = MTH_ICU_GetStore() - store.SHOW_CUSTOM_TITLES = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.showCustomTitles = showTitles - end - - local rightHeader = container:CreateFontString("MetaHuntOptionsICURightHeader", "ARTWORK", "GameFontHighlight") - rightHeader:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_ICU_LAYOUT.RIGHT_X, -146) - rightHeader:SetText("Background Colors") - - MTH_ICU_CreateOptionRow(container, "MetaHuntOptionsICUPlayerColorMode", "Player colors", "PLAYER_COLOR_MODE", -172, MTH_ICU_LAYOUT.RIGHT_X, MTH_ICU_LAYOUT.RIGHT_DROPDOWN_X) - - local reactionOnlyPvp = MTH_CreateCheckbox(container, "MetaHuntOptionsICUReactionOnlyPvp", "Reaction only when PvP-flagged", -200, MTH_ICU_LAYOUT.RIGHT_X) - if reactionOnlyPvp then - reactionOnlyPvp:SetScript("OnClick", function() - if not this then return end + -- ===== RIGHT: Background Colors ===== + local bgBox = panel:Box("right", "MTH_ICUBgBox", "Background Colors") + controls.PLAYER_COLOR_MODE = { dropdown = bgBox:Dropdown("MetaHuntOptionsICUPlayerColorModeDrop", + { label = "Player colors", width = 130, initialize = makeInit("PLAYER_COLOR_MODE") }) } + controls.reactionOnlyPvp = bgBox:Checkbox("MetaHuntOptionsICUReactionOnlyPvp", "Reaction only when PvP-flagged", { + onClick = function() local store = MTH_ICU_GetStore() store.REACTION_ONLY_PVP_PLAYERS = this:GetChecked() == 1 and true or false - end) - MTH_ICU_OPT_STATE.controls.reactionOnlyPvp = reactionOnlyPvp - end + end, + }) - local colorHeader = container:CreateFontString("MetaHuntOptionsICUColorsHeader", "ARTWORK", "GameFontHighlight") - colorHeader:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_ICU_LAYOUT.RIGHT_X, -224) - - local colorScope = container:CreateFontString("MetaHuntOptionsICUColorsScope", "ARTWORK", "GameFontNormalSmall") - colorScope:SetPoint("TOPLEFT", container, "TOPLEFT", MTH_ICU_LAYOUT.RIGHT_X, -242) - colorScope:SetWidth(250) - colorScope:SetJustifyH("LEFT") - colorScope:SetJustifyV("TOP") - colorScope:SetTextColor(0.35, 0.65, 1) - colorScope:SetText("Player pickers: REACTION / FACTION / CUSTOM\nNPC pickers: always") - - local colorY = -284 + -- ===== RIGHT: Color Pickers ===== + local colorBox = panel:Box("right", "MTH_ICUColorBox", "Color Pickers") + colorBox:Note("MetaHuntOptionsICUColorsScope", + "Player pickers: REACTION / FACTION / CUSTOM\nNPC pickers: always", { lines = 2 }) for i = 1, table.getn(MTH_ICU_COLOR_ROWS) do local row = MTH_ICU_COLOR_ROWS[i] - MTH_ICU_CreateColorRow(container, "MetaHuntOptionsICUColor" .. tostring(i), row.label, row.key, colorY, MTH_ICU_LAYOUT.RIGHT_X, MTH_ICU_LAYOUT.COLOR_BUTTON_X) - colorY = colorY - MTH_ICU_LAYOUT.COLOR_ROW_STEP + local btn, swatch = colorBox:ColorRow("MetaHuntOptionsICUColor" .. tostring(i), { + label = row.label, + buttonText = "Pick", + buttonWidth = 78, + onClick = function() MTH_ICU_OpenColorPicker(row.key) end, + }) + controls[row.key] = { button = btn, swatch = swatch, isColor = true } end + -- ===== Player Info (placed on whichever column has the most room) ===== + local infoBox = panel:Box(panel:ShorterSide(), "MTH_ICUInfoBox", "Player Info") + controls.showGuildName = infoBox:Checkbox("MetaHuntOptionsICUShowGuild", "Show player guild names", { + onClick = function() + local store = MTH_ICU_GetStore() + store.SHOW_GUILD_NAME = this:GetChecked() == 1 and true or false + end, + }) + controls.showPlayerClass = infoBox:Checkbox("MetaHuntOptionsICUShowClass", "Show player class", { + onClick = function() + local store = MTH_ICU_GetStore() + store.SHOW_PLAYER_CLASS = this:GetChecked() == 1 and true or false + end, + }) + controls.showPlayerRace = infoBox:Checkbox("MetaHuntOptionsICUShowRace", "Show player race", { + onClick = function() + local store = MTH_ICU_GetStore() + store.SHOW_PLAYER_RACE = this:GetChecked() == 1 and true or false + end, + }) + controls.showCustomTitles = infoBox:Checkbox("MetaHuntOptionsICUShowTitles", "Show custom titles in alerts", { + onClick = function() + local store = MTH_ICU_GetStore() + store.SHOW_CUSTOM_TITLES = this:GetChecked() == 1 and true or false + end, + }) + panel:Finish() end function MTH_RefreshICUOptions() diff --git a/api/options-layout.lua b/api/options-layout.lua new file mode 100644 index 0000000..d8b8f66 --- /dev/null +++ b/api/options-layout.lua @@ -0,0 +1,608 @@ +-- ===================================================================== +-- MetaHunt Option Panel Layout Engine +-- --------------------------------------------------------------------- +-- One reusable builder that every option panel uses. It removes ALL +-- hand-typed pixel offsets and box heights from the panels: you declare +-- rows, the engine stacks them and auto-sizes each box. +-- +-- Think of it as CSS + flexbox for WoW 1.12: +-- * MTH_Layout.Theme -> the "CSS variables" (spacing / fonts / colors). +-- Change a value here to restyle every panel. +-- * panel / box methods -> the "flexbox": append rows, boxes grow to fit. +-- +-- Design rules baked in (so the classic bugs are impossible): +-- * Each box TITLE lives INSIDE its box -> titles can never collide. +-- * Box HEIGHT is computed from its content -> never cramped/too short. +-- * Box CONTENT is parented to the box frame -> correct z-order. +-- * Everything is reuse-by-name (getglobal) -> no duplicates on /rl. +-- +-- Typical usage: +-- local panel = MTH_Layout.Panel(container) +-- panel:Title("Feed-O-Matic") +-- panel:Note("blue helper paragraph", { lines = 3 }) +-- panel:TopCheckbox("MyEnable", "Enable module", { onClick = fn, checked = fn }) +-- local box = panel:Box("left", "MyBoxName", "General") +-- box:Text("MyStatus", "Feed key: P") +-- box:Buttons({ { name="A", label="Set Key", width=90, onClick=fn }, +-- { name="B", label="Clear", width=70, onClick=fn } }) +-- box:Note("MyHint", "blue helper text", { lines = 3 }) +-- local cb = box:Checkbox("MyOpt", "Some option", { onClick = fn, checked = true }) +-- box:EditRow("MyLbl", "Keep open bag slots:", "MyEdit", 40, { numeric = true, onChange = fn }) +-- panel:Finish() +-- ===================================================================== + +MTH_Layout = MTH_Layout or {} +local L = MTH_Layout + +-- --------------------------------------------------------------------- +-- THEME ("CSS variables": tweak these to restyle the whole UI) +-- --------------------------------------------------------------------- +L.Theme = { + -- Spacing (pixels) + panelPad = 12, -- outer gutter from the panel edges + colGap = 12, -- horizontal gap between the two columns + boxGap = 16, -- vertical gap between stacked boxes + colTopGap = 8, -- gap between the header block and the first boxes + + boxPadX = 10, -- content inset from a box's left edge + boxPadTop = 28, -- first row offset from a box's top (clears inside title) + boxPadBottom = 12, -- padding beneath the last row inside a box + cbInset = 8, -- checkbox inset from a box's left edge + + rowCheckbox = 24, -- vertical step consumed by a checkbox / edit row + rowButton = 30, -- vertical step consumed by a button row + rowSlider = 46, -- vertical step consumed by a slider (label+track+value) + rowDropdown = 50, -- vertical step consumed by a dropdown (label+menu) + textLine = 14, -- height of one wrapped text line + textGap = 6, -- extra gap after a text/note block + + titleInsetX = 10, -- box title inset X (inside the box) + titleInsetY = -12, -- box title inset Y (inside the box) + + headTop = -12, -- Y where the panel header starts + headTitleStep = 24, -- vertical step after the panel title + headNoteGap = 10, -- extra gap after a header note + headRowGap = 8, -- extra gap after a header checkbox + headLinkStep = 18, -- vertical step after a header link + + -- Fonts + fontPanelTitle = "GameFontNormal", -- panel heading + fontBoxTitle = "GameFontNormal", -- box title (recolored) + fontLabel = "GameFontNormalSmall", -- checkbox / edit labels + fontText = "GameFontNormalSmall", -- plain text + fontNote = "GameFontNormalSmall", -- helper paragraphs + fontLink = "GameFontHighlightSmall", + + -- Colors { r, g, b } + colPanelTitle = { 1.00, 0.82, 0.00 }, -- gold + colBoxTitle = { 1.00, 1.00, 1.00 }, -- white + colNote = { 0.35, 0.65, 1.00 }, -- blue helper text + colText = { 0.93, 0.93, 0.93 }, -- normal text + colLink = { 0.40, 0.75, 1.00 }, -- link idle + colLinkHover = { 0.60, 0.90, 1.00 }, -- link hover + + -- Templates + boxTemplate = "OptionFrameBoxTemplate", + checkTemplate = "UICheckButtonTemplate", + buttonTemplate = "UIPanelButtonTemplate", + editTemplate = "InputBoxTemplate", +} + +-- --------------------------------------------------------------------- +-- Small shared helpers +-- --------------------------------------------------------------------- +local function applyColor(region, c) + if region and c then region:SetTextColor(c[1], c[2], c[3]) end +end + +local function ensureFontString(parent, name, font) + local fs = getglobal(name) + if not fs then + fs = parent:CreateFontString(name, "ARTWORK", font) + end + fs:Show() + return fs +end + +local function ensureFrame(kind, name, parent, template) + local f = getglobal(name) + if not f then + f = CreateFrame(kind, name, parent, template) + end + f:SetParent(parent) + return f +end + +-- Attach (or refresh) a checkbox's label, tooltip, checked state and click. +-- labelWidth (optional) enables word-wrap; the wrapped text height is returned +-- so the caller can grow the row to fit multi-line labels. +local function configureCheckbox(cb, label, opts, T, labelWidth) + local txt = getglobal(cb:GetName() .. "Text") + if not txt then + txt = cb:CreateFontString(cb:GetName() .. "Text", "ARTWORK", T.fontLabel) + end + -- Anchor the label to the checkbox's top-right and let it grow downward so + -- wrapped lines never collide with the row above. Centre a single line + -- vertically against the checkbox. + local cbH = (cb.GetHeight and cb:GetHeight()) or 24 + local topOff = -((cbH - T.textLine) / 2) + if topOff > 0 then topOff = 0 end + txt:ClearAllPoints() + txt:SetPoint("TOPLEFT", cb, "TOPRIGHT", 4, topOff) + if labelWidth and labelWidth > 0 then + txt:SetWidth(labelWidth) + else + txt:SetWidth(0) + end + txt:SetJustifyH("LEFT") + txt:SetJustifyV("TOP") + txt:SetText(label or "") + txt:Show() + + if opts.checked ~= nil then + if type(opts.checked) == "function" then + cb:SetChecked(opts.checked()) + else + cb:SetChecked(opts.checked) + end + end + + if opts.onClick then + cb:SetScript("OnClick", opts.onClick) + end + + if opts.tooltip and opts.tooltip ~= "" then + cb.mthTooltip = opts.tooltip + cb:SetScript("OnEnter", function() + GameTooltip:SetOwner(cb, "ANCHOR_RIGHT") + GameTooltip:SetText(cb.mthTooltip, 1, 1, 1, 1, true) + GameTooltip:Show() + end) + cb:SetScript("OnLeave", function() GameTooltip:Hide() end) + end + + local h = 0 + if txt.GetStringHeight then h = txt:GetStringHeight() end + if (not h or h <= 0) and txt.GetHeight then h = txt:GetHeight() end + return h or 0 +end + +-- --------------------------------------------------------------------- +-- Box object (returned by panel:Box). Appends rows top-to-bottom and +-- keeps the box height in sync with its content. +-- --------------------------------------------------------------------- +local function makeBox(container, box, boxWidth, state, T) + local self = {} + + local function apply() + state.height = -state.cursor + T.boxPadBottom + box:SetHeight(state.height) + end + + function self:Checkbox(name, label, opts) + opts = opts or {} + local cb = ensureFrame("CheckButton", name, box, T.checkTemplate) + cb:ClearAllPoints() + cb:SetPoint("TOPLEFT", box, "TOPLEFT", T.cbInset, state.cursor) + cb:Show() + -- Wrap width = box content width minus the checkbox frame, the 4px label + -- gap, and a 2px right cushion so wrapped text never touches the border. + local cbW = (cb.GetWidth and cb:GetWidth()) or 26 + local labelWidth = boxWidth - T.boxPadX - (T.cbInset + cbW + 4) - 2 + local txtH = configureCheckbox(cb, label, opts, T, labelWidth) + -- A single line keeps the compact row height; a wrapped label grows the + -- row to clear its top-centre offset plus a gap before the next row. + local rowH = T.rowCheckbox + local needed = txtH + 8 + if txtH > (T.textLine + 4) then needed = txtH + 14 end + if needed > rowH then rowH = needed end + state.cursor = state.cursor - rowH + apply() + return cb + end + + function self:Text(name, text, opts) + opts = opts or {} + local fs = ensureFontString(box, name, opts.font or T.fontText) + fs:ClearAllPoints() + fs:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor) + fs:SetWidth(opts.width or (boxWidth - T.boxPadX * 2)) + fs:SetJustifyH("LEFT") + fs:SetJustifyV("TOP") + fs:SetText(text or "") + applyColor(fs, opts.color or T.colText) + -- Reserve vertical space: honour an explicit line count, otherwise + -- measure the wrapped height so callers don't have to guess. + local reserve + if opts.lines then + reserve = opts.lines * T.textLine + else + local h = (fs.GetStringHeight and fs:GetStringHeight()) or 0 + if (not h or h <= 0) and fs.GetHeight then h = fs:GetHeight() end + if not h or h <= 0 then h = T.textLine end + reserve = h + end + state.cursor = state.cursor - reserve - T.textGap + apply() + return fs + end + + -- Blue helper paragraph (same as MM Widget style), usable inside a box. + function self:Note(name, text, opts) + opts = opts or {} + opts.color = opts.color or T.colNote + opts.font = opts.font or T.fontNote + return self:Text(name, text, opts) + end + + -- One row of buttons placed left-to-right. + -- list = { { name, label, width, height, onClick, gap, template }, ... } + function self:Buttons(list) + local out = {} + local x = T.boxPadX + local i = 1 + while list and i <= table.getn(list) do + local spec = list[i] + local b = ensureFrame("Button", spec.name, box, spec.template or T.buttonTemplate) + b:ClearAllPoints() + b:SetPoint("TOPLEFT", box, "TOPLEFT", x, state.cursor) + b:SetWidth(spec.width or 90) + b:SetHeight(spec.height or 22) + b:SetText(spec.label or "") + b:Show() + if spec.onClick then b:SetScript("OnClick", spec.onClick) end + table.insert(out, b) + x = x + (spec.width or 90) + (spec.gap or 8) + i = i + 1 + end + state.cursor = state.cursor - T.rowButton + apply() + return out + end + + -- Label + edit box on one row (e.g. "Keep open bag slots: [__]"). + function self:EditRow(labelName, labelText, editName, editWidth, opts) + opts = opts or {} + local lbl = ensureFontString(box, labelName, T.fontLabel) + lbl:ClearAllPoints() + lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor) + lbl:SetText(labelText or "") + applyColor(lbl, T.colText) + + local edit = ensureFrame("EditBox", editName, box, T.editTemplate) + edit:ClearAllPoints() + edit:SetPoint("LEFT", lbl, "RIGHT", 8, 0) + edit:SetWidth(editWidth or 40) + edit:SetHeight(opts.height or 20) + if opts.numeric then edit:SetNumeric(true) end + edit:SetAutoFocus(false) + edit:Show() + if opts.onChange then edit:SetScript("OnTextChanged", opts.onChange) end + edit:SetScript("OnEnterPressed", function() if this then this:ClearFocus() end end) + edit:SetScript("OnEscapePressed", function() if this then this:ClearFocus() end end) + + state.cursor = state.cursor - T.rowCheckbox + apply() + return edit, lbl + end + + -- Slider with a left label and a right-aligned live value readout. + -- opts = { label, min, max, step, value, width, onChange, format } + -- format(value) -> string (defaults to a rounded integer). + function self:Slider(name, opts) + opts = opts or {} + local lbl = ensureFontString(box, name .. "Caption", T.fontLabel) + lbl:ClearAllPoints() + lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor) + lbl:SetText(opts.label or "") + applyColor(lbl, T.colText) + + local valueFs = ensureFontString(box, name .. "Value", T.fontLabel) + valueFs:ClearAllPoints() + valueFs:SetPoint("TOPRIGHT", box, "TOPRIGHT", -T.boxPadX, state.cursor) + valueFs:SetJustifyH("RIGHT") + applyColor(valueFs, T.colPanelTitle) + + local slider = ensureFrame("Slider", name, box, "OptionsSliderTemplate") + slider:ClearAllPoints() + slider:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX + 4, state.cursor - 18) + slider:SetWidth(opts.width or (boxWidth - T.boxPadX * 2 - 8)) + slider:SetMinMaxValues(opts.min or 0, opts.max or 1) + slider:SetValueStep(opts.step or 1) + slider:Show() + + -- OptionsSliderTemplate ships Low/High/Text children; silence them. + local lowFs = getglobal(name .. "Low"); if lowFs then lowFs:SetText(""); lowFs:Hide() end + local highFs = getglobal(name .. "High"); if highFs then highFs:SetText(""); highFs:Hide() end + local midFs = getglobal(name .. "Text"); if midFs then midFs:SetText("") end + + slider.mthValueFs = valueFs + slider.mthFormat = opts.format + slider.mthOnChange = opts.onChange + slider:SetScript("OnValueChanged", function() + local v = tonumber(arg1) + if not v and this and this.GetValue then v = this:GetValue() end + if not v then v = 0 end + if this.mthValueFs then + if this.mthFormat then + this.mthValueFs:SetText(this.mthFormat(v)) + else + this.mthValueFs:SetText(tostring(math.floor(v + 0.5))) + end + end + if this.mthOnChange then this.mthOnChange(v) end + end) + slider:SetValue(opts.value or opts.min or 0) + + state.cursor = state.cursor - T.rowSlider + apply() + return slider + end + + -- Dropdown menu with a left label. The caller supplies the standard + -- UIDropDownMenu initialize function and (optionally) the current text. + -- opts = { label, width, initialize, text } + function self:Dropdown(name, opts) + opts = opts or {} + local lbl = ensureFontString(box, name .. "Caption", T.fontLabel) + lbl:ClearAllPoints() + lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor) + lbl:SetText(opts.label or "") + applyColor(lbl, T.colText) + + local dd = ensureFrame("Frame", name, box, "UIDropDownMenuTemplate") + dd:ClearAllPoints() + -- UIDropDownMenu textures carry ~15px of built-in left inset. + dd:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX - 15, state.cursor - 16) + dd:Show() + if UIDropDownMenu_SetWidth then + UIDropDownMenu_SetWidth(opts.width or (boxWidth - T.boxPadX * 2 - 20), dd) + end + if UIDropDownMenu_JustifyText then UIDropDownMenu_JustifyText("LEFT", dd) end + if opts.initialize and UIDropDownMenu_Initialize then + UIDropDownMenu_Initialize(dd, opts.initialize) + end + if opts.text and UIDropDownMenu_SetText then + UIDropDownMenu_SetText(opts.text, dd) + end + + state.cursor = state.cursor - T.rowDropdown + apply() + return dd + end + + -- Single button occupying one button row (thin wrapper over :Buttons). + function self:Button(name, label, opts) + opts = opts or {} + local out = self:Buttons({ { + name = name, label = label, + width = opts.width, height = opts.height, + onClick = opts.onClick, template = opts.template, + } }) + return out[1] + end + + -- Label on the left, a colour swatch + "Pick" button on the right. + -- opts = { label, color = {r,g,b}, buttonText, buttonWidth, onClick } + function self:ColorRow(name, opts) + opts = opts or {} + local lbl = ensureFontString(box, name .. "Caption", T.fontLabel) + lbl:ClearAllPoints() + lbl:SetPoint("TOPLEFT", box, "TOPLEFT", T.boxPadX, state.cursor) + lbl:SetText(opts.label or "") + applyColor(lbl, T.colText) + + local btn = ensureFrame("Button", name .. "Button", box, T.buttonTemplate) + btn:ClearAllPoints() + btn:SetPoint("TOPRIGHT", box, "TOPRIGHT", -T.boxPadX, state.cursor + 2) + btn:SetWidth(opts.buttonWidth or 60) + btn:SetHeight(20) + btn:SetText(opts.buttonText or "Pick") + btn:Show() + if opts.onClick then btn:SetScript("OnClick", opts.onClick) end + + local swatch = getglobal(name .. "Swatch") + if not swatch then + swatch = btn:CreateTexture(name .. "Swatch", "OVERLAY") + swatch:SetTexture("Interface\\Buttons\\WHITE8X8") + swatch:SetWidth(12) + swatch:SetHeight(12) + swatch:SetPoint("LEFT", btn, "LEFT", 5, 0) + end + if opts.color then + swatch:SetVertexColor(opts.color[1], opts.color[2], opts.color[3]) + end + + state.cursor = state.cursor - T.rowCheckbox + apply() + return btn, swatch + end + + -- Manual vertical spacer. + function self:Gap(px) + state.cursor = state.cursor - (px or 12) + apply() + end + + -- Reserve a custom row of the given pixel height. Returns the box frame and + -- the TOPLEFT y-offset (within the box) where custom widgets can anchor. + -- Use for composite rows the primitives don't cover (e.g. checkbox + + -- dropdown + edit box on one line). x anchoring is left to the caller. + function self:Row(height) + local y = state.cursor + state.cursor = state.cursor - (height or T.rowCheckbox) + apply() + return box, y + end + + -- Left content inset (px) for callers positioning custom row widgets. + function self:PadX() return T.boxPadX end + + function self:Frame() return box end + + return self +end + +-- --------------------------------------------------------------------- +-- Panel object (returned by MTH_Layout.Panel). +-- --------------------------------------------------------------------- +function L.Panel(container) + if not container then return nil end + local T = L.Theme + MTH_ClearContainer(container) + + local cname = (container.GetName and container:GetName()) or "MTHPanel" + local width = (container.GetWidth and container:GetWidth()) or 0 + if not width or width < 460 then width = 580 end + + local colWidth = math.floor((width - T.panelPad * 2 - T.colGap) / 2) + if colWidth < 220 then colWidth = 220 end + local leftX = T.panelPad + local rightX = T.panelPad + colWidth + T.colGap + local fullWidth = width - T.panelPad * 2 + + local headY = T.headTop + local started = false + local col = { + left = { x = leftX, y = 0, open = nil }, + right = { x = rightX, y = 0, open = nil }, + } + local noteId = 0 + + local panel = {} + + local function startColumns() + if started then return end + started = true + local top = headY - T.colTopGap + col.left.y = top + col.right.y = top + end + + local function closeSide(side) + local c = col[side] + if c and c.open then + c.y = c.y - c.open.height - T.boxGap + c.open = nil + end + end + + -- ---- Header elements (full width, above the columns) ---- + function panel:Title(text) + local fs = ensureFontString(container, cname .. "_Title", T.fontPanelTitle) + fs:ClearAllPoints() + fs:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY) + fs:SetText(text or "") + applyColor(fs, T.colPanelTitle) + headY = headY - T.headTitleStep + return fs + end + + function panel:Note(text, opts) + opts = opts or {} + noteId = noteId + 1 + local fs = ensureFontString(container, cname .. "_Note" .. noteId, T.fontNote) + fs:ClearAllPoints() + fs:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY) + fs:SetWidth(opts.width or fullWidth) + fs:SetJustifyH("LEFT") + fs:SetJustifyV("TOP") + fs:SetText(text or "") + applyColor(fs, opts.color or T.colNote) + local lines = opts.lines or 1 + headY = headY - (lines * T.textLine) - T.headNoteGap + return fs + end + + function panel:Link(name, text, onClick) + local btn = ensureFrame("Button", name, container, nil) + btn:ClearAllPoints() + btn:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY) + btn:SetHeight(16) + btn:SetWidth(fullWidth) + local fs = getglobal(name .. "Text") + if not fs then + fs = btn:CreateFontString(name .. "Text", "ARTWORK", T.fontLink) + fs:SetPoint("LEFT", btn, "LEFT", 0, 0) + fs:SetJustifyH("LEFT") + end + fs:SetText(text or "") + applyColor(fs, T.colLink) + btn:Show() + btn:SetScript("OnEnter", function() applyColor(fs, T.colLinkHover) end) + btn:SetScript("OnLeave", function() applyColor(fs, T.colLink) end) + if onClick then btn:SetScript("OnClick", onClick) end + headY = headY - T.headLinkStep + return btn + end + + function panel:TopCheckbox(name, label, opts) + opts = opts or {} + local cb = ensureFrame("CheckButton", name, container, T.checkTemplate) + cb:ClearAllPoints() + cb:SetPoint("TOPLEFT", container, "TOPLEFT", T.panelPad, headY) + cb:Show() + local txtH = configureCheckbox(cb, label, opts, T, fullWidth - 26) + local rowH = T.rowCheckbox + if (txtH + 8) > rowH then rowH = txtH + 8 end + headY = headY - rowH - T.headRowGap + return cb + end + + -- ---- Boxes (columns) ---- + function panel:Box(side, name, title) + startColumns() + side = (side == "right") and "right" or "left" + closeSide(side) + local c = col[side] + + local box = ensureFrame("Frame", name, container, T.boxTemplate) + box:ClearAllPoints() + box:SetPoint("TOPLEFT", container, "TOPLEFT", c.x, c.y) + box:SetWidth(colWidth) + box:Show() + + local titleFS = getglobal(name .. "Title") + if titleFS then + titleFS:SetText(title or "") + applyColor(titleFS, T.colBoxTitle) + titleFS:ClearAllPoints() + titleFS:SetPoint("TOPLEFT", box, "TOPLEFT", T.titleInsetX, T.titleInsetY) + end + + local state = { + cursor = -T.boxPadTop, + height = T.boxPadTop + T.boxPadBottom, + } + box:SetHeight(state.height) + c.open = state + + return makeBox(container, box, colWidth, state, T) + end + + -- Returns "left" or "right": whichever column currently has more room + -- left (i.e. its content bottom is higher). Lets callers auto-balance + -- a variable number of boxes without hand-picking columns. + function panel:ShorterSide() + startColumns() + local function bottom(side) + local c = col[side] + local y = c.y + if c.open then y = y - c.open.height - T.boxGap end + return y + end + if bottom("right") > bottom("left") then return "right" end + return "left" + end + + function panel:Finish() + closeSide("left") + closeSide("right") + end + + -- Expose geometry for callers that need it (rare). + panel.colWidth = colWidth + panel.fullWidth = fullWidth + + return panel +end diff --git a/api/options-profiles.lua b/api/options-profiles.lua index d11aad4..c8272b3 100644 --- a/api/options-profiles.lua +++ b/api/options-profiles.lua @@ -300,11 +300,10 @@ function MTH_SetupProfilesOptions() 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) + local panel = MTH_Layout.Panel(container) + + -- Title (engine header, consistent gold styling) + local title = panel:Title("Profiles") -- Active profile indicator gActiveLabel = container:CreateFontString(nil, "ARTWORK", "GameFontNormalSmall") diff --git a/api/options-shell.lua b/api/options-shell.lua index c73f4c0..610e61c 100644 --- a/api/options-shell.lua +++ b/api/options-shell.lua @@ -3,8 +3,8 @@ local MTH_OPTIONS_SETUP = {} -- Track which frames have been set up MTH_OPTIONS_CONST = MTH_OPTIONS_CONST or {} MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH = MTH_OPTIONS_CONST.NAV_DEFAULT_WIDTH or 120 MTH_OPTIONS_CONST.WINDOW_PADDING = MTH_OPTIONS_CONST.WINDOW_PADDING or 12 -MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET = MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET or -58 -MTH_OPTIONS_CONST.CONTENT_HEIGHT = MTH_OPTIONS_CONST.CONTENT_HEIGHT or 566 +MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET = MTH_OPTIONS_CONST.CONTENT_TOP_OFFSET or -30 +MTH_OPTIONS_CONST.CONTENT_HEIGHT = MTH_OPTIONS_CONST.CONTENT_HEIGHT or 600 MTH_OPTIONS_CONST.CLOSE_BTN_SIZE = MTH_OPTIONS_CONST.CLOSE_BTN_SIZE or 22 MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_X or -8 MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y = MTH_OPTIONS_CONST.CLOSE_BTN_OFFSET_Y or -8 diff --git a/api/options-zbuttons.lua b/api/options-zbuttons.lua index f5e02d0..016f1e1 100644 --- a/api/options-zbuttons.lua +++ b/api/options-zbuttons.lua @@ -338,315 +338,90 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma local rightX = leftWidth + 44 local rightWidth = containerWidth - rightX - 12 - local function MTH_ZB_EnsureSection(sectionName, titleText, yOffset, sectionHeight) - local section = CreateFrame("Frame", sectionName, container, "OptionFrameBoxTemplate") - if not section then return nil end - section:ClearAllPoints() - section:SetPoint("TOPLEFT", container, "TOPLEFT", 16, yOffset) - section:SetWidth(leftWidth) - section:SetHeight(sectionHeight) - section:Show() - - local title = getglobal(sectionName .. "Title") - if title then - title:SetText(titleText) - end - - return section - end + local panel = MTH_Layout.Panel(container) local enabledValue = saved["enabled"] and true or false local enableLabel = tostring(displayName or "") if string.len(enableLabel) > 0 then enableLabel = string.lower(string.sub(enableLabel, 1, 1)) .. string.sub(enableLabel, 2) end - - local enableButton = MTH_CreateCheckbox(container, containerName.."EnableButton", string.format(MTH_ZB_L("ZB_LABEL_ENABLE_FMT", "Enable %s"), enableLabel), -8) - if enableButton then - enableButton:SetChecked(enabledValue and true or false) - enableButton.buttonName = buttonName - enableButton.buttonObj = buttonObj - enableButton:SetScript("OnClick", function() + panel:TopCheckbox(containerName.."EnableButton", string.format(MTH_ZB_L("ZB_LABEL_ENABLE_FMT", "Enable %s"), enableLabel), { + checked = enabledValue, + onClick = function() if not this then return end - local checked = MTH_ZB_IsChecked(this) - MTH_SetButtonEnabledState(this.buttonName, this.buttonObj, checked) - end) - end + MTH_SetButtonEnabledState(buttonName, buttonObj, this:GetChecked() == 1) + end, + }) - local parentSectionHeight = 116 - if buttonName == "zButtonAspect" or buttonName == "zButtonTrack" or buttonName == "zButtonPet" then - parentSectionHeight = 200 - elseif buttonName == "zButtonMounts" then - parentSectionHeight = 225 - elseif buttonName == "zButtonCompanions" then - parentSectionHeight = 196 - end - local childrenSectionY = -52 - parentSectionHeight - 16 - local parentSection = MTH_ZB_EnsureSection(containerName.."ParentSection", MTH_ZB_L("ZB_SECTION_PARENT_BUTTON", "Parent Button"), -52, parentSectionHeight) - local childrenSection = MTH_ZB_EnsureSection(containerName.."ChildrenSection", MTH_ZB_L("ZB_SECTION_CHILDREN_BUTTONS", "Children Buttons"), childrenSectionY, 290) + local parentBox = panel:Box("left", containerName.."ParentSection", MTH_ZB_L("ZB_SECTION_PARENT_BUTTON", "Parent Button")) - local parentYOffset = -18 - local childrenYOffset = -18 - - local btnSize = MTH_CreateSlider(childrenSection or container, containerName.."ButtonSize", MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"), 10, 100, 1, childrenYOffset) - if btnSize then - btnSize:SetWidth(leftWidth - 40) - btnSize:SetValue(saved["children"]["size"] or 36) - btnSize.buttonName = buttonName - btnSize.buttonObj = buttonObj - btnSize.itemList = MTH_GetButtonItemList(buttonName) - btnSize.maxButtons = maxButtons - btnSize.onChange = function(val, slider) - local frame = slider or btnSize - local btnName = frame and frame.buttonName - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - if not ZHunterMod_Saved[btnName]["children"] then - ZHunterMod_Saved[btnName]["children"] = {} + parentBox:Slider(containerName.."MainButtonSize", { + label = MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"), + min = 10, max = 100, step = 1, value = saved["parent"]["size"] or 36, + onChange = function(val) + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + if not ZHunterMod_Saved[buttonName]["parent"] then + ZHunterMod_Saved[buttonName]["parent"] = {} end local size = math.floor((val or 0) + 0.5) - ZHunterMod_Saved[btnName]["children"]["size"] = size - MTH_RefreshButtonGeometry(btnName, frame.buttonObj) - end - end - childrenYOffset = childrenYOffset - 50 - - local rowCount = MTH_CreateSlider(childrenSection or container, containerName.."RowCount", MTH_ZB_L("ZB_LABEL_NUMBER_OF_ROWS", "Number of Rows"), 1, maxButtons, 1, childrenYOffset) - if rowCount then - rowCount:SetWidth(leftWidth - 40) - rowCount:SetValue(saved["rows"] or 1) - rowCount.buttonName = buttonName - rowCount.buttonObj = buttonObj - rowCount.itemList = MTH_GetButtonItemList(buttonName) - rowCount.maxButtons = maxButtons - rowCount.onChange = function(val, slider) - local frame = slider or rowCount - local btnName = frame and frame.buttonName - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - ZHunterMod_Saved[btnName]["rows"] = math.floor((val or 1) + 0.5) - MTH_RefreshButtonGeometry(btnName, frame.buttonObj) - end - end - childrenYOffset = childrenYOffset - 40 - - local expandLeft = MTH_CreateCheckbox(childrenSection or container, containerName.."ExpandLeft", MTH_ZB_L("ZB_LABEL_EXPAND_LEFT", "Expand Left (opposite side)"), childrenYOffset) - if expandLeft then - expandLeft:SetChecked(saved["firstbutton"] == "LEFT") - expandLeft.buttonName = buttonName - expandLeft.buttonObj = buttonObj - expandLeft:SetScript("OnClick", function() - if not this then return end - local checked = MTH_ZB_IsChecked(this) - local btnName = this.buttonName - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - ZHunterMod_Saved[btnName]["firstbutton"] = checked and "LEFT" or "RIGHT" - ZHunterMod_Saved[btnName]["horizontal"] = checked and 1 or nil - ZHunterMod_Saved[btnName]["vertical"] = checked and 1 or nil - MTH_RefreshButtonGeometry(btnName, this.buttonObj) - end) - end - childrenYOffset = childrenYOffset - 30 - - local hideClick = MTH_CreateCheckbox(childrenSection or container, containerName.."HideOnClick", MTH_ZB_L("ZB_LABEL_HIDE_BUTTONS_ON_CLICK", "Hide Buttons On Click"), childrenYOffset) - if hideClick then - hideClick:SetChecked(saved["children"]["hideonclick"]) - hideClick.buttonName = buttonName - hideClick.buttonObj = buttonObj - if hideClick.buttonObj then - hideClick.buttonObj.hideonclick = saved["children"]["hideonclick"] and true or false - end - hideClick:SetScript("OnClick", function() - if not this then return end - local checked = MTH_ZB_IsChecked(this) - ZHunterMod_Saved[this.buttonName]["children"]["hideonclick"] = checked - local btnObj = this.buttonObj or getglobal(this.buttonName) - if btnObj then btnObj.hideonclick = checked end - end) - end - childrenYOffset = childrenYOffset - 25 - - local expandOnHover = MTH_CreateCheckbox(childrenSection or container, containerName.."ExpandOnHover", MTH_ZB_L("ZB_LABEL_EXPAND_ON_HOVER", "Expand on Hover"), childrenYOffset) - if expandOnHover then - expandOnHover:SetChecked(saved["children"]["expandonhover"]) - expandOnHover.buttonName = buttonName - expandOnHover.buttonObj = buttonObj - expandOnHover:SetScript("OnClick", function() - if not this then return end - local checked = MTH_ZB_IsChecked(this) - local btnName = this.buttonName - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - if not ZHunterMod_Saved[btnName]["children"] then - ZHunterMod_Saved[btnName]["children"] = {} + ZHunterMod_Saved[buttonName]["parent"]["size"] = size + if buttonObj then + buttonObj:SetWidth(size) + buttonObj:SetHeight(size) end - ZHunterMod_Saved[btnName]["children"]["expandonhover"] = checked - local btnObj = this.buttonObj or getglobal(btnName) - if btnObj then btnObj.expandonhover = checked end - end) - end - childrenYOffset = childrenYOffset - 40 + MTH_RefreshButtonGeometry(buttonName, buttonObj) + end, + }) - local fadeTimer = MTH_CreateSlider(childrenSection or container, containerName.."FadeTimer", MTH_ZB_L("ZB_LABEL_AUTO_HIDE_TIMER", "Auto-Hide Timer (seconds)"), 0, 10, 1, childrenYOffset) - if fadeTimer then - fadeTimer:SetWidth(leftWidth - 40) - fadeTimer:SetValue(saved["children"]["fadetimer"] or 0) - fadeTimer.buttonName = buttonName - fadeTimer.buttonObj = buttonObj - fadeTimer.onChange = function(val, slider) - local frame = slider or fadeTimer - local btnName = frame and frame.buttonName - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - if not ZHunterMod_Saved[btnName]["children"] then - ZHunterMod_Saved[btnName]["children"] = {} - end - local secs = math.floor((val or 0) + 0.5) - ZHunterMod_Saved[btnName]["children"]["fadetimer"] = secs - local btnObj = frame.buttonObj or getglobal(btnName) - if btnObj then - btnObj.fadetimer = secs - if secs <= 0 then - ZSpellButton_StopFadeTimer(btnObj) - elseif btnObj.children and btnObj.children:IsVisible() then - ZSpellButton_StartFadeTimer(btnObj) - end - end - end - end - childrenYOffset = childrenYOffset - 40 - - local showTooltip = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowTooltip", MTH_ZB_L("ZB_LABEL_SHOW_TOOLTIP", "Show Tooltip"), childrenYOffset) - if showTooltip then - showTooltip:SetChecked(saved["tooltip"]) - showTooltip.buttonName = buttonName - showTooltip.buttonObj = buttonObj - if showTooltip.buttonObj then - showTooltip.buttonObj.tooltip = saved["tooltip"] and true or false - end - showTooltip:SetScript("OnClick", function() - if not this then return end - local checked = MTH_ZB_IsChecked(this) - ZHunterMod_Saved[this.buttonName]["tooltip"] = checked - local btnObj = this.buttonObj or getglobal(this.buttonName) - if btnObj then btnObj.tooltip = checked end - end) - end - childrenYOffset = childrenYOffset - 20 - - if buttonName == "zButtonAmmo" then - local showAmmoName = MTH_CreateCheckbox(childrenSection or container, containerName.."ShowAmmoName", MTH_ZB_L("ZB_LABEL_SHOW_AMMO_NAME", "Show ammo name"), childrenYOffset) - if showAmmoName then - showAmmoName:SetChecked(saved["showammoname"]) - showAmmoName.buttonName = buttonName - showAmmoName.buttonObj = buttonObj - showAmmoName.maxButtons = maxButtons - showAmmoName:SetScript("OnClick", function() - if not this then return end - local checked = MTH_ZB_IsChecked(this) - local btnName = this.buttonName - ZHunterMod_Saved[btnName]["showammoname"] = checked - local btnObj = this.buttonObj or getglobal(btnName) - if btnObj then - for i = 1, (btnObj.count or 0) do - local child = getglobal(btnName..i) - if child and child.ammoname and not child.isspell then - zButtonAmmo_UpdateButton(child) - end - end - if btnObj.ammoname and not btnObj.isspell then - zButtonAmmo_UpdateButton(btnObj) - end - end - end) - end - childrenYOffset = childrenYOffset - 25 - else - childrenYOffset = childrenYOffset - 25 - end - - local mainSize = MTH_CreateSlider(parentSection or container, containerName.."MainButtonSize", MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"), 10, 100, 1, parentYOffset) - if mainSize then - mainSize:SetWidth(leftWidth - 40) - mainSize:SetValue(saved["parent"]["size"] or 36) - mainSize.buttonName = buttonName - mainSize.buttonObj = buttonObj - mainSize.onChange = function(val, slider) - local frame = slider or mainSize - local btnName = frame and frame.buttonName - local btnObj = (frame and frame.buttonObj) or (btnName and getglobal(btnName)) - if not btnName or not (ZHunterMod_Saved and ZHunterMod_Saved[btnName]) then return end - if not ZHunterMod_Saved[btnName]["parent"] then - ZHunterMod_Saved[btnName]["parent"] = {} - end - local size = math.floor((val or 0) + 0.5) - ZHunterMod_Saved[btnName]["parent"]["size"] = size - if btnObj then - btnObj:SetWidth(size) - btnObj:SetHeight(size) - end - MTH_RefreshButtonGeometry(btnName, btnObj) - end - end - parentYOffset = parentYOffset - 35 - - local hideButton = MTH_CreateCheckbox(parentSection or container, containerName.."HideButton", MTH_ZB_L("ZB_LABEL_HIDE_BUTTON", "Hide Button"), parentYOffset) - if hideButton then - hideButton:SetChecked(saved["parent"]["hide"]) - hideButton.buttonName = buttonName - hideButton.buttonObj = buttonObj - hideButton:SetScript("OnClick", function() + parentBox:Checkbox(containerName.."HideButton", MTH_ZB_L("ZB_LABEL_HIDE_BUTTON", "Hide Button"), { + checked = saved["parent"]["hide"], + onClick = function() if not this then return end local checked = this:GetChecked() == 1 - ZHunterMod_Saved[this.buttonName]["parent"]["hide"] = checked - if this.buttonObj then - if checked then this.buttonObj:Hide() else this.buttonObj:Show() end + ZHunterMod_Saved[buttonName]["parent"]["hide"] = checked + if buttonObj then + if checked then buttonObj:Hide() else buttonObj:Show() end end - end) - end - parentYOffset = parentYOffset - 25 + end, + }) - local useCircle = MTH_CreateCheckbox(parentSection or container, containerName.."UseCircle", MTH_ZB_L("ZB_LABEL_USE_CIRCLE_BUTTON", "Use Circle Button"), parentYOffset) - if useCircle then - useCircle:SetChecked(saved["parent"]["circle"]) - useCircle.buttonName = buttonName - useCircle.buttonObj = buttonObj - useCircle:SetScript("OnClick", function() + parentBox:Checkbox(containerName.."UseCircle", MTH_ZB_L("ZB_LABEL_USE_CIRCLE_BUTTON", "Use Circle Button"), { + checked = saved["parent"]["circle"], + onClick = function() if not this then return end local checked = this:GetChecked() == 1 - ZHunterMod_Saved[this.buttonName]["parent"]["circle"] = checked - if this.buttonObj and this.buttonObj.circle then - if checked then this.buttonObj.circle:Show() else this.buttonObj.circle:Hide() end + ZHunterMod_Saved[buttonName]["parent"]["circle"] = checked + if buttonObj and buttonObj.circle then + if checked then buttonObj.circle:Show() else buttonObj.circle:Hide() end end - end) - end + end, + }) - -- Smart Parent: Aspect/Track/Pet if buttonName == "zButtonAspect" or buttonName == "zButtonTrack" or buttonName == "zButtonPet" then - parentYOffset = parentYOffset - 25 - local smartParent = MTH_CreateCheckbox(parentSection or container, containerName.."SmartParent", MTH_ZB_L("ZB_LABEL_SMART_PARENT", "Smart Parent"), parentYOffset) - if smartParent then - smartParent:SetChecked(saved["parent"]["smart"]) - smartParent.buttonName = buttonName - smartParent.buttonObj = buttonObj - smartParent:SetScript("OnClick", function() + parentBox:Checkbox(containerName.."SmartParent", MTH_ZB_L("ZB_LABEL_SMART_PARENT", "Smart Parent"), { + checked = saved["parent"]["smart"], + onClick = function() if not this then return end local checked = this:GetChecked() == 1 - ZHunterMod_Saved[this.buttonName]["parent"]["smart"] = checked - local btn = this.buttonObj or getglobal(this.buttonName) + ZHunterMod_Saved[buttonName]["parent"]["smart"] = checked + local btn = buttonObj or getglobal(buttonName) if btn then if not checked then - local firstChild = getglobal(this.buttonName .. "1") + local firstChild = getglobal(buttonName .. "1") if firstChild and firstChild.id then btn.id = firstChild.id ZSpellButton_UpdateButton(btn) ZSpellButton_UpdateCooldown(btn) end else - local setupFunc = getglobal(this.buttonName .. "_SetupSizeAndPosition") + local setupFunc = getglobal(buttonName .. "_SetupSizeAndPosition") if type(setupFunc) == "function" then setupFunc() end end end - end) - end + end, + }) local smartDescText if buttonName == "zButtonAspect" then smartDescText = "When ON, the parent toggles between your 1st and 2nd aspect. If the 1st is active, the parent shows the 2nd and vice-versa. Useful for quickly swapping between two aspects in combat. When OFF, the parent always shows the 1st aspect." @@ -657,56 +432,151 @@ local function MTH_SetupButtonOptions(containerName, buttonName, displayName, ma else smartDescText = "Dynamically swap the parent icon based on context." end - local smartDesc = (parentSection or container):CreateFontString(containerName.."SmartParentDesc", "ARTWORK", "GameFontNormalSmall") - smartDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24) - smartDesc:SetWidth(leftWidth - 50) - smartDesc:SetJustifyH("LEFT") - smartDesc:SetText(smartDescText) - smartDesc:SetTextColor(0.5, 0.7, 1.0) + parentBox:Note(containerName.."SmartParentDesc", smartDescText) end - -- Random Parent: Mounts/Companions if buttonName == "zButtonMounts" or buttonName == "zButtonCompanions" then - parentYOffset = parentYOffset - 25 - local randomParent = MTH_CreateCheckbox(parentSection or container, containerName.."RandomParent", MTH_ZB_L("ZB_LABEL_RANDOM_PARENT", "Random Parent"), parentYOffset) - if randomParent then - randomParent:SetChecked(saved["parent"]["random"]) - randomParent.buttonName = buttonName - randomParent.buttonObj = buttonObj - randomParent:SetScript("OnClick", function() + parentBox:Checkbox(containerName.."RandomParent", MTH_ZB_L("ZB_LABEL_RANDOM_PARENT", "Random Parent"), { + checked = saved["parent"]["random"], + onClick = function() if not this then return end local checked = this:GetChecked() == 1 - ZHunterMod_Saved[this.buttonName]["parent"]["random"] = checked - end) - end - local randomDesc = (parentSection or container):CreateFontString(containerName.."RandomParentDesc", "ARTWORK", "GameFontNormalSmall") - randomDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24) - randomDesc:SetWidth(leftWidth - 50) - randomDesc:SetJustifyH("LEFT") - randomDesc:SetText(MTH_ZB_L("ZB_DESC_RANDOM_PARENT", "Pick a random child as parent on each login and after each use.")) - randomDesc:SetTextColor(0.5, 0.7, 1.0) + ZHunterMod_Saved[buttonName]["parent"]["random"] = checked + end, + }) + parentBox:Note(containerName.."RandomParentDesc", MTH_ZB_L("ZB_DESC_RANDOM_PARENT", "Pick a random child as parent on each login and after each use.")) end - -- AQ Mount Filter: Mounts only if buttonName == "zButtonMounts" then - parentYOffset = parentYOffset - 45 - local aqFilter = MTH_CreateCheckbox(parentSection or container, containerName.."AQFilter", MTH_ZB_L("ZB_LABEL_AQ_FILTER", "AQ Mount Filter"), parentYOffset) - if aqFilter then - aqFilter:SetChecked(saved["parent"]["aqfilter"]) - aqFilter.buttonName = buttonName - aqFilter.buttonObj = buttonObj - aqFilter:SetScript("OnClick", function() + parentBox:Checkbox(containerName.."AQFilter", MTH_ZB_L("ZB_LABEL_AQ_FILTER", "AQ Mount Filter"), { + checked = saved["parent"]["aqfilter"], + onClick = function() if not this then return end local checked = this:GetChecked() == 1 - ZHunterMod_Saved[this.buttonName]["parent"]["aqfilter"] = checked - end) - end - local aqDesc = (parentSection or container):CreateFontString(containerName.."AQFilterDesc", "ARTWORK", "GameFontNormalSmall") - aqDesc:SetPoint("TOPLEFT", parentSection or container, "TOPLEFT", 40, parentYOffset - 24) - aqDesc:SetWidth(leftWidth - 50) - aqDesc:SetJustifyH("LEFT") - aqDesc:SetText(MTH_ZB_L("ZB_DESC_AQ_FILTER", "Hide Qiraji mounts outside AQ40. Show only Qiraji inside.")) - aqDesc:SetTextColor(0.5, 0.7, 1.0) + ZHunterMod_Saved[buttonName]["parent"]["aqfilter"] = checked + end, + }) + parentBox:Note(containerName.."AQFilterDesc", MTH_ZB_L("ZB_DESC_AQ_FILTER", "Hide Qiraji mounts outside AQ40. Show only Qiraji inside.")) + end + + local childrenBox = panel:Box("left", containerName.."ChildrenSection", MTH_ZB_L("ZB_SECTION_CHILDREN_BUTTONS", "Children Buttons")) + childrenBox:Slider(containerName.."ButtonSize", { + label = MTH_ZB_L("ZB_LABEL_BUTTON_SIZE", "Button Size"), + min = 10, max = 100, step = 1, value = saved["children"]["size"] or 36, + onChange = function(val) + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + if not ZHunterMod_Saved[buttonName]["children"] then + ZHunterMod_Saved[buttonName]["children"] = {} + end + ZHunterMod_Saved[buttonName]["children"]["size"] = math.floor((val or 0) + 0.5) + MTH_RefreshButtonGeometry(buttonName, buttonObj) + end, + }) + + childrenBox:Slider(containerName.."RowCount", { + label = MTH_ZB_L("ZB_LABEL_NUMBER_OF_ROWS", "Number of Rows"), + min = 1, max = maxButtons, step = 1, value = saved["rows"] or 1, + onChange = function(val) + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + ZHunterMod_Saved[buttonName]["rows"] = math.floor((val or 1) + 0.5) + MTH_RefreshButtonGeometry(buttonName, buttonObj) + end, + }) + + childrenBox:Checkbox(containerName.."ExpandLeft", MTH_ZB_L("ZB_LABEL_EXPAND_LEFT", "Expand Left (opposite side)"), { + checked = (saved["firstbutton"] == "LEFT"), + onClick = function() + if not this then return end + local checked = this:GetChecked() == 1 + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + ZHunterMod_Saved[buttonName]["firstbutton"] = checked and "LEFT" or "RIGHT" + ZHunterMod_Saved[buttonName]["horizontal"] = checked and 1 or nil + ZHunterMod_Saved[buttonName]["vertical"] = checked and 1 or nil + MTH_RefreshButtonGeometry(buttonName, buttonObj) + end, + }) + + childrenBox:Checkbox(containerName.."HideOnClick", MTH_ZB_L("ZB_LABEL_HIDE_BUTTONS_ON_CLICK", "Hide Buttons On Click"), { + checked = saved["children"]["hideonclick"], + onClick = function() + if not this then return end + local checked = this:GetChecked() == 1 + ZHunterMod_Saved[buttonName]["children"]["hideonclick"] = checked + if buttonObj then buttonObj.hideonclick = checked end + end, + }) + if buttonObj then + buttonObj.hideonclick = saved["children"]["hideonclick"] and true or false + end + + childrenBox:Checkbox(containerName.."ExpandOnHover", MTH_ZB_L("ZB_LABEL_EXPAND_ON_HOVER", "Expand on Hover"), { + checked = saved["children"]["expandonhover"], + onClick = function() + if not this then return end + local checked = this:GetChecked() == 1 + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + if not ZHunterMod_Saved[buttonName]["children"] then + ZHunterMod_Saved[buttonName]["children"] = {} + end + ZHunterMod_Saved[buttonName]["children"]["expandonhover"] = checked + if buttonObj then buttonObj.expandonhover = checked end + end, + }) + + childrenBox:Slider(containerName.."FadeTimer", { + label = MTH_ZB_L("ZB_LABEL_AUTO_HIDE_TIMER", "Auto-Hide Timer (seconds)"), + min = 0, max = 10, step = 1, value = saved["children"]["fadetimer"] or 0, + onChange = function(val) + if not (ZHunterMod_Saved and ZHunterMod_Saved[buttonName]) then return end + if not ZHunterMod_Saved[buttonName]["children"] then + ZHunterMod_Saved[buttonName]["children"] = {} + end + local secs = math.floor((val or 0) + 0.5) + ZHunterMod_Saved[buttonName]["children"]["fadetimer"] = secs + if buttonObj then + buttonObj.fadetimer = secs + if secs <= 0 then + ZSpellButton_StopFadeTimer(buttonObj) + elseif buttonObj.children and buttonObj.children:IsVisible() then + ZSpellButton_StartFadeTimer(buttonObj) + end + end + end, + }) + + childrenBox:Checkbox(containerName.."ShowTooltip", MTH_ZB_L("ZB_LABEL_SHOW_TOOLTIP", "Show Tooltip"), { + checked = saved["tooltip"], + onClick = function() + if not this then return end + local checked = this:GetChecked() == 1 + ZHunterMod_Saved[buttonName]["tooltip"] = checked + if buttonObj then buttonObj.tooltip = checked end + end, + }) + if buttonObj then + buttonObj.tooltip = saved["tooltip"] and true or false + end + + if buttonName == "zButtonAmmo" then + childrenBox:Checkbox(containerName.."ShowAmmoName", MTH_ZB_L("ZB_LABEL_SHOW_AMMO_NAME", "Show ammo name"), { + checked = saved["showammoname"], + onClick = function() + if not this then return end + local checked = this:GetChecked() == 1 + ZHunterMod_Saved[buttonName]["showammoname"] = checked + if buttonObj then + for i = 1, (buttonObj.count or 0) do + local child = getglobal(buttonName..i) + if child and child.ammoname and not child.isspell then + zButtonAmmo_UpdateButton(child) + end + end + if buttonObj.ammoname and not buttonObj.isspell then + zButtonAmmo_UpdateButton(buttonObj) + end + end + end, + }) end MTH_SetButtonEnabledState(buttonName, buttonObj, saved["enabled"] and true or false) @@ -862,29 +732,24 @@ function MTH_SetupZBarOptions() local rightX = leftWidth + 44 local rightWidth = containerWidth - rightX - 12 - -- ===== Enable checkbox ===== - local enableBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarEnable", - "Enable zBar — group all zButtons into a single draggable bar", -8) - if enableBtn then - enableBtn:SetChecked(s.enabled and true or false) - enableBtn:SetScript("OnClick", function() + local panel = MTH_Layout.Panel(container) + + panel:TopCheckbox("MetaHuntOptionsZBarEnable", "Enable zBar", { + checked = s.enabled, + onClick = function() if type(MTH_ZBar_SetEnabled) == "function" then MTH_ZBar_SetEnabled(this:GetChecked() == 1) end - end) - end + end, + }) + panel:Note("Group all zButtons into a single draggable bar.", { width = 240 }) - -- ===== Direction header ===== - local dirLabel = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - dirLabel:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -46) - dirLabel:SetText("Bar Direction") + -- ===== Bar Direction box ===== + local dirBox = panel:Box("left", "MetaHuntOptionsZBarDirBox", "Bar Direction") - -- ===== Horizontal radio ===== - local horizBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarHoriz", - "Horizontal (side by side)", -64) - if horizBtn then - horizBtn:SetChecked(s.direction ~= "VERTICAL") - horizBtn:SetScript("OnClick", function() + dirBox:Checkbox("MetaHuntOptionsZBarHoriz", "Horizontal (side by side)", { + checked = (s.direction ~= "VERTICAL"), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() sv.direction = "HORIZONTAL" @@ -901,15 +766,12 @@ function MTH_SetupZBarOptions() else this:SetChecked(true) end - end) - end + end, + }) - -- ===== Vertical radio ===== - local vertBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarVert", - "Vertical (stacked)", -90) - if vertBtn then - vertBtn:SetChecked(s.direction == "VERTICAL") - vertBtn:SetScript("OnClick", function() + dirBox:Checkbox("MetaHuntOptionsZBarVert", "Vertical (stacked)", { + checked = (s.direction == "VERTICAL"), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() sv.direction = "VERTICAL" @@ -926,69 +788,59 @@ function MTH_SetupZBarOptions() else this:SetChecked(true) end - end) - end + end, + }) - -- ===== Children expand side (contextual to bar direction) ===== - local expandLbl = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - expandLbl:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -120) - if s.direction == "VERTICAL" then - expandLbl:SetText("Children expand side (Vertical bar):") - else - expandLbl:SetText("Children expand side (Horizontal bar):") - end + -- ===== Children box ===== + local childBox = panel:Box("left", "MetaHuntOptionsZBarChildBox", "Children") - local exp1Val, exp2Val, exp1Lbl, exp2Lbl + local exp1Val, exp2Val, exp1Lbl, exp2Lbl, expNote if s.direction == "VERTICAL" then exp1Val = "LEFT" ; exp1Lbl = "Left" exp2Val = "RIGHT" ; exp2Lbl = "Right" + expNote = "Expand side (Vertical bar):" else exp1Val = "TOP" ; exp1Lbl = "Top (above)" exp2Val = "BOTTOM" ; exp2Lbl = "Bottom (below)" + expNote = "Expand side (Horizontal bar):" end - local exp1Btn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarExp1", exp1Lbl, -138, 30) - if exp1Btn then - exp1Btn:SetChecked(s.childexpand == exp1Val) - exp1Btn.zbarVal = exp1Val - exp1Btn:SetScript("OnClick", function() + childBox:Note("MetaHuntOptionsZBarExpNote", expNote) + + childBox:Checkbox("MetaHuntOptionsZBarExp1", exp1Lbl, { + checked = (s.childexpand == exp1Val), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() - sv.childexpand = this.zbarVal + sv.childexpand = exp1Val if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end MTH_ResetAndSelectOptionsTab("ZBar") else this:SetChecked(true) end - end) - end + end, + }) - local exp2Btn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarExp2", exp2Lbl, -164, 30) - if exp2Btn then - exp2Btn:SetChecked(s.childexpand == exp2Val) - exp2Btn.zbarVal = exp2Val - exp2Btn:SetScript("OnClick", function() + childBox:Checkbox("MetaHuntOptionsZBarExp2", exp2Lbl, { + checked = (s.childexpand == exp2Val), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() - sv.childexpand = this.zbarVal + sv.childexpand = exp2Val if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end MTH_ResetAndSelectOptionsTab("ZBar") else this:SetChecked(true) end - end) - end + end, + }) - -- ===== Children layout (how children #2+ are arranged relative to #1) ===== - local arrLbl = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight") - arrLbl:SetPoint("TOPLEFT", container, "TOPLEFT", 16, -196) - arrLbl:SetText("Children layout:") + childBox:Gap(6) + childBox:Note("MetaHuntOptionsZBarArrNote", "Layout:") - local arrVBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarArrV", - "Vertical (stacked column)", -214, 30) - if arrVBtn then - arrVBtn:SetChecked((s.childarrange or "VERTICAL") == "VERTICAL") - arrVBtn:SetScript("OnClick", function() + childBox:Checkbox("MetaHuntOptionsZBarArrV", "Vertical (stacked column)", { + checked = ((s.childarrange or "VERTICAL") == "VERTICAL"), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() sv.childarrange = "VERTICAL" @@ -997,14 +849,12 @@ function MTH_SetupZBarOptions() else this:SetChecked(true) end - end) - end + end, + }) - local arrHBtn = MTH_CreateCheckbox(container, "MetaHuntOptionsZBarArrH", - "Horizontal (side-by-side row)", -240, 30) - if arrHBtn then - arrHBtn:SetChecked((s.childarrange or "VERTICAL") == "HORIZONTAL") - arrHBtn:SetScript("OnClick", function() + childBox:Checkbox("MetaHuntOptionsZBarArrH", "Horizontal (side-by-side row)", { + checked = ((s.childarrange or "VERTICAL") == "HORIZONTAL"), + onClick = function() if this:GetChecked() == 1 then local sv = MTH_ZBar_GetSaved() sv.childarrange = "HORIZONTAL" @@ -1013,36 +863,33 @@ function MTH_SetupZBarOptions() else this:SetChecked(true) end - end) - end + end, + }) - -- ===== Spacing slider ===== - local spacingSlider = MTH_CreateSlider(container, "MetaHuntOptionsZBarSpacing", - "Spacing (px)", 0, 30, 1, -300) - if spacingSlider then - spacingSlider:SetWidth(leftWidth - 40) - spacingSlider:SetValue(tonumber(s.spacing) or 4) - spacingSlider.onChange = function(val) + -- ===== Sizing box ===== + local sizeBox = panel:Box("left", "MetaHuntOptionsZBarSizeBox", "Sizing") + + sizeBox:Slider("MetaHuntOptionsZBarSpacing", { + label = "Spacing (px)", + min = 0, max = 30, step = 1, value = tonumber(s.spacing) or 4, + onChange = function(val) local sv = MTH_ZBar_GetSaved() sv.spacing = math.floor((tonumber(val) or 4) + 0.5) if sv.enabled and type(MTH_ZBar_ApplyLayout) == "function" then MTH_ZBar_ApplyLayout() end - end - end + end, + }) - -- ===== Button size slider ===== - local sizeSlider = MTH_CreateSlider(container, "MetaHuntOptionsZBarSize", - "Button Size", 10, 100, 1, -360) - if sizeSlider then - sizeSlider:SetWidth(leftWidth - 40) - sizeSlider:SetValue(tonumber(s.size) or 36) - sizeSlider.onChange = function(val) + sizeBox:Slider("MetaHuntOptionsZBarSize", { + label = "Button Size", + min = 10, max = 100, step = 1, value = tonumber(s.size) or 36, + onChange = function(val) if type(MTH_ZBar_SetSize) == "function" then MTH_ZBar_SetSize(val) end - end - end + end, + }) -- ===== Right column: Bar Order ===== local orderHeader = container:CreateFontString(nil, "ARTWORK", "GameFontHighlight") @@ -1381,375 +1228,254 @@ function MTH_SetupGeneralOptions() return end - MTH_ClearContainer(parentFrame) - local stripSaved = AutoStrip_GetSaved and AutoStrip_GetSaved() or {} - local parentWidth = parentFrame:GetWidth() or 0 - if parentWidth < 460 then - parentWidth = 520 + local panel = MTH_Layout.Panel(parentFrame) + + -- ===== LEFT: Smart Ammo ===== + local smartBox = panel:Box("left", "MetaHuntGeneralSmartAmmoBox", "Smart Ammo") + local smartModuleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("smartammo", true) + smartBox:Checkbox("MetaHuntGeneralSmartAmmoModuleToggle", "Enable module", { + checked = smartModuleEnabled, + onClick = function() + local enabled = this:GetChecked() == 1 + if MTH and MTH.SetModuleEnabled then + local ok = MTH:SetModuleEnabled("smartammo", enabled) + if not ok then + this:SetChecked(smartModuleEnabled and true or false) + return + end + smartModuleEnabled = enabled + end + MTH_SetupGeneralOptions() + end, + }) + + local smartEnabled = true + if type(MTHSmartAmmo_GetSmartEnabled) == "function" then + smartEnabled = MTHSmartAmmo_GetSmartEnabled() and true or false end + local smartToggle = smartBox:Checkbox("MetaHuntGeneralSmartAmmoEnabledToggle", "Enable Junk Shot Swaps", { + checked = smartEnabled, + onClick = function() + if type(MTHSmartAmmo_SetSmartEnabled) == "function" then + MTHSmartAmmo_SetSmartEnabled(this:GetChecked() == 1 and 1 or nil) + end + end, + }) + if not smartModuleEnabled and smartToggle and smartToggle.Disable then smartToggle:Disable() end + smartBox:Note("MetaHuntGeneralSmartAmmoEnabledHelp", "Swaps to low-tier ammo for shots that don't scale on weapon damage, then swaps back to your previous ammo.") - local gutter = 16 - local colGap = 16 - local colWidth = math.floor((parentWidth - (gutter * 2) - colGap) / 2) - if colWidth < 220 then - colWidth = 220 + local reloadEnabled = type(MTHSmartAmmo_GetReloadEnabled) == "function" and MTHSmartAmmo_GetReloadEnabled() and true or false + local reloadToggle = smartBox:Checkbox("MetaHuntGeneralSmartAmmoReloadToggle", "Enable Auto-Fallback", { + checked = reloadEnabled, + onClick = function() + if type(MTHSmartAmmo_SetReloadEnabled) == "function" then + MTHSmartAmmo_SetReloadEnabled(this:GetChecked() == 1 and 1 or nil) + end + end, + }) + if not smartModuleEnabled and reloadToggle and reloadToggle.Disable then reloadToggle:Disable() end + smartBox:Note("MetaHuntGeneralSmartAmmoReloadHelp", "Falls back to any available ammo when equipped ammo is out of stock.") + + local weaponSwapEnabled = type(MTHSmartAmmo_GetWeaponSwapEnabled) == "function" and MTHSmartAmmo_GetWeaponSwapEnabled() and true or false + local weaponSwapToggle = smartBox:Checkbox("MetaHuntGeneralSmartAmmoWeaponSwapToggle", "Enable Weapon-Swap Auto Ammo", { + checked = weaponSwapEnabled, + onClick = function() + if type(MTHSmartAmmo_SetWeaponSwapEnabled) == "function" then + MTHSmartAmmo_SetWeaponSwapEnabled(this:GetChecked() == 1 and 1 or nil) + end + end, + }) + if not smartModuleEnabled and weaponSwapToggle and weaponSwapToggle.Disable then weaponSwapToggle:Disable() end + smartBox:Note("MetaHuntGeneralSmartAmmoWeaponSwapHelp", "When you swap between gun and bow/crossbow, instantly equips the best matching ammo from your bags.") + + -- ===== LEFT: Auto-Strip ===== + local stripBox = panel:Box("left", "MetaHuntGeneralAutoStripBox", "Auto-Strip") + stripBox:Checkbox("MetaHuntGeneralAutoStripToggle", "Enable Auto-Strip on Combat Exit", { + checked = stripSaved["autostrip"] and true or false, + onClick = function() + if type(AutoStrip_SetAutoStripToggle) == "function" then + AutoStrip_SetAutoStripToggle(this:GetChecked() == 1) + end + end, + }) + stripBox:Checkbox("MetaHuntGeneralAutoStripDisplay", "Show Strip Button", { + checked = stripSaved["display"] and true or false, + onClick = function() + if type(AutoStrip_SetDisplayToggle) == "function" then + AutoStrip_SetDisplayToggle(this:GetChecked() == 1) + end + end, + }) + stripBox:Note("MetaHuntGeneralAutoStripHelp", "Auto-strip unequips items when combat ends. Requires at least one empty bag slot.") + + -- ===== LEFT: Anti-Daze ===== + local antiBox = panel:Box("left", "MetaHuntGeneralAntiDazeBox", "Anti-Daze") + local antiDazeEnabled = (AntiDaze_GetEnabled and AntiDaze_GetEnabled()) or false + antiBox:Checkbox("MetaHuntGeneralAntiDazeToggle", "Enable Anti-Daze", { + checked = antiDazeEnabled, + onClick = function() + local enabled = this:GetChecked() == 1 + if type(AntiDaze_SetEnabled) == "function" then + AntiDaze_SetEnabled(enabled, 1) + end + if MTH and MTH.Print then + MTH:Print("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) + elseif DEFAULT_CHAT_FRAME then + DEFAULT_CHAT_FRAME:AddMessage("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) + end + end, + }) + antiBox:Note("MetaHuntGeneralAntiDazeHelp", "Cancels Cheetah/Pack when dazed.") + + -- ===== LEFT: Pet Auto-Teach ===== + local petTeachBox = panel:Box("left", "MetaHuntGeneralPetTeachBox", "Pet Auto-Teach") + local growlEnabled = (type(MTH_Growl_IsEnabled) == "function" and MTH_Growl_IsEnabled()) or false + petTeachBox:Checkbox("MetaHuntGeneralPetTeachGrowlToggle", "Activate Auto-Teach of Growl (Max rank)", { + checked = growlEnabled, + onClick = function() + if type(MTH_Growl_SetEnabled) == "function" then + MTH_Growl_SetEnabled(this:GetChecked() == 1) + end + end, + }) + petTeachBox:Note("MetaHuntGeneralPetTeachGrowlHelp", "When you tame a beast, offers to teach it the best Growl rank for its level.") + + -- ===== RIGHT: Tooltips ===== + local tooltipsBox = panel:Box("right", "MetaHuntGeneralTooltipsBox", "Tooltips") + local tooltipsStore = MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("tooltips") + if type(tooltipsStore) ~= "table" then + tooltipsStore = {} end - - local leftX = gutter - local rightX = gutter + colWidth + colGap - local topY = -8 - - local function ensureSection(name, title, yOffset, height, column) - local section = getglobal(name) - if not section then - section = CreateFrame("Frame", name, parentFrame, "OptionFrameBoxTemplate") + if MTH and MTH.GetModuleSavedVariables and next(tooltipsStore) == nil then + local accountStore = MTH:GetModuleSavedVariables("tooltips") + if type(accountStore) == "table" and next(accountStore) ~= nil then + for key, value in pairs(accountStore) do + if type(value) == "table" then + local copied = {} + for subKey, subValue in pairs(value) do + copied[subKey] = subValue + end + tooltipsStore[key] = copied + else + tooltipsStore[key] = value + end + end end - if not section then - return nil - end - - section:SetParent(parentFrame) - section:ClearAllPoints() - if column == "right" then - section:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", rightX, yOffset) + end + if tooltipsStore.beastTooltips == nil then + tooltipsStore.beastTooltips = true + end + if tooltipsStore.ammoVendorTooltips == nil then + tooltipsStore.ammoVendorTooltips = true + end + if tooltipsStore.foodItemTooltips == nil then + if type(FOM_Config) == "table" and FOM_Config.Tooltip ~= nil then + tooltipsStore.foodItemTooltips = FOM_Config.Tooltip and true or false else - section:SetPoint("TOPLEFT", parentFrame, "TOPLEFT", leftX, yOffset) + tooltipsStore.foodItemTooltips = true end - section:SetWidth(colWidth) - section:SetHeight(height) - section:Show() - - local titleFrame = getglobal(name .. "Title") - if titleFrame then - titleFrame:SetText(title) - end - - return section + end + if tooltipsStore.ownPetTooltips == nil then + tooltipsStore.ownPetTooltips = false end - local function ensureCheckbox(section, name, label, yOffset, checked) - local check = getglobal(name) - if not check then - check = CreateFrame("CheckButton", name, section, "OptionsCheckButtonTemplate") - end - if not check then - return nil - end + local tooltipsModuleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("tooltips", true) + local foodLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD", "Activate on food")) or "Activate on food" + local foodHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD_HELP", "Show your current pet's food preference directly in item tooltips.")) or "Show your current pet's food preference directly in item tooltips." + local ownPetLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET", "Activate on my pet")) or "Activate on my pet" + local ownPetHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET_HELP", "Show your own pet status details in tooltip when hovering your pet context.")) or "Show your own pet status details in tooltip when hovering your pet context." - check:SetParent(section) - check:ClearAllPoints() - check:SetPoint("TOPLEFT", section, "TOPLEFT", 12, yOffset) - check:SetChecked(checked and true or false) - check:Show() - - local text = getglobal(name .. "Text") - if text then - text:SetText(label) - else - local lbl = section:CreateFontString(name .. "Text", "ARTWORK", "GameFontNormal") - lbl:SetPoint("LEFT", check, "RIGHT", 5, 0) - lbl:SetText(label) - end - - return check - end - - local function ensureHelpText(section, name, text, yOffset) - local help = getglobal(name) - if not help then - help = section:CreateFontString(name, "ARTWORK") - help:SetFont("Fonts\\FRIZQT__.TTF", 10) - help:SetJustifyH("LEFT") - end - help:ClearAllPoints() - help:SetPoint("TOPLEFT", section, "TOPLEFT", 14, yOffset) - help:SetWidth(colWidth - 24) - help:SetText(text) - help:Show() - end - - local smartAmmoSection = ensureSection("MetaHuntGeneralSmartAmmoBox", "Smart Ammo", topY, 254, "left") - if smartAmmoSection then - local smartModuleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("smartammo", true) - local smartModuleToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoModuleToggle", "Enable module", -10, smartModuleEnabled) - if smartModuleToggle then - smartModuleToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if MTH and MTH.SetModuleEnabled then - local ok = MTH:SetModuleEnabled("smartammo", enabled) - if not ok then - this:SetChecked(smartModuleEnabled and true or false) - return - end - smartModuleEnabled = enabled + tooltipsBox:Checkbox("MetaHuntGeneralTooltipsModuleToggle", "Enable module", { + checked = tooltipsModuleEnabled, + onClick = function() + local enabled = this:GetChecked() == 1 + if MTH and MTH.SetModuleEnabled then + local ok = MTH:SetModuleEnabled("tooltips", enabled) + if not ok then + this:SetChecked(tooltipsModuleEnabled and true or false) + return end - MTH_SetupGeneralOptions() - end) - end - - local moduleHelp = getglobal("MetaHuntGeneralSmartAmmoModuleHelp") - if moduleHelp then - moduleHelp:Hide() - end - - local smartEnabled = true - if type(MTHSmartAmmo_GetSmartEnabled) == "function" then - smartEnabled = MTHSmartAmmo_GetSmartEnabled() and true or false - end - local smartToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoEnabledToggle", "Enable Junk Shot Swaps", -58, smartEnabled) - if smartToggle then - if not smartModuleEnabled then - smartToggle:Disable() + tooltipsModuleEnabled = enabled end - smartToggle:SetScript("OnClick", function() - if type(MTHSmartAmmo_SetSmartEnabled) == "function" then - MTHSmartAmmo_SetSmartEnabled(MTH_ZB_IsChecked(this) and 1 or nil) - end - end) - end - ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoEnabledHelp", "Swaps to low-tier ammo for shots that don't scale on weapon damage, then swaps back to your previous ammo.", -86) + end, + }) + tooltipsBox:Note("MetaHuntGeneralTooltipsModuleHelp", "This module allows to enhance NPC tooltips with additional information useful for hunters.") - local reloadEnabled = type(MTHSmartAmmo_GetReloadEnabled) == "function" and MTHSmartAmmo_GetReloadEnabled() and true or false - local reloadToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadToggle", "Enable Auto-Fallback", -126, reloadEnabled) - if reloadToggle then - if not smartModuleEnabled then - reloadToggle:Disable() + tooltipsBox:Checkbox("MetaHuntGeneralTooltipsBeastToggle", "Activate on Beasts", { + checked = tooltipsStore.beastTooltips and true or false, + onClick = function() + local enabled = this:GetChecked() == 1 + tooltipsStore.beastTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetBeastTooltipsEnabled then + tooltipsModule:SetBeastTooltipsEnabled(enabled) end - reloadToggle:SetScript("OnClick", function() - if type(MTHSmartAmmo_SetReloadEnabled) == "function" then - MTHSmartAmmo_SetReloadEnabled(MTH_ZB_IsChecked(this) and 1 or nil) - end - end) - end - ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoReloadHelp", "Falls back to any available ammo when equipped ammo is out of stock.", -154) + end, + }) + tooltipsBox:Note("MetaHuntGeneralTooltipsBeastHelp", "When mouseover on a Beast, its tooltip will show if it can learn you any pet abilities. They appear Green if you already know them, and Red if you don't know them yet.") - local weaponSwapEnabled = type(MTHSmartAmmo_GetWeaponSwapEnabled) == "function" and MTHSmartAmmo_GetWeaponSwapEnabled() and true or false - local weaponSwapToggle = ensureCheckbox(smartAmmoSection, "MetaHuntGeneralSmartAmmoWeaponSwapToggle", "Enable Weapon-Swap Auto Ammo", -180, weaponSwapEnabled) - if weaponSwapToggle then - if not smartModuleEnabled then - weaponSwapToggle:Disable() + tooltipsBox:Checkbox("MetaHuntGeneralTooltipsOwnPetToggle", ownPetLabel, { + checked = tooltipsStore.ownPetTooltips and true or false, + onClick = function() + local enabled = this:GetChecked() == 1 + tooltipsStore.ownPetTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetOwnPetTooltipsEnabled then + tooltipsModule:SetOwnPetTooltipsEnabled(enabled) end - weaponSwapToggle:SetScript("OnClick", function() - if type(MTHSmartAmmo_SetWeaponSwapEnabled) == "function" then - MTHSmartAmmo_SetWeaponSwapEnabled(MTH_ZB_IsChecked(this) and 1 or nil) - end - end) - end - ensureHelpText(smartAmmoSection, "MetaHuntGeneralSmartAmmoWeaponSwapHelp", "When you swap between gun and bow/crossbow, instantly equips the best matching ammo from your bags.", -208) - end + end, + }) + tooltipsBox:Note("MetaHuntGeneralTooltipsOwnPetHelp", ownPetHelp) - local stripSection = ensureSection("MetaHuntGeneralAutoStripBox", "Auto-Strip", topY - 270, 100, "left") - if stripSection then - local autoStripToggle = ensureCheckbox(stripSection, "MetaHuntGeneralAutoStripToggle", "Enable Auto-Strip on Combat Exit", -10, stripSaved["autostrip"] and true or false) - if autoStripToggle then - autoStripToggle:SetScript("OnClick", function() - if type(AutoStrip_SetAutoStripToggle) == "function" then - AutoStrip_SetAutoStripToggle(MTH_ZB_IsChecked(this)) - end - end) - end - - local displayToggle = ensureCheckbox(stripSection, "MetaHuntGeneralAutoStripDisplay", "Show Strip Button", -36, stripSaved["display"] and true or false) - if displayToggle then - displayToggle:SetScript("OnClick", function() - if type(AutoStrip_SetDisplayToggle) == "function" then - AutoStrip_SetDisplayToggle(MTH_ZB_IsChecked(this)) - end - end) - end - - ensureHelpText(stripSection, "MetaHuntGeneralAutoStripHelp", "Auto-strip unequips items when combat ends. Requires at least one empty bag slot.", -64) - end - - local antiSection = ensureSection("MetaHuntGeneralAntiDazeBox", "Anti-Daze", topY - 384, 76, "left") - if antiSection then - local antiDazeEnabled = (AntiDaze_GetEnabled and AntiDaze_GetEnabled()) or false - local antiToggle = ensureCheckbox(antiSection, "MetaHuntGeneralAntiDazeToggle", "Enable Anti-Daze", -10, antiDazeEnabled) - if antiToggle then - antiToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if type(AntiDaze_SetEnabled) == "function" then - AntiDaze_SetEnabled(enabled, 1) - end - if DEFAULT_CHAT_FRAME then - if MTH and MTH.Print then - MTH:Print("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) - else - DEFAULT_CHAT_FRAME:AddMessage("AntiDaze " .. (enabled and "Enabled." or "Disabled.")) - end - end - end) - end - - ensureHelpText(antiSection, "MetaHuntGeneralAntiDazeHelp", "Cancels Cheetah/Pack when dazed.", -40) - end - - local petTeachSection = ensureSection("MetaHuntGeneralPetTeachBox", "Pet Auto-Teach", topY - 474, 76, "left") - if petTeachSection then - local growlEnabled = (type(MTH_Growl_IsEnabled) == "function" and MTH_Growl_IsEnabled()) or false - local growlToggle = ensureCheckbox(petTeachSection, "MetaHuntGeneralPetTeachGrowlToggle", "Activate Auto-Teach of Growl (Max rank)", -10, growlEnabled) - if growlToggle then - growlToggle:SetScript("OnClick", function() - if type(MTH_Growl_SetEnabled) == "function" then - MTH_Growl_SetEnabled(MTH_ZB_IsChecked(this)) - end - end) - end - - ensureHelpText(petTeachSection, "MetaHuntGeneralPetTeachGrowlHelp", "When you tame a beast, offers to teach it the best Growl rank for its level.", -40) - end - - local tooltipsSection = ensureSection("MetaHuntGeneralTooltipsBox", "Tooltips", topY, 356, "right") - if tooltipsSection then - local tooltipsStore = MTH and MTH.GetModuleCharSavedVariables and MTH:GetModuleCharSavedVariables("tooltips") - if type(tooltipsStore) ~= "table" then - tooltipsStore = {} - end - if MTH and MTH.GetModuleSavedVariables and next(tooltipsStore) == nil then - local accountStore = MTH:GetModuleSavedVariables("tooltips") - if type(accountStore) == "table" and next(accountStore) ~= nil then - for key, value in pairs(accountStore) do - if type(value) == "table" then - local copied = {} - for subKey, subValue in pairs(value) do - copied[subKey] = subValue - end - tooltipsStore[key] = copied - else - tooltipsStore[key] = value - end - end + tooltipsBox:Checkbox("MetaHuntGeneralTooltipsAmmoVendorToggle", "Activate on Vendors", { + checked = tooltipsStore.ammoVendorTooltips and true or false, + onClick = function() + local enabled = this:GetChecked() == 1 + tooltipsStore.ammoVendorTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetAmmoVendorTooltipsEnabled then + tooltipsModule:SetAmmoVendorTooltipsEnabled(enabled) end - end - if tooltipsStore.beastTooltips == nil then - tooltipsStore.beastTooltips = true - end - if tooltipsStore.ammoVendorTooltips == nil then - tooltipsStore.ammoVendorTooltips = true - end - if tooltipsStore.foodItemTooltips == nil then - if type(FOM_Config) == "table" and FOM_Config.Tooltip ~= nil then - tooltipsStore.foodItemTooltips = FOM_Config.Tooltip and true or false - else - tooltipsStore.foodItemTooltips = true + end, + }) + tooltipsBox:Note("MetaHuntGeneralTooltipsVendorHelp", "When mouseover on a vendor, its tooltip will tell if it sells Arrows and/or Bullets.") + + tooltipsBox:Checkbox("MetaHuntGeneralTooltipsFoodToggle", foodLabel, { + checked = tooltipsStore.foodItemTooltips and true or false, + onClick = function() + local enabled = this:GetChecked() == 1 + tooltipsStore.foodItemTooltips = enabled + local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") + if tooltipsModule and tooltipsModule.SetFoodItemTooltipsEnabled then + tooltipsModule:SetFoodItemTooltipsEnabled(enabled) end - end - if tooltipsStore.ownPetTooltips == nil then - tooltipsStore.ownPetTooltips = false - end + end, + }) + tooltipsBox:Note("MetaHuntGeneralTooltipsFoodHelp", foodHelp) - local moduleEnabled = MTH and MTH.IsModuleEnabled and MTH:IsModuleEnabled("tooltips", true) - local foodLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD", "Activate on food")) or "Activate on food on food" - local foodHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_FOOD_HELP", "Show your current pet's food preference directly in item tooltips.")) or "Show your current pet's food preference directly in item tooltips." - - local moduleToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsModuleToggle", "Enable module", -10, moduleEnabled) - if moduleToggle then - moduleToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if MTH and MTH.SetModuleEnabled then - local ok = MTH:SetModuleEnabled("tooltips", enabled) - if not ok then - this:SetChecked(moduleEnabled and true or false) - return - end - moduleEnabled = enabled - end - end) - end - - ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsModuleHelp", "This module allows to enhance NPC tooltips with additional information useful for hunters.", -34) - - local beastToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsBeastToggle", "Activate on Beasts", -86, tooltipsStore.beastTooltips and true or false) - if beastToggle then - beastToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - tooltipsStore.beastTooltips = enabled - local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") - if tooltipsModule and tooltipsModule.SetBeastTooltipsEnabled then - tooltipsModule:SetBeastTooltipsEnabled(enabled) - end - end) - end - - ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsBeastHelp", "When mouseover on a Beast, its tooltip will show if it can learn you any pet abilities. They appear Green if you already know them, and Red if you don't know them yet.", -110) - - local ownPetLabel = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET", "Activate on my pet")) or "Activate on my pet" - local ownPetHelp = (MTH and MTH.GetLocalization and MTH:GetLocalization("TOOLTIPS_OPTION_OWNPET_HELP", "Show your own pet status details in tooltip when hovering your pet context.")) or "Show your own pet status details in tooltip when hovering your pet context." - local ownPetToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsOwnPetToggle", ownPetLabel, -158, tooltipsStore.ownPetTooltips and true or false) - if ownPetToggle then - ownPetToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - tooltipsStore.ownPetTooltips = enabled - local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") - if tooltipsModule and tooltipsModule.SetOwnPetTooltipsEnabled then - tooltipsModule:SetOwnPetTooltipsEnabled(enabled) - end - end) - end - - ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsOwnPetHelp", ownPetHelp, -182) - - local ammoToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsAmmoVendorToggle", "Activate on Vendors", -222, tooltipsStore.ammoVendorTooltips and true or false) - if ammoToggle then - ammoToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - tooltipsStore.ammoVendorTooltips = enabled - local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") - if tooltipsModule and tooltipsModule.SetAmmoVendorTooltipsEnabled then - tooltipsModule:SetAmmoVendorTooltipsEnabled(enabled) - end - end) - end - - ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsVendorHelp", "When mouseover on a vendor, its tooltip will tell if it sells Arrows and/or Bullets.", -246) - - local foodToggle = ensureCheckbox(tooltipsSection, "MetaHuntGeneralTooltipsFoodToggle", foodLabel, -274, tooltipsStore.foodItemTooltips and true or false) - if foodToggle then - foodToggle:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - tooltipsStore.foodItemTooltips = enabled - local tooltipsModule = MTH and MTH.GetModule and MTH:GetModule("tooltips") - if tooltipsModule and tooltipsModule.SetFoodItemTooltipsEnabled then - tooltipsModule:SetFoodItemTooltipsEnabled(enabled) - end - end) - end - - ensureHelpText(tooltipsSection, "MetaHuntGeneralTooltipsFoodHelp", foodHelp, -298) - end - - local bagsSection = ensureSection("MetaHuntGeneralBagsBox", "Bag Ammo Labels", topY - 372, 168, "right") - if bagsSection then - local bagLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBagLabelsToggle", "Add text to ammunitions in bags", -10, - MTH_BagAmmoLabels_GetEnabled and MTH_BagAmmoLabels_GetEnabled() or false) - if bagLabels then - bagLabels:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if MTH_BagAmmoLabels_SetEnabled then MTH_BagAmmoLabels_SetEnabled(enabled) end - end) - end - - local bankLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBankLabelsToggle", "Add text to ammunitions in bank", -36, - MTH_BankAmmoLabels_GetEnabled and MTH_BankAmmoLabels_GetEnabled() or false) - if bankLabels then - bankLabels:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if MTH_BankAmmoLabels_SetEnabled then MTH_BankAmmoLabels_SetEnabled(enabled) end - end) - end - - local dmgLabels = ensureCheckbox(bagsSection, "MetaHuntGeneralBagDmgToggle", "Show ammo damage (heat-coloured)", -62, - MTH_BagAmmoDamage_GetEnabled and MTH_BagAmmoDamage_GetEnabled() or false) - if dmgLabels then - dmgLabels:SetScript("OnClick", function() - local enabled = MTH_ZB_IsChecked(this) - if MTH_BagAmmoDamage_SetEnabled then MTH_BagAmmoDamage_SetEnabled(enabled) end - end) - end - - ensureHelpText(bagsSection, "MetaHuntGeneralBagDmgHelp", "Damage is heat-coloured from red (7.5) to green (20.5).", -88) - end + -- ===== RIGHT: Bag Ammo Labels ===== + local bagsBox = panel:Box("right", "MetaHuntGeneralBagsBox", "Bag Ammo Labels") + bagsBox:Checkbox("MetaHuntGeneralBagLabelsToggle", "Add text to ammunitions in bags", { + checked = MTH_BagAmmoLabels_GetEnabled and MTH_BagAmmoLabels_GetEnabled() or false, + onClick = function() + if MTH_BagAmmoLabels_SetEnabled then MTH_BagAmmoLabels_SetEnabled(this:GetChecked() == 1) end + end, + }) + bagsBox:Checkbox("MetaHuntGeneralBankLabelsToggle", "Add text to ammunitions in bank", { + checked = MTH_BankAmmoLabels_GetEnabled and MTH_BankAmmoLabels_GetEnabled() or false, + onClick = function() + if MTH_BankAmmoLabels_SetEnabled then MTH_BankAmmoLabels_SetEnabled(this:GetChecked() == 1) end + end, + }) + bagsBox:Checkbox("MetaHuntGeneralBagDmgToggle", "Show ammo damage (heat-coloured)", { + checked = MTH_BagAmmoDamage_GetEnabled and MTH_BagAmmoDamage_GetEnabled() or false, + onClick = function() + if MTH_BagAmmoDamage_SetEnabled then MTH_BagAmmoDamage_SetEnabled(this:GetChecked() == 1) end + end, + }) + bagsBox:Note("MetaHuntGeneralBagDmgHelp", "Damage is heat-coloured from red (7.5) to green (20.5).") + panel:Finish() end diff --git a/api/options.xml b/api/options.xml index e3df266..56024b1 100644 --- a/api/options.xml +++ b/api/options.xml @@ -46,12 +46,12 @@ - + - + diff --git a/api/pet-growl-teach.lua b/api/pet-growl-teach.lua index bb271a7..b3769e5 100644 --- a/api/pet-growl-teach.lua +++ b/api/pet-growl-teach.lua @@ -1,23 +1,5 @@ -- MetaHunt: auto-train Growl to a freshly tamed pet. -- --- Growl is a "trainer" pet ability (Growl / Great Stamina / Natural Armor / --- Great Resistance) that the hunter learns from a Pet Trainer NPC and then --- teaches to the active pet through the always-available BEAST TRAINING window --- (a spellbook-like hunter spell, no NPC required). Growl specifically costs --- 0 training points, so it can always be taught to a brand-new pet. --- --- The Beast Training window is driven by the vanilla Craft API: --- GetNumCrafts() -> number of rows --- GetCraftInfo(i) -> name, rankSubText, type, ... --- DoCraft(i) -> trains that row (the "Select + Train" action) --- CloseCraft() -> closes the window --- --- The correct Growl rank is a function of the pet's level: --- rank = floor(level / 10) + 1 (ranks 1..7, at pet levels 1/10/.../60) --- --- Flow on a confirmed fresh tame (when enabled): compute the target rank, open --- the Beast Training window, find the Growl row at that rank once the craft list --- populates, DoCraft() it silently, then close the window if we opened it. local MTH_GROWL_MAX_RANK = 7 diff --git a/init/api.xml b/init/api.xml index e04672e..31d8377 100644 --- a/init/api.xml +++ b/init/api.xml @@ -14,6 +14,7 @@ +