You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2263 lines
70 KiB

3 years ago
if not WeakAuras.IsLibsOK() then return end
---@type string
local AddonName = ...
---@class OptionsPrivate
local OptionsPrivate = select(2, ...)
-- Lua APIs
local tinsert, tremove, wipe = table.insert, table.remove, wipe
local pairs, type = pairs, type
local error = error
local coroutine = coroutine
local _G = _G
-- WoW APIs
local InCombatLockdown = InCombatLockdown
local CreateFrame = CreateFrame
local AceGUI = LibStub("AceGUI-3.0")
---@class WeakAuras
local WeakAuras = WeakAuras
local L = WeakAuras.L
local ADDON_NAME = "WeakAurasOptions";
local displayButtons = {};
OptionsPrivate.displayButtons = displayButtons;
local spellCache = WeakAuras.spellCache;
local savedVars = {};
OptionsPrivate.savedVars = savedVars;
OptionsPrivate.expanderAnchors = {}
OptionsPrivate.expanderButtons = {}
local collapsedOptions = {}
local collapsed = {} -- magic value
local tempGroup = {
id = {"tempGroup"},
regionType = "group",
controlledChildren = {},
load = {},
triggers = {{}},
config = {},
authorOptions = {},
anchorPoint = "CENTER",
anchorFrameType = "SCREEN",
xOffset = 0,
yOffset = 0
};
OptionsPrivate.tempGroup = tempGroup;
4 years ago
-- Does not duplicate child auras.
function OptionsPrivate.DuplicateAura(data, newParent, massEdit, targetIndex)
local base_id = data.id .. " "
local num = 2
-- if the old id ends with a number increment the number
local matchName, matchNumber = string.match(data.id, "^(.-)(%d*)$")
matchNumber = tonumber(matchNumber)
if (matchName ~= "" and matchNumber ~= nil) then
base_id = matchName
num = matchNumber + 1
end
local new_id = base_id .. num
while(WeakAuras.GetData(new_id)) do
new_id = base_id .. num
num = num + 1
end
local newData = CopyTable(data)
newData.id = new_id
newData.parent = nil
newData.uid = WeakAuras.GenerateUniqueID()
if newData.controlledChildren then
newData.controlledChildren = {}
end
WeakAuras.Add(newData)
WeakAuras.NewDisplayButton(newData, massEdit)
if(newParent or data.parent) then
local parentId = newParent or data.parent
local parentData = WeakAuras.GetData(parentId)
local index
if targetIndex then
index = targetIndex
elseif newParent then
index = #parentData.controlledChildren + 1
else
index = tIndexOf(parentData.controlledChildren, data.id) + 1
end
if(index) then
tinsert(parentData.controlledChildren, index, newData.id)
newData.parent = parentId
WeakAuras.Add(newData)
WeakAuras.Add(parentData)
OptionsPrivate.Private.AddParents(parentData)
for index, id in pairs(parentData.controlledChildren) do
local childButton = OptionsPrivate.GetDisplayButton(id)
childButton:SetGroup(parentData.id, parentData.regionType == "dynamicgroup")
childButton:SetGroupOrder(index, #parentData.controlledChildren)
end
if not massEdit then
local button = OptionsPrivate.GetDisplayButton(parentData.id)
button.callbacks.UpdateExpandButton()
button:UpdateParentWarning()
end
OptionsPrivate.ClearOptions(parentData.id)
end
end
return newData
end
AceGUI:RegisterLayout("AbsoluteList", function(content, children)
local yOffset = 0;
for i = 1, #children do
local child = children[i]
local frame = child.frame;
frame:ClearAllPoints();
frame:Show();
frame:SetPoint("LEFT", content);
frame:SetPoint("RIGHT", content);
frame:SetPoint("TOP", content, "TOP", 0, yOffset)
if child.DoLayout then
child:DoLayout()
end
yOffset = yOffset - ((frame.height or frame:GetHeight() or 0) + 2);
end
if(content.obj.LayoutFinished) then
content.obj:LayoutFinished(nil, yOffset * -1);
end
end);
AceGUI:RegisterLayout("ButtonsScrollLayout", function(content, children, skipLayoutFinished)
local yOffset = 0
local scrollTop, scrollBottom = content.obj:GetScrollPos()
for i = 1, #children do
local child = children[i]
local frame = child.frame;
if not child.dragging then
local frameHeight = (frame.height or frame:GetHeight() or 0);
frame:ClearAllPoints();
if (-yOffset + frameHeight > scrollTop and -yOffset - frameHeight < scrollBottom) then
frame:Show();
frame:SetPoint("LEFT", content);
frame:SetPoint("RIGHT", content);
frame:SetPoint("TOP", content, "TOP", 0, yOffset)
else
frame:Hide();
frame.yOffset = yOffset
end
yOffset = yOffset - (frameHeight + 2);
end
if child.DoLayout then
child:DoLayout()
end
end
if(content.obj.LayoutFinished and not skipLayoutFinished) then
content.obj:LayoutFinished(nil, yOffset * -1)
end
end)
function OptionsPrivate.MultipleDisplayTooltipDesc()
local desc = {{L["Multiple Displays"], L["Temporary Group"]}};
for index, id in pairs(tempGroup.controlledChildren) do
desc[index + 1] = {" ", id};
end
desc[2][1] = L["Children:"]
tinsert(desc, " ");
tinsert(desc, {" ", "|cFF00FFFF"..L["Right-click for more options"]});
tinsert(desc, {" ", "|cFF00FFFF"..L["Drag to move"]});
return desc;
end
local frame;
local db;
local odb;
--- @type boolean?
local reopenAfterCombat = false;
local loadedFrame = CreateFrame("Frame");
loadedFrame:RegisterEvent("ADDON_LOADED");
loadedFrame:RegisterEvent("PLAYER_REGEN_ENABLED");
loadedFrame:RegisterEvent("PLAYER_REGEN_DISABLED");
loadedFrame:SetScript("OnEvent", function(self, event, addon)
if (event == "ADDON_LOADED") then
if(addon == ADDON_NAME) then
db = WeakAurasSaved;
WeakAurasOptionsSaved = WeakAurasOptionsSaved or {};
odb = WeakAurasOptionsSaved;
-- Remove icon and id cache (replaced with spellCache)
if (odb.iconCache) then
odb.iconCache = nil;
end
if (odb.idCache) then
odb.idCache = nil;
end
odb.spellCache = odb.spellCache or {};
spellCache.Load(odb);
if odb.magnetAlign == nil then
odb.magnetAlign = true
end
if db.import_disabled then
db.import_disabled = nil
end
savedVars.db = db;
savedVars.odb = odb;
end
elseif (event == "PLAYER_REGEN_DISABLED") then
if(frame and frame:IsVisible()) then
reopenAfterCombat = true;
WeakAuras.HideOptions();
end
elseif (event == "PLAYER_REGEN_ENABLED") then
if (reopenAfterCombat) then
reopenAfterCombat = nil;
WeakAuras.ShowOptions()
end
end
end);
local function addParents(hash, data)
local parent = data.parent
if parent then
hash[parent] = true
local parentData = WeakAuras.GetData(parent)
if parentData then
addParents(hash, parentData)
end
end
end
local function commonParent(controlledChildren)
local allSame = true
local parent = nil
local targetIndex = math.huge
for index, id in ipairs(controlledChildren) do
local childData = WeakAuras.GetData(id);
local childButton = OptionsPrivate.GetDisplayButton(id)
targetIndex = min(targetIndex, childButton:GetGroupOrder() or math.huge)
if (parent == nil) then
parent = childData.parent
elseif not childData.parent then
allSame = false
elseif childData.parent ~= parent then
allSame = false
end
end
if allSame then
return parent, targetIndex
end
end
local function CreateNewGroupFromSelection(regionType, resetChildPositions)
local data = {
id = OptionsPrivate.Private.FindUnusedId(tempGroup.controlledChildren[1].." Group"),
regionType = regionType,
};
WeakAuras.DeepMixin(data, OptionsPrivate.Private.data_stub)
data.internalVersion = WeakAuras.InternalVersion()
OptionsPrivate.Private.validate(data, OptionsPrivate.Private.regionTypes[regionType].default);
local parent, targetIndex = commonParent(tempGroup.controlledChildren)
if (parent) then
local parentData = WeakAuras.GetData(parent)
tinsert(parentData.controlledChildren, targetIndex, data.id)
data.parent = parent
WeakAuras.Add(data);
WeakAuras.Add(parentData);
OptionsPrivate.Private.AddParents(parentData)
WeakAuras.NewDisplayButton(data);
WeakAuras.UpdateGroupOrders(parentData);
OptionsPrivate.ClearOptions(parentData.id);
local parentButton = OptionsPrivate.GetDisplayButton(parent)
parentButton.callbacks.UpdateExpandButton();
parentButton:Expand();
parentButton:ReloadTooltip();
parentButton:UpdateParentWarning();
else
WeakAuras.Add(data);
WeakAuras.NewDisplayButton(data);
end
for index, childId in pairs(tempGroup.controlledChildren) do
local childData = WeakAuras.GetData(childId);
local childButton = OptionsPrivate.GetDisplayButton(childId)
local oldParent = childData.parent
local oldParentData = WeakAuras.GetData(oldParent)
if (oldParent) then
local oldIndex = childButton:GetGroupOrder()
tremove(oldParentData.controlledChildren, oldIndex)
WeakAuras.Add(oldParentData)
OptionsPrivate.Private.AddParents(oldParentData)
WeakAuras.UpdateGroupOrders(oldParentData);
WeakAuras.ClearAndUpdateOptions(oldParent);
local oldParentButton = OptionsPrivate.GetDisplayButton(oldParent)
oldParentButton.callbacks.UpdateExpandButton();
oldParentButton:ReloadTooltip()
oldParentButton:UpdateParentWarning()
end
tinsert(data.controlledChildren, childId);
childData.parent = data.id;
if resetChildPositions then
childData.xOffset = 0;
childData.yOffset = 0;
end
WeakAuras.Add(data);
WeakAuras.Add(childData);
OptionsPrivate.ClearOptions(childData.id)
childButton:SetGroup(data.id, data.regionType == "dynamicgroup");
childButton:SetGroupOrder(index, #data.controlledChildren);
end
local button = OptionsPrivate.GetDisplayButton(data.id);
button.callbacks.UpdateExpandButton();
button:UpdateParentWarning()
OptionsPrivate.SortDisplayButtons();
button:Expand();
if data.parent then
OptionsPrivate.Private.AddParents(data)
end
end
function OptionsPrivate.MultipleDisplayTooltipMenu()
local frame = frame;
local menu = {
{
text = L["Add to new Group"],
notCheckable = 1,
func = function()
CreateNewGroupFromSelection("group")
end
},
{
text = L["Add to new Dynamic Group"],
notCheckable = 1,
func = function()
CreateNewGroupFromSelection("dynamicgroup", true)
end
},
{
text = L["Duplicate All"],
notCheckable = 1,
func = function()
local duplicated = {};
for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do
local newData = OptionsPrivate.DuplicateAura(child)
tinsert(duplicated, newData.id);
end
OptionsPrivate.ClearPicks();
frame:PickDisplayBatch(duplicated);
end
},
{
text = " ",
notCheckable = 1,
notClickable = 1
},
{
text = L["Delete all"],
notCheckable = 1,
func = function()
local toDelete = {};
local parents = {};
for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do
tinsert(toDelete, child)
addParents(parents, child)
end
OptionsPrivate.ConfirmDelete(toDelete, parents)
end
},
{
text = " ",
notClickable = 1,
notCheckable = 1,
},
{
text = L["Close"],
notCheckable = 1,
func = function() WeakAuras_DropDownMenu:Hide() end
}
};
local anyGroup = false;
local allSameParent = true
local commonParent = nil
local first = true
for _, id in pairs(tempGroup.controlledChildren) do
local childData = WeakAuras.GetData(id);
if(childData and childData.controlledChildren) then
anyGroup = true;
end
if (first) then
commonParent = childData.parent
first = false
elseif childData.parent ~= commonParent then
allSameParent = false
end
end
if(anyGroup) then
-- Disable "Add to New Dynamic Group"
menu[2].notClickable = 1;
menu[2].text = "|cFF777777"..menu[2].text;
end
-- Also disable Add to New Dynamic Group/Group if that would create
-- a group inside a dynamic group
if (allSameParent and commonParent) then
local parentData = WeakAuras.GetData(commonParent);
if (parentData and parentData.regionType == "dynamicgroup") then
menu[1].notClickable = 1;
menu[1].text = "|cFF777777"..menu[1].text;
menu[2].notClickable = 1;
menu[2].text = "|cFF777777"..menu[1].text;
end
end
return menu;
end
StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"] = {
text = "",
button1 = L["Delete"],
button2 = L["Cancel"],
OnAccept = function(self)
if self.data then
OptionsPrivate.DeleteAuras(self.data.toDelete, self.data.parents)
end
end,
OnCancel = function(self)
self.data = nil
end,
showAlert = true,
whileDead = true,
5 months ago
preferredindex = 4,
}
function OptionsPrivate.IsWagoUpdateIgnored(auraId)
local auraData = WeakAuras.GetData(auraId)
if auraData then
for child in OptionsPrivate.Private.TraverseAll(auraData) do
if child.ignoreWagoUpdate then
return true
end
end
end
return false
end
function OptionsPrivate.HasWagoUrl(auraId)
local auraData = WeakAuras.GetData(auraId)
if auraData then
for child in OptionsPrivate.Private.TraverseAll(auraData) do
if child.url and child.url ~= "" then
return true
end
end
end
return false
end
function OptionsPrivate.ConfirmDelete(toDelete, parents)
if toDelete then
local warningForm = L["You are about to delete %d aura(s). |cFFFF0000This cannot be undone!|r Would you like to continue?"]
StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"].text = warningForm:format(#toDelete)
StaticPopup_Show("WEAKAURAS_CONFIRM_DELETE", "", "", {toDelete = toDelete, parents = parents})
end
end
local function AfterScanForLoads()
if(frame) then
if (frame:IsVisible()) then
OptionsPrivate.SortDisplayButtons(nil, true);
else
frame.needsSort = true;
end
end
end
local function OnAboutToDelete(event, uid, id, parentUid, parentId)
local data = OptionsPrivate.Private.GetDataByUID(uid)
if(data.controlledChildren) then
for index, childId in pairs(data.controlledChildren) do
local childButton = displayButtons[childId];
if(childButton) then
childButton:SetGroup();
end
local childData = db.displays[childId];
if(childData) then
childData.parent = nil;
end
end
end
OptionsPrivate.Private.CollapseAllClones(id);
OptionsPrivate.ClearOptions(id)
frame:ClearPicks();
if(displayButtons[id])then
frame.buttonsScroll:DeleteChild(displayButtons[id]);
displayButtons[id] = nil;
end
collapsedOptions[id] = nil
end
local function OnRename(event, uid, oldid, newid)
local data = OptionsPrivate.Private.GetDataByUID(uid)
OptionsPrivate.displayButtons[newid] = OptionsPrivate.displayButtons[oldid];
OptionsPrivate.displayButtons[newid]:SetData(data)
OptionsPrivate.displayButtons[oldid] = nil;
OptionsPrivate.ClearOptions(oldid)
OptionsPrivate.displayButtons[newid]:SetTitle(newid);
collapsedOptions[newid] = collapsedOptions[oldid]
collapsedOptions[oldid] = nil
if(data.controlledChildren) then
for _, childId in pairs(data.controlledChildren) do
OptionsPrivate.displayButtons[childId]:SetGroup(newid)
end
end
OptionsPrivate.StopGrouping()
5 months ago
OptionsPrivate.SortDisplayButtons(nil, true)
frame:OnRename(uid, oldid, newid)
WeakAuras.PickDisplay(newid)
local parent = data.parent
while parent do
OptionsPrivate.ClearOptions(parent)
local parentData = WeakAuras.GetData(parent)
parent = parentData.parent
end
end
local function OptionsFrame()
if(frame) then
return frame
else
return nil
end
end
5 months ago
if not WeakAuras.ToggleOptions then
---@type fun(msg: string, Private: Private)
function WeakAuras.ToggleOptions(msg, Private)
if not Private then
return
end
if not OptionsPrivate.Private then
OptionsPrivate.Private = Private
Private.OptionsFrame = OptionsFrame
for _, fn in ipairs(OptionsPrivate.registerRegions) do
fn()
end
5 months ago
OptionsPrivate.Private.callbacks:RegisterCallback("AuraWarningsUpdated", function(event, uid)
local id = OptionsPrivate.Private.UIDtoID(uid)
if displayButtons[id] then
-- The button does not yet exists if a new aura is created
displayButtons[id]:UpdateWarning()
end
5 months ago
local data = Private.GetDataByUID(uid)
if data and data.parent then
local button = OptionsPrivate.GetDisplayButton(data.parent);
if button then
button:UpdateParentWarning()
end
end
end)
5 months ago
OptionsPrivate.Private.callbacks:RegisterCallback("ScanForLoads", AfterScanForLoads)
OptionsPrivate.Private.callbacks:RegisterCallback("AboutToDelete", OnAboutToDelete)
OptionsPrivate.Private.callbacks:RegisterCallback("Rename", OnRename)
OptionsPrivate.Private.OpenUpdate = OptionsPrivate.OpenUpdate
end
5 months ago
if(frame and frame:IsVisible()) then
WeakAuras.HideOptions();
elseif (InCombatLockdown()) then
WeakAuras.prettyPrint(L["Options will open after combat ends."])
reopenAfterCombat = true;
else
WeakAuras.ShowOptions(msg);
end
end
end
function WeakAuras.HideOptions()
if(frame) then
frame:Hide()
end
end
function WeakAuras.IsOptionsOpen()
if(frame and frame:IsVisible()) then
return true;
else
return false;
end
end
local function EnsureDisplayButton(data)
local id = data.id;
if not(displayButtons[id]) then
displayButtons[id] = AceGUI:Create("WeakAurasDisplayButton");
if(displayButtons[id]) then
displayButtons[id]:SetData(data);
displayButtons[id]:Initialize();
displayButtons[id]:UpdateWarning()
else
print("|cFF8800FFWeakAuras|r: Error creating button for", id);
end
end
end
local function GetSortedOptionsLists()
local loadedSorted, unloadedSorted = {}, {};
local to_sort = {};
for id, data in pairs(db.displays) do
if(data.parent) then
-- Do nothing; children will be added later
elseif(OptionsPrivate.Private.loaded[id]) then
tinsert(to_sort, id);
end
end
table.sort(to_sort, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(to_sort) do
local data = WeakAuras.GetData(id);
for child in OptionsPrivate.Private.TraverseAll(data) do
tinsert(loadedSorted, child.id)
end
end
wipe(to_sort);
for id, data in pairs(db.displays) do
if(data.parent) then
-- Do nothing; children will be added later
elseif not(OptionsPrivate.Private.loaded[id]) then
tinsert(to_sort, id);
end
end
table.sort(to_sort, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(to_sort) do
local data = WeakAuras.GetData(id);
for child in OptionsPrivate.Private.TraverseAll(data) do
tinsert(unloadedSorted, child.id)
end
end
return loadedSorted, unloadedSorted;
end
local function LayoutDisplayButtons(msg)
local total = 0;
for _,_ in pairs(db.displays) do
total = total + 1;
end
local loadedSorted, unloadedSorted = GetSortedOptionsLists();
frame:SetLoadProgressVisible(true)
if OptionsPrivate.Private.CompanionData.slugs then
frame.buttonsScroll:AddChild(frame.pendingInstallButton);
frame.buttonsScroll:AddChild(frame.pendingUpdateButton);
end
frame.buttonsScroll:AddChild(frame.loadedButton);
frame.buttonsScroll:AddChild(frame.unloadedButton);
local func2 = function()
local num = frame.loadProgressNum or 0;
for _, id in pairs(unloadedSorted) do
local data = WeakAuras.GetData(id);
if(data) then
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(data);
frame.buttonsScroll:AddChild(displayButtons[data.id]);
if (num % 50 == 0) then
frame.buttonsScroll:ResumeLayout()
frame.buttonsScroll:PerformLayout()
frame.buttonsScroll:PauseLayout()
end
num = num + 1;
end
frame.loadProgress:SetText(L["Creating buttons: "]..num.."/"..total);
frame.loadProgressNum = num;
coroutine.yield();
end
frame.buttonsScroll:ResumeLayout()
frame.buttonsScroll:PerformLayout()
OptionsPrivate.SortDisplayButtons(msg);
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
if (WeakAuras.IsOptionsOpen()) then
for id, button in pairs(displayButtons) do
if OptionsPrivate.Private.loaded[id] then
button:PriorityShow(1);
5 months ago
coroutine.yield()
end
end
OptionsPrivate.Private.OptionsFrame().loadedButton:RecheckVisibility()
5 months ago
coroutine.yield()
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
frame:SetLoadProgressVisible(false)
end
local func1 = function()
local num = frame.loadProgressNum or 0;
frame.buttonsScroll:PauseLayout()
for _, id in pairs(loadedSorted) do
local data = WeakAuras.GetData(id);
if(data) then
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(data);
local button = displayButtons[data.id]
frame.buttonsScroll:AddChild(button);
num = num + 1;
end
if (num % 50 == 0) then
frame.buttonsScroll:ResumeLayout()
frame.buttonsScroll:PerformLayout()
frame.buttonsScroll:PauseLayout()
end
frame.loadProgress:SetText(L["Creating buttons: "]..num.."/"..total);
frame.loadProgressNum = num;
coroutine.yield();
end
local co2 = coroutine.create(func2);
OptionsPrivate.Private.Threads:Add("LayoutDisplayButtons2", co2);
end
local co1 = coroutine.create(func1);
OptionsPrivate.Private.Threads:Add("LayoutDisplayButtons1", co1);
end
function OptionsPrivate.DeleteAuras(auras, parents)
local func1 = function()
frame:SetLoadProgressVisible(true)
local num = 0
local total = 0
for _, auraData in pairs(auras) do
total = total +1
end
frame.loadProgress:SetText(L["Deleting auras: "]..num.."/"..total)
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
OptionsPrivate.massDelete = true
for _, auraData in pairs(auras) do
WeakAuras.Delete(auraData)
num = num +1
frame.loadProgress:SetText(L["Deleting auras: "]..num.."/"..total)
coroutine.yield()
end
OptionsPrivate.massDelete = false
if parents then
for id in pairs(parents) do
local parentData = WeakAuras.GetData(id)
local parentButton = OptionsPrivate.GetDisplayButton(id)
WeakAuras.UpdateGroupOrders(parentData)
if(#parentData.controlledChildren == 0) then
parentButton:DisableExpand()
else
parentButton:EnableExpand()
end
parentButton:SetNormalTooltip()
WeakAuras.Add(parentData)
WeakAuras.ClearAndUpdateOptions(parentData.id)
parentButton:UpdateParentWarning()
frame.loadProgress:SetText(L["Finishing..."])
coroutine.yield()
end
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
OptionsPrivate.SortDisplayButtons(nil, true)
frame:SetLoadProgressVisible(false)
end
local co1 = coroutine.create(func1)
OptionsPrivate.Private.Threads:Add("Deleting Auras", co1)
end
function WeakAuras.ShowOptions(msg)
local firstLoad = not(frame);
OptionsPrivate.Private.Pause();
OptionsPrivate.Private.SetFakeStates()
WeakAuras.spellCache.Build()
if (firstLoad) then
frame = OptionsPrivate.CreateFrame();
frame.buttonsScroll.frame:Show();
LayoutDisplayButtons(msg);
end
if (frame:GetWidth() > GetScreenWidth()) then
frame:SetWidth(GetScreenWidth())
end
if (frame:GetHeight() > GetScreenHeight() - 50) then
frame:SetHeight(GetScreenHeight() - 50)
end
frame.buttonsScroll.frame:Show();
if (frame.needsSort) then
OptionsPrivate.SortDisplayButtons();
frame.needsSort = nil;
end
frame:Show();
if (OptionsPrivate.Private.mouseFrame) then
OptionsPrivate.Private.mouseFrame:OptionsOpened();
end
if (OptionsPrivate.Private.personalRessourceDisplayFrame) then
OptionsPrivate.Private.personalRessourceDisplayFrame:OptionsOpened();
end
if frame.moversizer then
frame.moversizer:OptionsOpened()
end
if not(firstLoad) then
-- Show what was last shown
local suspended = OptionsPrivate.Private.PauseAllDynamicGroups()
for id, button in pairs(displayButtons) do
4 years ago
button:SyncVisibility()
end
OptionsPrivate.Private.ResumeAllDynamicGroups(suspended)
end
if (frame.pickedDisplay) then
if (OptionsPrivate.IsPickedMultiple()) then
local children = {}
for k,v in pairs(tempGroup.controlledChildren) do
children[k] = v
end
frame:PickDisplayBatch(children);
else
WeakAuras.PickDisplay(frame.pickedDisplay);
end
else
frame:NewAura();
end
if (frame.window == "codereview") then
local codereview = OptionsPrivate.CodeReview(frame, true)
if codereview then
codereview:Close();
end
end
if firstLoad then
frame:ShowTip()
end
end
function OptionsPrivate.UpdateOptions()
frame:UpdateOptions()
end
function WeakAuras.ClearAndUpdateOptions(id, clearChildren)
frame:ClearAndUpdateOptions(id, clearChildren)
end
function OptionsPrivate.ClearOptions(id)
frame:ClearOptions(id)
end
function WeakAuras.FillOptions()
frame:FillOptions()
end
function OptionsPrivate.EnsureOptions(data, subOption)
return frame:EnsureOptions(data, subOption)
end
function OptionsPrivate.GetPickedDisplay()
return frame:GetPickedDisplay()
end
function OptionsPrivate.OpenTextEditor(...)
OptionsPrivate.TextEditor(frame):Open(...);
end
function OptionsPrivate.ExportToString(id)
OptionsPrivate.ImportExport(frame):Open("export", id);
end
function OptionsPrivate.ExportToTable(id)
OptionsPrivate.ImportExport(frame):Open("table", id);
end
function OptionsPrivate.ImportFromString()
OptionsPrivate.ImportExport(frame):Open("import");
end
3 years ago
function OptionsPrivate.OpenDebugLog(text)
OptionsPrivate.DebugLog(frame):Open(text)
3 years ago
end
function OptionsPrivate.OpenUpdate(data, children, target, linkedAuras, sender, callbackFunc)
return OptionsPrivate.UpdateFrame(frame):Open(data, children, target, linkedAuras, sender, callbackFunc)
end
function OptionsPrivate.ConvertDisplay(data, newType)
local id = data.id;
local visibility = displayButtons[id]:GetVisibility();
displayButtons[id]:PriorityHide(2);
if OptionsPrivate.Private.regions[id] and OptionsPrivate.Private.regions[id].region then
OptionsPrivate.Private.regions[id].region:Collapse()
end
OptionsPrivate.Private.CollapseAllClones(id);
OptionsPrivate.Private.Convert(data, newType);
displayButtons[id]:Initialize();
displayButtons[id]:PriorityShow(visibility);
frame:ClearOptions(id)
frame:FillOptions();
WeakAuras.UpdateThumbnail(data);
WeakAuras.SetMoverSizer(id)
OptionsPrivate.ResetMoverSizer();
OptionsPrivate.SortDisplayButtons()
end
function WeakAuras.NewDisplayButton(data, massEdit)
local id = data.id;
OptionsPrivate.Private.ScanForLoads({[id] = true});
EnsureDisplayButton(db.displays[id]);
WeakAuras.UpdateThumbnail(db.displays[id]);
frame.buttonsScroll:AddChild(displayButtons[id]);
if not massEdit then
OptionsPrivate.SortDisplayButtons()
end
end
function WeakAuras.UpdateGroupOrders(data)
if(data.controlledChildren) then
local total = #data.controlledChildren;
for index, id in pairs(data.controlledChildren) do
local button = OptionsPrivate.GetDisplayButton(id);
button:SetGroupOrder(index, total);
end
end
end
function OptionsPrivate.UpdateButtonsScroll()
if OptionsPrivate.Private.IsOptionsProcessingPaused() then return end
frame.buttonsScroll:DoLayout()
end
local function addButton(button, aurasMatchingFilter, visible)
button.frame:Show();
if button.AcquireThumbnail then
button:AcquireThumbnail()
end
tinsert(frame.buttonsScroll.children, button);
visible[button] = true
if button.data.controlledChildren and button:GetExpanded() then
for _, childId in ipairs(button.data.controlledChildren) do
if aurasMatchingFilter[childId] then
addButton(displayButtons[childId], aurasMatchingFilter, visible)
end
end
end
end
local previousFilter;
local pendingUpdateButtons = {}
local pendingInstallButtons = {}
function OptionsPrivate.SortDisplayButtons(filter, overrideReset, id)
if (OptionsPrivate.Private.IsOptionsProcessingPaused()) then
return;
end
local recenter = false;
filter = filter or (overrideReset and previousFilter or "");
if(frame.filterInput:GetText() ~= filter) then
frame.filterInput:SetText(filter);
end
if(previousFilter and previousFilter ~= "" and (filter == "" or not filter)) then
recenter = true;
end
previousFilter = filter;
filter = filter:lower();
wipe(frame.buttonsScroll.children);
local pendingInstallButtonShown = false
if OptionsPrivate.Private.CompanionData.stash then
for id, companionData in pairs(OptionsPrivate.Private.CompanionData.stash) do
if not pendingInstallButtonShown then
tinsert(frame.buttonsScroll.children, frame.pendingInstallButton)
pendingInstallButtonShown = true
end
local child = pendingInstallButtons[id]
if frame.pendingInstallButton:GetExpanded() then
if not child then
child = AceGUI:Create("WeakAurasPendingInstallButton")
pendingInstallButtons[id] = child
child:Initialize(id, companionData)
if companionData.logo then
child:SetLogo(companionData.logo)
end
if companionData.refreshLogo then
child:SetRefreshLogo(companionData.refreshLogo)
end
child.frame:Show()
child:AcquireThumbnail()
frame.buttonsScroll:AddChild(child)
else
if not child.frame:IsShown() then
child.frame:Show()
child:AcquireThumbnail()
end
tinsert(frame.buttonsScroll.children, child)
end
elseif child then
child.frame:Hide()
if child.ReleaseThumbnail then
child:ReleaseThumbnail()
end
end
end
end
if not pendingInstallButtonShown and frame.pendingInstallButton then
frame.pendingInstallButton.frame:Hide()
end
local pendingUpdateButtonShown = false
if OptionsPrivate.Private.CompanionData.slugs then
local buttonsShown = {}
for _, button in pairs(pendingUpdateButtons) do
button:ResetLinkedAuras()
end
for id, aura in pairs(WeakAurasSaved.displays) do
if not aura.ignoreWagoUpdate and aura.url and aura.url ~= "" then
local slug, version = aura.url:match("wago.io/([^/]+)/([0-9]+)")
if not slug and not version then
slug = aura.url:match("wago.io/([^/]+)$")
version = 1
end
if slug and version then
local auraData = OptionsPrivate.Private.CompanionData.slugs[slug]
if auraData and auraData.wagoVersion then
if tonumber(auraData.wagoVersion) > tonumber(version) then
-- there is an update for this aura
if not pendingUpdateButtonShown then
tinsert(frame.buttonsScroll.children, frame.pendingUpdateButton)
pendingUpdateButtonShown = true
end
if frame.pendingUpdateButton:GetExpanded() then
local child = pendingUpdateButtons[slug]
if not child then
child = AceGUI:Create("WeakAurasPendingUpdateButton")
pendingUpdateButtons[slug] = child
child:Initialize(slug, auraData)
if auraData.logo then
child:SetLogo(auraData.logo)
end
if auraData.refreshLogo then
child:SetRefreshLogo(auraData.refreshLogo)
end
child.frame:Show()
child:AcquireThumbnail()
frame.buttonsScroll:AddChild(child)
buttonsShown[slug] = true
end
if not child.frame:IsShown() then
child.frame:Show()
child:AcquireThumbnail()
end
if not buttonsShown[slug] then
tinsert(frame.buttonsScroll.children, child)
buttonsShown[slug] = true
end
child:MarkLinkedAura(id)
for childData in OptionsPrivate.Private.TraverseAllChildren(aura) do
child:MarkLinkedChildren(childData.id)
end
end
end
end
end
end
end
-- hide all buttons not marked as shown
for slug, button in pairs(pendingUpdateButtons) do
if not buttonsShown[slug] then
if button and button.frame:IsShown() then
button.frame:Hide()
if button.ReleaseThumbnail then
button:ReleaseThumbnail()
end
end
end
end
end
if not pendingUpdateButtonShown and frame.pendingUpdateButton then
frame.pendingUpdateButton.frame:Hide()
end
tinsert(frame.buttonsScroll.children, frame.loadedButton);
local aurasMatchingFilter = {}
local useTextFilter = filter ~= ""
local filterTable = OptionsPrivate.Private.splitAtOr(filter)
local topLevelLoadedAuras = {}
local topLevelUnloadedAuras = {}
local visible = {}
for id, child in pairs(displayButtons) do
if child.data.controlledChildren then
local hasLoaded, hasStandBy, hasNotLoaded = 0, 0, 0
for leaf in OptionsPrivate.Private.TraverseLeafs(child.data) do
local id = leaf.id
if OptionsPrivate.Private.loaded[id] == true then
hasLoaded = hasLoaded + 1
elseif OptionsPrivate.Private.loaded[id] == false then
hasStandBy = hasStandBy + 1
else
hasNotLoaded = hasNotLoaded + 1
end
end
if hasLoaded > 0 then
5 months ago
child:SetLoaded(1, "loaded", L["Loaded"], L["%d displays loaded"]:format(hasLoaded))
elseif hasStandBy > 0 then
5 months ago
child:SetLoaded(2, "standby", L["Standby"], L["%d displays on standby"]:format(hasStandBy))
elseif hasNotLoaded > 0 then
5 months ago
child:SetLoaded(3, "unloaded", L["Not Loaded"], L["%d displays not loaded"]:format(hasNotLoaded))
else
child:ClearLoaded()
end
else
if OptionsPrivate.Private.loaded[id] == true then
5 months ago
child:SetLoaded(1, "loaded", L["Loaded"], L["This display is currently loaded"])
elseif OptionsPrivate.Private.loaded[id] == false then
5 months ago
child:SetLoaded(2, "standby", L["Standby"], L["This display is on standby, it will be loaded when needed."])
else
5 months ago
child:SetLoaded(3, "unloaded", L["Not Loaded"], L["This display is not currently loaded"])
end
end
if useTextFilter then
for _, word in ipairs(filterTable) do
if(id:lower():find(word, 1, true)) then
aurasMatchingFilter[id] = true
for parent in OptionsPrivate.Private.TraverseParents(child.data) do
aurasMatchingFilter[parent.id] = true
end
end
end
else
aurasMatchingFilter[id] = true
end
if not child:GetGroup() then
-- Top Level aura
if OptionsPrivate.Private.loaded[id] ~= nil then
tinsert(topLevelLoadedAuras, id)
else
tinsert(topLevelUnloadedAuras, id)
end
end
end
wipe(frame.loadedButton.childButtons)
if frame.loadedButton:GetExpanded() then
table.sort(topLevelLoadedAuras, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(topLevelLoadedAuras) do
if aurasMatchingFilter[id] then
addButton(displayButtons[id], aurasMatchingFilter, visible)
end
end
end
for _, id in ipairs(topLevelLoadedAuras) do
for child in OptionsPrivate.Private.TraverseLeafsOrAura(WeakAuras.GetData(id)) do
tinsert(frame.loadedButton.childButtons, displayButtons[child.id])
end
end
tinsert(frame.buttonsScroll.children, frame.unloadedButton);
wipe(frame.unloadedButton.childButtons)
if frame.unloadedButton:GetExpanded() then
table.sort(topLevelUnloadedAuras, function(a, b) return a:lower() < b:lower() end)
for _, id in ipairs(topLevelUnloadedAuras) do
if aurasMatchingFilter[id] then
addButton(displayButtons[id], aurasMatchingFilter, visible)
end
end
end
for _, id in ipairs(topLevelUnloadedAuras) do
for child in OptionsPrivate.Private.TraverseLeafsOrAura(WeakAuras.GetData(id)) do
tinsert(frame.unloadedButton.childButtons, displayButtons[child.id])
end
end
for _, child in pairs(displayButtons) do
if(not visible[child]) then
child.frame:Hide();
if child.ReleaseThumbnail then
child:ReleaseThumbnail()
end
end
end
frame.buttonsScroll:DoLayout();
if(recenter) then
frame:CenterOnPicked();
end
end
function OptionsPrivate.IsPickedMultiple()
if(frame.pickedDisplay == tempGroup) then
return true;
else
return false;
end
end
function OptionsPrivate.IsDisplayPicked(id)
if(frame.pickedDisplay == tempGroup) then
for child in OptionsPrivate.Private.TraverseLeafs(tempGroup) do
if(id == child.id) then
return true;
end
end
return false;
else
return frame.pickedDisplay == id;
end
end
function WeakAuras.PickDisplay(id, tab, noHide)
frame:PickDisplay(id, tab, noHide)
OptionsPrivate.UpdateButtonsScroll()
end
function OptionsPrivate.PickAndEditDisplay(id)
frame:PickDisplay(id);
OptionsPrivate.UpdateButtonsScroll()
displayButtons[id].callbacks.OnRenameClick();
end
function OptionsPrivate.ClearPick(id)
frame:ClearPick(id);
end
function OptionsPrivate.ClearPicks()
frame:ClearPicks();
end
function OptionsPrivate.PickDisplayMultiple(id)
frame:PickDisplayMultiple(id);
end
function OptionsPrivate.PickDisplayMultipleShift(target)
if (frame.pickedDisplay) then
-- get first aura selected
local first;
if (OptionsPrivate.IsPickedMultiple()) then
first = tempGroup.controlledChildren[#tempGroup.controlledChildren];
else
first = frame.pickedDisplay;
end
if (first and first ~= target) then
-- check if target and first are in same group and are not a group
local firstData = WeakAuras.GetData(first);
local targetData = WeakAuras.GetData(target);
if (firstData.parent == targetData.parent and not targetData.controlledChildren and not firstData.controlledChildren) then
local batchSelection = {};
-- in a group
if (firstData.parent) then
local group = WeakAuras.GetData(targetData.parent);
for index, child in ipairs(group.controlledChildren) do
-- 1st button
if (child == target or child == first) then
table.insert(batchSelection, child);
for i = index + 1, #group.controlledChildren do
local current = group.controlledChildren[i];
4 years ago
if (WeakAuras.GetData(current).controlledChildren) then
-- Skip sub groups
else
table.insert(batchSelection, current);
end
-- last button: stop selection
if (current == target or current == first) then
break;
end
end
break;
end
end
elseif (firstData.parent == nil and targetData.parent == nil) then
-- top-level
for index, button in ipairs(frame.buttonsScroll.children) do
if button.type == "WeakAurasDisplayButton" then
local data = button.data;
-- 1st button
if (data and (data.id == target or data.id == first)) then
table.insert(batchSelection, data.id);
for i = index + 1, #frame.buttonsScroll.children do
local current = frame.buttonsScroll.children[i];
local currentData = current.data;
if currentData and not currentData.parent and not currentData.controlledChildren then
table.insert(batchSelection, currentData.id);
-- last button: stop selection
if (currentData.id == target or currentData.id == first) then
break;
end
end
end
break;
end
end
end
end
if #batchSelection > 0 then
frame:PickDisplayBatch(batchSelection);
end
end
end
else
WeakAuras.PickDisplay(target);
end
end
function OptionsPrivate.GetDisplayButton(id)
if(id and displayButtons[id]) then
return displayButtons[id];
end
end
function OptionsPrivate.AddDisplayButton(data)
EnsureDisplayButton(data);
WeakAuras.UpdateThumbnail(data);
frame.buttonsScroll:AddChild(displayButtons[data.id]);
end
function OptionsPrivate.StartGrouping(data)
if not data then
return
end
if not OptionsPrivate.IsDisplayPicked(data) then
WeakAuras.PickDisplay(data.id)
end
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
local children = {};
-- start grouping for selected buttons
for index, childId in ipairs(tempGroup.controlledChildren) do
local button = OptionsPrivate.GetDisplayButton(childId);
button:StartGrouping(tempGroup.controlledChildren, true);
children[childId] = true;
end
-- set grouping for non selected buttons
for _, button in pairs(displayButtons) do
if not children[button.data.id] then
button:StartGrouping(tempGroup.controlledChildren, false);
end
end
else
local children = {};
for child in OptionsPrivate.Private.TraverseAllChildren(data) do
children[child.id] = true
end
for id, button in pairs(displayButtons) do
button:StartGrouping({data.id},
data.id == id,
data.regionType == "dynamicgroup" or data.regionType == "group",
children[id]);
end
end
end
function OptionsPrivate.StopGrouping(data)
for id, button in pairs(displayButtons) do
button:StopGrouping();
end
end
function OptionsPrivate.Ungroup(data)
4 years ago
if not OptionsPrivate.IsDisplayPicked(data.id) then
WeakAuras.PickDisplay(data.id)
end
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
for index, childId in ipairs(tempGroup.controlledChildren) do
local button = OptionsPrivate.GetDisplayButton(childId);
button:Ungroup(data);
end
else
local button = OptionsPrivate.GetDisplayButton(data.id);
button:Ungroup(data);
end
WeakAuras.FillOptions()
end
function OptionsPrivate.DragReset()
for _, button in pairs(displayButtons) do
button:DragReset();
end
OptionsPrivate.UpdateButtonsScroll()
end
local function CompareButtonOrder(a, b)
if (a.data.parent == b.data.parent) then
if (a.data.parent) then
return a:GetGroupOrder() < b:GetGroupOrder()
else
return a.data.id < b.data.id
end
end
-- Different parents, so find common parent by first
-- going up a's hierarchy
local parents = {}
local aNode = a.data.id
local lastAParent = aNode
while(aNode) do
local parent = WeakAuras.GetData(aNode).parent
if (parent) then
parents[parent] = aNode
lastAParent = parent
end
aNode = parent
end
local bNode = b.data.id
local lastBParent = bNode
while(bNode) do
local parent = WeakAuras.GetData(bNode).parent
if parent then
if (parents[parent]) then
-- We have found the common parent, the last node in the chain is
-- Compare the previous nodes GroupOrder
local aButton = OptionsPrivate.GetDisplayButton(parents[parent])
local bButton = OptionsPrivate.GetDisplayButton(bNode)
return aButton:GetGroupOrder() < bButton:GetGroupOrder()
end
lastBParent = parent
end
bNode = parent
end
-- If we are here there was no common parent
local aButton = OptionsPrivate.GetDisplayButton(lastAParent)
local bButton = OptionsPrivate.GetDisplayButton(lastBParent)
return aButton.data.id < bButton.data.id
end
local function CompareButtonOrderReverse(a, b)
return CompareButtonOrder(b, a)
end
function OptionsPrivate.Drop(mainAura, target, action, area)
WeakAuras_DropDownMenu:Hide()
local func1 = function()
frame:SetLoadProgressVisible(true)
local total = 0
local num = 0
for id, button in pairs(displayButtons) do
if button:IsDragging() then
total = total + 1
end
end
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
local mode = ""
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
mode = "MULTI"
elseif mainAura.controlledChildren then
mode = "GROUP"
else
mode = "SINGLE"
end
local buttonsToSort = {}
for id, button in pairs(displayButtons) do
if button:IsDragging() then
tinsert(buttonsToSort, button)
num = num + 1
frame.loadProgress:SetText(L["Preparing auras: "]..num.."/"..total)
else
button:Drop(mode, mainAura, target, action);
end
coroutine.yield()
end
num = 0
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
if mode == "MULTI" then
-- If we are dragging and dropping multiple auras at once, the order in which we drop is important
-- We want to preserve the top-down order
-- Depending on how exactly we find the insert position, we need to use the right order of insertions
if area == "GROUP" then
table.sort(buttonsToSort, CompareButtonOrderReverse)
elseif area == "BEFORE" then
table.sort(buttonsToSort, CompareButtonOrder)
else -- After
table.sort(buttonsToSort, CompareButtonOrderReverse)
end
end
for _, button in ipairs(buttonsToSort) do
button:Drop(mode, mainAura, target, action)
num = num + 1
frame.loadProgress:SetText(L["Moving auras: "]..num.."/"..total)
coroutine.yield()
end
-- Update offset, this is a bit wasteful to do for every aura
-- But we also need to update the offset if a parent was dragged
for _, button in pairs(displayButtons) do
button:UpdateOffset();
end
coroutine.yield()
frame:SetLoadProgressVisible(false)
OptionsPrivate.SortDisplayButtons()
OptionsPrivate.UpdateButtonsScroll()
WeakAuras.FillOptions()
end
local co1 = coroutine.create(func1)
OptionsPrivate.Private.Threads:Add("Dropping Auras", co1)
end
function OptionsPrivate.StartDrag(mainAura)
WeakAuras_DropDownMenu:Hide()
if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0) then
-- Multi selection
local children = {};
local size = #tempGroup.controlledChildren;
-- set dragging for selected buttons in reverse for ordering
for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do
local button = OptionsPrivate.GetDisplayButton(child.id);
button:DragStart("MULTI", true, mainAura, size)
children[child.id] = true
end
-- set dragging for non selected buttons
for id, button in pairs(displayButtons) do
if not children[button.data.id] then
button:DragStart("MULTI", false, mainAura);
end
end
else
if mainAura.controlledChildren then
-- Group aura
local mode = "GROUP"
local children = {};
for child in OptionsPrivate.Private.TraverseAll(mainAura) do
local button = OptionsPrivate.GetDisplayButton(child.id);
button:DragStart(mode, true, mainAura)
children[child.id] = true
end
-- set dragging for non selected buttons
for _, button in pairs(displayButtons) do
if not children[button.data.id] then
button:DragStart(mode, false, mainAura);
end
end
else
for id, button in pairs(displayButtons) do
button:DragStart("SINGLE", id == mainAura.id, mainAura);
end
end
end
3 years ago
OptionsPrivate.UpdateButtonsScroll()
end
function OptionsPrivate.DropIndicator()
local indicator = frame.dropIndicator
if not indicator then
---@class Frame
indicator = CreateFrame("Frame", "WeakAuras_DropIndicator")
indicator:SetHeight(4)
indicator:SetFrameStrata("FULLSCREEN")
local groupTexture = indicator:CreateTexture(nil, "ARTWORK")
groupTexture:SetBlendMode("ADD")
groupTexture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_FullWhite")
local lineTexture = indicator:CreateTexture(nil, "ARTWORK")
lineTexture:SetBlendMode("ADD")
lineTexture:SetAllPoints(indicator)
lineTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight")
indicator.lineTexture = lineTexture
indicator.groupTexture = groupTexture
frame.dropIndicator = indicator
indicator:Hide()
function indicator:ShowAction(target, action)
self:Show()
self:ClearAllPoints()
if action == "GROUP" then
self.groupTexture:ClearAllPoints()
self.groupTexture:SetVertexColor(0.4, 0.7, 1, 0.7)
self.groupTexture:Show()
self.groupTexture:SetPoint("TOPLEFT", target.icon, "TOPRIGHT", 2, -1)
self.groupTexture:SetPoint("BOTTOMRIGHT", target.frame, "BOTTOMRIGHT", 0, 1)
else
self.groupTexture:Hide()
end
-- Position line texture, if needed
if action == "BEFORE" then
self.lineTexture:Show()
self:SetPoint("BOTTOMLEFT", target.frame, "TOPLEFT", 0, -1)
self:SetPoint("BOTTOMRIGHT", target.frame, "TOPRIGHT", 0, -1)
self:SetHeight(4)
elseif action == "AFTER" then
self.lineTexture:Show()
self:SetPoint("TOPLEFT", target.frame, "BOTTOMLEFT", 0, 1)
self:SetPoint("TOPRIGHT", target.frame, "BOTTOMRIGHT", 0, 1)
self:SetHeight(4)
else
self.lineTexture:Hide()
end
end
end
return indicator
end
function WeakAuras.UpdateThumbnail(data)
local id = data.id
local button = displayButtons[id]
if (not button) then
return
end
button:UpdateThumbnail()
end
function OptionsPrivate.OpenTexturePicker(baseObject, paths, properties, textures, SetTextureFunc, adjustSize)
OptionsPrivate.TexturePicker(frame):Open(baseObject, paths, properties, textures, SetTextureFunc, adjustSize)
end
function OptionsPrivate.OpenIconPicker(baseObject, paths, groupIcon)
OptionsPrivate.IconPicker(frame):Open(baseObject, paths, groupIcon)
end
function OptionsPrivate.OpenModelPicker(baseObject, path)
if not(C_AddOns.IsAddOnLoaded("WeakAurasModelPaths")) then
local loaded, reason = C_AddOns.LoadAddOn("WeakAurasModelPaths");
if not(loaded) then
reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.")
WeakAuras.prettyPrint(string.format(L["ModelPaths could not be loaded, the addon is %s"], reason));
WeakAuras.ModelPaths = {};
end
OptionsPrivate.ModelPicker(frame).modelTree:SetTree(WeakAuras.ModelPaths)
end
OptionsPrivate.ModelPicker(frame):Open(baseObject, path);
end
function OptionsPrivate.OpenCodeReview(data)
OptionsPrivate.CodeReview(frame):Open(data);
end
function OptionsPrivate.OpenTriggerTemplate(data, targetId)
if not(C_AddOns.IsAddOnLoaded("WeakAurasTemplates")) then
local loaded, reason = C_AddOns.LoadAddOn("WeakAurasTemplates");
if not(loaded) then
reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.")
WeakAuras.prettyPrint(string.format(L["Templates could not be loaded, the addon is %s"], reason));
return;
end
frame.newView = WeakAuras.CreateTemplateView(OptionsPrivate.Private, frame);
end
-- This is called multiple times if a group is selected
if frame.window ~= "newView" then
frame.newView:Open(data, targetId);
end
end
OptionsPrivate.currentDynamicTextInput = false;
local BaseDynamicTextCodes = {
trigger = {
{type = "mini", name = "p", desc = L["Progress - The remaining time of a timer, or a non-timer value"]},
{type = "mini", name = "t", desc = L["Total - The maximum duration of a timer, or a maximum non-timer value"]},
{type = "mini", name = "n", desc = L["Name - The name of the display (usually an aura name), or the display's ID if there is no dynamic name"]},
{type = "mini", name = "i", desc = L["Icon - The icon associated with the display"]},
{type = "mini", name = "s", desc = L["Stacks - The number of stacks of an aura (usually)"]},
},
global = {
{type = "mini", name = "c", desc = L["Custom - Allows you to define a custom Lua function that returns a list of string values. %c1 will be replaced by the first value returned, %c2 by the second, etc."]},
{type = "mini", name = "%", desc = L["% - To show a percent sign"]},
}
}
function OptionsPrivate.UpdateTextReplacements(frame, data)
frame.scrollList:ReleaseChildren()
local props = OptionsPrivate.Private.GetAdditionalProperties(data)
local sortedProps = {}
-- Add global header and markers
table.insert(sortedProps, {type = "header", triggerNum = 0, name = "Global Properties"})
for index, icon in ipairs(ICON_LIST) do
table.insert(sortedProps, {type = "marker", triggerNum = 0, name = "{rt"..index.."}", desc = icon..":0|t", widthFraction = #ICON_LIST})
end
-- Add base dynamic text codes
local globalProps = {}
tAppendAll(globalProps, CopyTable(BaseDynamicTextCodes.trigger))
tAppendAll(globalProps, CopyTable(BaseDynamicTextCodes.global))
for _, prop in ipairs(globalProps) do
prop.widthFraction = #globalProps
prop.triggerNum = 0
table.insert(sortedProps, prop)
end
-- Process each trigger's properties
for triggerNum, triggerProps in pairs(props) do
if next(triggerProps) then
-- Create a temporary table for this trigger's properties
local tempProps = {}
-- Add the properties to the temporary table
5 months ago
for name, data in pairs(triggerProps) do
table.insert(tempProps, {triggerNum = triggerNum, name = name, desc = data.display})
end
-- Sort the temporary table by name
table.sort(tempProps, function(a, b)
return a.name < b.name
end)
-- Add a header for the trigger
table.insert(sortedProps, {type = "header", triggerNum = triggerNum, name = OptionsPrivate.GetTriggerTitle(data, triggerNum)})
-- Add the base properties for the trigger
for _, v in ipairs(BaseDynamicTextCodes.trigger) do
local prop = CopyTable(v)
prop.widthFraction = #BaseDynamicTextCodes.trigger
prop.triggerNum = triggerNum
table.insert(sortedProps, prop)
end
-- Add the sorted properties to the sortedProps table
for _, prop in ipairs(tempProps) do
table.insert(sortedProps, prop)
end
end
end
-- Create a modified WeakAurasSnippetButton for each property and add it to ScrollList
local lastType, miniGroup
for i, prop in ipairs(sortedProps) do
if prop.type == "header" then
local heading = AceGUI:Create("Heading")
heading:SetText(prop.name)
heading:SetRelativeWidth(1)
heading.label:SetFontObject(GameFontNormalSmall2)
frame.scrollList:AddChild(heading)
else
if ((prop.type == "mini" or prop.type == "marker") and prop.type ~= lastType)
then
miniGroup = AceGUI:Create("SimpleGroup")
miniGroup:SetLayout("Flow")
miniGroup:SetAutoAdjustHeight(true)
miniGroup:SetRelativeWidth(1)
frame.scrollList:AddChild(miniGroup)
end
local button = AceGUI:Create("WeakAurasSnippetButton")
local propIndex = prop.triggerNum > 0 and ("%s"):format(prop.triggerNum) or ""
local propPrefix = prop.triggerNum > 0 and ("%%%s."):format(propIndex) or "%"
if prop.type == "marker" then
button:SetTitle(prop.desc)
else
button:SetTitle(string.format("|cFFFFCC00%s|r%s", propPrefix, prop.name))
end
if prop.type == "mini" or prop.type == "marker" then
button:SetRelativeWidth((1/prop.widthFraction) - 1e-10)
else
button:SetRelativeWidth(1)
end
button.title:SetFontObject(GameFontNormal)
button.frame:SetHeight(28)
button:SetDynamicTextStyle()
-- Set Tooltip
if prop.type ~= "marker" then
button.frame:SetScript("OnEnter", function(frame)
local tooltip = GameTooltip
tooltip:SetWidth(300)
tooltip:SetOwner(frame, "ANCHOR_RIGHT")
tooltip:ClearLines()
tooltip:AddLine(("%s%s"):format(propPrefix, prop.name))
tooltip:AddLine(prop.desc, 1, 1, 1, true)
if prop.name ~= "c" and prop.name ~= "%" then
tooltip:AddLine("\n")
tooltip:AddLine(
prop.triggerNum > 0
and L["The trigger number is optional. When no trigger number is specified, the trigger selected via dynamic information will be used."]
or L["By default this shows the information from the trigger selected via dynamic information. The information from a specific trigger can be shown via e.g. %2.p."],
0.8, 0.8, 0.8,
true)
end
tooltip:Show()
frame.obj:Fire("OnEnter")
end)
else
button.frame:SetScript("OnEnter", nil)
end
-- Insert dynamic text property on click
button:SetCallback("OnClick", function()
local insertProp
if prop.type == "marker" then
insertProp = prop.name
else
if IsShiftKeyDown() then
insertProp = prop.name == "%" and "%%" or ("%%{%s}"):format(prop.name)
if prop.triggerNum > 0 then
insertProp = string.format("%%{%d.%s}", propIndex, prop.name)
end
else
insertProp = prop.name == "%" and "%%" or ("%%%s"):format(prop.name)
if prop.triggerNum > 0 then
insertProp = string.format("%%%d.%s", propIndex, prop.name)
end
end
end
OptionsPrivate.currentDynamicTextInput.editbox:Insert(insertProp)
OptionsPrivate.currentDynamicTextInput.editbox:SetFocus()
end)
if prop.type == "mini" or prop.type == "marker" then
miniGroup:AddChild(button)
else
frame.scrollList:AddChild(button)
end
end
lastType = prop.type
end
end
function OptionsPrivate.ResetMoverSizer()
if(frame and frame.mover and frame.moversizer and frame.mover.moving.region and frame.mover.moving.data) then
frame.moversizer:SetToRegion(frame.mover.moving.region, frame.mover.moving.data);
end
end
function WeakAuras.SetMoverSizer(id)
OptionsPrivate.Private.EnsureRegion(id)
if OptionsPrivate.Private.regions[id].region.toShow then
frame.moversizer:SetToRegion(OptionsPrivate.Private.regions[id].region, db.displays[id])
else
if OptionsPrivate.Private.clones[id] then
local _, clone = next(OptionsPrivate.Private.clones[id])
if clone then
frame.moversizer:SetToRegion(clone, db.displays[id])
end
end
end
end
function WeakAuras.GetMoverSizerId()
return frame.moversizer:GetCurrentId()
end
local function AddDefaultSubRegions(data)
data.subRegions = data.subRegions or {}
for type, subRegionData in pairs(OptionsPrivate.Private.subRegionTypes) do
if subRegionData.addDefaultsForNewAura then
subRegionData.addDefaultsForNewAura(data)
end
end
end
function WeakAuras.NewAura(sourceData, regionType, targetId)
local function ensure(t, k, v)
return t and k and v and t[k] == v
end
local new_id = OptionsPrivate.Private.FindUnusedId("New")
local data = {id = new_id, regionType = regionType, uid = WeakAuras.GenerateUniqueID()}
WeakAuras.DeepMixin(data, OptionsPrivate.Private.data_stub);
if (sourceData) then
WeakAuras.DeepMixin(data, sourceData);
end
data.internalVersion = WeakAuras.InternalVersion();
OptionsPrivate.Private.validate(data, OptionsPrivate.Private.regionTypes[regionType].default);
AddDefaultSubRegions(data)
if targetId then
local target = OptionsPrivate.GetDisplayButton(targetId);
local group
if (target) then
if (target:IsGroup()) then
group = target;
else
group = OptionsPrivate.GetDisplayButton(target.data.parent);
end
if (group) then
-- Sanity check so that we don't create a group/dynamic group in a group
if (regionType == "group" or regionType == "dynamicgroup") and group.data.regionType == "dynamicgroup" then
return
end
local children = group.data.controlledChildren;
local index = target:GetGroupOrder();
if (ensure(children, index, target.data.id)) then
-- account for insert position
index = index + 1;
tinsert(children, index, data.id);
else
-- move source into group as the first child
tinsert(children, 1, data.id);
end
data.parent = group.data.id;
WeakAuras.Add(data);
WeakAuras.Add(group.data);
OptionsPrivate.Private.AddParents(group.data)
WeakAuras.NewDisplayButton(data);
WeakAuras.UpdateGroupOrders(group.data);
OptionsPrivate.ClearOptions(group.data.id);
group.callbacks.UpdateExpandButton();
group:UpdateParentWarning();
group:Expand();
group:ReloadTooltip();
OptionsPrivate.PickAndEditDisplay(data.id);
else
-- move source into the top-level list
WeakAuras.Add(data);
WeakAuras.NewDisplayButton(data);
OptionsPrivate.PickAndEditDisplay(data.id);
end
else
error(string.format("Calling 'WeakAuras.NewAura' with invalid groupId %s. Reload your UI to fix the display list.", targetId))
end
else
-- move source into the top-level list
WeakAuras.Add(data);
WeakAuras.NewDisplayButton(data);
OptionsPrivate.PickAndEditDisplay(data.id);
end
end
function OptionsPrivate.ResetCollapsed(id, namespace)
if id then
if namespace and collapsedOptions[id] then
collapsedOptions[id][namespace] = nil
else
collapsedOptions[id] = nil
end
end
end
function OptionsPrivate.IsCollapsed(id, namespace, path, default)
local tmp = collapsedOptions[id]
if tmp == nil then return default end
tmp = tmp[namespace]
if tmp == nil then return default end
if type(path) ~= "table" then
tmp = tmp[path]
else
for _, key in ipairs(path) do
tmp = tmp[key]
if tmp == nil or tmp[collapsed] then
break
end
end
end
if tmp == nil or tmp[collapsed] == nil then
return default
else
return tmp[collapsed]
end
end
function OptionsPrivate.SetCollapsed(id, namespace, path, v)
collapsedOptions[id] = collapsedOptions[id] or {}
collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {}
if type(path) ~= "table" then
collapsedOptions[id][namespace][path] = collapsedOptions[id][namespace][path] or {}
collapsedOptions[id][namespace][path][collapsed] = v
else
local tmp = collapsedOptions[id][namespace] or {}
for _, key in ipairs(path) do
tmp[key] = tmp[key] or {}
tmp = tmp[key]
end
tmp[collapsed] = v
end
end
function OptionsPrivate.MoveCollapseDataUp(id, namespace, path)
collapsedOptions[id] = collapsedOptions[id] or {}
collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {}
if type(path) ~= "table" then
collapsedOptions[id][namespace][path], collapsedOptions[id][namespace][path - 1]
= collapsedOptions[id][namespace][path - 1], collapsedOptions[id][namespace][path]
else
local tmp = collapsedOptions[id][namespace]
local lastKey = tremove(path)
for _, key in ipairs(path) do
tmp[key] = tmp[key] or {}
tmp = tmp[key]
end
tmp[lastKey], tmp[lastKey - 1] = tmp[lastKey - 1], tmp[lastKey]
end
end
function OptionsPrivate.MoveCollapseDataDown(id, namespace, path)
collapsedOptions[id] = collapsedOptions[id] or {}
collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {}
if type(path) ~= "table" then
collapsedOptions[id][namespace][path], collapsedOptions[id][namespace][path + 1]
= collapsedOptions[id][namespace][path + 1], collapsedOptions[id][namespace][path]
else
local tmp = collapsedOptions[id][namespace]
local lastKey = tremove(path)
for _, key in ipairs(path) do
tmp[key] = tmp[key] or {}
tmp = tmp[key]
end
tmp[lastKey], tmp[lastKey + 1] = tmp[lastKey + 1], tmp[lastKey]
end
end
function OptionsPrivate.RemoveCollapsed(id, namespace, path)
local data = collapsedOptions[id] and collapsedOptions[id][namespace]
if not data then
return
end
local index
local maxIndex = 0
if type(path) ~= "table" then
index = path
else
index = path[#path]
for i = 1, #path - 1 do
data = data[path[i]]
if not data then
return
end
end
end
for k in pairs(data) do
if k ~= collapsed then
maxIndex = max(maxIndex, k)
end
end
while index <= maxIndex do
data[index] = data[index + 1]
index = index + 1
end
end
function OptionsPrivate.InsertCollapsed(id, namespace, path, value)
local data = collapsedOptions[id] and collapsedOptions[id][namespace]
if not data then
return
end
local insertPoint
local maxIndex
if type(path) ~= "table" then
insertPoint = path
else
insertPoint = path[#path]
for i = 1, #path - 1 do
data = data[path[i]]
if not data then
return
end
end
end
for k in pairs(data) do
if k ~= collapsed and k >= insertPoint then
if not maxIndex or k > maxIndex then
maxIndex = k
end
end
end
if maxIndex then -- may be nil if insertPoint is greater than the max of anything else
for i = maxIndex, insertPoint, -1 do
data[i + 1] = data[i]
end
end
data[insertPoint] = {[collapsed] = value}
end
function OptionsPrivate.DuplicateCollapseData(id, namespace, path)
collapsedOptions[id] = collapsedOptions[id] or {}
collapsedOptions[id][namespace] = collapsedOptions[id][namespace] or {}
if type(path) ~= "table" then
if (collapsedOptions[id][namespace][path]) then
tinsert(collapsedOptions[id][namespace], path + 1, CopyTable(collapsedOptions[id][namespace][path]))
end
else
local tmp = collapsedOptions[id][namespace]
local lastKey = tremove(path)
for _, key in ipairs(path) do
tmp[key] = tmp[key] or {}
tmp = tmp[key]
end
if (tmp[lastKey]) then
tinsert(tmp, lastKey + 1, CopyTable(tmp[lastKey]))
end
end
end
function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, hidden, setHidden,
withoutColor, index, total)
local headerOption
if withHeader and (not index or index == 1) then
headerOption = {
type = "execute",
control = "WeakAurasExpandSmall",
3 years ago
name = L["|cffffcc00Format Options|r"],
width = WeakAuras.doubleWidth,
func = function(info, button)
setHidden(not hidden())
end,
image = function()
return hidden() and "collapsed" or "expanded"
end,
imageWidth = 15,
imageHeight = 15,
arg = {
expanderName = tostring(addOption)
}
}
addOption("header", headerOption)
else
hidden = false
end
local seenSymbols = {}
local parseFn = function(symbol)
if not seenSymbols[symbol] then
local _, sym = string.match(symbol, "(.+)%.(.+)")
sym = sym or symbol
if sym == "i" then
-- No special options for these
else
addOption(symbol .. "desc", {
type = "description",
name = L["Format for %s"]:format("%" .. symbol),
width = WeakAuras.normalWidth,
hidden = hidden
})
addOption(symbol .. "_format", {
type = "select",
name = L["Format"],
width = WeakAuras.normalWidth,
values = OptionsPrivate.Private.format_types_display,
hidden = hidden,
reloadOptions = true
})
local selectedFormat = get(symbol .. "_format")
if (OptionsPrivate.Private.format_types[selectedFormat]) then
4 years ago
OptionsPrivate.Private.format_types[selectedFormat].AddOptions(symbol, hidden, addOption, get, withoutColor)
end
seenSymbols[symbol] = true
end
end
end
if type(input) == "table" then
for _, txt in ipairs(input) do
OptionsPrivate.Private.ParseTextStr(txt, parseFn)
end
else
OptionsPrivate.Private.ParseTextStr(input, parseFn)
end
if withHeader and (not index or index == total) then
addOption("header_anchor",
{
type = "description",
name = "",
control = "WeakAurasExpandAnchor",
arg = {
expanderName = tostring(addOption)
}
}
)
end
if not next(seenSymbols) and headerOption and not index then
headerOption.hidden = true
end
return next(seenSymbols) ~= nil
end