Files
coa-clique/Clique/Clique.lua
T
1dkfa 0666b73b24 Fix bindings not working on raid frames (#5)
This commit fixes an issue where using bindings on raid frames were not
working at all.

The root cause of the issue was that raid frames were not included, nor
updated to the internal table of supported frames. Since raid frames,
specifically the compact raid frames, are dynamic and added to the UI
on-demand, active raid frames must be re-collected again every time
there's a change in the group's or raid's roster (incl. pets).

To implement this, I created a handler that collects active raid frames
and maintains the addon's internal `self.ccframes` table by always
keeping the default frames while refreshing the active raid frames and
pruning the staled ones.

The created handler reacts to messages returned by the following events:
* `PLAYER_ENTERING_WORLD`
* `RAID_ROSTER_UPDATE`
* `PARTY_MEMBERS_CHANGED`
* `UNIT_PET`
* `CVAR_UPDATE`
2025-08-27 12:58:03 -07:00

888 lines
25 KiB
Lua
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
--[[---------------------------------------------------------------------------------
Clique by Cladhaire <cladhaire@gmail.com>
----------------------------------------------------------------------------------]]
Clique = {
Locals = {},
}
assert(DongleStub, string.format("Clique requires DongleStub."))
DongleStub("Dongle-1.2"):New("Clique", Clique)
Clique.version = GetAddOnMetadata("Clique", "Version")
if Clique.version == "wowi:revision" then
Clique.version = "SVN"
end
local L = Clique.Locals
function Clique:Enable()
-- Grab the localisation header
L = Clique.Locals
self.ooc = {}
self.defaults = {
profile = {
clicksets = {
[L.CLICKSET_DEFAULT] = {},
[L.CLICKSET_HARMFUL] = {},
[L.CLICKSET_HELPFUL] = {},
[L.CLICKSET_OOC] = {}
},
blacklist = {},
tooltips = false
},
char = {
switchSpec = false,
downClick = false
},
frames = {
PlayerFrame,
PetFrame,
PartyMemberFrame1,
PartyMemberFrame2,
PartyMemberFrame3,
PartyMemberFrame4,
PartyMemberFrame1PetFrame,
PartyMemberFrame2PetFrame,
PartyMemberFrame3PetFrame,
PartyMemberFrame4PetFrame,
TargetFrame,
TargetFrameToT,
FocusFrame,
FocusFrameToT,
Boss1TargetFrame,
Boss2TargetFrame,
Boss3TargetFrame,
Boss4TargetFrame,
}
}
self.db = self:InitializeDB("CliqueDB", self.defaults)
self.profile = self.db.profile
self.clicksets = self.profile.clicksets
self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
ClickCastFrames = ClickCastFrames or {}
self.ccframes = ClickCastFrames
local newindex = function(t, k, v)
if v == nil then
Clique:UnregisterFrame(k)
rawset(self.ccframes, k, nil)
else
Clique:RegisterFrame(k)
rawset(self.ccframes, k, v)
end
end
ClickCastFrames = setmetatable({}, {
__newindex = newindex
})
Clique:OptionsOnLoad()
Clique:EnableFrames()
-- Register for dongle events
self:RegisterMessage("DONGLE_PROFILE_CHANGED")
self:RegisterMessage("DONGLE_PROFILE_DELETED")
self:RegisterMessage("DONGLE_PROFILE_RESET")
self:RegisterEvent("PLAYER_REGEN_ENABLED")
self:RegisterEvent("PLAYER_REGEN_DISABLED")
self:RegisterEvent("LEARNED_SPELL_IN_TAB")
self:RegisterEvent("ASCENSION_CA_SPECIALIZATION_ACTIVE_ID_CHANGED")
self:RegisterEvent("ADDON_LOADED")
self:RegisterEvent("PLAYER_ENTERING_WORLD")
self:RegisterEvent("RAID_ROSTER_UPDATE")
self:RegisterEvent("PARTY_MEMBERS_CHANGED")
self:RegisterEvent("UNIT_PET")
self:RegisterEvent("CVAR_UPDATE")
-- Change to correct profile based on talent spec
if self.db.char.switchSpec then
self.silentProfile = true
self.talentGroup = CA_GetActiveSpecId() + 1
if self.db.char["spec" .. self.talentGroup .. "Profile"] then
self.db:SetProfile(self.db.char["spec" .. self.talentGroup .. "Profile"])
end
self.silentProfile = false
end
self:UpdateClicks()
-- Register all frames that snuck in before we did =)
for frame in pairs(self.ccframes) do
self:RegisterFrame(frame)
end
-- Securehook CreateFrame to catch any new raid frames
local raidFunc = function(type, name, parent, template)
if template == "RaidPulloutButtonTemplate" then
ClickCastFrames[getglobal(name .. "ClearButton")] = true
end
end
local oldotsu = GameTooltip:GetScript("OnTooltipSetUnit")
if oldotsu then
GameTooltip:SetScript("OnTooltipSetUnit", function(...)
Clique:AddTooltipLines()
return oldotsu(...)
end)
else
GameTooltip:SetScript("OnTooltipSetUnit", function(...)
Clique:AddTooltipLines()
end)
end
hooksecurefunc("CreateFrame", raidFunc)
-- Create our slash command
self.cmd = self:InitializeSlashCommand("Clique commands", "CLIQUE", "clique")
self.cmd:RegisterSlashHandler("debug - Enables extra messages for debugging purposes", "debug", "ShowAttributes")
self.cmd:InjectDBCommands(self.db, "copy", "delete", "list", "reset", "set")
self.cmd:RegisterSlashHandler("tooltip - Enables binding lists in tooltips.", "tooltip", "ToggleTooltip")
self.cmd:RegisterSlashHandler("showbindings - Shows a window that contains the current bindings", "showbindings",
"ShowBindings")
-- Place the Clique tab
self:LEARNED_SPELL_IN_TAB()
-- Register the arena frames, if they're already loaded
if IsAddOnLoaded("Blizzard_ArenaUI") then
self:EnableArenaFrames()
end
end
function Clique:EnableFrames()
local tbl = self.defaults.frames
for i, frame in pairs(tbl) do
rawset(self.ccframes, frame, true)
end
end
function Clique:SpellBookButtonPressed(frame, button)
local texture = getglobal(frame:GetParent():GetName() .. "IconTexture"):GetTexture()
local name = getglobal(frame:GetParent():GetName() .. "SpellName"):GetText()
local rank = getglobal(frame:GetParent():GetName() .. "SubSpellName"):GetText()
if rank == L.RACIAL_PASSIVE or rank == L.PASSIVE then
StaticPopup_Show("CLIQUE_PASSIVE_SKILL")
return
end
local type = "spell"
if self.editSet == self.clicksets[L.CLICKSET_HARMFUL] then
button = string.format("%s%d", "harmbutton", self:GetButtonNumber(button))
elseif self.editSet == self.clicksets[L.CLICKSET_HELPFUL] then
button = string.format("%s%d", "helpbutton", self:GetButtonNumber(button))
else
button = self:GetButtonNumber(button)
end
-- Clear the rank if "Show all spell ranks" is selected
if not GetCVarBool("ShowAllSpellRanks") then
rank = nil
end
-- Build the structure
local t = {
["button"] = button,
["modifier"] = self:GetModifierText(),
["texture"] = texture,
["type"] = type,
["arg1"] = name,
["arg2"] = rank
}
local key = t.modifier .. t.button
if self:CheckBinding(key) then
StaticPopup_Show("CLIQUE_BINDING_PROBLEM")
return
end
self.editSet[key] = t
self:ListScrollUpdate()
self:UpdateClicks()
-- We can only make changes when out of combat
self:PLAYER_REGEN_ENABLED()
end
-- Player is LEAVING combat
function Clique:PLAYER_REGEN_ENABLED()
self:ApplyClickSet(L.CLICKSET_DEFAULT)
self:RemoveClickSet(L.CLICKSET_HARMFUL)
self:RemoveClickSet(L.CLICKSET_HELPFUL)
self:ApplyClickSet(self.ooc)
end
-- Player is ENTERING combat
function Clique:PLAYER_REGEN_DISABLED()
self:RemoveClickSet(self.ooc)
self:ApplyClickSet(L.CLICKSET_DEFAULT)
self:ApplyClickSet(L.CLICKSET_HARMFUL)
self:ApplyClickSet(L.CLICKSET_HELPFUL)
end
function Clique:UpdateClicks()
local ooc = self.clicksets[L.CLICKSET_OOC]
local harm = self.clicksets[L.CLICKSET_HARMFUL]
local help = self.clicksets[L.CLICKSET_HELPFUL]
-- Since harm/help buttons take priority over any others, we can't
--
-- just apply the OOC set last. Instead we use the self.ooc pseudo
-- set (which we build here) which contains only those help/harm
-- buttons that don't conflict with those defined in OOC.
self.ooc = table.wipe(self.ooc or {})
-- Create a hash map of the "taken" combinations
local takenBinds = {}
for name, entry in pairs(ooc) do
local key = string.format("%s:%s", entry.modifier, entry.button)
takenBinds[key] = true
table.insert(self.ooc, entry)
end
for name, entry in pairs(harm) do
local button = string.gsub(entry.button, "harmbutton", "")
local key = string.format("%s:%s", entry.modifier, button)
if not takenBinds[key] then
table.insert(self.ooc, entry)
end
end
for name, entry in pairs(help) do
local button = string.gsub(entry.button, "helpbutton", "")
local key = string.format("%s:%s", entry.modifier, button)
if not takenBinds[key] then
table.insert(self.ooc, entry)
end
end
self:UpdateTooltip()
end
function Clique:RegisterFrame(frame)
local name = frame:GetName()
if self.profile.blacklist[name] then
rawset(self.ccframes, frame, false)
return
end
if not ClickCastFrames[frame] then
rawset(self.ccframes, frame, true)
if CliqueTextListFrame then
Clique:TextListScrollUpdate()
end
end
-- Register AnyUp or AnyDown on this frame, depending on configuration
self:SetClickType(frame)
if frame:CanChangeAttribute() or frame:CanChangeProtectedState() then
if InCombatLockdown() then
self:ApplyClickSet(L.CLICKSET_DEFAULT, frame)
self:ApplyClickSet(L.CLICKSET_HELPFUL, frame)
self:ApplyClickSet(L.CLICKSET_HARMFUL, frame)
else
self:ApplyClickSet(L.CLICKSET_DEFAULT, frame)
self:ApplyClickSet(self.ooc, frame)
end
end
end
function Clique:ApplyClickSet(name, frame)
local set = self.clicksets[name] or name
if frame then
for modifier, entry in pairs(set) do
self:SetAttribute(entry, frame)
end
else
for modifier, entry in pairs(set) do
self:SetAction(entry)
end
end
end
function Clique:RemoveClickSet(name, frame)
local set = self.clicksets[name] or name
if frame then
for modifier, entry in pairs(set) do
self:DeleteAttribute(entry, frame)
end
else
for modifier, entry in pairs(set) do
self:DeleteAction(entry)
end
end
end
function Clique:UnregisterFrame(frame)
assert(not InCombatLockdown(), "An addon attempted to unregister a frame from Clique while in combat.")
for name, set in pairs(self.clicksets) do
for modifier, entry in pairs(set) do
self:DeleteAttribute(entry, frame)
end
end
end
function Clique:DONGLE_PROFILE_CHANGED(event, db, parent, svname, profileKey)
if db == self.db then
if not self.silentProfile then
self:PrintF(L.PROFILE_CHANGED, profileKey)
end
for name, set in pairs(self.clicksets) do
self:RemoveClickSet(set)
end
self:RemoveClickSet(self.ooc)
self.profile = self.db.profile
self.clicksets = self.profile.clicksets
self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
self.profileKey = profileKey
-- Refresh the profile editor if it exists
self.textlistSelected = nil
self:TextListScrollUpdate()
self:ListScrollUpdate()
self:UpdateClicks()
self:PLAYER_REGEN_ENABLED()
end
end
function Clique:DONGLE_PROFILE_RESET(event, db, parent, svname, profileKey)
if db == self.db then
for name, set in pairs(self.clicksets) do
self:RemoveClickSet(set)
end
self:RemoveClickSet(self.ooc)
self.profile = self.db.profile
self.clicksets = self.profile.clicksets
self.editSet = self.clicksets[L.CLICKSET_DEFAULT]
self.profileKey = profileKey
-- Refresh the profile editor if it exists
self.textlistSelected = nil
self:TextListScrollUpdate()
self:ListScrollUpdate()
self:UpdateClicks()
self:PLAYER_REGEN_ENABLED()
self:Print(L.PROFILE_RESET, profileKey)
end
end
function Clique:DONGLE_PROFILE_DELETED(event, db, parent, svname, profileKey)
if db == self.db then
self:PrintF(L.PROFILE_DELETED, profileKey)
self.textlistSelected = nil
self:TextListScrollUpdate()
self:ListScrollUpdate()
end
end
function Clique:SetAttribute(entry, frame)
local name = frame:GetName()
if self.profile.blacklist and self.profile.blacklist[name] then
return
end
-- Set up any special attributes
local type, button, value
if not tonumber(entry.button) then
type, button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
frame:SetAttribute(entry.modifier .. entry.button, type .. button)
assert(frame:GetAttribute(entry.modifier .. entry.button, type .. button))
button = string.format("-%s%s", type, button)
end
button = button or entry.button
if entry.type == "actionbar" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "action" .. button, entry.arg1)
elseif entry.type == "action" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "action" .. button, entry.arg1)
if entry.arg2 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg2)
end
elseif entry.type == "pet" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "action" .. button, entry.arg1)
if entry.arg2 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg2)
end
elseif entry.type == "spell" then
local rank = entry.arg2
local cast
if rank then
if tonumber(rank) then
-- The rank is a number (pre-2.3) so fill in the format
cast = L.CAST_FORMAT:format(entry.arg1, rank)
else
-- The whole rank string is saved (post-2.3) so use it
cast = string.format("%s(%s)", entry.arg1, rank)
end
else
cast = entry.arg1
end
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "spell" .. button, cast)
frame:SetAttribute(entry.modifier .. "bag" .. button, entry.arg2)
frame:SetAttribute(entry.modifier .. "slot" .. button, entry.arg3)
frame:SetAttribute(entry.modifier .. "item" .. button, entry.arg4)
if entry.arg5 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg5)
end
elseif entry.type == "item" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "bag" .. button, entry.arg1)
frame:SetAttribute(entry.modifier .. "slot" .. button, entry.arg2)
frame:SetAttribute(entry.modifier .. "item" .. button, entry.arg3)
if entry.arg4 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg4)
end
elseif entry.type == "macro" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
if entry.arg1 then
frame:SetAttribute(entry.modifier .. "macro" .. button, entry.arg1)
else
local unit = SecureButton_GetModifiedUnit(frame, entry.modifier .. "unit" .. button)
local macro = tostring(entry.arg2)
if unit and macro then
macro = macro:gsub("target%s*=%s*clique", "target=" .. unit)
end
frame:SetAttribute(entry.modifier .. "macro" .. button, nil)
frame:SetAttribute(entry.modifier .. "macrotext" .. button, macro)
end
elseif entry.type == "stop" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
elseif entry.type == "target" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
if entry.arg1 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1)
end
elseif entry.type == "focus" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
if entry.arg1 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1)
end
elseif entry.type == "assist" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
if entry.arg1 then
frame:SetAttribute(entry.modifier .. "unit" .. button, entry.arg1)
end
elseif entry.type == "click" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
frame:SetAttribute(entry.modifier .. "clickbutton" .. button, getglobal(entry.arg1))
elseif entry.type == "menu" then
frame:SetAttribute(entry.modifier .. "type" .. button, entry.type)
end
end
function Clique:DeleteAttribute(entry, frame)
local name = frame:GetName()
if self.profile.blacklist and self.profile.blacklist[name] then
return
end
local type, button, value
if not tonumber(entry.button) then
type, button = select(3, string.find(entry.button, "(%a+)button(%d+)"))
frame:SetAttribute(entry.modifier .. entry.button, nil)
button = string.format("-%s%s", type, button)
end
button = button or entry.button
entry.delete = true
frame:SetAttribute(entry.modifier .. "type" .. button, nil)
frame:SetAttribute(entry.modifier .. entry.type .. button, nil)
end
function Clique:SetAction(entry)
for frame, enabled in pairs(self.ccframes) do
if enabled then
self:SetAttribute(entry, frame)
end
end
end
function Clique:DeleteAction(entry)
for frame in pairs(self.ccframes) do
self:DeleteAttribute(entry, frame)
end
end
function Clique:ShowAttributes()
self:Print("Enabled enhanced debugging.")
PlayerFrame:SetScript("OnAttributeChanged", function(...)
self:Print(...)
end)
self:UnregisterFrame(PlayerFrame)
self:RegisterFrame(PlayerFrame)
end
local tt_ooc = {}
local tt_help = {}
local tt_harm = {}
local tt_default = {}
function Clique:UpdateTooltip()
local ooc = self.ooc
local default = self.clicksets[L.CLICKSET_DEFAULT]
local harm = self.clicksets[L.CLICKSET_HARMFUL]
local help = self.clicksets[L.CLICKSET_HELPFUL]
for k, v in pairs(tt_ooc) do
tt_ooc[k] = nil
end
for k, v in pairs(tt_help) do
tt_help[k] = nil
end
for k, v in pairs(tt_harm) do
tt_harm[k] = nil
end
for k, v in pairs(tt_default) do
tt_default[k] = nil
end
-- Build the ooc lines, which includes both helpful and harmful
for k, v in pairs(ooc) do
local button = self:GetButtonText(v.button)
local mod = string.format("%s%s", v.modifier or "", button)
local action = string.format("%s (%s)", v.arg1 or "", v.type)
table.insert(tt_ooc, {
mod = mod,
action = action
})
end
-- Build the default lines
for k, v in pairs(default) do
local button = self:GetButtonText(v.button)
local mod = string.format("%s%s", v.modifier or "", button)
local action = string.format("%s (%s)", v.arg1 or "", v.type)
table.insert(tt_default, {
mod = mod,
action = action
})
end
-- Build the harm lines
for k, v in pairs(harm) do
local button = self:GetButtonText(v.button)
local mod = string.format("%s%s", v.modifier or "", button)
local action = string.format("%s (%s)", v.arg1 or "", v.type)
table.insert(tt_harm, {
mod = mod,
action = action
})
end
-- Build the help lines
for k, v in pairs(help) do
local button = self:GetButtonText(v.button)
local mod = string.format("%s%s", v.modifier or "", button)
local action = string.format("%s (%s)", v.arg1 or "", v.type)
table.insert(tt_help, {
mod = mod,
action = action
})
end
local function sort(a, b)
return a.mod < b.mod
end
table.sort(tt_ooc, sort)
table.sort(tt_default, sort)
table.sort(tt_harm, sort)
table.sort(tt_help, sort)
end
function Clique:AddTooltipLines()
if not self.profile.tooltips then
return
end
local frame = GetMouseFocus()
if not frame then
return
end
if not self.ccframes[frame] then
return
end
-- Add a buffer line
GameTooltip:AddLine(" ")
if UnitAffectingCombat("player") then
if #tt_default ~= 0 then
GameTooltip:AddLine("Default bindings:")
for k, v in ipairs(tt_default) do
GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
if #tt_help ~= 0 and not UnitCanAttack("player", "mouseover") then
GameTooltip:AddLine("Helpful bindings:")
for k, v in ipairs(tt_help) do
GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
if #tt_harm ~= 0 and UnitCanAttack("player", "mouseover") then
GameTooltip:AddLine("Hostile bindings:")
for k, v in ipairs(tt_harm) do
GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
else
if #tt_ooc ~= 0 then
GameTooltip:AddLine("Out of combat bindings:")
for k, v in ipairs(tt_ooc) do
GameTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
end
end
function Clique:ToggleTooltip()
self.profile.tooltips = not self.profile.tooltips
self:PrintF("Listing of bindings in tooltips has been %s", self.profile.tooltips and "Enabled" or "Disabled")
end
function Clique:ShowBindings()
if not CliqueTooltip then
CliqueTooltip = CreateFrame("GameTooltip", "CliqueTooltip", UIParent, "GameTooltipTemplate")
CliqueTooltip:SetPoint("CENTER", 0, 0)
CliqueTooltip.close = CreateFrame("Button", nil, CliqueTooltip)
CliqueTooltip.close:SetHeight(32)
CliqueTooltip.close:SetWidth(32)
CliqueTooltip.close:SetPoint("TOPRIGHT", 1, 0)
CliqueTooltip.close:SetScript("OnClick", function()
CliqueTooltip:Hide()
end)
CliqueTooltip.close:SetNormalTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Up")
CliqueTooltip.close:SetPushedTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Down")
CliqueTooltip.close:SetHighlightTexture("Interface\\Buttons\\UI-Panel-MinimizeButton-Highlight")
CliqueTooltip:EnableMouse()
CliqueTooltip:SetMovable()
CliqueTooltip:SetPadding(16)
CliqueTooltip:SetBackdropBorderColor(TOOLTIP_DEFAULT_COLOR.r, TOOLTIP_DEFAULT_COLOR.g, TOOLTIP_DEFAULT_COLOR.b);
CliqueTooltip:SetBackdropColor(TOOLTIP_DEFAULT_BACKGROUND_COLOR.r, TOOLTIP_DEFAULT_BACKGROUND_COLOR.g,
TOOLTIP_DEFAULT_BACKGROUND_COLOR.b);
CliqueTooltip:RegisterForDrag("LeftButton")
CliqueTooltip:SetScript("OnDragStart", function(self)
self:StartMoving()
end)
CliqueTooltip:SetScript("OnDragStop", function(self)
self:StopMovingOrSizing()
ValidateFramePosition(self)
end)
CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE")
end
if not CliqueTooltip:IsShown() then
CliqueTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE")
end
-- Actually fill it with the bindings
CliqueTooltip:SetText("Clique Bindings")
if #tt_default > 0 then
CliqueTooltip:AddLine(" ")
CliqueTooltip:AddLine("Default bindings:")
for k, v in ipairs(tt_default) do
CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
if #tt_help > 0 then
CliqueTooltip:AddLine(" ")
CliqueTooltip:AddLine("Helpful bindings:")
for k, v in ipairs(tt_help) do
CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
if #tt_harm > 0 then
CliqueTooltip:AddLine(" ")
CliqueTooltip:AddLine("Hostile bindings:")
for k, v in ipairs(tt_harm) do
CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
if #tt_ooc > 0 then
CliqueTooltip:AddLine(" ")
CliqueTooltip:AddLine("Out of combat bindings:")
for k, v in ipairs(tt_ooc) do
CliqueTooltip:AddDoubleLine(v.mod, v.action, 1, 1, 1, 1, 1, 1)
end
end
CliqueTooltip:Show()
end
function Clique:ASCENSION_CA_SPECIALIZATION_ACTIVE_ID_CHANGED(event, newGroup)
if self.db.char.switchSpec then
self:Print("Detected a talent spec change, changing profile")
-- self:Print("Detected "..typeName..", changing profile to "..newGroup)
if self.db.char["spec" .. newGroup .. "Profile"] then
self.db:SetProfile(self.db.char["spec" .. newGroup .. "Profile"])
end
if CliqueFrame then
CliqueFrame.title:SetText("Clique v. " .. Clique.version .. " - " .. tostring(Clique.db.keys.profile));
end
end
end
function Clique:SetClickType(frame)
local clickType = Clique.db.char.downClick and "AnyDown" or "AnyUp"
if frame then
frame:RegisterForClicks(clickType)
else
for frame, enabled in pairs(self.ccframes) do
if enabled then
frame:RegisterForClicks(clickType)
end
end
end
end
function Clique:EnableArenaFrames()
local arenaFrames = {ArenaEnemyFrame1, ArenaEnemyFrame2, ArenaEnemyFrame3, ArenaEnemyFrame4, ArenaEnemyFrame5}
for idx, frame in ipairs(arenaFrames) do
rawset(self.ccframes, frame, true)
end
end
function Clique:ADDON_LOADED(event, addon)
if addon == "Blizzard_ArenaUI" then
self:EnableArenaFrames()
end
end
-- Check whether the user is in a raid group or has raid-style party frames (CompactRaidFrames) enabled
function Clique:IsInRaidOrHasRaidFramesEnabled()
return IsInRaid() or GetCVarBool("useCompactPartyFrames")
end
-- Remove stale frames from `self.ccframes` that are no longer valid.
--
-- Ensures we only store the default frames and currently active raid frames.
function Clique:CleanStaleCCFrames(frames)
local validFrames = {}
-- add default frames
for _, f in ipairs(self.defaults.frames) do
validFrames[f] = true
end
-- add provided frames (e.g. dynamic raid frames)
for _, f in ipairs(frames) do
validFrames[f] = true
end
-- prune stale frames that are no longer valid
for f in pairs(self.ccframes) do
if not validFrames[f] then
self.ccframes[f] = nil
end
end
end
-- Enumerate and return currently active raid frames
--
-- NOTE: CompactRaidFrames, or rather all raid frames in general for either a
-- player or their pets should be defined in the following format:
-- CompactRaidFrame{index} e.g. CompactRaidFrame1.
function Clique:GetActiveRaidFrames()
local frames = {}
local frame = EnumerateFrames()
while frame do
local name = frame:GetName()
-- include only raid frames starting with `CompactRaidFrame` and ending with
-- a digit while having a valid unit value (ensures the frame belongs to a
-- player)
if name and name:find("^CompactRaidFrame%d+$") and frame.unit then
table.insert(frames, frame)
end
frame = EnumerateFrames(frame)
end
return frames
end
-- Collect currently active raid frames, prune stale ones and re-register valid
-- ones
function Clique:EnableRaidFrames()
local raidFrames = self:GetActiveRaidFrames()
self:CleanStaleCCFrames(raidFrames)
for _, f in ipairs(raidFrames) do
self:RegisterFrame(f)
end
end
-- Shared event handler for syncing dynamic raid/party frame changes
--
-- NOTE: CompactRaidFrames, or rather all raid frames in general, are dynamic and are added to the
-- UIParent's "frame tree" on-demand, which requires hooking into multiple
-- different events that gets fired when the party or raid group roster changes.
-- Pets or CVar -related events need special handling and thus are separated.
local function HandleRaidFrameSync(self, event, unit)
if not self:IsInRaidOrHasRaidFramesEnabled() then return end
if event == "UNIT_PET" then
-- only refresh if the pet belongs to player/party/raid
if unit == "player" or unit:match("^party%d+$") or unit:match("^raid%d+$") then
self:EnableRaidFrames()
end
elseif event == "CVAR_UPDATE" then
-- sync raid frames when toggling the interface option on/off
if unit == "USE_RAID_STYLE_PARTY_FRAMES" then
self:EnableRaidFrames()
end
else
self:EnableRaidFrames()
end
end
-- Bind handler to events
Clique.PLAYER_ENTERING_WORLD = HandleRaidFrameSync
Clique.RAID_ROSTER_UPDATE = HandleRaidFrameSync
Clique.PARTY_MEMBERS_CHANGED = HandleRaidFrameSync
Clique.UNIT_PET = HandleRaidFrameSync
Clique.CVAR_UPDATE = HandleRaidFrameSync