Thx.
Addition:
Snippets from the new Blizzard FrameXML files including some documentation on attributes.
FrameXML\SecureGroupHeaders.lua
Code:
--[[
filter = [STRING] -- a pipe-separated list of aura filter options ("RAID" will be ignored)
separateOwn = [NUMBER] -- indicate whether buffs you cast yourself should be separated before (1) or after (-1) others. If 0 or nil, no separation is done.
sortMethod = ["INDEX", "NAME", "TIME"] -- defines how the group is sorted (Default: "INDEX")
sortDir = ["+", "-"] -- defines the sort order (Default: "+")
groupBy = [nil, auraFilter] -- if present, a series of comma-separated filters, appended to the base filter to separate auras into groups within a single stream
includeWeapons = [nil, NUMBER] -- The aura sub-stream before which to include temporary weapon enchants. If nil or 0, they are ignored.
consolidateTo = [nil, NUMBER] -- The aura sub-stream before which to place a proxy for the consolidated header. If nil or 0, consolidation is ignored.
consolidateDuration = [nil, NUMBER] -- the minimum total duration an aura should have to be considered for consolidation (Default: 30)
consolidateThreshold = [nil, NUMBER] -- buffs with less remaining duration than this many seconds should not be consolidated (Default: 10)
consolidateFraction = [nil, NUMBER] -- The fraction of remaining duration a buff should still have to be eligible for consolidation (Default: .10)
template = [STRING] -- the XML template to use for the unit buttons. If the created widgets should be something other than Buttons, append the Widget name after a comma.
weaponTemplate = [STRING] -- the XML template to use for temporary enchant buttons. Can be nil if you preset the tempEnchant1 and tempEnchant2 attributes, or if you don't include temporary enchants.
consolidateProxy = [STRING|Frame] -- Either the button which represents consolidated buffs, or the name of the template used to construct one.
consolidateHeader = [STRING|Frame] -- Either the aura header which contains consolidated buffs, or the name of the template used to construct one.
point = [STRING] -- a valid XML anchoring point (Default: "TOPRIGHT")
minWidth = [nil, NUMBER] -- the minimum width of the container frame
minHeight = [nil, NUMBER] -- the minimum height of the container frame
xOffset = [NUMBER] -- the x-Offset to use when anchoring the unit buttons (Default: width)
yOffset = [NUMBER] -- the y-Offset to use when anchoring the unit buttons (Default: height)
wrapAfter = [NUMBER] -- begin a new row or column after this many auras
wrapXOffset = [NUMBER] -- the x-offset from one row or column to the next
wrapYOffset = [NUMBER] -- the y-offset from one row or column to the next
maxWraps = [NUMBER] -- limit the number of rows or columns
--]]
function SecureAuraHeader_OnLoad(self)
self:RegisterEvent("UNIT_AURA");
-- self:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED");
end
function SecureAuraHeader_OnUpdate(self)
local hasMainHandEnchant, hasOffHandEnchant, _;
hasMainHandEnchant, _, _, hasOffHandEnchant, _, _ = GetWeaponEnchantInfo();
if ( hasMainHandEnchant ~= self:GetAttribute("_mainEnchanted") ) then
self:SetAttribute("_mainEnchanted", hasMainHandEnchant);
end
if ( hasOffHandEnchant ~= self:GetAttribute("_secondaryEnchanted") ) then
self:SetAttribute("_secondaryEnchanted", hasOffHandEnchant);
end
end
function SecureAuraHeader_OnEvent(self, event, ...)
if ( self:IsVisible() ) then
local unit = SecureButton_GetUnit(self);
if ( event == "UNIT_AURA" and ... == unit ) then
SecureAuraHeader_Update(self);
end
end
end
function SecureAuraHeader_OnAttributeChanged(self, name, value)
if ( name == "_ignore" or self:GetAttribute("_ignore") ) then
return;
end
if ( self:IsVisible() ) then
SecureAuraHeader_Update(self);
end
end
local buttons = {};
local function extractTemplateInfo(template, defaultWidget)
local widgetType;
if ( template ) then
template, widgetType = strsplit(",", tostring(template):trim():gsub("%s*,%s*", ","));
if ( template ~= "" ) then
if ( not widgetType or widgetType == "" ) then
widgetType = defaultWidget;
end
return template, widgetType;
end
end
return nil;
end
local function configureAuras(self, auraTable, consolidateTable, weaponPosition)
local point = self:GetAttribute("point") or "TOPRIGHT";
local xOffset = tonumber(self:GetAttribute("xOffset")) or 0;
local yOffset = tonumber(self:GetAttribute("yOffset")) or 0;
local wrapXOffset = tonumber(self:GetAttribute("wrapXOffset")) or 0;
local wrapYOffset = tonumber(self:GetAttribute("wrapYOffset")) or 0;
local wrapAfter = tonumber(self:GetAttribute("wrapAfter"));
if ( wrapAfter == 0 ) then wrapAfter = nil; end
local maxWraps = self:GetAttribute("maxWraps");
local minWidth = tonumber(self:GetAttribute("minWidth")) or 0;
local minHeight = tonumber(self:GetAttribute("minHeight")) or 0;
if ( consolidateTable and #consolidateTable == 0 ) then
consolidateTable = nil;
end
local name = self:GetName();
wipe(buttons);
local buffTemplate, buffWidget = extractTemplateInfo(self:GetAttribute("template"), "Button");
if ( buffTemplate ) then
for i=1, #auraTable do
local childAttr = "child"..i;
local button = self:GetAttribute("child"..i);
if ( button ) then
button:ClearAllPoints();
else
button = CreateFrame(buffWidget, name and name.."AuraButton"..i, self, buffTemplate);
setAttributesWithoutResponse(self, childAttr, button, "frameref-"..childAttr, GetFrameHandle(button));
end
local buffInfo = auraTable[i];
button:SetID(buffInfo.index);
button:SetAttribute("index", buffInfo.index);
button:SetAttribute("filter", buffInfo.filter);
buttons[i] = button;
end
end
local deadIndex = #buttons + 1;
local button = self:GetAttribute("child"..deadIndex);
while ( button ) do
button:Hide();
deadIndex = deadIndex + 1;
button = self:GetAttribute("child"..deadIndex)
end
local consolidateProxy = nil;
if ( consolidateTable ) then
consolidateProxy = self:GetAttribute("consolidateProxy");
if ( type(consolidateProxy) == 'STRING' ) then
local template, widgetType = extractTemplateInfo(consolidateProxy, "Button");
if ( template ) then
consolidateProxy = CreateFrame(widgetType, name and name.."ProxyButton", self, template);
setAttributesWithoutResponse(self, "consolidateProxy", consolidateProxy, "frameref-proxy", GetFrameHandle(consolidateProxy));
else
consolidateProxy = nil;
end
end
if ( consolidateProxy ) then
if ( consolidateTable.position ) then
tinsert(buttons, consolidateTable.position, consolidateProxy);
else
tinsert(buttons, consolidateProxy);
end
consolidateProxy:ClearAllPoints();
end
else
local consolidateProxy = self:GetAttribute("consolidateProxy");
if ( consolidateProxy and type(consolidateProxy.Hide) == 'function' ) then
consolidateProxy:Hide();
end
end
if ( weaponPosition ) then
local hasMainHandEnchant, hasOffHandEnchant, _, tempEnchant1, tempEnchant2;
hasMainHandEnchant, _, _, hasOffHandEnchant, _, _ = GetWeaponEnchantInfo();
if ( hasOffHandEnchant ) then
tempEnchant2 = self:GetAttribute("tempEnchant2");
if ( not tempEnchant2 ) then
local template, widgetType = extractTemplateInfo(self:GetAttribute("weaponTemplate"), "Button");
if ( template ) then
tempEnchant2 = CreateFrame(widgetType, name and name.."TempEnchant2", self, template);
setAttributesWithoutResponse(self, "tempEnchant2", tempEnchant2);
end
end
if ( tempEnchant2 ) then
tempEnchant2:ClearAllPoints();
local slot = GetInventorySlotInfo("MainHandSlot");
tempEnchant2:SetAttribute("target-slot", slot);
tempEnchant2:SetID(slot);
if ( weaponPosition == 0 ) then
tinsert(buttons, tempEnchant2);
else
tinsert(buttons, weaponPosition, tempEnchant2);
end
end
else
tempEnchant2 = self:GetAttribute("tempEnchant2");
if ( tempEnchant2 and type(tempEnchant2.Hide) == 'function' ) then
tempEnchant2:Hide();
end
end
if ( hasMainHandEnchant ) then
tempEnchant1 = self:GetAttribute("tempEnchant1");
if ( not tempEnchant1 ) then
local template, widgetType = extractTemplateInfo(self:GetAttribute("weaponTemplate"), "Button");
if ( template ) then
tempEnchant1 = CreateFrame(widgetType, name and name.."TempEnchant1", self, template);
setAttributesWithoutResponse(self, "tempEnchant1", tempEnchant1);
end
end
if ( tempEnchant1 ) then
tempEnchant1:ClearAllPoints();
local slot = GetInventorySlotInfo("MainHandSlot");
tempEnchant1:SetAttribute("target-slot", slot);
tempEnchant1:SetID(slot);
if ( weaponPosition == 0 ) then
tinsert(buttons, tempEnchant1);
else
tinsert(buttons, weaponPosition, tempEnchant1);
end
end
else
tempEnchant1 = self:GetAttribute("tempEnchant1");
if ( tempEnchant1 and type(tempEnchant1.Hide) == 'function' ) then
tempEnchant1:Hide();
end
end
end
local display = #buttons
if ( wrapAfter and maxWraps ) then
display = min(display, wrapAfter * maxWraps);
end
local left, right, top, bottom = math.huge, 0, 0, math.huge;
for index=1,display do
local button = buttons[index];
local tick, cycle = floor((index - 1) % wrapAfter), floor((index - 1) / wrapAfter);
button:SetPoint(point, self, cycle * wrapXOffset + tick * xOffset, cycle * wrapYOffset + tick * yOffset);
button:Show();
left = min(left, button:GetLeft() or math.huge);
right = max(right, button:GetRight() or 0);
top = max(top, button:GetTop() or 0);
bottom = min(bottom, button:GetBottom() or math.huge);
end
if ( display >= 1 ) then
self:SetWidth(max(right - left, minWidth));
self:SetHeight(max(top - bottom, minHeight));
else
self:SetWidth(minWidth);
self:SetHeight(minHeight);
end
if ( consolidateTable ) then
local header = self:GetAttribute("consolidateHeader");
if ( type(header) == 'STRING' ) then
local template, widgetType = extractTemplateInfo(header, "Frame");
if ( template ) then
header = CreateFrame(widgetType, name and name.."ProxyHeader", consolidateProxy, template);
setAttributesWithoutResponse(self, "consolidateHeader", header);
consolidateProxy:SetAttribute("header", header);
consolidateProxy:SetAttribute("frameref-header", GetFrameHandle(header))
end
end
if ( header ) then
configureAuras(header, consolidateTable);
end
end
end
local tremove = table.remove;
local function stripRAID(filter)
return filter and tostring(filter):upper():gsub("RAID", ""):gsub("|+", "|"):match("^|?(.+[^|])|?$");
end
local freshTable;
local releaseTable;
do
local tableReserve = {};
freshTable = function ()
local t = next(tableReserve) or {};
tableReserve[t] = nil;
return t;
end
releaseTable = function (t)
tableReserve[t] = wipe(t);
end
end
local sorters = {};
local function sortFactory(key, separateOwn, reverse)
if ( separateOwn ) then
if ( reverse ) then
return function (a, b)
if ( groupingTable[a.filter] == groupingTable[b.filter] ) then
local ownA, ownB = a.caster == "player", b.caster == "player";
if ( ownA ~= ownB ) then
return ownA == (separateOwn > 0)
end
return a[key] > b[key];
else
return groupingTable[a.filter] < groupingTable[b.filter];
end
end;
else
return function (a, b)
if ( groupingTable[a.filter] == groupingTable[b.filter] ) then
local ownA, ownB = a.caster == "player", b.caster == "player";
if ( ownA ~= ownB ) then
return ownA == (separateOwn > 0)
end
return a[key] < b[key];
else
return groupingTable[a.filter] < groupingTable[b.filter];
end
end;
end
else
if ( reverse ) then
return function (a, b)
if ( groupingTable[a.filter] == groupingTable[b.filter] ) then
return a[key] > b[key];
else
return groupingTable[a.filter] < groupingTable[b.filter];
end
end;
else
return function (a, b)
if ( groupingTable[a.filter] == groupingTable[b.filter] ) then
return a[key] < b[key];
else
return groupingTable[a.filter] < groupingTable[b.filter];
end
end;
end
end
end
for i, key in ipairs{"index", "name", "expires"} do
local label = key:upper();
sorters[label] = {};
for bool in pairs{[true] = true, [false] = false} do
sorters[label][bool] = {}
for sep=-1,1 do
sorters[label][bool][sep] = sortFactory(key, sep, bool);
end
end
end
sorters.TIME = sorters.EXPIRES;
function SecureAuraHeader_Update(self)
local filter = self:GetAttribute("filter");
local groupBy = self:GetAttribute("groupBy");
local unit = SecureButton_GetUnit(self) or "player";
local includeWeapons = tonumber(self:GetAttribute("includeWeapons"));
local consolidateTo = tonumber(self:GetAttribute("consolidateTo"));
local consolidateDuration, consolidateThreshold, consolidateFraction;
if ( consolidateTo ) then
consolidateDuration = tonumber(self:GetAttribute("consolidateDuration")) or 30;
consolidateThreshold = tonumber(self:GetAttribute("consolidateThreshold")) or 10;
consolidateFraction = tonumber(self:GetAttribute("consolidateFraction")) or 0.1;
end
local sortDirection = self:GetAttribute("sortDirection");
local separateOwn = tonumber(self:GetAttribute("separateOwn")) or 0;
if ( separateOwn > 0 ) then
separateOwn = 1;
elseif (separateOwn < 0 ) then
separateOwn = -1;
end
local sortMethod = (sorters[tostring(self:GetAttribute("sortMethod")):upper()] or sorters["INDEX"])[sortDirection == "-"][separateOwn];
local time = GetTime();
local consolidateTable;
if ( consolidateTo and consolidateTo ~= 0 ) then
consolidateTable = wipe(tokenTable);
end
wipe(sortingTable);
wipe(groupingTable);
if ( groupBy ) then
local i = 1;
for subFilter in groupBy:gmatch("[^,]+") do
if ( filter ) then
subFilter = stripRAID(filter.."|"..subFilter);
else
subFilter = stripRAID(subFilter);
end
groupingTable[subFilter], groupingTable[i] = i, subFilter;
end
else
filter = stripRAID(filter);
groupingTable[filter], groupingTable[1] = 1, filter;
end
if ( consolidateTable and consolidateTo < 0 ) then
consolidateTo = #groupingTable + consolidateTo + 1;
end
if ( includeWeapons and includeWeapons < 0 ) then
includeWeapons = #groupingTable + includeWeapons + 1;
end
local weaponPosition;
for filterIndex, fullFilter in ipairs(groupingTable) do
if ( consolidateTable and not consolidateTable.position and filterIndex >= consolidateTo ) then
consolidateTable.position = #sortingTable + 1;
end
if ( includeWeapons and not weaponPosition and filterIndex >= includeWeapons ) then
weaponPosition = #sortingTable + 1;
end
local i = 1;
repeat
local aura, _, duration = freshTable();
aura.name, _, _, _, _, duration, aura.expires, aura.caster, _, aura.shouldConsolidate, _ = UnitAura(unit, i, fullFilter);
if ( aura.name ) then
aura.filter = fullFilter;
aura.index = i;
local targetList = sortingTable;
if ( consolidateTable and aura.shouldConsolidate ) then
if ( not aura.expires or duration <= 30 or (aura.expires - time < max(consolidateThreshold, duration * consolidateFraction)) ) then
targetList = consolidateTable;
end
end
tinsert(targetList, aura);
else
releaseTable(aura);
end
i = i + 1;
until ( not aura.name );
end
if ( includeWeapons and not weaponPosition ) then
weaponPosition = 0;
end
table.sort(sortingTable, sortMethod);
if ( consolidateTable ) then
table.sort(consolidateTable, sortMethod);
end
configureAuras(self, sortingTable, consolidateTable, weaponPosition);
while ( sortingTable[1] ) do
releaseTable(tremove(sortingTable));
end
while ( consolidateTable and consolidateTable[1] ) do
releaseTable(tremove(consolidateTable));
end
end
FrameXML\SecureGroupHeaders.xml
Code:
<!-- A frame used for managing group pets -->
<Frame name="SecureAuraHeaderTemplate" inherits="SecureFrameTemplate" hidden="true" virtual="true">
<Scripts>
<OnLoad function="SecureAuraHeader_OnLoad"/>
<OnEvent function="SecureAuraHeader_OnEvent"/>
<OnShow function="SecureAuraHeader_Update"/>
<OnUpdate function="SecureAuraHeader_OnUpdate" />
<OnAttributeChanged function="SecureAuraHeader_OnAttributeChanged"/>
</Scripts>
</Frame>
|