Files
pfUI/modules/thirdparty.lua
T
Brues ef1fa47539 Dock GreedMeter into the chat panel
Register GreedMeter's primary window on the meter dock's damage slot,
mirroring the ShaguDPS integration. GreedMeter builds its windows lazily
and supports several, so frames are resolved at call time.

A second GreedMeter window docks into the panel's other half: pfUI's
dock decides fill-vs-split by whether the threat slot is filled, so hook
GreedMeter's window add/remove to reconcile that slot and re-run the
resize. Only the first two windows fit the panel; any beyond that float.
2026-08-10 23:52:52 -05:00

461 lines
17 KiB
Lua

pfUI:RegisterModule("thirdparty", function()
-- This module includes the core logic of thirdparty modules.
-- Right now, in particular the functions to register addons to the
-- dockframe of the chat panel aswell as the thirdparty root-table.
-- For vanilla-specific glue, see: thirdparty-vanilla.
pfUI.thirdparty = {}
do -- addon dockframe
pfUI.thirdparty.meters = CreateFrame("Frame")
pfUI.thirdparty.meters:SetScript("OnEvent", function()
if C.thirdparty.showmeter == "1" then
pfUI.thirdparty.meters.state = nil
else
pfUI.thirdparty.meters.state = true
end
pfUI.thirdparty.meters:Toggle()
this:UnregisterAllEvents()
end)
pfUI.thirdparty.meters.damage = nil
pfUI.thirdparty.meters.threat = nil
pfUI.thirdparty.meters.state = nil
function pfUI.thirdparty.meters:RegisterMeter(side, data)
-- abort when one addon is already register on the side
if pfUI.thirdparty.meters[side] then return end
-- load addon table
local config, addon, frame, single, dual, show, hide, once = unpack(data)
-- put addon into dockmode when enabled
if C.thirdparty[config] and C.thirdparty[config].dock == "1" then
pfUI.thirdparty.meters[side] = data
-- initialize and hide the frame in the beginning
if once then once() end
if hide then hide() end
-- enable toggle event on the panel button
if pfUI.panel then
pfUI.panel.right.hide:SetScript("OnClick", function()
pfUI.thirdparty.meters:Toggle()
end)
end
-- show/hide meter on login by default
self:RegisterEvent("PLAYER_ENTERING_WORLD")
end
end
function pfUI.thirdparty.meters:Resize()
if not pfUI.chat or not pfUI.panel then return end
if self.damage then -- resize damage meter
local config, addon, frame, single, dual, show, hide = unpack(self.damage)
if frame and C.thirdparty[config].dock == "1" then
if not self.threat then single() else dual() end
end
end
if self.threat then -- resize threat meter
local config, addon, frame, single, dual, show, hide = unpack(self.threat)
if frame and C.thirdparty[config].dock == "1" then
if not self.damage then single() else dual() end
end
end
end
function pfUI.thirdparty.meters:Toggle()
self:Resize()
-- show/hide right chatframe
if pfUI.chat and C.chat.right.enable == "1" then
-- make sure the panel header stays hidden
if self.state then
pfUI.chat.right.panelTop:Show()
else
pfUI.chat.right.panelTop:Hide()
end
-- set chat visibility
pfUI.chat.right:SetAlpha(self.state and 1 or 0)
end
-- show/hide damage meters
if self.damage then
local config, addon, frame, single, dual, show, hide = unpack(self.damage)
if not self.state then show() else hide() end
end
-- show/hide threat meters
if self.threat then
local config, addon, frame, single, dual, show, hide = unpack(self.threat)
if not self.state then show() else hide() end
end
-- show meters
if not self.state then
self.state = true
else
self.state = nil
end
end
end
do -- bag sort addon
pfUI.thirdparty.bagsort = nil
-- Third-party sort addons (SortBags, MrPlow, etc.) override pfUI's built-
-- in sorter by rebinding the existing pfUI.bag.{right,left}.sort buttons
-- created in modules/bags.lua. First-come-first-served — if multiple
-- third-party sorters are installed, the first to register wins.
function pfUI.thirdparty.RegisterBagSort(name, bag, bagtooltip, bank, banktooltip)
if pfUI.thirdparty.bagsort then return end
if not pfUI.bag or not pfUI.bag.right or not pfUI.bag.right.sort then return end
pfUI.thirdparty.bagsort = name
-- Rebind bag (right) button
local rb = pfUI.bag.right.sort
rb:SetScript("OnClick", bag)
rb:SetScript("OnEnter", function()
rb.backdrop:SetBackdropBorderColor(1,1,.25,1)
rb.texture:SetVertexColor(1,1,.25,1)
if bagtooltip then bagtooltip() end
end)
-- Rebind bank (left) button
if pfUI.bag.left and pfUI.bag.left.sort then
local lb = pfUI.bag.left.sort
lb:SetScript("OnClick", bank)
lb:SetScript("OnEnter", function()
lb.backdrop:SetBackdropBorderColor(1,1,.25,1)
lb.texture:SetVertexColor(1,1,.25,1)
if banktooltip then banktooltip() end
end)
end
end
end
-- MrPlow Bag Sorting Addon.
-- Vanilla: https://www.wowace.com/projects/mr-plow/files/288059
-- TBC: https://www.wowace.com/projects/mr-plow/files/136162
HookAddonOrVariable("MrPlow", function()
if C.thirdparty.mrplow.enable == "0" then return end
local sort = CreateFrame("Frame", nil)
sort:RegisterEvent("PLAYER_ENTERING_WORLD")
sort:SetScript("OnEvent", function()
this:UnregisterAllEvents()
local MrPlowL = AceLibrary and AceLibrary("AceLocale-2.2"):new("MrPlow")
if not (MrPlowL and MrPlowL["Bank"]) then return end
pfUI.thirdparty.RegisterBagSort("MrPlow",
function()
MrPlow:Works()
end,
function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(MrPlowL["Mr Plow"])
GameTooltip:AddLine(MrPlowL["The Works"],1,1,1)
GameTooltip:Show()
end,
function()
MrPlow:Works(MrPlowL["Bank"])
end,
function()
GameTooltip:SetOwner(this, "ANCHOR_RIGHT")
GameTooltip:SetText(MrPlowL["Mr Plow"])
GameTooltip:AddLine(MrPlowL["Bank"],1,1,1)
GameTooltip:Show()
end)
end)
end)
-- ShaguDPS Damage Meter
-- Vanilla: https://github.com/shagu/ShaguDPS
-- TBC: https://github.com/shagu/ShaguDPS
HookAddonOrVariable("ShaguDPS", function()
local docktable = { "shagudps", "ShaguDPS", "ShaguDPSWindow",
function() -- single
ShaguDPSWindow:ClearAllPoints()
ShaguDPSWindow:SetAllPoints(pfUI.chat.right)
ShaguDPSWindow:SetWidth(pfUI.chat.right:GetWidth())
if ShaguDPSWindow.Resize then ShaguDPSWindow:Resize() end
end,
function() -- dual
ShaguDPSWindow:ClearAllPoints()
ShaguDPSWindow:SetPoint("TOPLEFT", pfUI.chat.right, "TOP", 0, 0)
ShaguDPSWindow:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
ShaguDPSWindow:SetWidth(pfUI.chat.right:GetWidth() / 2)
if ShaguDPSWindow.Resize then ShaguDPSWindow:Resize() end
end,
function() -- show
ShaguDPS.config.visible = 1
ShaguDPS.window.Refresh(true)
end,
function() -- hide
ShaguDPS.config.visible = 0
ShaguDPS.window.Refresh(true)
end
}
pfUI.thirdparty.meters:RegisterMeter("damage", docktable)
if C.thirdparty.shagudps.skin == "1" then
local hookRefresh = ShaguDPS.window.Refresh
ShaguDPS.window.Refresh = function (arg1, arg2)
hookRefresh(arg1, arg2)
for wid=1,10 do
local window = ShaguDPS.window[wid]
-- legacy single-window support
if wid == 1 and not window then
window = ShaguDPS.window
end
if window then
local _, chat_border = GetBorderSize("chat")
window.title:Hide()
window.title:SetPoint("TOPLEFT", 1, -1)
window.title:SetPoint("TOPRIGHT", -1, -1)
CreateBackdrop(window, chat_border, nil, (C.thirdparty.chatbg == "1" and .8))
CreateBackdropShadow(window)
local hook = window.Refresh
if not window.pfRefreshHook then
window.pfRefreshHook = window.Refresh
window.Refresh = function(self, arg1, arg2)
window.pfRefreshHook(self, arg1, arg2)
-- keep backdrop hidden
if C.thirdparty.shagudps.skin == "1" then
window:SetBackdrop(nil)
window.border:SetBackdrop(nil)
end
end
end
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background)
window.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
window.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
-- skin buttons
local buttons = {
window.btnAnnounce, window.btnReset, window.btnSegment, window.btnMode,
window.btnDamage, window.btnDPS, window.btnHeal, window.btnHPS,
window.btnCurrent, window.btnOverall, window.btnWindow, window.btnSettings,
}
for _, button in pairs(buttons) do
if button then
button:SetHeight(14)
CreateBackdrop(button, -1, true, .75)
button:SetBackdropBorderColor(.4,.4,.4,1)
if button:GetWidth() == 16 then
button:SetWidth(14)
end
end
end
window.border:Hide()
end
end
end
end
end)
-- GreedMeter Damage Meter
-- Vanilla: https://github.com/iGreed/GreedMeter
-- GreedMeter builds its windows lazily and supports several of them, so the
-- frames are resolved at call time. The primary window takes the "damage"
-- dock slot; a second window (GreedMeter.UI.frames[2]) takes the "threat"
-- slot so the two tile the panel side-by-side. Docking triggers a
-- LayoutBars/RefreshFrame so the bars refit the new width.
HookAddonOrVariable("GreedMeter", function()
local UI = GreedMeter.UI
local function frameAt(i)
return UI and UI.frames and UI.frames[i]
end
local function primary()
return (UI and UI.mainFrame) or getglobal("GreedMeterFrame1")
end
local function relayout(frame)
if not UI then return end
if UI.LayoutBars then UI:LayoutBars(frame) end
if UI.RefreshFrame then UI:RefreshFrame(frame) end
end
-- Builds a docktable for a window resolved through getframe(). left picks
-- the left half in split mode; the primary window uses the right half.
local function BuildDock(name, getframe, left)
return { "greedmeter", "GreedMeter", name,
function() -- single: fill the panel
local frame = getframe()
if not frame then return end
frame:ClearAllPoints()
frame:SetAllPoints(pfUI.chat.right)
frame:SetWidth(pfUI.chat.right:GetWidth())
relayout(frame)
end,
function() -- dual: take one half
local frame = getframe()
if not frame then return end
frame:ClearAllPoints()
if left then
frame:SetPoint("TOPLEFT", pfUI.chat.right, "TOPLEFT", 0, 0)
frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOM", 0, 0)
else
frame:SetPoint("TOPLEFT", pfUI.chat.right, "TOP", 0, 0)
frame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
end
frame:SetWidth(pfUI.chat.right:GetWidth() / 2)
relayout(frame)
end,
function() -- show
local frame = getframe()
if frame then frame:Show() relayout(frame) end
end,
function() -- hide
local frame = getframe()
if frame then frame:Hide() end
end
}
end
local threatdock = BuildDock("GreedMeterFrame2", function() return frameAt(2) end, true)
pfUI.thirdparty.meters:RegisterMeter("damage", BuildDock("GreedMeterFrame1", primary, false))
-- pfUI's dock decides fill-vs-split by whether the threat slot is filled,
-- not by counting windows, so reconcile that slot whenever GreedMeter adds
-- or removes one. Only the first two windows fit; any beyond that float.
local function SyncSecond()
if not pfUI.thirdparty.meters.damage then return end -- dock disabled
local has2 = frameAt(2) ~= nil
if has2 and not pfUI.thirdparty.meters.threat then
pfUI.thirdparty.meters.threat = threatdock
elseif not has2 and pfUI.thirdparty.meters.threat == threatdock then
pfUI.thirdparty.meters.threat = nil
end
pfUI.thirdparty.meters:Resize()
-- match the second window's visibility to the panel (the primary tracks it)
local f1, f2 = primary(), frameAt(2)
if f2 then
if f1 and f1:IsShown() then f2:Show() relayout(f2) else f2:Hide() end
end
end
-- AddFrame and the login-time layout restore both go through
-- CreateMeterFrame; RemoveFrame handles closing a window.
local origCreate = UI.CreateMeterFrame
UI.CreateMeterFrame = function(self, isPrimary, copyFrom)
local frame = origCreate(self, isPrimary, copyFrom)
SyncSecond()
return frame
end
local origRemove = UI.RemoveFrame
UI.RemoveFrame = function(self, frame)
origRemove(self, frame)
SyncSecond()
end
end)
-- DPSMate Damage Meter
-- Vanilla: https://github.com/Geigerkind/DPSMate
-- TBC: https://github.com/Geigerkind/DPSMateTBC
HookAddonOrVariable("DPSMate_DPSMate", function()
local docktable = { "dpsmate", "DPSMate", "DPSMate_DPSMate",
function() -- single
DPSMate_DPSMate:ClearAllPoints()
DPSMate_DPSMate:SetAllPoints(pfUI.chat.right)
DPSMate_DPSMate:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_ScrollFrame:ClearAllPoints()
DPSMate_DPSMate_ScrollFrame:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_ScrollFrame:SetPoint("TOPLEFT", DPSMate_DPSMate_Head, "BOTTOMLEFT", 0, 0)
DPSMate_DPSMate_ScrollFrame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, pfUI.panel.right:GetHeight())
DPSMate_DPSMate_ScrollFrame_Child:SetWidth(pfUI.chat.right:GetWidth())
DPSMate_DPSMate_Resize:Hide()
end,
function() -- dual
DPSMate_DPSMate:ClearAllPoints()
DPSMate_DPSMate:SetPoint("TOPLEFT", pfUI.chat.right, "TOP", 0, 0)
DPSMate_DPSMate:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, 0)
DPSMate_DPSMate:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_ScrollFrame:ClearAllPoints()
DPSMate_DPSMate_ScrollFrame:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_ScrollFrame:SetPoint("TOPLEFT", DPSMate_DPSMate_Head, "BOTTOMLEFT", 0, 0)
DPSMate_DPSMate_ScrollFrame:SetPoint("BOTTOMRIGHT", pfUI.chat.right, "BOTTOMRIGHT", 0, pfUI.panel.right:GetHeight())
DPSMate_DPSMate_ScrollFrame_Child:SetWidth(pfUI.chat.right:GetWidth() / 2)
DPSMate_DPSMate_Resize:Hide()
end,
function() -- show
DPSMate_DPSMate:Show()
end,
function() -- hide
DPSMate_DPSMate:Hide()
end
}
pfUI.thirdparty.meters:RegisterMeter("damage", docktable)
if C.thirdparty.dpsmate.skin == "1" then
if DPSMateSettings then
-- set DPSMate appearance to match pfUI
for w in pairs(DPSMateSettings["windows"]) do
DPSMateSettings["windows"][w]["titlebarheight"] = 20
DPSMateSettings["windows"][w]["titlebarfontsize"] = 12
DPSMateSettings["windows"][w]["titlebarfont"] = "Accidental Presidency"
DPSMateSettings["windows"][w]["titlebaropacity"] = 0
DPSMateSettings["windows"][w]["titlebarfontcolor"][1] = 1
DPSMateSettings["windows"][w]["titlebarfontcolor"][2] = 1
DPSMateSettings["windows"][w]["titlebarfontcolor"][3] = 1
DPSMateSettings["windows"][w]["barheight"] = 11
DPSMateSettings["windows"][w]["barfontsize"] = 13
DPSMateSettings["windows"][w]["bartexture"] = "normTex"
DPSMateSettings["windows"][w]["barfont"] = "Accidental Presidency"
DPSMateSettings["windows"][w]["opacity"] = 1
DPSMateSettings["windows"][w]["contentbgtexture"] = "Solid Background"
DPSMateSettings["windows"][w]["bgopacity"] = 0
DPSMateSettings["windows"][w]["borderopacity"] = 0
end
if DPSMate.UpdatePointer then
DPSMate:UpdatePointer()
end
DPSMate:InitializeFrames()
for k, val in pairs(DPSMateSettings["windows"]) do
local frame = _G["DPSMate_"..val["name"]]
CreateBackdrop(frame, nil, nil, (C.thirdparty.chatbg == "1" and .8))
CreateBackdropShadow(frame)
if C.thirdparty.chatbg == "1" and C.chat.global.custombg == "1" then
local r, g, b, a = GetStringColor(C.chat.global.background)
frame.backdrop:SetBackdropColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
local r, g, b, a = GetStringColor(C.chat.global.border)
frame.backdrop:SetBackdropBorderColor(tonumber(r), tonumber(g), tonumber(b), tonumber(a))
end
end
end
end
end)
end)