From af9edb215d4e583b7a4ba52e2d572221ee3b8ca8 Mon Sep 17 00:00:00 2001
From: Brues <5278969+brues-code@users.noreply.github.com>
Date: Fri, 30 Oct 2026 19:33:40 -0500
Subject: [PATCH] Show pfUI's dropdowns and menus with ClassicAPI's menu system
pfUIDropdownTemplate (a DropdownButton) replaces CreateDropDownButton in
the config GUI, addon profiles, the trigger editor and the icon picker
filter. The equipment set gear menu and the tracking menu open
MenuUtil context menus. pfUIMenuStyleMixin draws pfUI's backdrop behind
them, and the skin module makes it the default style and draws the
checkboxes and radios as pfUI boxes.
---
api/ui-widgets.lua | 277 +++++++----------------------------
api/ui-widgets.xml | 48 ++++++
modules/addons.lua | 36 ++---
modules/equipmentmanager.lua | 188 ++++++++----------------
modules/gui.lua | 88 ++++++-----
modules/skin.lua | 56 +++++++
modules/tracking.lua | 43 +++---
7 files changed, 292 insertions(+), 444 deletions(-)
diff --git a/api/ui-widgets.lua b/api/ui-widgets.lua
index ad2b7a18..afda557c 100644
--- a/api/ui-widgets.lua
+++ b/api/ui-widgets.lua
@@ -100,219 +100,53 @@ do -- statusbars
end
end
-do -- dropdown
- local function ListEntryOnShow()
- this.icon:SetShown(this.parent.id == this.id)
+-- [ pfUIMenuStyleMixin ]
+-- Style for ClassicAPI's menus: pfUI's backdrop behind the entries. A dropdown
+-- takes it as its menuMixin, and a context menu takes it from the menuMixin
+-- field of its owner.
+_G.pfUIMenuStyleMixin = CreateFromMixins(MenuStyleMixin)
+
+-- Menus share pooled frames that clear their backdrop on release, so the
+-- backdrop goes on the frame itself.
+function pfUIMenuStyleMixin:Generate()
+ CreateBackdrop(self, nil, true)
+end
+
+do
+ local inset = { left = 4, top = 4, right = 4, bottom = 4 }
+ function pfUIMenuStyleMixin:GetInset()
+ return inset
end
- local function ListEntryOnClick()
- this.parent:SetSelection(this.id)
-
- if this.parent.mode == "MULTISELECT" then
- this.parent:ShowMenu()
- else
- this.parent:HideMenu()
- end
-
- if this.parent.menu[this.id].func then
- this.parent.menu[this.id].func()
- end
+ -- room for a submenu arrow or a color swatch, as in the stock styles
+ local padding = { width = 10, height = 0 }
+ function pfUIMenuStyleMixin:GetChildExtentPadding()
+ return padding
end
+end
- local function ListEntryOnEnter()
- this.hover:Show()
- end
+-- [ pfUIDropdownTemplate ]
+-- Mixin of the template in ui-widgets.xml: a dropdown that shows its selection
+-- as text, with pfUI's arrow box at the right.
+_G.pfUIDropdownMixin = CreateFromMixins(ButtonStateBehaviorMixin, DropdownSelectionTextMixin)
- local function ListEntryOnLeave()
- this.hover:Hide()
- end
+function pfUIDropdownMixin:OnLoad()
+ self.Text:SetFont(pfUI.font_default, pfUI_config.global.font_size - 1, "OUTLINE")
+ CreateBackdrop(self.Arrow, nil, true)
+ self.Arrow.Icon:SetTexture(pfUI.media["img:down"])
+ self.Arrow.Icon:SetVertexColor(.8, .8, .8)
+ self.Arrow.Icon:SetAlpha(.8)
- local function ListButtonOnClick()
- if this.ToggleMenu then
- this:ToggleMenu()
- else
- this:GetParent():ToggleMenu()
- end
- end
+ ButtonStateBehaviorMixin.OnLoad(self)
+ DropdownSelectionTextMixin.OnLoad(self)
+end
- local function MenuOnUpdate()
- if not this:IsMouseOver(100, -100, -100, 100) then
- this.button:HideMenu()
- end
- end
-
- local function ListButtonOnEnter()
- this.button:SetBackdropBorderColor(this.button.cr,this.button.cg,this.button.cb,(this.button.ca or 1))
- end
-
- local function ListButtonOnLeave()
- this.button:SetBackdropBorderColor(this.button.rr,this.button.rg,this.button.rb,(this.button.ra or 1))
- end
-
- local handlers = {
- ["SetSelection"] = function(self, id)
- if id and self.menu and self.menu[id] then
- self.text:SetText(self.menu[id].text)
- self.id = id
- end
- end,
- ["SetSelectionByText"] = function(self, name)
- self:UpdateMenu()
- for id, entry in pairs(self.menu) do
- if entry.text == name then
- self:SetSelection(id)
- return true
- end
- end
-
- self.text:SetText(name)
- return nil
- end,
- ["GetSelection"] = function(self)
- self:UpdateMenu()
- if self.menu and self.menu[self.id] then
- return self.id, self.menu[self.id].text, self.menu[self.id].func
- end
- end,
- ["SetMenu"] = function(self, menu)
- if type(menu) == "function" then
- self.menu = menu()
- self.menufunc = menu
- else
- self.menu = menu
- self.menufunc = nil
- end
- end,
- ["GetMenu"] = function(self)
- self:UpdateMenu()
- return self.menu
- end,
- ["ShowMenu"] = function(self)
- self:UpdateMenu()
- self.menuframe:SetFrameLevel(self:GetFrameLevel() + 8)
- self.menuframe:SetHeight(table.getn(self.menu)*20+4)
- self.menuframe:Show()
- end,
- ["HideMenu"] = function(self)
- self.menuframe:Hide()
- end,
- ["ToggleMenu"] = function(self)
- if self.menuframe:IsShown() then
- self:HideMenu()
- else
- self:ShowMenu()
- end
- end,
- ["UpdateMenu"] = function(self)
- -- run/reload menu function if available
- if self.menufunc then self.menu = self.menufunc() end
- if not self.menu then return end
-
- -- set caption to the current value
- self.text:SetText(self.menu[self.id] and self.menu[self.id].text or "")
-
- -- refresh menu buttons
- for id, element in pairs(self.menuframe.elements) do
- element:Hide()
- end
-
- for id, data in pairs(self.menu) do
- self:CreateMenuEntry(id)
- end
- end,
- ["CreateMenuEntry"] = function(self, id)
- if not self.menu[id] then return end
-
- local frame, entry
- for count, existing in pairs(self.menuframe.elements) do
- if not existing:IsShown() then
- frame = existing
- entry = count
- break
- end
- end
-
- if not frame and not entry then
- entry = table.getn(self.menuframe.elements) + 1
- frame = CreateFrame("Button", nil, self.menuframe)
- frame:SetFrameStrata("FULLSCREEN")
- frame:ClearAllPoints()
- frame:SetPoint("TOPLEFT", self.menuframe, "TOPLEFT", 2, -(entry-1)*20-2)
- frame:SetPoint("TOPRIGHT", self.menuframe, "TOPRIGHT", -2, -(entry-1)*20-2)
- frame:SetHeight(20)
- frame.parent = self
-
- frame.icon = frame:CreateTexture(nil, "OVERLAY")
- frame.icon:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
- frame.icon:SetSize(16, 16)
- frame.icon:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
-
- frame.text = frame:CreateFontString(nil, "OVERLAY")
- frame.text:SetFontObject(GameFontWhite)
- frame.text:SetFont(pfUI.font_default, pfUI_config.global.font_size-1, "OUTLINE")
- frame.text:SetJustifyH("RIGHT")
- frame.text:SetPoint("LEFT", frame, "LEFT", 2, 0)
- frame.text:SetPoint("RIGHT", frame.icon, "LEFT", -2, 0)
-
- frame.hover = frame:CreateTexture(nil, "BACKGROUND")
- frame.hover:SetAllPoints(frame)
- frame.hover:SetTexture(.4,.4,.4,.4)
- frame.hover:Hide()
-
- table.insert(self.menuframe.elements, frame)
- end
-
- frame.id = id
- frame.text:SetText(self.menu[id].text)
-
- frame:SetScript("OnShow", ListEntryOnShow)
- frame:SetScript("OnClick", ListEntryOnClick)
- frame:SetScript("OnEnter", ListEntryOnEnter)
- frame:SetScript("OnLeave", ListEntryOnLeave)
- frame:Show()
- end,
- }
- function pfUI.api.CreateDropDownButton(name, parent)
- local frame = CreateFrame("Button", name, parent)
- frame:SetScript("OnEnter", ListButtonOnEnter)
- frame:SetScript("OnLeave", ListButtonOnLeave)
- frame:SetScript("OnClick", ListButtonOnClick)
- frame:SetHeight(20)
- frame.id = nil
-
- CreateBackdrop(frame, nil, true)
-
- local button = CreateFrame("Button", nil, frame)
- button:SetPoint("RIGHT", frame, "RIGHT", -2, 0)
- button:SetSize(16, 16)
- button:SetScript("OnClick", ListButtonOnClick)
- SkinArrowButton(button, "down")
- button.icon:SetVertexColor(1,.9,.1)
-
- local text = frame:CreateFontString(nil, "OVERLAY")
- text:SetFontObject(GameFontWhite)
- text:SetFont(pfUI.font_default, pfUI_config.global.font_size-1, "OUTLINE")
- text:SetPoint("RIGHT", button, "LEFT", -4, 0)
- text:SetJustifyH("RIGHT")
-
- local menuframe = CreateFrame("Frame", tostring(frame).."menu", parent)
- menuframe.button = frame
- menuframe.elements = {}
- menuframe:SetPoint("TOPLEFT", frame, "BOTTOMLEFT", 0, -2)
- menuframe:SetPoint("TOPRIGHT", frame, "TOPRIGHT", 0, 2)
- menuframe:SetScript("OnUpdate", MenuOnUpdate)
- menuframe:Hide()
- CreateBackdrop(menuframe, nil, true)
-
- for name, func in pairs(handlers) do
- frame[name] = func
- end
-
- frame.menuframe = menuframe
- frame.button = button
- frame.text = text
-
- return frame
+-- the arrow box takes the class color while the cursor is over the dropdown
+function pfUIDropdownMixin:OnButtonStateChanged()
+ if self:IsOver() then
+ self.Arrow:SetBackdropBorderColor(PFUI_CLASS_COLORS[UnitClassBase("player")]:GetRGB())
+ else
+ self.Arrow:SetBackdropBorderColor(GetStringColor(pfUI_config.appearance.border.color))
end
end
@@ -1149,11 +983,11 @@ function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
grid:SetSize(gridWidth + 4 + 7 + 10, gridHeight + 10)
-- Filter dropdown: All / Spells / Items
- local filterDropdown = CreateFrame("Frame", name.."Filter", parent, "UIDropDownMenuTemplate")
- filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", 0, -80)
+ local function IsFilter(value)
+ return currentFilter == value
+ end
local function ApplyFilter(value)
currentFilter = value
- UIDropDownMenu_SetSelectedValue(filterDropdown, value)
if provider then
if value == "spells" then provider:SetIconTypes({ IconDataProviderIconType.Spell })
elseif value == "items" then provider:SetIconTypes({ IconDataProviderIconType.Item })
@@ -1162,24 +996,15 @@ function pfUI.api.CreateIconPicker(name, parent, extraType, nameLabelText)
RefreshIconGrid()
end
end
- UIDropDownMenu_Initialize(filterDropdown, function()
- local info
- info = {}; info.text = ICON_FILTER_ALL; info.value = "all"
- info.func = function() ApplyFilter("all") end
- info.checked = currentFilter == "all"
- UIDropDownMenu_AddButton(info)
- info = {}; info.text = ICON_FILTER_SPELL; info.value = "spells"
- info.func = function() ApplyFilter("spells") end
- info.checked = currentFilter == "spells"
- UIDropDownMenu_AddButton(info)
- info = {}; info.text = ICON_FILTER_ITEM; info.value = "items"
- info.func = function() ApplyFilter("items") end
- info.checked = currentFilter == "items"
- UIDropDownMenu_AddButton(info)
+ local filterDropdown = CreateFrame("DropdownButton", nil, parent, "pfUIDropdownTemplate")
+ filterDropdown:SetPoint("TOPRIGHT", parent, "TOPRIGHT", -14, -84)
+ filterDropdown:SetWidth(140)
+ CreateBackdrop(filterDropdown, nil, true)
+ filterDropdown:SetupMenu(function(dropdown, rootDescription)
+ rootDescription:CreateRadio(ICON_FILTER_ALL, IsFilter, ApplyFilter, "all")
+ rootDescription:CreateRadio(ICON_FILTER_SPELL, IsFilter, ApplyFilter, "spells")
+ rootDescription:CreateRadio(ICON_FILTER_ITEM, IsFilter, ApplyFilter, "items")
end)
- UIDropDownMenu_SetWidth(120, filterDropdown)
- UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
- SkinDropDown(filterDropdown)
-- the selected icon gets a gold border in the grid and shows in the preview
local function UpdateSelection()
diff --git a/api/ui-widgets.xml b/api/ui-widgets.xml
index 9fd6dc72..df083d14 100644
--- a/api/ui-widgets.xml
+++ b/api/ui-widgets.xml
@@ -102,4 +102,52 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/modules/addons.lua b/modules/addons.lua
index aac03cc8..37f16cc5 100644
--- a/modules/addons.lua
+++ b/modules/addons.lua
@@ -110,6 +110,12 @@ pfUI:RegisterModule("addons", function ()
return selected_addons
end
+ local selectedProfile
+
+ local function IsSelectedProfile(profile)
+ return profile == selectedProfile
+ end
+
local function SetAddonProfile(profile)
-- create new profile if not existing
if not pfUI_addon_profiles[profile] then
@@ -117,7 +123,8 @@ pfUI:RegisterModule("addons", function ()
end
-- set dropdown to profile
- pfUI.addons.profile.dropdown:SetSelectionByText(profile)
+ selectedProfile = profile
+ pfUI.addons.profile.dropdown:GenerateMenu()
-- load profile
local selected = {}
@@ -163,7 +170,7 @@ pfUI:RegisterModule("addons", function ()
pfUI.addons.profile.del:SetText("-")
pfUI.addons.profile.del:SetTextColor(1,.5,.5,1)
pfUI.addons.profile.del:SetScript("OnClick", function() -- TODO
- local id, name = pfUI.addons.profile.dropdown:GetSelection()
+ local name = selectedProfile
if not name then return end
CreateQuestionDialog(T["Delete profile"] .. " '|cff33ffcc" .. name .. "|r'?", function()
pfUI_addon_profiles[name] = nil
@@ -193,26 +200,13 @@ pfUI:RegisterModule("addons", function ()
end)
-- addon profile: dropdown
- pfUI.addons.profile.dropdown = CreateDropDownButton("pfAddonListProfileList", pfUI.addons.profile)
- pfUI.addons.profile.dropdown:SetBackdrop(nil)
- pfUI.addons.profile.dropdown:ClearAllPoints()
+ pfUI.addons.profile.dropdown = CreateFrame("DropdownButton", nil, pfUI.addons.profile, "pfUIDropdownTemplate")
pfUI.addons.profile.dropdown:SetPoint("RIGHT", pfUI.addons.profile.add, "LEFT", -2, 0)
pfUI.addons.profile.dropdown:SetWidth(220)
- pfUI.addons.profile.dropdown:SetMenu(function()
- local menu = {}
-
- for name, list in pairs(pfUI_addon_profiles) do
- local name = name
- local entry = {}
- entry.text = name
- entry.checked = false
- entry.func = function()
- SetAddonProfile(name)
- end
- table.insert(menu, entry)
+ pfUI.addons.profile.dropdown:SetupMenu(function(dropdown, rootDescription)
+ for name in pairs(pfUI_addon_profiles) do
+ rootDescription:CreateRadio(name, IsSelectedProfile, SetAddonProfile, name)
end
-
- return menu
end)
local function AddDependencyLines(header, deps)
@@ -263,9 +257,7 @@ pfUI:RegisterModule("addons", function ()
local function InputOnClick(self)
SetAddonEnabled(self:GetParent().addon.index, self:GetChecked())
- local _, name = pfUI.addons.profile.dropdown:GetSelection()
- name = name or T["Current"]
- pfUI_addon_profiles[name] = GetSelectedAddonsList()
+ pfUI_addon_profiles[selectedProfile or T["Current"]] = GetSelectedAddonsList()
end
local function InitRow(row, addon)
diff --git a/modules/equipmentmanager.lua b/modules/equipmentmanager.lua
index b32e8ad0..2bd6aa83 100644
--- a/modules/equipmentmanager.lua
+++ b/modules/equipmentmanager.lua
@@ -204,66 +204,37 @@ pfUI:RegisterModule("equipmentmanager", function()
end)
-- ============================================================
- -- Per-row context menu (single instance, repositioned per click).
- -- Triggered by the gear icon on each set row.
+ -- Per-row context menu, opened from the gear icon on each set row.
-- ============================================================
- local rowMenu = CreateFrame("Frame", "pfEqMgrRowMenu", UIParent)
- rowMenu:SetFrameStrata("DIALOG")
- rowMenu:SetSize(140, 50);
- rowMenu:Hide()
- CreateBackdrop(rowMenu, nil, nil, .95)
- CreateBackdropShadow(rowMenu)
- rowMenu:EnableMouse(true)
-
- -- Close on click outside via GLOBAL_MOUSE_DOWN (fires on any click
- -- without consuming it). The anchorBtn check defers the gear-click
- -- toggle to the gear's own OnClick so opening doesn't trigger close.
- rowMenu:RegisterEvent("GLOBAL_MOUSE_DOWN")
- rowMenu:SetScript("OnEvent", function()
- if not this:IsShown() then return end
- if MouseIsOverHitRect(this) then return end
- if this.anchorBtn and MouseIsOverHitRect(this.anchorBtn) then return end
- this:Hide()
- end)
- tinsert(UISpecialFrames, "pfEqMgrRowMenu") -- Escape closes it
-
- rowMenu.changeBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
- rowMenu.changeBtn:SetSize(130, 20);
- rowMenu.changeBtn:SetPoint("TOPLEFT", rowMenu, "TOPLEFT", 5, -3)
- rowMenu.changeBtn:SetText(EQUIPMENT_SET_EDIT)
- SkinButton(rowMenu.changeBtn)
- rowMenu.changeBtn:SetScript("OnClick", function()
- rowMenu:Hide()
- if not rowMenu.targetSetID then return end
- local name, icon = C_EquipmentSet.GetEquipmentSetInfo(rowMenu.targetSetID)
- selectedSetID = rowMenu.targetSetID
+ local function EditSet(setID)
+ local name, icon = C_EquipmentSet.GetEquipmentSetInfo(setID)
+ selectedSetID = setID
OpenNamePopup("save", name, icon)
- end)
+ end
- rowMenu.deleteBtn = CreateFrame("Button", nil, rowMenu, "UIPanelButtonTemplate")
- rowMenu.deleteBtn:SetSize(130, 20);
- rowMenu.deleteBtn:SetPoint("TOP", rowMenu.changeBtn, "BOTTOM", 0, -2)
- rowMenu.deleteBtn:SetText(DELETE)
- SkinButton(rowMenu.deleteBtn)
- rowMenu.deleteBtn:SetScript("OnClick", function()
- rowMenu:Hide()
- if not rowMenu.targetSetID then return end
- local targetID = rowMenu.targetSetID
- local name = C_EquipmentSet.GetEquipmentSetInfo(targetID)
+ local function DeleteSet(setID)
+ local name = C_EquipmentSet.GetEquipmentSetInfo(setID)
StaticPopupDialogs["PFUI_EQMGR_DELETE"] = {
text = string.format(CONFIRM_DELETE_EQUIPMENT_SET, name or "?"),
button1 = YES, button2 = NO,
OnAccept = function()
- pendingIgnoredToggles[targetID] = nil
- C_EquipmentSet.DeleteEquipmentSet(targetID)
- if selectedSetID == targetID then selectedSetID = nil end
+ pendingIgnoredToggles[setID] = nil
+ C_EquipmentSet.DeleteEquipmentSet(setID)
+ if selectedSetID == setID then selectedSetID = nil end
pfUI.equipmentmanager.Refresh()
end,
timeout = 0, whileDead = 1, hideOnEscape = 1, exclusive = 1,
}
StaticPopup_Show("PFUI_EQMGR_DELETE")
- end)
+ end
+
+ -- The row owns the menu, not the gear: the gear hides when the cursor
+ -- leaves the row for the menu, and a menu closes with its owner.
+ local function GenerateSetMenu(row, rootDescription)
+ rootDescription:CreateButton(EQUIPMENT_SET_EDIT, EditSet, row.setID)
+ rootDescription:CreateButton(DELETE, DeleteSet, row.setID)
+ end
-- ============================================================
-- Set list (left column)
@@ -303,20 +274,9 @@ pfUI:RegisterModule("equipmentmanager", function()
-- moving onto the gear does not leave the row
row.gear:SetPropagateMouseMotion(true)
row.gear:Hide()
+ row.menuMixin = pfUIMenuStyleMixin
row.gear:SetScript("OnClick", function()
- if not row.setID then return end
- -- Toggle: clicking the gear that owns the open menu closes it.
- -- GLOBAL_MOUSE_DOWN's anchorBtn check excludes the gear, so the
- -- close has to happen here.
- if rowMenu:IsShown() and rowMenu.targetSetID == row.setID then
- rowMenu:Hide()
- return
- end
- rowMenu.targetSetID = row.setID
- rowMenu.anchorBtn = row.gear
- rowMenu:ClearAllPoints()
- rowMenu:SetPoint("TOPRIGHT", row.gear, "BOTTOMRIGHT", 0, 0)
- rowMenu:Show()
+ if row.setID then MenuUtil.CreateContextMenu(row, GenerateSetMenu) end
end)
row:SetScript("OnClick", function()
@@ -904,19 +864,6 @@ pfUI:RegisterModule("equipmentmanager", function()
return b
end
- -- Dropdown entries carry a `.value`; select one by value instead of index.
- local function SelectDropdownValue(dd, value)
- local menu = dd:GetMenu()
- for i, entry in ipairs(menu or {}) do
- if entry.value == value then
- dd:SetSelection(i)
- return
- end
- end
- dd.id = nil
- dd.text:SetText("")
- end
-
local function Print(msg)
DEFAULT_CHAT_FRAME:AddMessage("|cff33ffccpf|cffffffffUI|r: " .. msg)
end
@@ -1783,9 +1730,10 @@ pfUI:RegisterModule("equipmentmanager", function()
end
local function EditorDropdown(row)
- local dd = CreateDropDownButton(nil, editor)
+ local dd = CreateFrame("DropdownButton", nil, editor, "pfUIDropdownTemplate")
dd:SetWidth(190)
dd:SetPoint("TOPRIGHT", editor, "TOPRIGHT", 0, EDITOR_ROW_Y[row])
+ CreateBackdrop(dd, nil, true)
return dd
end
@@ -1801,41 +1749,49 @@ pfUI:RegisterModule("equipmentmanager", function()
RefreshTriggerPanel()
end
+ -- The radio callbacks for one field of the selected trigger; a trigger
+ -- without the field reads as `default`.
+ local function TriggerField(field, default)
+ local function IsSelected(value)
+ local t = SelectedTrigger()
+ return t ~= nil and (t[field] or default) == value
+ end
+ local function SetSelected(value)
+ local t = SelectedTrigger()
+ if not t then return end
+ t[field] = value
+ OnTriggerEdited()
+ end
+ return IsSelected, SetSelected
+ end
+
EditorLabel(T["When"], 1)
local ddType = EditorDropdown(1)
- ddType:SetMenu(function()
- local menu = {}
+ local IsType = TriggerField("type")
+ local function SetType(key)
+ local t = SelectedTrigger()
+ if not t or t.type == key then return end
+ t.type = key
+ t.value = DefaultTriggerValue(key)
+ OnTriggerEdited()
+ end
+ ddType:SetupMenu(function(dropdown, rootDescription)
for _, info in ipairs(TRIGGER_TYPES) do
if info.value ~= "list" or info.options then
- local key = info.key
- table.insert(menu, { text = info.text, value = key, func = function()
- local t = SelectedTrigger()
- if not t or t.type == key then return end
- t.type = key
- t.value = DefaultTriggerValue(key)
- OnTriggerEdited()
- end })
+ rootDescription:CreateRadio(info.text, IsType, SetType, info.key)
end
end
- return menu
end)
local valueLabel = EditorLabel(T["Value"], 2)
local ddValue = EditorDropdown(2)
- ddValue:SetMenu(function()
- local menu = {}
+ local IsValue, SetValue = TriggerField("value")
+ ddValue:SetupMenu(function(dropdown, rootDescription)
local t = SelectedTrigger()
local info = t and TRIGGER_TYPE[t.type]
for _, o in ipairs(info and info.options or {}) do
- local value = o.value
- table.insert(menu, { text = o.text, value = value, func = function()
- local t = SelectedTrigger()
- if not t then return end
- t.value = value
- OnTriggerEdited()
- end })
+ rootDescription:CreateRadio(o.text, IsValue, SetValue, o.value)
end
- return menu
end)
local editValue = CreateTextBox(nil, editor)
@@ -1907,39 +1863,22 @@ pfUI:RegisterModule("equipmentmanager", function()
EditorLabel(T["Equip"], 3)
local ddSet = EditorDropdown(3)
- ddSet:SetMenu(function()
- local menu = {}
+ local IsSet, SetSet = TriggerField("setID")
+ ddSet:SetupMenu(function(dropdown, rootDescription)
for _, id in ipairs(GetVisibleSetIDs()) do
- local setID = id
- table.insert(menu, { text = SetName(setID), value = setID, func = function()
- local t = SelectedTrigger()
- if not t then return end
- t.setID = setID
- OnTriggerEdited()
- end })
+ rootDescription:CreateRadio(SetName(id), IsSet, SetSet, id)
end
- return menu
end)
EditorLabel(T["When It Ends"], 4)
local ddLeave = EditorDropdown(4)
- ddLeave:SetMenu(function()
- local function Choose(value)
- return function()
- local t = SelectedTrigger()
- if not t then return end
- t.leave = value
- OnTriggerEdited()
- end
- end
- local menu = {
- { text = T["Restore Previous Gear"], value = "restore", func = Choose("restore") },
- { text = T["Do Nothing"], value = "none", func = Choose("none") },
- }
+ local IsLeave, SetLeave = TriggerField("leave", "restore")
+ ddLeave:SetupMenu(function(dropdown, rootDescription)
+ rootDescription:CreateRadio(T["Restore Previous Gear"], IsLeave, SetLeave, "restore")
+ rootDescription:CreateRadio(T["Do Nothing"], IsLeave, SetLeave, "none")
for _, id in ipairs(GetVisibleSetIDs()) do
- table.insert(menu, { text = T["Equip"] .. " " .. SetName(id), value = id, func = Choose(id) })
+ rootDescription:CreateRadio(T["Equip"] .. " " .. SetName(id), IsLeave, SetLeave, id)
end
- return menu
end)
editor.noSets = editor:CreateFontString(nil, "OVERLAY", "GameFontDisableSmall")
@@ -1984,7 +1923,7 @@ pfUI:RegisterModule("equipmentmanager", function()
editor:Show()
editor.placeholder:Hide()
- SelectDropdownValue(ddType, t.type)
+ ddType:GenerateMenu()
local info = TRIGGER_TYPE[t.type]
local kind = info and info.value
@@ -1993,7 +1932,7 @@ pfUI:RegisterModule("equipmentmanager", function()
editValue:SetShown(kind == "text")
btnCurrentZone:SetShown(t.type == "zone")
if kind == "list" then
- SelectDropdownValue(ddValue, t.value)
+ ddValue:GenerateMenu()
elseif kind == "text" then
editValue:ClearAllPoints()
editValue:SetPoint("TOPLEFT", editor, "TOPRIGHT", -190, EDITOR_ROW_Y[2])
@@ -2014,8 +1953,8 @@ pfUI:RegisterModule("equipmentmanager", function()
local hasSets = table.getn(GetVisibleSetIDs()) > 0
ddSet:SetShown(hasSets)
editor.noSets:SetShown(not hasSets)
- SelectDropdownValue(ddSet, t.setID)
- SelectDropdownValue(ddLeave, t.leave or "restore")
+ ddSet:GenerateMenu()
+ ddLeave:GenerateMenu()
end
triggerPanel:SetScript("OnShow", function() RefreshTriggerPanel() end)
@@ -2027,7 +1966,6 @@ pfUI:RegisterModule("equipmentmanager", function()
RefreshTriggerRows()
end)
triggerPanel:SetScript("OnHide", function()
- for _, dd in ipairs({ ddType, ddValue, ddSet, ddLeave }) do dd:HideMenu() end
editValue:ClearFocus()
end)
diff --git a/modules/gui.lua b/modules/gui.lua
index dc59de0a..beb96f44 100644
--- a/modules/gui.lua
+++ b/modules/gui.lua
@@ -48,9 +48,8 @@ pfUI:RegisterModule("gui", function ()
end})
function EntryUpdate()
- -- detect and skip during dropdowns
- local focus = GetMouseFocus()
- if focus and focus.parent and focus.parent.menu then
+ -- skip while the cursor is over an open menu
+ if Menu.GetManager():ContainsCursor() then
if this.over then
this.tex:Hide()
this.over = nil
@@ -344,45 +343,43 @@ pfUI:RegisterModule("gui", function ()
-- use dropdown widget
if widget == "dropdown" and values then
- frame.input = CreateDropDownButton(nil, frame)
- frame.input:SetBackdrop(nil)
- frame.input.menuframe:SetParent(pfUI.gui)
+ local function IsSelected(value)
+ return category[config] == value
+ end
+ local function SetSelected(value)
+ if category[config] ~= value then
+ category[config] = value
+ if ufunc then ufunc() else pfUI.gui.settingChanged = true end
+ pfUI.events:TriggerEvent("config:changed", category, config)
+ end
+ end
+
+ frame.input = CreateFrame("DropdownButton", nil, frame, "pfUIDropdownTemplate")
frame.input:SetPoint("RIGHT", frame, "RIGHT", 0, 0)
frame.input:SetWidth(180)
- frame.input:SetMenu(function()
- local menu = {}
-
- for i, k in pairs(_G.type(values) == "function" and values() or values) do
- local entry = {}
+ frame.input:SetupMenu(function(dropdown, rootDescription)
+ for _, k in pairs(_G.type(values) == "function" and values() or values) do
-- get human readable
local value, text = strsplit(":", k)
- text = text or value
-
- entry.text = text
- entry.func = function()
- if category and category[config] ~= value then
- category[config] = value
- if ufunc then ufunc() else pfUI.gui.settingChanged = true end
- pfUI.events:TriggerEvent("config:changed", category, config)
- end
- end
-
- if category and category[config] == value then
- frame.input.current = i
- end
-
- table.insert(menu, entry)
+ rootDescription:CreateRadio(text or value, IsSelected, SetSelected, value)
end
-
- return menu
end)
-
- frame.input:SetSelection(frame.input.current)
end
-- use list widget
if widget == "list" then
+ -- index of the selected entry among the "#" separated ones
+ local selected
+
+ local function IsSelected(index)
+ return index == selected
+ end
+
+ local function Select(index)
+ selected = index
+ end
+
frame.del = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
SkinButton(frame.del)
frame.del:SetWidth(16)
@@ -392,15 +389,16 @@ pfUI:RegisterModule("gui", function ()
frame.del:SetText("-")
frame.del:SetTextColor(1,.5,.5,1)
frame.del:SetScript("OnClick", function()
- local sel = frame.input:GetSelection()
+ if not selected then return end
local newconf = ""
- for id, val in pairs({strsplit("#", category[config])}) do
- if id ~= sel then newconf = newconf .. "#" .. val end
+ for id, val in ipairs({strsplit("#", category[config])}) do
+ if id ~= selected and val ~= "" then newconf = newconf .. "#" .. val end
end
+ selected = nil
category[config] = newconf
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
- frame.input:UpdateMenu()
+ frame.input:GenerateMenu()
end)
frame.add = CreateFrame("Button", nil, frame, "UIPanelButtonTemplate")
@@ -416,21 +414,19 @@ pfUI:RegisterModule("gui", function ()
category[config] = category[config] .. "#" .. this:GetParent().input:GetText()
if ufunc then ufunc() else pfUI.gui.settingChanged = true end
pfUI.events:TriggerEvent("config:changed", category, config)
- frame.input:UpdateMenu()
+ frame.input:GenerateMenu()
end, false, true)
end)
- frame.input = CreateDropDownButton(nil, frame)
- frame.input:SetBackdrop(nil)
- frame.input.menuframe:SetParent(pfUI.gui)
+ frame.input = CreateFrame("DropdownButton", nil, frame, "pfUIDropdownTemplate")
frame.input:SetPoint("RIGHT", frame.add, "LEFT", -2, 0)
frame.input:SetWidth(140)
- frame.input:SetMenu(function()
- local menu = {}
- for i, val in pairs({strsplit("#", category[config])}) do
- table.insert(menu, { text = val })
+ frame.input:SetupMenu(function(dropdown, rootDescription)
+ for id, val in ipairs({strsplit("#", category[config])}) do
+ if val ~= "" then
+ rootDescription:CreateRadio(val, IsSelected, Select, id)
+ end
end
- return menu
end)
end
@@ -2700,8 +2696,8 @@ pfUI:RegisterModule("gui", function ()
elseif is_horizontal then
if uneven ~= "Left" and uneven ~= "Right" then C.bars["bar"..id].uneven = "Left" end
end
- if uneven_frame and uneven_frame.input and uneven_frame.input.UpdateMenu then
- uneven_frame.input:UpdateMenu()
+ if uneven_frame then
+ uneven_frame.input:GenerateMenu()
end
if U["bars"] then U["bars"]() end
end
diff --git a/modules/skin.lua b/modules/skin.lua
index d7f98e49..69081ee8 100644
--- a/modules/skin.lua
+++ b/modules/skin.lua
@@ -35,6 +35,62 @@ pfUI:RegisterModule("skin", function ()
CreateBackdropShadow(_G["DropDownList"..i.."Backdrop"])
end
+ -- ClassicAPI menus that don't name a style of their own take pfUI's
+ function MenuVariants.GetDefaultMenuMixin() return pfUIMenuStyleMixin end
+ function MenuVariants.GetDefaultContextMenuMixin() return pfUIMenuStyleMixin end
+
+ -- ClassicAPI menus draw their checkboxes and radios with these. Both get a
+ -- pfUI box: a checked box holds a check, a selected radio a filled square.
+ local function CreateSelectionBox(frame, text)
+ local _, border = GetBorderSize()
+
+ local box = frame:AttachTexture()
+ frame.leftTexture1 = box
+ box:SetDrawLayer("BORDER")
+ box:SetPoint("LEFT")
+ box:SetSize(12, 12)
+ box:SetColorTexture(GetStringColorObject(C.appearance.border.color):GetRGBA())
+
+ local inside = frame:AttachTexture()
+ inside:SetDrawLayer("ARTWORK")
+ inside:SetPoint("TOPLEFT", box, "TOPLEFT", border, -border)
+ inside:SetPoint("BOTTOMRIGHT", box, "BOTTOMRIGHT", -border, border)
+ local r, g, b = GetStringColorObject(C.appearance.border.background):GetRGB()
+ inside:SetColorTexture(r, g, b, 1)
+
+ local fontString = frame:AttachFontString()
+ frame.fontString = fontString
+ fontString:SetPoint("LEFT", box, "RIGHT", 6, 0)
+ fontString:SetHeight(20)
+ fontString:SetTextToFit(text)
+
+ return box
+ end
+
+ function MenuVariants.CreateCheckbox(text, frame, isSelected, data)
+ local box = CreateSelectionBox(frame, text)
+ if isSelected(data) then
+ local check = frame:AttachTexture()
+ check:SetDrawLayer("OVERLAY")
+ check:SetPoint("CENTER", box)
+ check:SetSize(16, 16)
+ check:SetTexture("Interface\\Buttons\\UI-CheckBox-Check")
+ end
+ return box
+ end
+
+ function MenuVariants.CreateRadio(text, frame, isSelected, data)
+ local box = CreateSelectionBox(frame, text)
+ if isSelected(data) then
+ local dot = frame:AttachTexture()
+ dot:SetDrawLayer("OVERLAY")
+ dot:SetPoint("TOPLEFT", box, "TOPLEFT", 3, -3)
+ dot:SetPoint("BOTTOMRIGHT", box, "BOTTOMRIGHT", -3, 3)
+ dot:SetColorTexture(.2, 1, .8, 1)
+ end
+ return box
+ end
+
if C.global.errors_limit == "1" then
UIErrorsFrame:SetHeight(25)
end
diff --git a/modules/tracking.lua b/modules/tracking.lua
index 5e4e99a9..974edc97 100644
--- a/modules/tracking.lua
+++ b/modules/tracking.lua
@@ -42,7 +42,23 @@ pfUI:RegisterModule("tracking", function ()
fade:SetDuration(.5)
fade:SetSmoothing("IN_OUT")
- pfUI.tracking.menu = CreateFrame("Frame", "pfUIDropDownMenuTracking", nil, "UIDropDownMenuTemplate")
+ pfUI.tracking.menuMixin = pfUIMenuStyleMixin
+
+ local function IsTracking(spell)
+ return spell.texture == state.texture
+ end
+
+ local function Track(spell)
+ SetTracking(spell.index)
+ end
+
+ local function GenerateTrackingMenu(owner, rootDescription)
+ rootDescription:CreateTitle(T["Minimap Tracking"])
+ for _, spell in pairs(state.spells) do
+ local icon = CreateTextureMarkup(spell.texture, 64, 64, 16, 16, .1, .9, .1, .9)
+ rootDescription:CreateRadio(icon .. " " .. spell.name, IsTracking, Track, spell)
+ end
+ end
pfUI.tracking:RegisterEvent("PLAYER_ENTERING_WORLD")
pfUI.tracking:RegisterEvent("PLAYER_AURAS_CHANGED")
@@ -59,8 +75,7 @@ pfUI:RegisterModule("tracking", function ()
pfUI.tracking:RegisterForClicks("LeftButtonUp", "RightButtonUp")
pfUI.tracking:SetScript("OnClick", function()
if arg1 == "RightButton" then
- pfUI.tracking:InitMenu()
- ToggleDropDownMenu(1, nil, pfUI.tracking.menu, this, -5, -5)
+ MenuUtil.CreateContextMenu(this, GenerateTrackingMenu)
end
if arg1 == "LeftButton" and state.texture then
CancelTrackingBuff()
@@ -128,26 +143,4 @@ pfUI:RegisterModule("tracking", function ()
function pfUI.tracking:PlayerIsDruidInCatForm(playerClass)
return playerClass == "DRUID" and GetShapeshiftFormID() == 1
end
-
- function pfUI.tracking:InitMenu()
- UIDropDownMenu_Initialize(pfUI.tracking.menu, function ()
- UIDropDownMenu_AddButton({text = T["Minimap Tracking"], isTitle = 1})
- for _, spell in pairs(state.spells) do
- UIDropDownMenu_AddButton({
- text = spell.name,
- icon = spell.texture,
- tCoordLeft = .1,
- tCoordRight = .9,
- tCoordTop = .1,
- tCoordBottom = .9,
- checked = spell.texture == state.texture,
- arg1 = spell,
- func = function (arg1)
- SetTracking(arg1.index)
- CloseDropDownMenus()
- end
- })
- end
- end, "MENU")
- end
end)
\ No newline at end of file