mirror of
https://github.com/brues-code/pfUI.git
synced 2026-10-03 12:10:01 +00:00
Equipment manager: - Move the OK button next to Cancel at the bottom-right. - Add an icon search box in the freed bottom-left space. - Shift the popup contents up and shrink the window after the title was removed. - Show the current icon when you edit a set. Keep a full icon path as is instead of adding a second prefix, and match grid icons by name. - Close the name/icon popup when the manager frame closes. Remove the duplicate OnHide hook. Both pickers now use Blizzard global strings for the labels and the icon filters.
337 lines
14 KiB
Lua
337 lines
14 KiB
Lua
-- Macro icon picker
|
|
-- Replaces Blizzard's MacroPopupFrame (the "name + icon" dialog opened by the
|
|
-- New / Change-Icon buttons) with a pfUI-native picker driven by
|
|
-- IconDataProviderMixin, so the full spell + item + loose icon set is
|
|
-- available instead of the stock spell-only list. Saving goes through
|
|
-- C_Macro.CreateMacro / C_Macro.EditMacro, which take the icon as a texture
|
|
-- string -- so arbitrary icons (including index-less INV_* item icons) persist
|
|
-- on both the modern (Turtle/Octo) and stock-vanilla macro UIs. The surrounding
|
|
-- MacroFrame (list + body editor) is Blizzard's, already skinned elsewhere.
|
|
pfUI:RegisterModule("macroicons", function ()
|
|
HookAddonOrVariable("Blizzard_MacroUI", function()
|
|
local QUESTION_MARK = "INTERFACE\\ICONS\\INV_MISC_QUESTIONMARK"
|
|
|
|
local ICON_GRID_COLS = 10
|
|
local ICON_GRID_ROWS = 8
|
|
local ICON_BTN_SIZE = 36
|
|
local ICON_BTN_PAD = 6
|
|
|
|
-- Shared picker state (forward-declared so the grid/filter closures below
|
|
-- can capture them before RefreshIconGrid is assigned).
|
|
local provider = nil
|
|
local selectedIconPath = QUESTION_MARK
|
|
local currentFilter = "all"
|
|
local searchText = ""
|
|
local filtered = nil -- provider indices matching the search, or nil when empty
|
|
local RefreshIconGrid, RebuildFilter
|
|
|
|
local function EnsureProvider()
|
|
if not provider then
|
|
-- Spellbook extra type leads with class-relevant spell/talent icons.
|
|
provider = CreateAndInitFromMixin(IconDataProviderMixin, IconDataProviderExtraType.Spellbook)
|
|
end
|
|
end
|
|
|
|
-- ============================================================
|
|
-- Popup frame
|
|
-- ============================================================
|
|
|
|
local picker = CreateFrame("Frame", "pfMacroIconPicker", UIParent)
|
|
picker:SetFrameStrata("DIALOG")
|
|
picker:SetSize(472, 498)
|
|
-- Starts anchored to the right of the macro panel (re-anchored at open
|
|
-- once the skin's backdrop exists); dragging pins it in place after.
|
|
picker:SetPoint("BOTTOMLEFT", MacroFrame, "BOTTOMRIGHT", 8, 0)
|
|
picker:Hide()
|
|
CreateBackdrop(picker, nil, nil, .9)
|
|
CreateBackdropShadow(picker)
|
|
picker:EnableMouse(true)
|
|
picker:SetMovable(true)
|
|
picker:RegisterForDrag("LeftButton")
|
|
picker:SetScript("OnDragStart", function() this:StartMoving() end)
|
|
picker:SetScript("OnDragStop", function()
|
|
this:StopMovingOrSizing()
|
|
this.userMoved = true
|
|
end)
|
|
|
|
picker.title = picker:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
picker.title:SetPoint("TOP", picker, "TOP", 0, -10)
|
|
|
|
picker.nameLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
picker.nameLabel:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, -34)
|
|
picker.nameLabel:SetText(MACRO_POPUP_TEXT)
|
|
|
|
picker.editbox = CreateFrame("EditBox", "pfMacroIconPickerName", picker, "InputBoxTemplate")
|
|
picker.editbox:SetSize(280, 20)
|
|
picker.editbox:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, -52)
|
|
picker.editbox:SetAutoFocus(false)
|
|
picker.editbox:SetMaxLetters(16)
|
|
CreateBackdrop(picker.editbox)
|
|
|
|
picker.selectedPreview = CreateFrame("Frame", nil, picker)
|
|
picker.selectedPreview:SetSize(42, 42)
|
|
picker.selectedPreview:SetPoint("TOPRIGHT", picker, "TOPRIGHT", -14, -44)
|
|
CreateBackdrop(picker.selectedPreview)
|
|
picker.selectedPreview.tex = picker.selectedPreview:CreateTexture(nil, "ARTWORK")
|
|
picker.selectedPreview.tex:SetAllPoints(picker.selectedPreview)
|
|
picker.selectedPreview.tex:SetTexCoord(.08, .92, .08, .92)
|
|
|
|
picker.selectedLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
picker.selectedLabel:SetPoint("TOPRIGHT", picker, "TOPRIGHT", -14, -28)
|
|
picker.selectedLabel:SetText(ICON_SELECTION_TITLE_CURRENT)
|
|
picker.selectedLabel:SetTextColor(1, 0.82, 0)
|
|
|
|
picker.iconLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
picker.iconLabel:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, -100)
|
|
picker.iconLabel:SetText(MACRO_POPUP_CHOOSE_ICON)
|
|
|
|
-- Icon grid + scroll
|
|
local iconScroll = CreateFrame("ScrollFrame", "pfMacroIconScroll", picker, "FauxScrollFrameTemplate")
|
|
iconScroll:SetPoint("TOPLEFT", picker, "TOPLEFT", 14, -130)
|
|
iconScroll:SetWidth(ICON_GRID_COLS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
|
iconScroll:SetHeight(ICON_GRID_ROWS * (ICON_BTN_SIZE + ICON_BTN_PAD) - ICON_BTN_PAD)
|
|
|
|
local scrollbar = _G["pfMacroIconScrollScrollBar"]
|
|
scrollbar:ClearAllPoints()
|
|
scrollbar:SetPoint("TOPLEFT", iconScroll, "TOPRIGHT", 8, -16)
|
|
scrollbar:SetPoint("BOTTOMLEFT", iconScroll, "BOTTOMRIGHT", 8, 16)
|
|
SkinScrollbar(scrollbar)
|
|
|
|
-- Filter dropdown: All / Spells / Items
|
|
local filterDropdown = CreateFrame("Frame", "pfMacroIconFilter", picker, "UIDropDownMenuTemplate")
|
|
filterDropdown:SetPoint("TOPRIGHT", picker, "TOPRIGHT", 0, -94)
|
|
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 })
|
|
else provider:SetIconTypes(nil) end
|
|
RebuildFilter()
|
|
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)
|
|
end)
|
|
UIDropDownMenu_SetWidth(120, filterDropdown)
|
|
UIDropDownMenu_SetSelectedValue(filterDropdown, "all")
|
|
SkinDropDown(filterDropdown)
|
|
|
|
local iconButtons = {}
|
|
for r = 1, ICON_GRID_ROWS do
|
|
for c = 1, ICON_GRID_COLS do
|
|
local i = (r - 1) * ICON_GRID_COLS + c
|
|
local btn = CreateFrame("Button", nil, picker)
|
|
btn:SetSize(ICON_BTN_SIZE, ICON_BTN_SIZE)
|
|
btn:SetPoint("TOPLEFT", iconScroll, "TOPLEFT", (c-1) * (ICON_BTN_SIZE + ICON_BTN_PAD), -(r-1) * (ICON_BTN_SIZE + ICON_BTN_PAD))
|
|
CreateBackdrop(btn)
|
|
btn.texture = btn:CreateTexture(nil, "ARTWORK")
|
|
btn.texture:SetAllPoints(btn)
|
|
btn.texture:SetTexCoord(.08, .92, .08, .92)
|
|
btn:SetScript("OnClick", function()
|
|
if this.iconIndex and provider then
|
|
local path = provider:GetIconByIndex(this.iconIndex)
|
|
if path then selectedIconPath = path end
|
|
RefreshIconGrid()
|
|
end
|
|
end)
|
|
btn:SetScript("OnEnter", function()
|
|
if not this.iconIndex or not provider then return end
|
|
local path = provider:GetIconByIndex(this.iconIndex)
|
|
if type(path) == "string" then
|
|
local name = string.gsub(path, "^.-INTERFACE\\\\ICONS\\\\", "")
|
|
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
|
|
GameTooltip:SetText(name)
|
|
GameTooltip:Show()
|
|
end
|
|
end)
|
|
btn:SetScript("OnLeave", GameTooltip_Hide)
|
|
iconButtons[i] = btn
|
|
end
|
|
end
|
|
|
|
-- Normalize a texture to an uppercase basename so selection matching
|
|
-- works across sources: GetMacroInfo returns "Interface\Icons\<name>"
|
|
-- while provider paths are uppercase-prefixed for base icons and
|
|
-- mixed-case for spellbook extras.
|
|
local function IconKey(path)
|
|
if type(path) ~= "string" then return path end
|
|
return string.gsub(string.upper(path), "^.*[\\/]", "")
|
|
end
|
|
|
|
-- Rebuild the search-filtered index list (provider indices whose icon
|
|
-- basename contains the search text); nil when the search box is empty.
|
|
function RebuildFilter()
|
|
EnsureProvider()
|
|
if searchText == "" then
|
|
filtered = nil
|
|
return
|
|
end
|
|
filtered = {}
|
|
for i = 1, provider:GetNumIcons() do
|
|
local path = provider:GetIconByIndex(i)
|
|
if type(path) == "string" and string.find(IconKey(path), searchText, 1, true) then
|
|
table.insert(filtered, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
function RefreshIconGrid()
|
|
EnsureProvider()
|
|
local numIcons = filtered and table.getn(filtered) or provider:GetNumIcons()
|
|
local numRows = math.ceil(numIcons / ICON_GRID_COLS)
|
|
FauxScrollFrame_Update(iconScroll, numRows, ICON_GRID_ROWS, ICON_BTN_SIZE + ICON_BTN_PAD)
|
|
local offset = FauxScrollFrame_GetOffset(iconScroll)
|
|
for i = 1, ICON_GRID_ROWS * ICON_GRID_COLS do
|
|
local listIdx = i + offset * ICON_GRID_COLS
|
|
local btn = iconButtons[i]
|
|
if listIdx <= numIcons then
|
|
btn:Show()
|
|
local providerIdx = filtered and filtered[listIdx] or listIdx
|
|
btn.iconIndex = providerIdx
|
|
local path = provider:GetIconByIndex(providerIdx)
|
|
btn.texture:SetTexture(path)
|
|
if IconKey(path) == IconKey(selectedIconPath) then
|
|
btn.backdrop:SetBackdropBorderColor(1, 0.82, 0, 1)
|
|
else
|
|
btn.backdrop:SetBackdropBorderColor(pfUI.cache.er, pfUI.cache.eg, pfUI.cache.eb, pfUI.cache.ea)
|
|
end
|
|
else
|
|
btn:Hide()
|
|
btn.iconIndex = nil
|
|
end
|
|
end
|
|
picker.selectedPreview.tex:SetTexture(selectedIconPath)
|
|
end
|
|
|
|
iconScroll:SetScript("OnVerticalScroll", function()
|
|
FauxScrollFrame_OnVerticalScroll(ICON_BTN_SIZE + ICON_BTN_PAD, function() RefreshIconGrid() end)
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- Open / save
|
|
-- ============================================================
|
|
|
|
-- Blizzard's popup disables these while it is open; mirror that so the
|
|
-- underlying frame can't be double-driven, then let MacroFrame_Update
|
|
-- restore the correct states on close.
|
|
local function SetMacroButtons(enabled)
|
|
local m = enabled and "Enable" or "Disable"
|
|
MacroNewButton[m](MacroNewButton)
|
|
MacroEditButton[m](MacroEditButton)
|
|
MacroDeleteButton[m](MacroDeleteButton)
|
|
end
|
|
|
|
local function OpenMacroPopup(mode)
|
|
if mode == "edit" and MacroFrame.selectedMacro then
|
|
picker.mode = "edit"
|
|
local name, texture = GetMacroInfo(MacroFrame.selectedMacro)
|
|
picker.editbox:SetText(name or "")
|
|
selectedIconPath = texture or QUESTION_MARK
|
|
picker.title:SetText(T["Edit Macro"] or "Edit Macro")
|
|
else
|
|
picker.mode = "new"
|
|
picker.editbox:SetText("")
|
|
selectedIconPath = QUESTION_MARK
|
|
picker.title:SetText(T["New Macro"] or "New Macro")
|
|
end
|
|
picker.search:SetText("")
|
|
if not picker.userMoved then
|
|
picker:ClearAllPoints()
|
|
picker:SetPoint("BOTTOMLEFT", MacroFrame.backdrop or MacroFrame, "BOTTOMRIGHT", 8, 0)
|
|
end
|
|
SetMacroButtons(false)
|
|
picker:Show()
|
|
picker.editbox:SetFocus()
|
|
RefreshIconGrid()
|
|
end
|
|
|
|
local function SaveMacroPopup()
|
|
local text = strtrim(picker.editbox:GetText())
|
|
if text == "" then return end
|
|
|
|
if picker.mode == "edit" and MacroFrame.selectedMacro then
|
|
C_Macro.EditMacro(MacroFrame.selectedMacro, text, selectedIconPath, nil)
|
|
MacroFrame_SelectMacro(MacroFrame.selectedMacro)
|
|
else
|
|
local idx = C_Macro.CreateMacro(text, selectedIconPath, nil, (MacroFrame.macroBase or 0) > 0)
|
|
if idx then MacroFrame_SelectMacro(idx) end
|
|
end
|
|
picker:Hide()
|
|
end
|
|
|
|
picker:SetScript("OnHide", function()
|
|
if provider then provider:Release(); provider = nil end
|
|
MacroFrame_Update()
|
|
end)
|
|
|
|
picker.editbox:SetScript("OnEnterPressed", SaveMacroPopup)
|
|
picker.editbox:SetScript("OnEscapePressed", function() picker:Hide() end)
|
|
|
|
-- OK / Cancel
|
|
local okay = CreateFrame("Button", "pfMacroIconPickerOkay", picker, "UIPanelButtonTemplate")
|
|
okay:SetSize(80, 22)
|
|
okay:SetText(OKAY)
|
|
okay:SetScript("OnClick", SaveMacroPopup)
|
|
SkinButton(okay)
|
|
|
|
local cancel = CreateFrame("Button", "pfMacroIconPickerCancel", picker, "UIPanelButtonTemplate")
|
|
cancel:SetSize(80, 22)
|
|
cancel:SetText(CANCEL)
|
|
cancel:SetPoint("BOTTOMRIGHT", picker, "BOTTOMRIGHT", -14, 12)
|
|
cancel:SetScript("OnClick", function() picker:Hide() end)
|
|
SkinButton(cancel)
|
|
|
|
-- Okay | Cancel, both anchored to the bottom-right corner.
|
|
okay:SetPoint("BOTTOMRIGHT", cancel, "BOTTOMLEFT", -6, 0)
|
|
|
|
-- Search box (bottom-left) filters the grid by icon name.
|
|
picker.searchLabel = picker:CreateFontString(nil, "OVERLAY", "GameFontNormal")
|
|
picker.searchLabel:SetPoint("BOTTOMLEFT", picker, "BOTTOMLEFT", 16, 18)
|
|
picker.searchLabel:SetText(SEARCH)
|
|
|
|
picker.search = CreateFrame("EditBox", "pfMacroIconSearch", picker, "InputBoxTemplate")
|
|
picker.search:SetSize(180, 20)
|
|
picker.search:SetPoint("LEFT", picker.searchLabel, "RIGHT", 10, 0)
|
|
picker.search:SetAutoFocus(false)
|
|
CreateBackdrop(picker.search)
|
|
picker.search:SetScript("OnEscapePressed", function() this:ClearFocus() end)
|
|
picker.search:SetScript("OnTextChanged", function()
|
|
searchText = strupper(strtrim(this:GetText()))
|
|
RebuildFilter()
|
|
scrollbar:SetValue(0)
|
|
RefreshIconGrid()
|
|
end)
|
|
|
|
-- ============================================================
|
|
-- Take over the New / Change-Icon buttons; retire Blizzard's popup
|
|
-- ============================================================
|
|
|
|
MacroNewButton:SetScript("OnClick", function()
|
|
MacroFrame_SaveMacro()
|
|
OpenMacroPopup("new")
|
|
end)
|
|
|
|
MacroEditButton:SetScript("OnClick", function()
|
|
MacroFrame_SaveMacro()
|
|
OpenMacroPopup("edit")
|
|
end)
|
|
|
|
-- Closing the macro window takes the picker with it.
|
|
MacroFrame:HookScript("OnHide", function() picker:Hide() end)
|
|
end)
|
|
end)
|