diff --git a/.luacheckrc b/.luacheckrc index 19b117f..4d453a3 100644 --- a/.luacheckrc +++ b/.luacheckrc @@ -31,16 +31,20 @@ ignore = { "211/CL", -- Unused local variable "CL" "212", -- Unused argument "213", -- Unused loop variable + "214", -- unused hint -- "231", -- Set but never accessed "311", -- Value assigned to a local variable is unused "314", -- Value of a field in a table literal is unused "42.", -- Shadowing a local variable, an argument, a loop variable. "43.", -- Shadowing an upvalue, an upvalue argument, an upvalue loop variable. "542", -- An empty if branch + "581", -- error-prone operator orders + "582", -- error-prone operator orders } globals = { -- compat + "ipairs_reverse", "tInvert", "Round", "tIndexOf", diff --git a/WeakAuras/AuraEnvironment.lua b/WeakAuras/AuraEnvironment.lua index 9830794..292498e 100644 --- a/WeakAuras/AuraEnvironment.lua +++ b/WeakAuras/AuraEnvironment.lua @@ -144,6 +144,7 @@ local blockedFunctions = { GuildUninvite = true, securecall = true, DeleteCursorItem = true, + ChatEdit_SendText = true } local blockedTables = { @@ -151,6 +152,9 @@ local blockedTables = { SendMailMailButton = true, SendMailMoneyGold = true, MailFrameTab2 = true, + ChatFrame1 = true, + WeakAurasOptions = true, + WeakAurasOptionsSaved = true } local aura_environments = {} @@ -303,13 +307,11 @@ local FakeWeakAurasMixin = { -- to discuss these. But Auras have no purpose for calling these Add = true, AddMany = true, - AddManyFromAddons = true, Delete = true, HideOptions = true, Rename = true, NewAura = true, OptionsFrame = true, - RegisterAddon = true, RegisterDisplay = true, RegisterRegionOptions = true, RegisterSubRegionOptions = true, @@ -320,28 +322,18 @@ local FakeWeakAurasMixin = { ShowOptions = true, -- Note these shouldn't exist in the WeakAuras namespace, but moving them takes a bit of effort, -- so for now just block them and clean them up later - CollisionResolved = true, ClearAndUpdateOptions = true, - CloseCodeReview = true, CloseImportExport = true, CreateTemplateView = true, - DisplayToString = true, FillOptions = true, FindUnusedId = true, GetMoverSizerId = true, GetDisplayButton = true, Import = true, NewDisplayButton = true, - NewAura = true, - OpenTriggerTemplate = true, - OpenCodeReview = true, PickDisplay = true, SetMoverSizer = true, - SetImporting = true, - SortDisplayButtons = true, - ShowOptions = true, ToggleOptions = true, - UpdateDisplayButton = true, UpdateGroupOrders = true, UpdateThumbnail = true, validate = true, @@ -362,9 +354,7 @@ local FakeWeakAurasMixin = { frames = true, loadFrame = true, unitLoadFrame = true, - importDisplayButtons = true, loaded = true - }, override = { me = UnitName("player"), @@ -432,7 +422,7 @@ function WeakAuras.LoadFunction(string, id, inTrigger) if function_cache[string] then return function_cache[string] else - local loadedFunction, errorString = loadstring("--[==[ Error in '" .. (id or "Unknown") .. (inTrigger and ("':'".. inTrigger) or "") .."' ]==] " .. string) + local loadedFunction, errorString = loadstring(string, "Error in: " .. (id or "Unknown") .. (inTrigger and ("':'".. inTrigger) or "")) if errorString then print(errorString) else diff --git a/WeakAuras/AuraWarnings.lua b/WeakAuras/AuraWarnings.lua index 456deba..4c4719d 100644 --- a/WeakAuras/AuraWarnings.lua +++ b/WeakAuras/AuraWarnings.lua @@ -12,7 +12,7 @@ local function OnDelete(event, uid) warnings[uid] = nil end -Private:RegisterCallback("Delete", OnDelete) +Private.callbacks:RegisterCallback("Delete", OnDelete) local function UpdateWarning(uid, key, severity, message, printOnConsole) if not uid then @@ -33,27 +33,32 @@ local function UpdateWarning(uid, key, severity, message, printOnConsole) severity = severity, message = message } + Private.callbacks:Fire("AuraWarningsUpdated", uid) else - warnings[uid][key] = nil + if warnings[uid][key] then + warnings[uid][key] = nil + Private.callbacks:Fire("AuraWarningsUpdated", uid) + end end - - Private.callbacks:Fire("AuraWarningsUpdated", uid) end local severityLevel = { info = 0, - warning = 1, - error = 2 + sound = 1, + warning = 2, + error = 3 } local icons = { info = [[Interface/friendsframe/informationicon.blp]], + sound = [[chatframe-button-icon-voicechat]], warning = [[Interface/buttons/adventureguidemicrobuttonalert.blp]], error = [[Interface/DialogFrame/UI-Dialog-Icon-AlertNew]] } local titles = { info = L["Information"], + sound = L["Sound"], warning = L["Warning"], error = L["Error"] } @@ -103,6 +108,7 @@ local function FormatWarnings(uid) local result = "" result = AddMessages(result, messagePerSeverity["error"], icons["error"], mixedSeverity) result = AddMessages(result, messagePerSeverity["warning"], icons["warning"], mixedSeverity) + result = AddMessages(result, messagePerSeverity["sound"], icons["sound"], mixedSeverity) result = AddMessages(result, messagePerSeverity["info"], icons["info"], mixedSeverity) return icons[maxSeverity], titles[maxSeverity], result end diff --git a/WeakAuras/BuffTrigger2.lua b/WeakAuras/BuffTrigger2.lua index c0b38fc..2d43a85 100644 --- a/WeakAuras/BuffTrigger2.lua +++ b/WeakAuras/BuffTrigger2.lua @@ -85,6 +85,7 @@ local groupScanFuncs = {} --Active ScanFuncs per actual unit id local activeGroupScanFuncs = {} +local raidMarkScanFuncs = {} -- Multi Target tracking local scanFuncNameMulti = {} @@ -102,6 +103,20 @@ local matchDataByTrigger = {} local matchDataChanged = {} +local function UnitExistsFixed(unit) + return UnitExists(unit) or UnitGUID(unit) +end + +local function UnitInSubgroupOrPlayer(unit, includePets) + if includePets == nil then + return UnitIsUnit("player", unit) + elseif includePets == "PlayersAndPets" then + return UnitIsUnit("player", unit) or UnitIsUnit("pet", unit) + elseif includePets == "PetsOnly" then + return UnitIsUnit("pet", unit) + end +end + local function GetOrCreateSubTable(base, next, ...) if not next then return base @@ -429,7 +444,7 @@ local function FindBestMatchDataForUnit(time, id, triggernum, triggerInfo, unit) return bestMatch, matchCount, stackCount, nextCheck end -local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, unaffected) +local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, unaffected, raidMark) if not triggerStates[cloneId] then triggerStates[cloneId] = { show = true, @@ -445,9 +460,11 @@ local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, mat casterName = bestMatch.casterName, spellId = bestMatch.spellId, index = bestMatch.index, + filter = bestMatch.filter, unit = bestMatch.unit, unitName = bestMatch.unitName, GUID = bestMatch.unit and UnitGUID(bestMatch.unit) or bestMatch.GUID, + raidMark = raidMark, matchCount = matchCount, unitCount = unitCount, maxUnitCount = maxUnitCount, @@ -481,6 +498,11 @@ local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, mat changed = true end + if state.raidMark ~= raidMark then + state.raidMark = raidMark + changed = true + end + if state.unitName ~= bestMatch.unitName then state.unitName = bestMatch.unitName changed = true @@ -555,6 +577,11 @@ local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, mat changed = true end + if state.filter ~= bestMatch.filter then + state.filter = bestMatch.filter + changed = true + end + if state.tooltip ~= bestMatch.tooltip then state.tooltip = bestMatch.tooltip changed = true @@ -622,7 +649,7 @@ local function UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, mat end end -local function UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, unaffected) +local function UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit, totalStacks, affected, unaffected, raidMark) local fallbackName, fallbackIcon = BuffTrigger.GetNameAndIconSimple(WeakAuras.GetData(triggerInfo.id), triggerInfo.triggernum) if not triggerStates[cloneId] then triggerStates[cloneId] = { @@ -640,6 +667,7 @@ local function UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, affected = affected, unaffected = unaffected, unit = unit, + raidMark = raidMark, unitName = unit and GetUnitName(unit, false) or "", destName = "", name = fallbackName, @@ -692,6 +720,17 @@ local function UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, changed = true end + local GUID = unit and UnitGUID(unit) + if state.GUID ~= GUID then + state.GUID = GUID + changed = true + end + + if state.raidMark ~= raidMark then + state.raidMark = raidMark + changed = true + end + local unitName = unit and GetUnitName(unit, false) or "" if state.unitName ~= unitName then state.unitName = unitName @@ -781,26 +820,55 @@ local function RemoveState(triggerStates, cloneId) end end -local function GetAllUnits(unit, allUnits) +local function GetAllUnits(unit, allUnits, includePets) if unit == "group" then if allUnits then local i = 1 local raid = true + local pets = true return function() if raid then if i <= 40 then - local ret = WeakAuras.raidUnits[i] - i = i + 1 + local ret + if includePets == "PlayersAndPets" then + ret = pets and WeakAuras.raidpetUnits[i] or WeakAuras.raidUnits[i] + pets = not pets + if pets then + i = i + 1 + end + elseif includePets == "PetsOnly" then + ret = WeakAuras.raidpetUnits[i] + i = i + 1 + else -- raid + ret = WeakAuras.raidUnits[i] + i = i + 1 + end return ret end + if includePets and pets then + pets = not pets + return "pet" + end raid = false i = 1 return "player" end if i <= 4 then - local ret = WeakAuras.partyUnits[i] - i = i + 1 + local ret + if includePets == "PlayersAndPets" then + ret = pets and WeakAuras.partypetUnits[i] or WeakAuras.partyUnits[i] + pets = not pets + if pets then + i = i + 1 + end + elseif includePets == "PetsOnly" then + ret = WeakAuras.partypetUnits[i] + i = i + 1 + else -- group + ret = WeakAuras.partyUnits[i] + i = i + 1 + end return ret end @@ -813,10 +881,23 @@ local function GetAllUnits(unit, allUnits) if IsInRaid() then local i = 1 local max = GetNumGroupMembers() + local pets = true return function() if i <= max then - local ret = WeakAuras.raidUnits[i] - i = i + 1 + local ret + if includePets == "PlayersAndPets" then + ret = pets and WeakAuras.raidpetUnits[i] or WeakAuras.raidUnits[i] + pets = not pets + if pets then + i = i + 1 + end + elseif includePets == "PetsOnly" then + ret = WeakAuras.raidpetUnits[i] + i = i + 1 + else -- raid + ret = WeakAuras.raidUnits[i] + i = i + 1 + end return ret end i = 1 @@ -824,14 +905,39 @@ local function GetAllUnits(unit, allUnits) else local i = 0 local max = GetNumSubgroupMembers() + local pets = true return function() if i == 0 then - i = 1 - return "player" + if includePets == "PlayersAndPets" then + local ret = pets and "pet" or "player" + pets = not pets + if pets then + i = i + 1 + end + return ret + elseif includePets == "PetsOnly" then + i = 1 + return "pet" + else -- group + i = 1 + return "player" + end else if i <= max then - local ret = WeakAuras.partyUnits[i] - i = i + 1 + local ret + if includePets == "PlayersAndPets" then + ret = pets and WeakAuras.partypetUnits[i] or WeakAuras.partyUnits[i] + pets = not pets + if pets then + i = i + 1 + end + elseif includePets == "PetsOnly" then + ret = WeakAuras.partypetUnits[i] + i = i + 1 + else -- group + ret = WeakAuras.partyUnits[i] + i = i + 1 + end return ret end end @@ -850,7 +956,7 @@ local function GetAllUnits(unit, allUnits) end return function() local ret = unit .. i - while not allUnits and not UnitExists(ret) do + while not allUnits and not UnitExistsFixed(ret) do i = i + 1 if i > max then i = 1 @@ -880,12 +986,18 @@ local function MaxUnitCount(triggerInfo) if triggerInfo.groupTrigger then return triggerInfo.maxUnitCount else - return UnitExists(triggerInfo.unit) and 1 or 0 + return UnitExistsFixed(triggerInfo.unit) and 1 or 0 end end local function TriggerInfoApplies(triggerInfo, unit) - if triggerInfo.ignoreSelf and UnitIsUnit("player", unit) then + local controllingUnit = unit + if WeakAuras.UnitIsPet(unit) then + controllingUnit = WeakAuras.petUnitToUnit[unit] + end + + + if triggerInfo.ignoreSelf and UnitIsUnit("player", controllingUnit) then return false end @@ -901,14 +1013,23 @@ local function TriggerInfoApplies(triggerInfo, unit) return false end + if triggerInfo.unit == "group" then + local isPet = WeakAuras.UnitIsPet(unit) + if triggerInfo.includePets == "PetsOnly" and not isPet then + return false + elseif triggerInfo.includePets == nil and isPet then -- exclude pets + return false + end + end + if triggerInfo.unit == "group" and triggerInfo.groupSubType == "party" then if IsInRaid() then -- Filter our player/party# while in raid and keep only raid units that are correct - if not Private.multiUnitUnits.raid[unit] or not UnitIsUnit("player", unit) then + if not Private.multiUnitUnits.raid[unit] or not UnitInSubgroupOrPlayer(unit, triggerInfo.includePets) then return false end else - if not UnitIsUnit("player", unit) then + if not UnitInSubgroupOrPlayer(unit, triggerInfo.includePets) then return false end end @@ -923,7 +1044,7 @@ local function TriggerInfoApplies(triggerInfo, unit) return false end - if triggerInfo.class and not triggerInfo.class[select(2, UnitClass(unit))] then + if triggerInfo.class and not triggerInfo.class[select(2, UnitClass(controllingUnit))] then return false end @@ -937,7 +1058,7 @@ end local function FormatAffectedUnaffected(triggerInfo, matchedUnits) local affected = "" local unaffected = "" - for unit in GetAllUnits(triggerInfo.unit) do + for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then if matchedUnits[unit] then affected = affected .. (GetUnitName(unit, false) or unit) .. ", " @@ -965,6 +1086,27 @@ local function SatisfiesGroupMatchCount(triggerInfo, unitCount, maxUnitCount, ma return true end +local function SatisfiesMatchCountPerUnit(triggerInfo, countPerUnit) + return not triggerInfo.matchPerUnitCountFunc or triggerInfo.matchPerUnitCountFunc(countPerUnit) +end + +local function bestUnit(triggerInfo, bestMatch) + if bestMatch then + return bestMatch.unit + elseif not triggerInfo.groupTrigger and triggerInfo.unit then + return triggerInfo.unit + end +end + +local function markForTriggerInfo(triggerInfo, unit) + if triggerInfo.fetchRaidMark then + local rt = GetRaidTargetIndex(unit) + if rt then + return "{rt" .. GetRaidTargetIndex(unit) .. "}" + end + end +end + local function SortMatchDataByUnitIndex(a, b) if a.unit and b.unit and a.unit ~= b.unit then return a.unit < b.unit @@ -1006,10 +1148,13 @@ local function UpdateTriggerState(time, id, triggernum) affected, unaffected = FormatAffectedUnaffected(triggerInfo, matchedUnits) end + local unit = bestUnit(triggerInfo, bestMatch) + local mark = markForTriggerInfo(triggerInfo, unit) + if bestMatch then - updated = UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCount, totalStacks, affected, unaffected) + updated = UpdateStateWithMatch(time, bestMatch, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCount, totalStacks, affected, unaffected, mark) else - updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, not triggerInfo.groupTrigger and triggerInfo.unit or nil, 0, 0, maxUnitCount, 0, 0, affected, unaffected) + updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, cloneId, unit, 0, 0, maxUnitCount, 0, 0, affected, unaffected, mark) end else updated = RemoveState(triggerStates, cloneId) @@ -1067,12 +1212,17 @@ local function UpdateTriggerState(time, id, triggernum) usedCloneIds[cloneId] = 1 end - updated = UpdateStateWithMatch(time, auraData, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, matchCountPerUnit[auraData.unit], totalStacks, affected, unaffected) or updated + local mark = markForTriggerInfo(triggerInfo, auraData.unit) + updated = UpdateStateWithMatch(time, auraData, triggerStates, cloneId, matchCount, unitCount, maxUnitCount, + matchCountPerUnit[auraData.unit], totalStacks, affected, unaffected, mark) or updated cloneIds[cloneId] = true end if matchCount == 0 then - updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, "", nil, 0, 0, maxUnitCount, 0, totalStacks, affected, unaffected) or updated + local unit = bestUnit(triggerInfo, nil) + local mark = markForTriggerInfo(triggerInfo, unit) + updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, "", nil, 0, 0, maxUnitCount, 0, totalStacks, + affected, unaffected, mark) or updated cloneIds[""] = true end end @@ -1087,20 +1237,23 @@ local function UpdateTriggerState(time, id, triggernum) if matchDataByTrigger[id] and matchDataByTrigger[id][triggernum] then for unit, unitData in pairs(matchDataByTrigger[id][triggernum]) do local bestMatch, countPerUnit, stacks, nextCheckForMatch = FindBestMatchDataForUnit(time, id, triggernum, triggerInfo, unit) - matchCount = matchCount + countPerUnit - totalStacks = totalStacks + (stacks or 0) - if bestMatch then - unitCount = unitCount + 1 - matchedUnits[unit] = true - end - if not nextCheck then - nextCheck = nextCheckForMatch - elseif nextCheckForMatch then - nextCheck = min(nextCheck, nextCheckForMatch) + if SatisfiesMatchCountPerUnit(triggerInfo, countPerUnit) then + matchCount = matchCount + countPerUnit + totalStacks = totalStacks + (stacks or 0) + if bestMatch then + unitCount = unitCount + 1 + matchedUnits[unit] = true + end + + if not nextCheck then + nextCheck = nextCheckForMatch + elseif nextCheckForMatch then + nextCheck = min(nextCheck, nextCheckForMatch) + end + matches[unit] = bestMatch + matchCountPerUnit[unit] = countPerUnit end - matches[unit] = bestMatch - matchCountPerUnit[unit] = countPerUnit end end @@ -1116,22 +1269,31 @@ local function UpdateTriggerState(time, id, triggernum) if triggerInfo.perUnitMode == "affected" then for unit, bestMatch in pairs(matches) do if bestMatch then - updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + local mark = markForTriggerInfo(triggerInfo, unit) + updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, + matchCountPerUnit[unit], totalStacks, affected, unaffected, mark) + or updated cloneIds[unit] = true end end else -- state per unaffected unit - for unit in GetAllUnits(triggerInfo.unit) do + for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then local bestMatch = matches[unit] + local mark = markForTriggerInfo(triggerInfo, unit) if bestMatch then if triggerInfo.perUnitMode == "all" then - updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + updated = UpdateStateWithMatch(time, bestMatch, triggerStates, unit, matchCount, unitCount, maxUnitCount, + matchCountPerUnit[unit], totalStacks, affected, unaffected, mark) + or updated cloneIds[unit] = true end else - updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, unit, unit, matchCount, unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, affected, unaffected) or updated + updated = UpdateStateWithNoMatch(time, triggerStates, triggerInfo, unit, unit, matchCount, + unitCount, maxUnitCount, matchCountPerUnit[unit], totalStacks, + affected, unaffected) + or updated cloneIds[unit] = true end end @@ -1202,7 +1364,7 @@ local function CleanUpOutdatedMatchData(removeIndex, unit, filter) if matchData[unit] and matchData[unit][filter] then for index = removeIndex, #matchData[unit][filter] do local data = matchData[unit][filter][index] - if data.index >= removeIndex or not UnitExists(unit) then + if data.index >= removeIndex or not UnitExistsFixed(unit) then matchData[unit][filter][index] = nil for id, triggerData in pairs(data.auras) do for triggernum in pairs(triggerData) do @@ -1270,7 +1432,6 @@ end local function ScanUnitWithFilter(matchDataChanged, time, unit, filter, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup, scanFuncName, scanFuncSpellId, scanFuncGeneral) - if not scanFuncName and not scanFuncSpellId and not scanFuncGeneral and not scanFuncNameGroup and not scanFuncSpellIdGroup and not scanFuncGeneralGroup then if matchDataUpToDate[unit] then matchDataUpToDate[unit][filter] = nil @@ -1281,7 +1442,7 @@ local function ScanUnitWithFilter(matchDataChanged, time, unit, filter, local index = 1 - if UnitExists(unit) then + if UnitExistsFixed(unit) then while true do local name, rank, icon, stacks, debuffClass, duration, expirationTime, unitCaster, isStealable, _, spellId = UnitAura(unit, index, filter) if not name then @@ -1330,8 +1491,17 @@ local function UpdateStates(matchDataChanged, time) end end +local function ScanRaidMarkScanFunc(matchDataChanged) + for id, idData in pairs(raidMarkScanFuncs) do + matchDataChanged[id] = matchDataChanged[id] or {} + for _, triggerInfo in ipairs(idData) do + matchDataChanged[id][triggerInfo.triggernum] = true + end + end +end + local function ScanGroupUnit(time, matchDataChanged, unitType, unit) - local unitExists = UnitExists(unit) == 1 and true or false + local unitExists = UnitExistsFixed(unit) == 1 and true or false if existingUnits[unit] ~= unitExists then existingUnits[unit] = unitExists @@ -1348,7 +1518,6 @@ local function ScanGroupUnit(time, matchDataChanged, unitType, unit) scanFuncName[unit] = scanFuncName[unit] or {} scanFuncSpellId[unit] = scanFuncSpellId[unit] or {} scanFuncGeneral[unit] = scanFuncGeneral[unit] or {} - if unitType then scanFuncNameGroup[unit] = scanFuncNameGroup[unit] or {} scanFuncSpellIdGroup[unit] = scanFuncSpellIdGroup[unit] or {} @@ -1382,20 +1551,6 @@ local function ScanGroupUnit(time, matchDataChanged, unitType, unit) end end -local function ScanAllGroup(time, matchDataChanged) - -- We iterate over all raid/player unit ids here because ScanGroupUnit also - -- handles the cases where a unit existence changes. - for unit in GetAllUnits("group") do - ScanGroupUnit(time, matchDataChanged, "group", unit) - end -end - -local function ScanAllBoss(time, matchDataChanged) - for unit in GetAllUnits("boss") do - ScanGroupUnit(time, matchDataChanged, "boss", unit) - end -end - local function ScanUnit(time, arg1) if not arg1 then return end if (Private.multiUnitUnits.raid[arg1] and IsInRaid()) then @@ -1411,8 +1566,7 @@ local function ScanUnit(time, arg1) end end -local function AddScanFuncs(triggerInfo, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) - local filter = triggerInfo.debuffType +local function AddScanFuncs(triggerInfo, filter, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) if triggerInfo.auranames then for _, name in ipairs(triggerInfo.auranames) do if name ~= "" then @@ -1440,8 +1594,7 @@ local function AddScanFuncs(triggerInfo, unit, scanFuncName, scanFuncSpellId, sc end end -local function RemoveScanFuncs(triggerInfo, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) - local filter = triggerInfo.debuffType +local function RemoveScanFuncs(triggerInfo, filter, unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) if triggerInfo.auranames then for _, name in ipairs(triggerInfo.auranames) do if name ~= "" then @@ -1472,11 +1625,17 @@ end local function RecheckActive(triggerInfo, unit, unitsToRemoveScan) local isSelf, role, inParty, class - local unitExists = UnitExists(unit) + local unitExists = UnitExistsFixed(unit) if unitExists and TriggerInfoApplies(triggerInfo, unit) then if (not activeGroupScanFuncs[unit] or not activeGroupScanFuncs[unit][triggerInfo]) then triggerInfo.maxUnitCount = triggerInfo.maxUnitCount + 1 - AddScanFuncs(triggerInfo, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + if triggerInfo.debuffType == "BOTH" then + AddScanFuncs(triggerInfo, "HELPFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + AddScanFuncs(triggerInfo, "HARMFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + else + AddScanFuncs(triggerInfo, triggerInfo.debuffType, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + end + activeGroupScanFuncs[unit] = activeGroupScanFuncs[unit] or {} activeGroupScanFuncs[unit][triggerInfo] = true matchDataChanged[triggerInfo.id] = matchDataChanged[triggerInfo.id] or {} @@ -1486,7 +1645,12 @@ local function RecheckActive(triggerInfo, unit, unitsToRemoveScan) -- Either the unit doesn't exist or the TriggerInfo no longer applies if activeGroupScanFuncs[unit] and activeGroupScanFuncs[unit][triggerInfo] then triggerInfo.maxUnitCount = triggerInfo.maxUnitCount - 1 - RemoveScanFuncs(triggerInfo, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + if triggerInfo.debuffType == "BOTH" then + RemoveScanFuncs(triggerInfo, "HELPFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + RemoveScanFuncs(triggerInfo, "HARMFUL", unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + else + RemoveScanFuncs(triggerInfo, triggerInfo.debuffType, unit, scanFuncNameGroup, scanFuncSpellIdGroup, scanFuncGeneralGroup) + end if unitsToRemoveScan then unitsToRemoveScan[unit] = unitsToRemoveScan[unit] or {} unitsToRemoveScan[unit][triggerInfo] = true @@ -1519,40 +1683,41 @@ local function EventHandler(frame, event, arg1, arg2, ...) local time = GetTime() if event == "PLAYER_TARGET_CHANGED" then ScanGroupUnit(time, matchDataChanged, nil, "target") - if not UnitExists("target") then + if not UnitExistsFixed("target") then tinsert(unitsToRemove, "target") end elseif event == "PLAYER_FOCUS_CHANGED" then ScanGroupUnit(time, matchDataChanged, nil, "focus") - if not UnitExists("focus") then + if not UnitExistsFixed("focus") then tinsert(unitsToRemove, "focus") end - elseif event == "UNIT_PET" and arg1 == "player" then - ScanGroupUnit(time, matchDataChanged, nil, "pet") - if not UnitExists("pet") then - tinsert(unitsToRemove, "pet") + elseif event == "UNIT_PET" then + local pet = WeakAuras.unitToPetUnit[arg1] + if pet then + ScanGroupUnit(time, matchDataChanged, nil, pet) + RecheckActiveForUnitType("group", pet, deactivatedTriggerInfos) + if not UnitExistsFixed(pet) then + tinsert(unitsToRemove, pet) + end end elseif event == "INSTANCE_ENCOUNTER_ENGAGE_UNIT" then - local unitsToCheck = {} for unit in GetAllUnits("boss", true) do RecheckActiveForUnitType("boss", unit, deactivatedTriggerInfos) - if not UnitExists(unit) then + if not UnitExistsFixed(unit) then tinsert(unitsToRemove, unit) end end elseif event =="ARENA_OPPONENT_UPDATE" then - local unitsToCheck = {} for unit in GetAllUnits("arena", true) do RecheckActiveForUnitType("arena", unit, deactivatedTriggerInfos) - if not UnitExists(unit) then + if not UnitExistsFixed(unit) then tinsert(unitsToRemove, unit) end end elseif event == "PARTY_MEMBERS_CHANGED" or event == "RAID_ROSTER_UPDATE" then - local unitsToCheck = {} - for unit in GetAllUnits("group", true) do + for unit in GetAllUnits("group", true, "PlayersAndPets") do RecheckActiveForUnitType("group", unit, deactivatedTriggerInfos) - if not UnitExists(unit) then + if not UnitExistsFixed(unit) then tinsert(unitsToRemove, unit) end end @@ -1569,10 +1734,12 @@ local function EventHandler(frame, event, arg1, arg2, ...) elseif event == "PLAYER_ENTERING_WORLD" then for unit in pairs(matchData) do ScanUnit(time, unit) - if not UnitExists(unit) then + if not UnitExistsFixed(unit) then tinsert(unitsToRemove, unit) end end + elseif event == "RAID_TARGET_UPDATE" then + ScanRaidMarkScanFunc(matchDataChanged) end DeactivateScanFuncs(deactivatedTriggerInfos) @@ -1591,6 +1758,7 @@ frame:RegisterEvent("UNIT_NAME_UPDATE") frame:RegisterEvent("UNIT_FLAGS") frame:RegisterEvent("PLAYER_FLAGS_CHANGED") frame:RegisterEvent("UNIT_PET") +frame:RegisterEvent("RAID_TARGET_UPDATE") frame:RegisterEvent("PLAYER_FOCUS_CHANGED") frame:RegisterEvent("ARENA_OPPONENT_UPDATE") frame:RegisterEvent("UNIT_ENTERED_VEHICLE") @@ -1686,6 +1854,7 @@ function BuffTrigger.UnloadAll() wipe(scanFuncSpellIdMulti) wipe(unitExistScanFunc) wipe(groupScanFuncs) + wipe(raidMarkScanFuncs) wipe(matchDataByTrigger) wipe(matchDataMulti) wipe(matchDataChanged) @@ -1697,20 +1866,29 @@ local function LoadAura(id, triggernum, triggerInfo) if not triggerInfo.unit then return end - local filter = triggerInfo.debuffType local time = GetTime(); local unitsToCheck = {} if triggerInfo.unit == "multi" then - AddScanFuncs(triggerInfo, nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil) + if triggerInfo.debuffType == "BOTH" then + AddScanFuncs(triggerInfo, "HELPFUL", nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil) + AddScanFuncs(triggerInfo, "HARMFUL", nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil) + else + AddScanFuncs(triggerInfo, triggerInfo.debuffType, nil, scanFuncNameMulti, scanFuncSpellIdMulti, nil) + end elseif triggerInfo.groupTrigger then triggerInfo.maxUnitCount = 0 - for unit in GetAllUnits(triggerInfo.unit) do + for unit in GetAllUnits(triggerInfo.unit, nil, triggerInfo.includePets) do RecheckActive(triggerInfo, unit, unitsToCheck) end else - AddScanFuncs(triggerInfo, triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) + if triggerInfo.debuffType == "BOTH" then + AddScanFuncs(triggerInfo, "HELPFUL", triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) + AddScanFuncs(triggerInfo, "HARMFUL", triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) + else + AddScanFuncs(triggerInfo, triggerInfo.debuffType, triggerInfo.unit, scanFuncName, scanFuncSpellId, scanFuncGeneral) + end unitsToCheck[triggerInfo.unit] = true end @@ -1720,10 +1898,15 @@ local function LoadAura(id, triggernum, triggerInfo) tinsert(unitExistScanFunc[triggerInfo.unit][id], triggerInfo) if existingUnits[triggerInfo.unit] == nil then - existingUnits[triggerInfo.unit] = UnitExists(triggerInfo.unit) == 1 and true or false + existingUnits[triggerInfo.unit] = UnitExistsFixed(triggerInfo.unit) == 1 and true or false end end + if triggerInfo.fetchRaidMark then + raidMarkScanFuncs[id] = raidMarkScanFuncs[id] or {} + tinsert(raidMarkScanFuncs[id], triggerInfo) + end + if triggerInfo.groupTrigger then groupScanFuncs[triggerInfo.unit] = groupScanFuncs[triggerInfo.unit] or {} tinsert(groupScanFuncs[triggerInfo.unit], triggerInfo) @@ -1762,6 +1945,8 @@ function BuffTrigger.UnloadDisplays(toUnload) unitData[id] = nil end + raidMarkScanFuncs[id] = nil + for unit, unitData in pairs(matchData) do for filter, filterData in pairs(unitData) do for index, indexData in pairs(filterData) do @@ -1839,6 +2024,8 @@ function BuffTrigger.Rename(oldid, newid) unitData[newid] = unitData[oldid] unitData[oldid] = nil end + raidMarkScanFuncs[newid] = raidMarkScanFuncs[oldid] + raidMarkScanFuncs[oldid] = nil matchDataChanged[newid] = matchDataChanged[oldid] matchDataChanged[oldid] = nil end @@ -2135,6 +2322,14 @@ function BuffTrigger.Add(data) end end + local matchPerUnitCountFunc + if IsGroupTrigger(trigger) and combineMode == "showPerUnit" and perUnitMode ~= "unaffected" and trigger.useMatchPerUnit_count + and tonumber(trigger.matchPerUnit_count) and trigger.matchPerUnit_countOperator then + local count = tonumber(trigger.matchPerUnit_count) + local match_countFuncStr = Private.function_strings.count:format(trigger.matchPerUnit_countOperator, count) + matchPerUnitCountFunc = WeakAuras.LoadFunction(match_countFuncStr) + end + local groupTrigger = trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" local effectiveIgnoreSelf = groupTrigger and trigger.ignoreSelf local effectiveClass = groupTrigger and trigger.useClass and trigger.class @@ -2190,6 +2385,7 @@ function BuffTrigger.Add(data) compareFunc = trigger.combineMode == "showHighest" and highestExpirationTime or lowestExpirationTime, unitExists = showIfInvalidUnit, fetchTooltip = not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip, + fetchRaidMark = trigger.unit ~= "multi" and trigger.fetchRaidMark, groupTrigger = IsGroupTrigger(trigger), ignoreSelf = effectiveIgnoreSelf, ignoreDead = effectiveIgnoreDead, @@ -2199,9 +2395,11 @@ function BuffTrigger.Add(data) groupCountFunc = groupCountFunc, class = effectiveClass, matchCountFunc = matchCountFunc, + matchPerUnitCountFunc = matchPerUnitCountFunc, useAffected = unit == "group" and trigger.useAffected, isMulti = trigger.unit == "multi", - nameChecker = effectiveNameCheck and WeakAuras.ParseNameCheck(trigger.unitName) + nameChecker = effectiveNameCheck and WeakAuras.ParseNameCheck(trigger.unitName), + includePets = trigger.use_includePets and trigger.includePets } triggerInfos[id] = triggerInfos[id] or {} triggerInfos[id][triggernum] = triggerInformation @@ -2247,9 +2445,9 @@ function BuffTrigger.SetToolTip(trigger, state) if not state.unit or not state.index then return false end - if trigger.debuffType == "HELPFUL" then + if state.filter == "HELPFUL" then GameTooltip:SetUnitBuff(state.unit, state.index) - elseif trigger.debuffType == "HARMFUL" then + elseif state.filter == "HARMFUL" then GameTooltip:SetUnitDebuff(state.unit, state.index) end return true @@ -2336,15 +2534,19 @@ function BuffTrigger.GetAdditionalProperties(data, triggernum) end if not IsSingleMissing(trigger) and trigger.unit ~= "multi" and trigger.fetchTooltip then - ret = ret .. "|cFFFF0000%tooltip|r - " .. L["Tooltip"] .. "\n" - ret = ret .. "|cFFFF0000%tooltip1|r - " .. L["First Value of Tooltip Text"] .. "\n" - ret = ret .. "|cFFFF0000%tooltip2|r - " .. L["Second Value of Tooltip Text"] .. "\n" - ret = ret .. "|cFFFF0000%tooltip3|r - " .. L["Third Value of Tooltip Text"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".tooltip|r - " .. L["Tooltip"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".tooltip1|r - " .. L["First Value of Tooltip Text"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".tooltip2|r - " .. L["Second Value of Tooltip Text"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".tooltip3|r - " .. L["Third Value of Tooltip Text"] .. "\n" + end + + if trigger.unit ~= "multi" and trigger.fetchRaidMark then + ret = ret .. "|cFFFF0000%".. triggernum .. ".raidMark|r - " .. L["Raid Mark"] .. "\n" end if (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and trigger.useAffected then - ret = ret .. "|cFFFF0000%affected|r - " .. L["Names of affected Players"] .. "\n" - ret = ret .. "|cFFFF0000%unaffected|r - " .. L["Names of unaffected Players"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".affected|r - " .. L["Names of affected Players"] .. "\n" + ret = ret .. "|cFFFF0000%".. triggernum .. ".unaffected|r - " .. L["Names of unaffected Players"] .. "\n" end return ret @@ -2697,7 +2899,12 @@ local function CleanUpMulti(guid) local time = GetTime() for key, data in pairs(matchDataMulti[guid]) do for source, sourceData in pairs(data) do - local removeAt = sourceData.expirationTime or (sourceData.time + 60) + local removeAt + if sourceData.expirationTime and sourceData.expirationTime ~= math.huge then + removeAt = sourceData.expirationTime + else + removeAt = sourceData.time + 60 + end if removeAt <= time then RemoveMatchDataMulti(matchDataMulti[guid], guid, key, source) else @@ -2714,8 +2921,8 @@ local function CleanUpMulti(guid) if nextCheck then local timeUntilNext = nextCheck - GetTime() if timeUntilNext > 0 then - cleanupTimerMulti[guid].handle = timer:ScheduleTimer(CleanUpMulti, timeUntilNext, guid) - cleanupTimerMulti[guid].nextTime = nextCheck + cleanupTimerMulti[guid].handle = timer:ScheduleTimer(CleanUpMulti, timeUntilNext, guid) + cleanupTimerMulti[guid].nextTime = nextCheck end end end diff --git a/WeakAuras/Conditions.lua b/WeakAuras/Conditions.lua index 312c605..1001ead 100644 --- a/WeakAuras/Conditions.lua +++ b/WeakAuras/Conditions.lua @@ -32,7 +32,7 @@ local function OnDelete(event, uid) end end -Private:RegisterCallback("Delete", OnDelete) +Private.callbacks:RegisterCallback("Delete", OnDelete) local function formatValueForAssignment(vType, value, pathToCustomFunction, pathToFormatters) if (value == nil) then @@ -63,9 +63,13 @@ local function formatValueForAssignment(vType, value, pathToCustomFunction, path return "{1, 1, 1, 1}"; elseif(vType == "chat") then if (value and type(value) == "table") then - local serialized = string.format("{message_type = %s, message = %s, message_dest = %s, message_channel = %s, message_custom = %s, message_formaters = %s}", + local serialized = string.format("{message_type = %s, message = %s, message_dest = %s, message_dest_isunit = %s, r = %s, g = %s, b = %s, message_custom = %s, message_formaters = %s}", Private.QuotedString(tostring(value.message_type)), Private.QuotedString(tostring(value.message or "")), - Private.QuotedString(tostring(value.message_dest)), Private.QuotedString(tostring(value.message_channel)), + Private.QuotedString(tostring(value.message_dest)), + tostring(value.message_dest_isunit), + type(value.message_color) == "table" and tostring(value.message_color[1] or "1") or "1", + type(value.message_color) == "table" and tostring(value.message_color[2] or "1") or "1", + type(value.message_color) == "table" and tostring(value.message_color[3] or "1") or "1", pathToCustomFunction, pathToFormatters) return serialized @@ -255,6 +259,36 @@ local function CreateTestForCondition(uid, input, allConditionsTemplate, usedSta else check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]".. string.format("[%q]", variable) .. op .. "'" .. value .. "'"; end + elseif (cType == "range" and value and op and input.type and input.op_range and input.range) then + local fn + if input.type == "group" then + fn = [[ + return function() + local found = 0 + local op = %q + local range = %s + for unit in WA_IterateGroupMembers() do + if not UnitIsUnit(unit, "player") and WeakAuras.CheckRange(unit, range, op) then + found = found + 1 + end + end + return found %s %d + end + ]] + fn = fn:format(input.op_range, input.range, op, value) + end + if fn then + local customCheck = WeakAuras.LoadFunction(fn, Private.UIDtoID(uid), "conditions range check") + if customCheck then + WeakAuras.conditionHelpers[uid] = WeakAuras.conditionHelpers[uid] or {} + WeakAuras.conditionHelpers[uid].customTestFunctions = WeakAuras.conditionHelpers[uid].customTestFunctions or {} + tinsert(WeakAuras.conditionHelpers[uid].customTestFunctions, customCheck); + local testFunctionNumber = #(WeakAuras.conditionHelpers[uid].customTestFunctions); + + check = string.format("state and WeakAuras.CallCustomConditionTest(%q, %s, state)", + uid, testFunctionNumber, trigger); + end + end elseif (cType == "bool" and value) then local rightSide = value == 0 and "false" or "true"; check = stateCheck .. stateVariableCheck .. "state[" .. trigger .. "]" .. string.format("[%q]", variable) .. "==" .. rightSide @@ -412,17 +446,7 @@ local function CreateActivateCondition(ret, id, condition, conditionNumber, prop return ret; end -function Private.GetProperties(data) - local properties; - local propertiesFunction = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties; - if (type(propertiesFunction) == "function") then - properties = propertiesFunction(data); - elseif propertiesFunction then - properties = CopyTable(propertiesFunction); - else - properties = {} - end - +function Private.GetSubRegionProperties(data, properties) if data.subRegions then local subIndex = {} for index, subRegion in ipairs(data.subRegions) do @@ -444,7 +468,20 @@ function Private.GetProperties(data) end end end +end +function Private.GetProperties(data) + local properties; + local propertiesFunction = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].properties; + if (type(propertiesFunction) == "function") then + properties = propertiesFunction(data); + elseif propertiesFunction then + properties = CopyTable(propertiesFunction); + else + properties = {} + end + + Private.GetSubRegionProperties(data, properties) return properties; end @@ -478,7 +515,7 @@ function Private.LoadConditionPropertyFunctions(data) end return change.value[fullKey] end - local formatters = change.value and Private.CreateFormatters(change.value.message, getter) + local formatters = change.value and Private.CreateFormatters(change.value.message, getter, true) WeakAuras.conditionTextFormatters[id] = WeakAuras.conditionTextFormatters[id] or {} WeakAuras.conditionTextFormatters[id][conditionNumber] = WeakAuras.conditionTextFormatters[id][conditionNumber] or {}; WeakAuras.conditionTextFormatters[id][conditionNumber].changes = WeakAuras.conditionTextFormatters[id][conditionNumber].changes or {}; @@ -508,6 +545,11 @@ local globalConditions = state.hastarget = UnitExists("target") == 1 and true or false; end }, + ["rangecheck"] = { + display = WeakAuras.newFeatureString .. L["Range Check"], + type = "range", + events = {"WA_SPELL_RANGECHECK"} + }, ["attackabletarget"] = { display = L["Attackable Target"], type = "bool", @@ -696,6 +738,22 @@ local function handleDynamicConditions(self, event) Private.StopProfileSystem("dynamic conditions") end +local function handleDynamicConditionsPerUnit(self, event, unit) + Private.StartProfileSystem("dynamic conditions") + if unit and unit == self.unit then + local unitEvent = event..":"..unit + if globalDynamicConditionFuncs[unitEvent] then + for i, func in ipairs(globalDynamicConditionFuncs[unitEvent]) do + func(globalConditionState); + end + end + if (dynamicConditions[unitEvent]) then + runDynamicConditionFunctions(dynamicConditions[unitEvent]); + end + end + Private.StopProfileSystem("dynamic conditions") +end + local lastDynamicConditionsUpdateCheck; local function handleDynamicConditionsOnUpdate(self) handleDynamicConditions(self, "FRAME_UPDATE"); @@ -719,7 +777,7 @@ local function EvaluateCheckForRegisterForGlobalConditions(uid, check, allCondit end elseif trigger == -1 and variable == "customcheck" then if check.op then - for event in string.gmatch(check.op, "[%w_]+") do + for event in string.gmatch(check.op, "[%w_:]+") do if (not dynamicConditions[event]) then register[event] = true; dynamicConditions[event] = {}; @@ -769,6 +827,7 @@ function Private.RegisterForGlobalConditions(uid) if (next(register) and not dynamicConditionsFrame) then dynamicConditionsFrame = CreateFrame("FRAME"); dynamicConditionsFrame:SetScript("OnEvent", handleDynamicConditions); + dynamicConditionsFrame.units = {} WeakAuras.frames["Rerun Conditions Frame"] = dynamicConditionsFrame end @@ -779,7 +838,18 @@ function Private.RegisterForGlobalConditions(uid) dynamicConditionsFrame.onUpdate = true; end else - pcall(dynamicConditionsFrame.RegisterEvent, dynamicConditionsFrame, event); + local unitEvent, unit = event:match("([^:]+):([^:]+)") + if unitEvent and unit then + unit = unit:lower() + if not dynamicConditionsFrame.units[unit] then + dynamicConditionsFrame.units[unit] = CreateFrame("FRAME"); + dynamicConditionsFrame.units[unit]:SetScript("OnEvent", handleDynamicConditionsPerUnit); + end + dynamicConditionsFrame.units[unit].unit = unit; + pcall(dynamicConditionsFrame.RegisterEvent, dynamicConditionsFrame.units[unit], unitEvent); + else + pcall(dynamicConditionsFrame.RegisterEvent, dynamicConditionsFrame, event); + end end end end @@ -787,10 +857,26 @@ end function Private.UnregisterForGlobalConditions(uid) for event, condFuncs in pairs(dynamicConditions) do condFuncs[uid] = nil; + if next(condFuncs) == nil then + local unitEvent, unit = event:match("([^:]+):([^:]+)") + if unitEvent and unit then + unit = unit:lower() + dynamicConditionsFrame.units[unit]:UnregisterEvent(unitEvent) + elseif (event == "FRAME_UPDATE" or event == "WA_SPELL_RANGECHECK") then + if (event == "FRAME_UPDATE" and dynamicConditions["WA_SPELL_RANGECHECK"] == nil) + or (event == "WA_SPELL_RANGECHECK" and dynamicConditions["FRAME_UPDATE"] == nil) + then + dynamicConditionsFrame:SetScript("OnUpdate", nil) + dynamicConditionsFrame.onUpdate = false + end + else + dynamicConditionsFrame:UnregisterEvent(event) + end + dynamicConditions[event] = nil + end end end - function Private.UnloadAllConditions() for uid in pairs(conditionChecksTimers.recheckTime) do if (conditionChecksTimers.recheckHandle[uid]) then @@ -803,6 +889,14 @@ function Private.UnloadAllConditions() wipe(conditionChecksTimers.recheckHandle) dynamicConditions = {} + if dynamicConditionsFrame then + dynamicConditionsFrame:UnregisterAllEvents() + for unit, frame in pairs(dynamicConditionsFrame.units) do + frame:UnregisterAllEvents() + end + dynamicConditionsFrame:SetScript("OnUpdate", nil) + dynamicConditionsFrame.onUpdate = false + end end function Private.UnloadConditions(uid) diff --git a/WeakAuras/GenericTrigger.lua b/WeakAuras/GenericTrigger.lua index 0b0f77a..3d31ec2 100644 --- a/WeakAuras/GenericTrigger.lua +++ b/WeakAuras/GenericTrigger.lua @@ -197,11 +197,21 @@ function ConstructTest(trigger, arg) test = TestForMultiSelect(trigger, arg); elseif(arg.type == "toggle") then test = TestForToggle(trigger, arg); + elseif (arg.type == "spell") then + if arg.test then + if arg.showExactOption then + test = "("..arg.test:format(trigger[name], tostring(trigger["use_exact_" .. name]) or "false") ..")"; + else + test = "("..arg.test:format(trigger[name])..")"; + end + else + test = "(".. name .." and "..name.."==" ..(number or "\""..(trigger[name] or "").."\"")..")"; + end elseif(arg.test) then - test = "("..arg.test:format(trigger[name])..")"; + test = "("..arg.test:format(tostring(trigger[name]) or "")..")"; elseif(arg.type == "longstring" and trigger[name.."_operator"]) then test = TestForLongString(trigger, arg); - elseif (arg.type == "string" or arg.type == "select" or arg.type == "spell" or arg.type == "item") then + elseif (arg.type == "string" or arg.type == "select" or arg.type == "item") then test = "(".. name .." and "..name.."==" ..(number or "\""..(trigger[name] or "").."\"")..")"; else if(type(trigger[name]) == "table") then @@ -541,6 +551,13 @@ local function RunTriggerFunc(allStates, data, id, triggernum, event, arg1, arg2 elseif ok and returnValue then updateTriggerState = true; end + for key, state in pairs(allStates) do + if (type(state) ~= "table") then + errorHandler(string.format(L["Error in aura '%s' in %s. trigger. All States table contains a non table at key: '%s'."], id, triggernum, key)) + wipe(allStates) + return + end + end elseif (data.statesParameter == "all") then local ok, returnValue = pcall(data.triggerFunc, allStates, event, arg1, arg2, ...); if not ok then @@ -945,28 +962,62 @@ function GenericTrigger.Rename(oldid, newid) Private.EveryFrameUpdateRename(oldid, newid) end -local function MultiUnitLoop(Func, unit, ...) +local function MultiUnitLoop(Func, unit, includePets, ...) unit = string.lower(unit) - if unit == "boss" or unit == "arena" then - for i = 1, MAX_BOSS_FRAMES do + if unit == "boss" then + for i = 1, 4 do + Func(unit..i, ...) + end + elseif unit == "arena" then + for i = 1, 5 do Func(unit..i, ...) end elseif unit == "group" then - Func("player", ...) + if includePets ~= "PetsOnly" then + Func("player", ...) + end + if includePets ~= nil then + Func("pet", ...) + end for i = 1, 4 do - Func("party"..i, ...) + if includePets ~= "PetsOnly" then + Func("party"..i, ...) + end + if includePets ~= nil then + Func("partypet"..i, ...) + end end for i = 1, 40 do - Func("raid"..i, ...) + if includePets ~= "PetsOnly" then + Func("raid"..i, ...) + end + if includePets ~= nil then + Func("raidpet"..i, ...) + end end elseif unit == "party" then - Func("player", ...) + if includePets ~= "PetsOnly" then + Func("player", ...) + end + if includePets ~= nil then + Func("pet", ...) + end for i = 1, 4 do - Func("party"..i, ...) + if includePets ~= "PetsOnly" then + Func("party"..i, ...) + end + if includePets ~= nil then + Func("partypet"..i, ...) + end end elseif unit == "raid" then for i = 1, 40 do - Func("raid"..i, ...) + if includePets ~= "PetsOnly" then + Func("raid"..i, ...) + end + if includePets ~= nil then + Func("raidpet"..i, ...) + end end else Func(unit, ...) @@ -997,16 +1048,17 @@ function LoadEvent(id, triggernum, data) end end if data.unit_events then + local includePets = data.includePets for unit, events in pairs(data.unit_events) do unit = string.lower(unit) for index, event in pairs(events) do MultiUnitLoop( - function(unit) - loaded_unit_events[unit] = loaded_unit_events[unit] or {}; - loaded_unit_events[unit][event] = loaded_unit_events[unit][event] or {}; - loaded_unit_events[unit][event][id] = loaded_unit_events[unit][event][id] or {} - loaded_unit_events[unit][event][id][triggernum] = data; - end, unit + function(u) + loaded_unit_events[u] = loaded_unit_events[u] or {}; + loaded_unit_events[u][event] = loaded_unit_events[u][event] or {}; + loaded_unit_events[u][event][id] = loaded_unit_events[u][event][id] or {} + loaded_unit_events[u][event][id][triggernum] = data; + end, unit, includePets ) end end @@ -1047,15 +1099,16 @@ function GenericTrigger.LoadDisplays(toLoad, loadEvent, ...) end end if data.unit_events then + local includePets = data.includePets for unit, events in pairs(data.unit_events) do for index, event in pairs(events) do MultiUnitLoop( - function (unit) - if not (genericTriggerRegisteredUnitEvents[unit] and genericTriggerRegisteredUnitEvents[unit][event]) then - unitEventsToRegister[unit] = unitEventsToRegister[unit] or {} - unitEventsToRegister[unit][event] = true + function (u) + if not (genericTriggerRegisteredUnitEvents[u] and genericTriggerRegisteredUnitEvents[u][event]) then + unitEventsToRegister[u] = unitEventsToRegister[u] or {} + unitEventsToRegister[u][event] = true end - end, unit + end, unit, includePets ) end end @@ -1127,6 +1180,7 @@ function GenericTrigger.Add(data, region) local trigger_events = {}; local internal_events = {}; local trigger_unit_events = {}; + local includePets local trigger_subevents = {}; local force_events = false; local durationFunc, overlayFuncs, nameFunc, iconFunc, textureFunc, stacksFunc, loadFunc; @@ -1216,6 +1270,11 @@ function GenericTrigger.Add(data, region) if (type(force_events) == "function") then force_events = force_events(trigger, untrigger) end + + + if prototype.includePets then + includePets = trigger.use_includePets == true and trigger.includePets or nil + end end end else -- CUSTOM @@ -1280,13 +1339,18 @@ function GenericTrigger.Add(data, region) hasParam = true end elseif trueEvent:match("^UNIT_") then - MultiUnitLoop( - function(unit) - trigger_unit_events[unit] = trigger_unit_events[unit] or {} - tinsert(trigger_unit_events[unit], trueEvent) - isUnitEvent = true - end, i - ) + isUnitEvent = true + + if string.lower(strsub(i, #i - 3)) == "pets" then + i = strsub(i, 1, #i-4) + includePets = "PlayersAndPets" + elseif string.lower(strsub(i, #i - 7)) == "petsonly" then + includePets = "PetsOnly" + i = strsub(i, 1, #i - 8) + end + + trigger_unit_events[i] = trigger_unit_events[i] or {} + tinsert(trigger_unit_events[i], trueEvent) end end if isCLEU then @@ -1330,6 +1394,7 @@ function GenericTrigger.Add(data, region) internal_events = internal_events, force_events = force_events, unit_events = trigger_unit_events, + includePets = includePets, inverse = trigger.use_inverse, subevents = trigger_subevents, durationFunc = durationFunc, @@ -1688,7 +1753,7 @@ do if(duration and duration > 0) then if not(gcdStart) then event = "GCD_START"; - elseif(gcdStart ~= startTime) then + elseif(gcdStart ~= startTime or gcdDuration ~= duration) then event = "GCD_CHANGE"; end gcdStart, gcdDuration = startTime, duration; @@ -1705,7 +1770,7 @@ do gcdSpellName, gcdSpellIcon = nil, nil; gcdEndCheck = 0; end - if(event) then + if(event and not WeakAuras.IsPaused()) then WeakAuras.ScanEvents(event); end end @@ -1766,7 +1831,9 @@ do if self.expirationTime[id] and self.expirationTime[id] > endTime and self.expirationTime[id] ~= 0 then self.duration[id] = 0 self.expirationTime[id] = 0 - self.readyTime[id] = time + if not self.readyTime[id] then + self.readyTime[id] = time + end changed = true nowReady = true end @@ -1787,7 +1854,9 @@ do end if duration == 0 then - self.readyTime[id] = time + if not self.readyTime[id] then + self.readyTime[id] = time + end else self.readyTime[id] = nil end @@ -1811,7 +1880,7 @@ do local spellCds = CreateSpellCDHandler(); local spellCdsRune = CreateSpellCDHandler(); - local spellIds = {} + local spellDetails = {} function WeakAuras.InitCooldownReady() cdReadyFrame = CreateFrame("FRAME"); @@ -2026,7 +2095,6 @@ do startTimeCooldown = startTimeCooldown - 2^32 / 1000 end - -- Default to GetSpellCharges local cooldownBecauseRune = false; if (enabled == 0) then startTimeCooldown, durationCooldown = 0, 0 @@ -2043,11 +2111,24 @@ do function Private.CheckSpellKnown() for id, _ in pairs(spells) do local known = WeakAuras.IsSpellKnownIncludingPet(id); + local changed = false if (known ~= spellKnown[id]) then spellKnown[id] = known - if not WeakAuras.IsPaused() then - WeakAuras.ScanEvents("SPELL_COOLDOWN_CHANGED", id) - end + changed = true + end + + local name, _, icon = GetSpellInfo(id) + if spellDetails[id].name ~= name then + spellDetails[id].name = name + changed = true + end + if spellDetails[id].icon ~= icon then + spellDetails[id].icon = icon + changed = true + end + + if changed and not WeakAuras.IsPaused() then + WeakAuras.ScanEvents("SPELL_COOLDOWN_CHANGED", id) end end end @@ -2063,11 +2144,6 @@ do spellCounts[id] = spellCount local changed = false - if spellIds[id] ~= id then - spellIds[id] = id - changed = true - chargesChanged = true - end local cdChanged, nowReady = spellCds:HandleSpell(id, startTimeCooldown, durationCooldown) changed = cdChanged or changed @@ -2275,7 +2351,11 @@ do return; end spells[id] = true; - spellIds[id] = id + local name, _, icon = GetSpellInfo(id) + spellDetails[id] = { + name = name, + icon = icon + } spellKnown[id] = WeakAuras.IsSpellKnownIncludingPet(id); local startTimeCooldown, durationCooldown, cooldownBecauseRune, spellCount = WeakAuras.GetSpellCooldownUnified(id, GetRuneDuration()); @@ -2356,6 +2436,7 @@ function WeakAuras.WatchUnitChange(unit) watchUnitChange.unitChangeGUIDS = {} watchUnitChange.unitRoles = {} watchUnitChange.inRaid = IsInRaid() + watchUnitChange.raidmark = {} WeakAuras.frames["Unit Change Frame"] = watchUnitChange; watchUnitChange:RegisterEvent("PLAYER_TARGET_CHANGED") @@ -2365,29 +2446,54 @@ function WeakAuras.WatchUnitChange(unit) watchUnitChange:RegisterEvent("PARTY_MEMBERS_CHANGED"); watchUnitChange:RegisterEvent("RAID_ROSTER_UPDATE"); watchUnitChange:RegisterEvent("PLAYER_ENTERING_WORLD") + watchUnitChange:RegisterEvent("UNIT_PET") + watchUnitChange:RegisterEvent("RAID_TARGET_UPDATE") watchUnitChange:SetScript("OnEvent", function(self, event, unit) Private.StartProfileSystem("generictrigger unit change"); - local inRaid = IsInRaid() - - for unit, guid in pairs(watchUnitChange.unitChangeGUIDS) do - local newGuid = WeakAuras.UnitExistsFixed(unit) and UnitGUID(unit) or "" - if guid ~= newGuid or event == "PLAYER_ENTERING_WORLD" then - WeakAuras.ScanEvents("UNIT_CHANGED_" .. unit, unit) - watchUnitChange.unitChangeGUIDS[unit] = newGuid - elseif Private.multiUnitUnits.group[unit] then - -- If in raid changed we send a UNIT_CHANGED for the group units - if inRaid ~= watchUnitChange.inRaid then + if event == "UNIT_PET" then + local pet = WeakAuras.unitToPetUnit[unit] + if pet then + WeakAuras.ScanEvents("UNIT_CHANGED_" .. pet, pet) + end + elseif event == "RAID_TARGET_UPDATE" then + for unit, marker in pairs(watchUnitChange.raidmark) do + local newMarker = GetRaidTargetIndex(unit) or 0 + if marker ~= newMarker then + watchUnitChange.raidmark[unit] = newMarker WeakAuras.ScanEvents("UNIT_CHANGED_" .. unit, unit) end end + else + local inRaid = IsInRaid() + local inRaidChanged = inRaid ~= watchUnitChange.inRaid + + for unit, guid in pairs(watchUnitChange.unitChangeGUIDS) do + local newGuid = WeakAuras.UnitExistsFixed(unit) and UnitGUID(unit) or "" + local newMarker = GetRaidTargetIndex(unit) or 0 + if guid ~= newGuid + or newMarker ~= watchUnitChange.raidmark[unit] + or event == "PLAYER_ENTERING_WORLD" + then + WeakAuras.ScanEvents("UNIT_CHANGED_" .. unit, unit) + watchUnitChange.unitChangeGUIDS[unit] = newGuid + watchUnitChange.raidmark[unit] = newMarker + elseif Private.multiUnitUnits.group[unit] then + -- If in raid changed we send a UNIT_CHANGED for the group units + if inRaidChanged then + WeakAuras.ScanEvents("UNIT_CHANGED_" .. unit, unit) + end + end + end + watchUnitChange.inRaid = inRaid end - watchUnitChange.inRaid = inRaid Private.StopProfileSystem("generictrigger unit change"); end) end watchUnitChange.unitChangeGUIDS = watchUnitChange.unitChangeGUIDS or {} watchUnitChange.unitChangeGUIDS[unit] = UnitGUID(unit) or "" + watchUnitChange.raidmark = watchUnitChange.raidmark or {} + watchUnitChange.raidmark[unit] = GetRaidTargetIndex(unit) or 0 end local equipmentItemIDs, equipmentSetItemIDs = {}, {} @@ -2990,9 +3096,7 @@ do castLatencyFrame = CreateFrame("Frame") castLatencyFrame:RegisterEvent("CURRENT_SPELL_CAST_CHANGED") castLatencyFrame:SetScript("OnEvent", function(event) - Private.StartProfileSystem("generictrigger") - WeakAuras.ScanEvents("CAST_LATENCY_UPDATE", "player") - Private.StopProfileSystem("generictrigger") + Private.LAST_CURRENT_SPELL_CAST_CHANGED = GetTime() end) end end @@ -3395,7 +3499,7 @@ function GenericTrigger.SetToolTip(trigger, state) local lines = { strsplit("\n", state.tooltip) }; GameTooltip:ClearLines(); for i, line in ipairs(lines) do - GameTooltip:AddLine(line); + GameTooltip:AddLine(line, nil, nil, nil, state.tooltipWrap); end return true elseif (state.spellId) then @@ -3457,6 +3561,19 @@ function GenericTrigger.GetAdditionalProperties(data, triggernum) ret = ret .. additional; end end + else + if (trigger.custom_type == "stateupdate") then + local variables = events[data.id][triggernum].tsuConditionVariables(); + if (type(variables) == "table") then + for var, varData in pairs(variables) do + if (type(varData) == "table") then + if varData.display then + ret = ret .. "|cFFFF0000%".. triggernum .. "." .. var .. "|r - " .. varData.display .. "\n" + end + end + end + end + end end return ret; diff --git a/WeakAuras/History.lua b/WeakAuras/History.lua index 0072e53..b216b24 100644 --- a/WeakAuras/History.lua +++ b/WeakAuras/History.lua @@ -37,11 +37,10 @@ function Private.CleanArchive(historyCutoff, migrationCutoff) end end -function Private.SetHistory(uid, data, source, addon) +function Private.SetHistory(uid, data, source) if uid and data then local repo = loadHistory() data.source = source - data.addon = source == "addon" and addon or nil local hist = repo:Set(uid, data, true) return hist end diff --git a/WeakAuras/Init.lua b/WeakAuras/Init.lua index 926ac72..67334f5 100644 --- a/WeakAuras/Init.lua +++ b/WeakAuras/Init.lua @@ -8,7 +8,7 @@ WeakAuras.halfWidth = WeakAuras.normalWidth / 2 WeakAuras.doubleWidth = WeakAuras.normalWidth * 2 local versionStringFromToc = GetAddOnMetadata("WeakAuras", "Version") -local versionString = "3.2.3" +local versionString = "3.7.9" local buildTime = "20201210233053" WeakAuras.versionString = versionStringFromToc @@ -24,8 +24,8 @@ function WeakAuras.IsCorrectVersion() return true end -WeakAuras.prettyPrint = function(msg) - print("|cff9900ffWeakAuras:|r " .. msg) +WeakAuras.prettyPrint = function(...) + print("|cff9900ffWeakAuras:|r ", ...) end if versionString ~= versionStringFromToc and versionStringFromToc ~= "Dev" then diff --git a/WeakAuras/Locales/ruRU.lua b/WeakAuras/Locales/ruRU.lua index cc8c90f..fa223d3 100644 --- a/WeakAuras/Locales/ruRU.lua +++ b/WeakAuras/Locales/ruRU.lua @@ -49,7 +49,10 @@ L["* Suffix"] = "Символ * вместо названия" L["/wa help - Show this message"] = "/wa help - показать данное сообщение" L["/wa minimap - Toggle the minimap icon"] = "/wa minimap - отобразить или скрыть иконку на миникарте" L["/wa pprint - Show the results from the most recent profiling"] = "/wa pprint - показать результаты последнего профилирования" -L["/wa pstart - Start profiling. Optionally include a duration in seconds after which profiling automatically stops. To profile the next combat/encounter, pass a \"combat\" or \"encounter\" argument."] = "/wa pstart - Начать профилирование. Дополнительно включает таймер в секундах, после которого профилирование останавливается. Чтобы профилировать следующий бой или сражение с боссом, добавьте аргумент \"combat\" или \"encounter\" соответственно." +L["/wa pstart - Start profiling. Optionally include a duration in seconds after which profiling automatically stops. To profile the next combat/encounter, pass a \"combat\" or \"encounter\" argument."] = [=[/wa pstart - запустить профилирование. Необязательные аргументы: +[число] - время в секундах, по истечении которого процесс будет завершён; +[combat] - запуск процесса при следующем вхождении в бой; +[encounter] - запуск процесса при следующем сражении с боссом.]=] L["/wa pstop - Finish profiling"] = "/wa pstop - завершить профилирование" L["/wa repair - Repair tool"] = "/wa repair - средство восстановления данных" L["|cffeda55fLeft-Click|r to toggle showing the main window."] = "|cFFEDA55FЛевый клик|r - показать или скрыть окно параметров." @@ -60,11 +63,31 @@ L["|cFFFF0000Not|r Item Bonus Id Equipped"] = "ID бонуса надетого L["|cFFFF0000Not|r Player Name/Realm"] = "Имя / Игровой мир игрока |cFFFF0000НЕ|r" L["|cFFffcc00Extra Options:|r %s"] = "|cFFFFCC00Дополнительные параметры:|r %s" L["|cFFffcc00Extra Options:|r None"] = "|cFFFFCC00Дополнительные параметры:|r нет" +--[[Translation missing --]] +L[ [=[• |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs. +• |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch. +|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger. +• |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs. +• |cffffff00Smart Group|r adjusts to your current group type, matching just the "player" when solo, "party" units (including "player") in a party or "raid" units in a raid. + +|cffffff00*|r Yellow Unit settings will create clones for each matching unit while this trigger is providing Dynamic Info to the Aura.]=] ] = [=[• |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs. +• |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch. +|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger. +• |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs. +• |cffffff00Smart Group|r adjusts to your current group type, matching just the "player" when solo, "party" units (including "player") in a party or "raid" units in a raid. + +|cffffff00*|r Yellow Unit settings will create clones for each matching unit while this trigger is providing Dynamic Info to the Aura.]=] L["10 Man Raid"] = "Рейд на 10 игроков" +--[[Translation missing --]] +L["10 Player Raid"] = "10 Player Raid" L["10 Player Raid (Heroic)"] = "Рейд на 10 игроков (героический)" L["10 Player Raid (Normal)"] = "Рейд на 10 игроков (обычный)" L["20 Man Raid"] = "Рейд на 20 игроков" +--[[Translation missing --]] +L["20 Player Raid"] = "20 Player Raid" L["25 Man Raid"] = "Рейд на 25 игроков" +--[[Translation missing --]] +L["25 Player Raid"] = "25 Player Raid" L["25 Player Raid (Heroic)"] = "Рейд на 25 игроков (героический)" L["25 Player Raid (Normal)"] = "Рейд на 25 игроков (обычный)" L["40 Man Raid"] = "Рейд на 40 игроков" @@ -82,9 +105,12 @@ L["Actions"] = "Действия" L["Active"] = "Активный" L["Add"] = "Прислужник (Add)" L["Add Missing Auras"] = "Добавить отсутствующие индикации" -L["Additional Trigger Replacements"] = "Дополнительные коды от триггера" +L["Additional Trigger Replacements"] = [=[Дополнительные шаблоны замены текста от триггера +]=] L["Affected"] = "Задействованные" L["Affected Unit Count"] = "Кол-во задейств-х единиц" +--[[Translation missing --]] +L["Afk"] = "Afk" L["Aggro"] = "Агро" L["Agility"] = "Ловкость" L["Ahn'Qiraj"] = "Ан'Кираж" @@ -109,6 +135,13 @@ L["Any"] = "Любая" L["Any Triggers"] = "Любые триггеры" L["AOE"] = "Урон по области (AOE)" L["Arcane Resistance"] = "Сопротивление тайной магии" +L[ [=[Are you sure you want to run the |cffff0000EXPERIMENTAL|r repair tool? +This will overwrite any changes you have made since the last database upgrade. +Last upgrade: %s]=] ] = [=[Вы уверены, что хотите запустить |cFFFF0000ЭКСПЕРИМЕНТАЛЬНОЕ|r средство восстановления данных? + +Все изменения, выполненные вами с момента последнего обновления базы данных, будут утеряны. + +Дата последнего обновления: %s]=] L["Arena"] = "Арена" L["Armor (%)"] = "Броня" L["Armor against Target (%)"] = "Броня против текущей цели" @@ -176,8 +209,9 @@ L["Black Wing Lair"] = "Логово Крыла Тьмы" L["Blizzard (2h | 3m | 10s | 2.4)"] = "Blizzard: 2ч. | 3м. | 10с. | 2.4" L["Blizzard Combat Text"] = "Текст боя Blizzard" L["Block"] = "Блок" -L["Block (%)"] = "Блок" +L["Block (%)"] = "Вероятность блока" L["Block against Target (%)"] = "Блок против текущей цели" +L["Block Value"] = "Показатель блока" L["Blocked"] = "Заблокировано" L["Bloodlord Mandokir"] = "Мандокир Повелитель Крови" L["Border"] = "Граница" @@ -192,7 +226,10 @@ L["Bounce"] = "Отскок" L["Bounce with Decay"] = "Отскок с затуханием" L["Broodlord Lashlayer"] = "Предводитель драконов Разящий Бич" L["Buff"] = "Бафф" +L["Buff/Debuff"] = "Бафф / Дебафф" L["Buffed/Debuffed"] = "Есть бафф / дебафф" +--[[Translation missing --]] +L["Burning Crusade"] = "Burning Crusade" L["Buru the Gorger"] = "Буру Ненасытный" L["Can be used for e.g. checking if \"boss1target\" is the same as \"player\"."] = [=[Используется для проверки того факта, что две единицы - одна и та же сущность, объект. Например: выбрав в качестве единицы игрока и указав для данного параметра значение "boss1target", можно определить, являетесь ли вы целью босса.]=] @@ -200,15 +237,17 @@ L["Cancel"] = "Отмена" L["Can't schedule timer with %i, due to a World of Warcraft bug with high computer uptime. (Uptime: %i). Please restart your computer."] = "Невозможно запустить таймер на %i c. из-за ошибки WoW (переполнение), связанной с большим временем непрерывной работы вашего компьютера - %i с. Пожалуйста, перезагрузите компьютер." L["Cast"] = "Применение заклинания" L["Cast Bar"] = "Полоса применения заклинания" -L["Cast Failed"] = "Каст провален" -L["Cast Start"] = "Каст начат" -L["Cast Success"] = "Каст успешно завершен" +L["Cast Failed"] = "Применение не удалось" +L["Cast Start"] = "Применение начато" +L["Cast Success"] = "Применение успешно завершено" L["Cast Type"] = "Тип каста" L["Caster"] = "Заклинатель" L["Caster Name"] = "Имя заклинателя" L["Caster Realm"] = "Игровой мир заклинателя" L["Caster Unit"] = "Заклинатель" L["Caster's Target"] = "Цель заклинателя" +--[[Translation missing --]] +L["Cataclysm"] = "Cataclysm" L["Ceil"] = "Ceil (к большему целому)" L["Center"] = "Центр" L["Centered Horizontal"] = "Горизонтально по центру" @@ -231,6 +270,8 @@ L["Circle"] = "Круг" L["Clamp"] = "Закрепить (растянуть края)" L["Class"] = "Класс" L["Class and Specialization"] = "Класс и специализация" +--[[Translation missing --]] +L["Classic"] = "Classic" L["Classification"] = "Классификация" L["Clockwise"] = "По часовой стрелке" L["Clone per Event"] = "Клон для каждого события" @@ -247,6 +288,7 @@ L["Cooldown Ready Event"] = "Восстановление завершено" L["Cooldown Ready Event (Item)"] = "Восстановление завершено (предмет)" L["Cooldown Ready Event (Slot)"] = "Восстановление завершено (ячейка)" L["Cooldown/Charges/Count"] = "Восстановление / Заряды / Количество" +L["Could not load WeakAuras Archive, the addon is %s"] = "Не удалось загрузить WeakAuras Archive. Причина - %s" L["Count"] = "Счетчик" L["Counter Clockwise"] = "Против часовой стрелки" L["Create"] = "Создание" @@ -268,7 +310,7 @@ L["Curse"] = "Проклятие" L["Custom"] = "Самостоятельно" L["Custom Check"] = "Свое условие" L["Custom Color"] = "Цвет" -L["Custom Configuration"] = "Настройки пользователя" +L["Custom Configuration"] = "Конфигурация пользователя" L["Custom Function"] = "Своя функция" L["Damage"] = "Урон" L["Damage Shield"] = "Урон от щита" @@ -281,6 +323,7 @@ L["Debuff"] = "Дебафф" L["Debuff Class"] = "Тип дебаффа" L["Debuff Class Icon"] = "Иконка дебаффа" L["Debuff Type"] = "Тип дебаффа" +L["Defense"] = "Защита" L["Deflect"] = "Отклонение" L["Desaturate"] = "Обесцветить" L["Desaturate Background"] = "Обесцветить задний план" @@ -299,9 +342,11 @@ L["Disable Spell Known Check"] = "Отключить проверку, изве L["Disabled Spell Known Check"] = "Проверка, известно ли заклинание, отключена" L["Disease"] = "Болезнь" L["Dispel"] = "Рассеивание" -L["Dispel Failed"] = "Рассеивание (неудача)" +L["Dispel Failed"] = "Рассеивание не удалось" L["Display"] = "Отображение" L["Distance"] = "Расстояние" +--[[Translation missing --]] +L["Do Not Disturb"] = "Do Not Disturb" L["Dodge"] = "Уклонение" L["Dodge (%)"] = "Уклонение" L["Dodge Rating"] = "Показатель уклонения" @@ -309,6 +354,8 @@ L["Done"] = "Выполнено" L["Down"] = "Вниз" L["Down, then Left"] = "Вниз, затем влево" L["Down, then Right"] = "Вниз, затем вправо" +--[[Translation missing --]] +L["Dragonflight"] = "Dragonflight" L["Drain"] = "Вытягивание" L["Dropdown Menu"] = "Выпадающее меню" L["Dungeon (Heroic)"] = "Подземелье (героическое)" @@ -343,13 +390,15 @@ L["Entering"] = "Вход" L["Entering/Leaving Combat"] = "Вход / Выход из боя" L["Entry Order"] = "Порядок записей" L["Environment Type"] = "Тип окружения" -L["Environmental"] = "Окружающая среда" +L["Environmental"] = "Окружающий мир" L["Equipment Set"] = "Комплект экипировки" L["Equipment Set Equipped"] = "Комплект экипировки надет" L["Equipment Slot"] = "Ячейка экипировки" L["Equipped"] = "Надето" L["Error"] = "Ошибка" L["Error Frame"] = "Область вывода ошибок" +L["ERROR in '%s' unknown or incompatible sub element type '%s'"] = "Ошибка в индикации %s. Внутренний элемент неизвестного или несовместимого типа %s." +L["Error in aura '%s' in %s. trigger. All States table contains a non table at key: '%s'."] = "Ошибка в триггере %2$s индикации '%1$s'. В таблице всех состояний (allstates) значение с ключом '%3$s' не является таблицей." L["Error not receiving display information from %s"] = [=[Ошибка при получении информации об индикации от %s]=] L[ [=['ERROR: Anchoring %s': @@ -429,6 +478,8 @@ L["Grid"] = "Сетка" L["Grobbulus"] = "Гроббулус" L["Group"] = "Группа" L["Group Arrangement"] = "Порядок и позиции индикаций в группе" +--[[Translation missing --]] +L["Group Type"] = "Group Type" L["Grow"] = "Рост" L["GTFO Alert"] = "Предупреждение GTFO" L["Guardian"] = "Страж" @@ -442,17 +493,20 @@ L["Haste Rating"] = "Показатель скорости" L["Heal"] = "Исцеление" L["Health"] = "Здоровье" L["Health (%)"] = "Здоровье (%)" +L["Health Deficit"] = "Потерянное здоровье" L["Heigan the Unclean"] = "Хейган Нечестивый" L["Height"] = "Высота" L["Hide"] = "Скрыть" L["Hide 0 cooldowns"] = "Скрыть 0" -L["High Damage"] = "Высокий урон" +L["High Damage"] = "Большой урон" L["High Priest Thekal"] = "Верховный жрец Текал" L["High Priest Venoxis"] = "Верховный жрец Веноксис" L["High Priestess Arlokk"] = "Верховная жрица Арлокк" L["High Priestess Jeklik"] = "Верховная жрица Джеклик" L["High Priestess Mar'li"] = "Верховная жрица Мар'ли" L["Higher Than Tank"] = "Больше чем у основной цели" +L["Hit (%)"] = "Меткость" +L["Hit Rating"] = "Показатель меткости" L["Holy Resistance"] = "Сопротивление светлой магии" L["Horde"] = "Орда" L["Hostile"] = "Враждебный" @@ -473,7 +527,7 @@ L["Import as Update"] = "Обновить" L["Import Group"] = "Импорт" L["Import in progress"] = "Импорт в процессе ..." L["Important"] = "Важно" -L["Importing is disabled while in combat"] = "Импорт отключен во время боя" +L["Importing will start after combat ends."] = "Импорт начнется после окончания боя." L["In Combat"] = "В бою" L["In Encounter"] = "В сражении с боссом" L["In Group"] = "В группе" @@ -482,7 +536,8 @@ L["In Raid"] = "В рейде" L["In Vehicle"] = "На транспорте" L["Include Bank"] = "Включая банк" L["Include Charges"] = "Включая заряды" -L["Incoming Heal"] = "Входящее исцеление" +L["Include Pets"] = "Учитывать питомцев" +L["Incoming Heal"] = "Поступающее исцеление" L["Increase Precision Below"] = "Увеличить точность, если меньше" L["Information"] = "Сообщение" L["Inherited"] = "Наследуемый атрибут" @@ -509,9 +564,6 @@ L["Island Expedition (Heroic)"] = "Островная экспедиция (ге L["Island Expedition (Mythic)"] = "Островная экспедиция (эпохальная)" L["Island Expedition (Normal)"] = "Островная экспедиция (обычная)" L["Island Expeditions (PvP)"] = "Островная экспедиция (PvP)" -L["It might not work correctly on Classic!"] = "В Classic версии она может работать неправильно!" -L["It might not work correctly on Retail!"] = "В Retail версии она может работать неправильно!" -L["It might not work correctly with your version!"] = "В вашей версии она может работать неправильно!" L["Item"] = "Предмет" L["Item Bonus Id"] = "ID бонуса предмета" L["Item Bonus Id Equipped"] = "ID бонуса надетого предмета" @@ -546,6 +598,8 @@ L["Legacy Aura (disabled):"] = "Аура (устаревший; отключен L["Legacy Looking for Raid"] = "Поиск рейда (до патча 5.4)" L["Legacy RGB Gradient"] = "Градиент RGB" L["Legacy RGB Gradient Pulse"] = "Градиентная пульсация RGB" +--[[Translation missing --]] +L["Legion"] = "Legion" L["Length"] = "Длина" L["Level"] = "Уровень" L["Limited"] = "Ограниченное" @@ -555,7 +609,7 @@ L["Loatheb"] = "Лотхиб" L["Looking for Raid"] = "Поиск рейда" L["Loop"] = "Повторять" L["Lost"] = "Израсходован" -L["Low Damage"] = "Низкий урон" +L["Low Damage"] = "Незначительный урон" L["Lower Than Tank"] = "Меньше чем у основной цели" L["Lucifron"] = "Люцифрон" L["Maexxna"] = "Мексна" @@ -598,6 +652,8 @@ L["Miss"] = "Промах" L["Miss Type"] = "Тип промаха" L["Missed"] = "Промах" L["Missing"] = "Эффект отсутствует" +--[[Translation missing --]] +L["Mists of Pandaria"] = "Mists of Pandaria" L["Moam"] = "Моам" L["Model"] = "Модель" L["Modern Blizzard (1h 3m | 3m 7s | 10s | 2.4)"] = "Blizzard (современный): 1ч. 3м. | 3м. 7с. | 10с. | 2.4" @@ -641,13 +697,18 @@ L["No Profiling information saved."] = "Нет данных профилиров L["None"] = "Нет" L["Non-player Character"] = "Неигровой персонаж (NPC)" L["Normal"] = "Обычный" +--[[Translation missing --]] +L["Normal Party"] = "Normal Party" L["Not in Group"] = "Не в группе" +--[[Translation missing --]] +L["Not in Smart Group"] = "Not in Smart Group" L["Not on Cooldown"] = "Не перезаряжается" L["Not On Threat Table"] = "Не в списке угроз" L["Note, that cross realm transmission is possible if you are on the same group"] = [=[Передача данных между игровыми мирами возможна, если вы находитесь в одной группе!]=] +L["Note: Due to how complicated the swing timer behaviour is and the lack of APIs from Blizzard, results are inaccurate in edge cases."] = "|cFFFFCC00Примечание.|r Из-за сложности поведения таймера Swing и отсутствия API со стороны Blizzard результаты в крайних случаях неточны." L["Note: 'Hide Alone' is not available in the new aura tracking system. A load option can be used instead."] = "|cFFFFCC00Предупреждение.|r Параметр \"Скрыть когда не в группе\" недоступен в новой версии триггера. Вместо него может быть использовано соответствующее условие на вкладке Загрузка." -L["Note: The available text replacements for multi triggers match the normal triggers now."] = "|cFFFFCC00Примечение.|r Теперь для данного варианта триггера (несколько целей) доступны такие же специальные коды отображения динамической информации в тексте, как и для остальных." +L["Note: The available text replacements for multi triggers match the normal triggers now."] = "|cFFFFCC00Примечение.|r Теперь для данного варианта триггера (несколько целей) доступны такие же шаблоны замены текста, как и для остальных." L["Note: This trigger type estimates the range to the hitbox of a unit. The actual range of friendly players is usually 3 yards more than the estimate. Range checking capabilities depend on your current class and known abilities as well as the type of unit being checked. Some of the ranges may also not work with certain NPCs.|n|n|cFFAAFFAAFriendly Units:|r %s|n|cFFFFAAAAHarmful Units:|r %s|n|cFFAAAAFFMiscellanous Units:|r %s"] = "|cFFFFCC00Примечание.|r Триггер оценивает (определяет приближённо) расстояние до хитбокса единицы. Фактическое расстояние до дружественных игроков обычно на 3 метра больше оценки. Возможности проверки дистанции зависят от вашего текущего класса, имеющихся способностей и от типа проверяемой единицы. Некоторые диапазоны могут не работать с отдельными NPC. |n|n|cFFAAFFAACоюзники:|r %s|n|cFFFFAAAAПротивники:|r %s|n|cFFAAAAFFПрочие цели:|r %s" L["Noth the Plaguebringer"] = "Нот Чумной" L["NPC"] = "NPC" @@ -675,6 +736,7 @@ L["Options will open after the login process has completed."] = "Парамет L["Orbit"] = "Вращение по орбите" L["Orientation"] = "Ориентация" L["Ossirian the Unscarred"] = "Оссириан Неуязвимый" +L["Other"] = "Другое" L["Other Addons"] = "Другие аддоны" L["Other Events"] = "Другие события" L["Ouro"] = "Оуро" @@ -682,7 +744,7 @@ L["Outline"] = "Контур" L["Overhealing"] = "Избыточное исцеление" L["Overkill"] = "Избыточный урон" L["Overlay %s"] = "Наложение %s" -L["Overlay Charged Combo Points"] = "Показать заряженные анимой приемы (наложение)" +L["Overlay Charged Combo Points"] = "Показать заряженные анимой приемы серии (наложение)" L["Overlay Cost of Casts"] = "Показать стоимость применения заклинаний (наложение)" L["Overlay Latency"] = "Показать задержку (наложение)" L["Parry"] = "Парирование" @@ -702,6 +764,7 @@ L["Pet"] = "Питомец" L["Pet Behavior"] = "Поведение питомца" L["Pet Specialization"] = "Специализация питомца" L["Pet Spell"] = "Заклинание питомца" +L["Pets only"] = "Только питомцы" L["Phase"] = "Фаза" L["Pixel Glow"] = "Пиксельное свечение" L["Placement"] = "Размещение" @@ -720,9 +783,11 @@ L["Player Race"] = "Раса игрока" L["Player(s) Affected"] = "Задействованные игроки" L["Player(s) Not Affected"] = "Незадействованные игроки" L["Player/Unit Info"] = "Информация об игроке / единице" +L["Players and Pets"] = "Игроки и питомцы" L["Poison"] = "Яд" L["Power"] = "Энергия" L["Power (%)"] = "Энергия (%)" +L["Power Deficit"] = "Недостающая энергия" L["Power Type"] = "Тип энергии" L["Precision"] = "Точность" L["Preset"] = "Набор эффектов" @@ -751,6 +816,8 @@ L["Raid (Heroic)"] = "Рейд (героический)" L["Raid (Mythic)"] = "Рейд (эпохальный)" L["Raid (Normal)"] = "Рейд (обычный)" L["Raid (Timewalking)"] = "Рейд (путешествие во времени)" +L["Raid Mark"] = "Метка цели" +L["Raid Mark Icon"] = "Иконка метки цели" L["Raid Role"] = "Роль в рейде" L["Raid Warning"] = "Объявление рейду" L["Raids"] = "Рейды" @@ -758,6 +825,7 @@ L["Range"] = "Дальний бой" L["Range Check"] = "Проверка дистанции" L["Rare"] = "Редкий" L["Rare Elite"] = "Редкий элитный" +L["Rated Arena"] = "Рейтинговая арена" L["Raw Threat Percent"] = "Исходный процент угрозы" L["Razorgore the Untamed"] = "Бритвосмерт Неукротимый" L["Ready Check"] = "Проверка готовности" @@ -820,6 +888,7 @@ L["Round"] = "Round (к ближайшему целому)" L["Round Mode"] = "Метод округления" L["Ruins of Ahn'Qiraj"] = "Руины Ан'Киража" L["Run Custom Code"] = "Выполнить свой код" +L["Run Speed (%)"] = "Скорость бега" L["Rune"] = "Руна" L["Rune #1"] = "Руна #1" L["Rune #2"] = "Руна #2" @@ -845,6 +914,8 @@ L["Set IDs can be found on websites such as wowhead.com/item-sets"] = "ID ком L["Set Maximum Progress"] = "Задать макс. прогресс" L["Set Minimum Progress"] = "Задать мин. прогресс" L["Shadow Resistance"] = "Сопротивление темной магии" +--[[Translation missing --]] +L["Shadowlands"] = "Shadowlands" L["Shake"] = "Дрожь" L["Shazzrah"] = "Шаззрах" L["Shift-Click to resume addon execution."] = "Shift-клик возобновит выполнение аддона." @@ -854,7 +925,7 @@ L["Show CD of Charge"] = "Показать восстановление заря L["Show Code"] = "Показать код" L["Show GCD"] = "Показать общее время восстановления" L["Show Global Cooldown"] = "Показать общее время восстановления (GCD)" -L["Show Incoming Heal"] = "Показать входящее исцеление (наложение)" +L["Show Incoming Heal"] = "Показать поступающее исцеление (наложение)" L["Show On"] = "Показать" L["Show Rested Overlay"] = "Показать дополнительный опыт после отдыха (наложение)" L["Shrink"] = "Сжатие" @@ -901,6 +972,7 @@ L["Specific Unit"] = "Конкретная единица" L["Spell"] = "Заклинание" L["Spell (Building)"] = "Заклинание (строение)" L["Spell Activation Overlay Glow"] = "Свечение иконки при активации заклинания" +L["Spell Cast Succeeded"] = "Применение заклинания успешно" L["Spell Cost"] = "Стоимость заклинания" L["Spell Count"] = "Количество заклинаний" L["Spell ID"] = "ID заклинания" @@ -910,10 +982,12 @@ L["Spell IDs:"] = "ID заклинаний:" L["Spell in Range"] = "В зоне действия (заклинание)" L["Spell Known"] = "Заклинание известно" L["Spell Name"] = "Название заклинания" +L["Spell School"] = "Школа магии" L["Spell Usable"] = "Заклинание доступно" L["Spin"] = "Вращение" L["Spiral"] = "Спираль" L["Spiral In And Out"] = "Спираль (вперед-назад)" +L["Spirit"] = "Дух" L["Stack Count"] = "Количество стаков" L["Stacks"] = "Стаки" L["Stagger Scale"] = [=[Масштаб пошатывания @@ -938,6 +1012,7 @@ L["Supports multiple entries, separated by commas"] = "Можно указать L[ [=[Supports multiple entries, separated by commas ]=] ] = [=[Можно указать несколько значений, разделенных запятыми. ]=] +L["Supports multiple entries, separated by commas. Escape ',' with \\"] = "Можно указать несколько значений, разделенных запятыми. Если название уже содержит запятую, экранируйте ее при помощи символа \\" L["Supports multiple entries, separated by commas. Group Zone IDs must be prefixed with 'g', e.g. g277."] = [=[ID группы игровых зон должен иметь префикс g. Например: g227 Можно указать несколько значений, разделенных запятыми.]=] @@ -969,16 +1044,8 @@ L["Thick Outline"] = "Толстый контур" L["Thickness"] = "Толщина" L["Third"] = "Третье" L["Third Value of Tooltip Text"] = "Третье значение из текста подсказки" -L["This aura contains custom Lua code."] = "Индикация содержит пользовательский код Lua." L["This aura has legacy aura trigger(s), which are no longer supported."] = "Индикация содержит триггеры Аура устаревшего (legacy) типа, который больше не поддерживается." -L["This aura was created with a newer version of WeakAuras."] = "Индикация была создана в новой версии WeakAuras." -L["This aura was created with the Classic version of World of Warcraft."] = "Индикация была создана в Classic версии WoW." -L["This aura was created with the retail version of World of Warcraft."] = "Индикация была создана в Retail версии WoW." -L["This is a modified version of your aura, |cff9900FF%s.|r"] = [=[Это модифицированная версия вашей индикации: -|cff9900FF%s|r. -]=] -L["This is a modified version of your group, |cff9900FF%s.|r"] = [=[Это модифицированная версия группы ваших -индикаций: |cff9900FF%s|r.]=] +L["This aura tried to show a tooltip on a anchoring restricted region"] = "Индикация попыталась показать подсказку при наведении курсора на кадр, имеющий ограничения для крепления." L["Threat Percent"] = "Процент угрозы" L["Threat Situation"] = "Положение в списке угроз" L["Threat Value"] = "Количество угрозы" @@ -1027,6 +1094,7 @@ L["Trigger Update"] = "При изменении состояния тригге L["Trigger:"] = "Триггер:" L["Trivial (Low Level)"] = "Тривиальный (низкий уровень)" L["True"] = "Истина" +L["Trying to repair broken conditions in %s likely caused by a WeakAuras bug."] = "Попытка восстановить поврежденные условия в индикации %s, причиной которых стала ошибка аддона." L["Twin Emperors"] = "Императоры-близнецы" L["Type"] = "Тип" L["Unaffected"] = "Незадействованные" @@ -1050,6 +1118,7 @@ L["Update Auras"] = "Обновить индикацию" L["Usage:"] = "Доступные команды:" L["Use /wa minimap to show the minimap icon again."] = "Используйте команду /wa minimap, чтобы вновь отобразить иконку на миникарте." L["Use Custom Color"] = "Использовать свой цвет" +L["Use Watched Faction"] = "Использовать фракцию, репутацию с которой вы сейчас отслеживаете" L["Vaelastrasz the Corrupt"] = "Валестраз Порочный" L["Value"] = "Значение" L["Values/Remaining Time above this value are displayed as full progress."] = "Значения (или оставшееся время) выше указанного числа отображаются как полный прогресс." @@ -1063,12 +1132,22 @@ L["Visions of N'Zoth"] = "Видения Н'Зота" L["War Mode Active"] = "Включен режим войны" L["Warfront (Heroic)"] = "Фронт (героический)" L["Warfront (Normal)"] = "Фронт (обычный)" +--[[Translation missing --]] +L["Warlords of Draenor"] = "Warlords of Draenor" L["Warning"] = "Предупреждение" L["Warning for unknown aura:"] = "Предупреждение для неизвестной индикации." L["Warning: Full Scan auras checking for both name and spell id can't be converted."] = "|cFFFFCC00Предупреждение.|r Триггер с функцией Полного сканирования, проверяющий как название эффекта, так и ID заклинания, нельзя преобразовать." L["Warning: Name info is now available via %affected, %unaffected. Number of affected group members via %unitCount. Some options behave differently now. This is not automatically adjusted."] = "|cFFFFCC00Примечение.|r Теперь имена задействованных и незадействованных игроков доступны посредством %affected и %unaffected; количество задействованных участников группы - %unitCount. Некоторые параметры работают иначе. Эти изменения автоматически не применяются." L["Warning: Tooltip values are now available via %tooltip1, %tooltip2, %tooltip3 instead of %s. This is not automatically adjusted."] = "|cFFFFCC00Примечение.|r Теперь значения из текста подсказки доступны посредством %tooltip1, %tooltip2 и %tooltip3. Это изменение автоматически не применяется." L["WeakAuras Built-In (63:42 | 3:07 | 10 | 2.4)"] = "WeakAuras (встроенный): 63:42 | 3:07 | 10 | 2.4" +L[ [=[WeakAuras has detected that it has been downgraded. +Your saved auras may no longer work properly. +Would you like to run the |cffff0000EXPERIMENTAL|r repair tool? This will overwrite any changes you have made since the last database upgrade. +Last upgrade: %s]=] ] = [=[WeakAuras обнаружил, что версия аддона была понижена (осуществлен downgrade). Ваши индикации могут больше не работать должным образом. + +Запустить |cFFFF0000ЭКСПЕРИМЕНТАЛЬНОЕ|r средство восстановления данных? Все изменения, выполненные вами с момента последнего обновления базы данных, будут утеряны. + +Дата последнего обновления: %s]=] L["WeakAuras has encountered an error during the login process. Please report this issue at https://github.com/WeakAuras/Weakauras2/issues/new."] = "Во время процесса входа в игру возникла ошибка. Пожалуйста, сообщите об этой проблеме по адресу https://github.com/WeakAuras/Weakauras2/issues/new." L["WeakAuras Profiling"] = "Профилирование WeakAuras" L["WeakAuras Profiling Report"] = "Отчёт профилирования" @@ -1082,6 +1161,8 @@ L["Width"] = "Ширина" L["Wobble"] = "Колебание" L["World Boss"] = "Мировой босс" L["Wrap"] = "Переносить слова" +--[[Translation missing --]] +L["Wrath of the Lich King"] = "Wrath of the Lich King" L["Writing to the WeakAuras table is not allowed."] = "Запись в таблицу WeakAuras запрещена." L["X-Offset"] = "Смещение по X" L["Yell"] = "Крик" diff --git a/WeakAuras/Media/Textures/edge-example.tga b/WeakAuras/Media/Textures/edge-example.tga new file mode 100644 index 0000000..1b81115 Binary files /dev/null and b/WeakAuras/Media/Textures/edge-example.tga differ diff --git a/WeakAuras/Media/Textures/logo_256.tga b/WeakAuras/Media/Textures/logo_256.tga new file mode 100644 index 0000000..0b7369b Binary files /dev/null and b/WeakAuras/Media/Textures/logo_256.tga differ diff --git a/WeakAuras/Media/Textures/logo_64.tga b/WeakAuras/Media/Textures/logo_64.tga new file mode 100644 index 0000000..6ed917c Binary files /dev/null and b/WeakAuras/Media/Textures/logo_64.tga differ diff --git a/WeakAuras/Media/Textures/logo_64_nobg.tga b/WeakAuras/Media/Textures/logo_64_nobg.tga new file mode 100644 index 0000000..44b4ba9 Binary files /dev/null and b/WeakAuras/Media/Textures/logo_64_nobg.tga differ diff --git a/WeakAuras/Media/Textures/swipe-example.tga b/WeakAuras/Media/Textures/swipe-example.tga new file mode 100644 index 0000000..78619cf Binary files /dev/null and b/WeakAuras/Media/Textures/swipe-example.tga differ diff --git a/WeakAuras/Modernize.lua b/WeakAuras/Modernize.lua index ca991b5..e1e26d3 100644 --- a/WeakAuras/Modernize.lua +++ b/WeakAuras/Modernize.lua @@ -1,5 +1,6 @@ if not WeakAuras.IsCorrectVersion() then return end local AddonName, Private = ... +local L = WeakAuras.L -- Takes as input a table of display data and attempts to update it to be compatible with the current version function Private.Modernize(data) @@ -1072,5 +1073,115 @@ function Private.Modernize(data) end end + if (data.internalVersion < 49) then + if not data.regionType:match("group") then + data.subRegions = data.subRegions or {} + -- rename aurabar_bar into subforeground, and subbarmodel into submodel + for index, subRegionData in ipairs(data.subRegions) do + if subRegionData.type == "aurabar_bar" then + subRegionData.type = "subforeground" + elseif subRegionData.type == "subbarmodel" then + subRegionData.type = "submodel" + end + if subRegionData.bar_model_visible ~= nil then + subRegionData.model_visible = subRegionData.bar_model_visible + subRegionData.bar_model_visible = nil + end + if subRegionData.bar_model_alpha ~= nil then + subRegionData.model_alpha = subRegionData.bar_model_alpha + subRegionData.bar_model_alpha = nil + end + end + -- rename conditions for bar_model_visible and bar_model_alpha + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + if type(condition.changes) == "table" then + for changeIndex, change in ipairs(condition.changes) do + if change.property then + local prefix, property = change.property:match("(sub%.%d+%.)(.*)") + if prefix and property then + if property == "bar_model_visible" then + change.property = prefix.."model_visible" + elseif property == "bar_model_alpha" then + change.property = prefix.."model_alpha" + end + end + end + end + end + end + end + end + end + + if (data.internalVersion == 49) then + -- Version 49 was a dud and contained a broken validation. Try to salvage the data, as + -- best as we can. + local broken = false + local properties = {} + Private.GetSubRegionProperties(data, properties) + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + if type(condition.changes) == "table" then + for changeIndex, change in ipairs(condition.changes) do + if change.property then + if not properties[change.property] then + -- The property does not exist, so maybe it's one that was accidentally not moved + local subRegionIndex, property = change.property:match("^sub%.(%d+)%.(.*)") + if subRegionIndex and property then + broken = true + for _, offset in ipairs({-1, 1}) do + local newProperty = "sub." .. subRegionIndex + offset .. "." .. property + if properties[newProperty] then + change.property = newProperty + end + end + end + end + end + end + end + end + end + if broken then + WeakAuras.prettyPrint(L["Trying to repair broken conditions in %s likely caused by a WeakAuras bug."]:format(data.id)) + end + end + + if (data.internalVersion < 52) then + local function matchTarget(input) + return input == "target" or input == "'target'" or input == "\"target\"" or input == "%t" or input == "'%t'" or input == "\"%t\"" + end + + if data.conditions then + for _, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if change.property == "chat" and change.value then + if matchTarget(change.value.message_dest) then + change.value.message_dest = "target" + change.value.message_dest_isunit = true + end + end + end + end + end + + if data.actions.start.do_message + and data.actions.start.message_type == "WHISPER" + and matchTarget(data.actions.start.message_dest) + then + data.actions.start.message_dest = "target" + data.actions.start.message_dest_isunit = true + end + + if data.actions.finish.do_message + and data.actions.finish.message_type == "WHISPER" + and matchTarget(data.actions.finish.message_dest) + then + data.actions.finish.message_dest = "target" + data.actions.finish.message_dest_isunit = true + end + end + data.internalVersion = max(data.internalVersion or 0, WeakAuras.InternalVersion()); end diff --git a/WeakAuras/Profiling.lua b/WeakAuras/Profiling.lua index c656247..7d7ba6a 100644 --- a/WeakAuras/Profiling.lua +++ b/WeakAuras/Profiling.lua @@ -665,7 +665,7 @@ function RealTimeProfilingWindow:Init() toggleButton:SetFrameLevel(statsFrame:GetFrameLevel() + 1) toggleButton:SetHeight(20) toggleButton:SetWidth(width) - toggleButton:SetText(L["Start"]) + toggleButton:SetText(L["Start Now"]) toggleButton:SetScript("OnClick", function(self) local parent = self:GetParent():GetParent() if (not profileData.systems.time or profileData.systems.time.count ~= 1) then @@ -764,7 +764,7 @@ function RealTimeProfilingWindow:Stop() self:Hide() self:ResetBars() WeakAuras.StopProfile() - self.toggleButton:SetText(L["Start"]) + self.toggleButton:SetText(L["Start Now"]) end function RealTimeProfilingWindow:Toggle() diff --git a/WeakAuras/Prototypes.lua b/WeakAuras/Prototypes.lua index 877e076..784b1fc 100644 --- a/WeakAuras/Prototypes.lua +++ b/WeakAuras/Prototypes.lua @@ -84,9 +84,18 @@ function WeakAuras.UnitDetailedThreatSituation(unit1, unit2) end local constants = { - nameRealmFilterDesc = L[" Filter formats: 'Name', 'Name-Realm', '-Realm'. \n\nSupports multiple entries, separated by commas\n"], + nameRealmFilterDesc = L[" Filter formats: 'Name', 'Name-Realm', '-Realm'. \n\nSupports multiple entries, separated by commas\nCan use \\ to escape -."], } +function WeakAuras.SpellSchool(school) + return Private.combatlog_spell_school_types[school] or "" +end + +function WeakAuras.TestSchool(spellSchool, test) + print(spellSchool, test, type(spellSchool), type(test)) + return spellSchool == test +end + Private.function_strings = { count = [[ return function(count) @@ -99,6 +108,9 @@ Private.function_strings = { ]], count_fraction = [[ return function(count, max) + if max == 0 then + return false + end local fraction = count/max if(fraction %s %s) then return true @@ -609,6 +621,8 @@ function WeakAuras.CheckCombatLogFlags(flags, flagToCheck) return bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_MINE) > 0 elseif (flagToCheck == "InGroup") then return bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) == 0 + elseif (flagToCheck == "InParty") then + return bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_PARTY) > 0 elseif (flagToCheck == "NotInGroup") then return bit.band(flags, COMBATLOG_OBJECT_AFFILIATION_OUTSIDER) > 0 end @@ -795,7 +809,7 @@ Private.load_prototype = { }, { name = "ingroup", - display = L["In Group"], + display = L["Group Type"], type = "multiselect", width = WeakAuras.normalWidth, init = "arg", @@ -936,7 +950,7 @@ Private.load_prototype = { } }; -local function AddUnitChangeInternalEvents(triggerUnit, t) +local function AddUnitChangeInternalEvents(triggerUnit, t, includePets) if (triggerUnit == nil) then return end @@ -947,9 +961,13 @@ local function AddUnitChangeInternalEvents(triggerUnit, t) tinsert(t, "PET_UPDATE") else if Private.multiUnitUnits[triggerUnit] then + local isPet for unit in pairs(Private.multiUnitUnits[triggerUnit]) do - tinsert(t, "UNIT_CHANGED_" .. string.lower(unit)) - WeakAuras.WatchUnitChange(unit) + isPet = WeakAuras.UnitIsPet(unit) + if (includePets ~= nil and isPet) or (includePets ~= "PetsOnly" and not isPet) then + tinsert(t, "UNIT_CHANGED_" .. string.lower(unit)) + WeakAuras.WatchUnitChange(unit) + end end else tinsert(t, "UNIT_CHANGED_" .. string.lower(triggerUnit)) @@ -958,6 +976,20 @@ local function AddUnitChangeInternalEvents(triggerUnit, t) end end +local function AddRemainingCastInternalEvents(triggerUnit, t) + if (triggerUnit == nil) then + return + end + + if Private.multiUnitUnits[triggerUnit] then + for unit in pairs(Private.multiUnitUnits[triggerUnit]) do + tinsert(t, "CAST_REMAINING_CHECK_" .. string.lower(unit)) + end + else + tinsert(t, "CAST_REMAINING_CHECK_" .. string.lower(triggerUnit)) + end +end + local function AddUnitEventForEvents(result, unit, event) if unit then if not result.unit_events then @@ -976,11 +1008,32 @@ local function AddUnitEventForEvents(result, unit, event) end local unitHelperFunctions = { + UnitChangedForceEventsWithPets = function(trigger) + local events = {} + local includePets = trigger.use_includePets == true and trigger.includePets or nil + if Private.multiUnitUnits[trigger.unit] then + local isPet + for unit in pairs(Private.multiUnitUnits[trigger.unit]) do + isPet = WeakAuras.UnitIsPet(unit) + if (includePets ~= nil and isPet) or (includePets ~= "PetsOnly" and not isPet) then + tinsert(events, {"UNIT_CHANGED_" .. unit, unit}) + end + end + else + if trigger.unit then + tinsert(events, {"UNIT_CHANGED_" .. trigger.unit, trigger.unit}) + end + end + return events + end, + UnitChangedForceEvents = function(trigger) local events = {} if Private.multiUnitUnits[trigger.unit] then for unit in pairs(Private.multiUnitUnits[trigger.unit]) do - tinsert(events, {"UNIT_CHANGED_" .. unit, unit}) + if not WeakAuras.UnitIsPet(unit) then + tinsert(events, {"UNIT_CHANGED_" .. unit, unit}) + end end else if trigger.unit then @@ -1072,6 +1125,7 @@ Private.event_prototypes = { AddUnitEventForEvents(result, unit, "UNIT_FACTION") AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") AddUnitEventForEvents(result, unit, "UNIT_FLAGS") + AddUnitEventForEvents(result, unit, "PLAYER_FLAGS_CHANGED") return result; end, internal_events = function(trigger) @@ -1107,6 +1161,7 @@ Private.event_prototypes = { type = "unit", init = "arg", values = "actual_unit_types_cast", + desc = Private.actual_unit_types_cast_tooltip, test = "true", store = true }, @@ -1141,6 +1196,7 @@ Private.event_prototypes = { { name = "namerealm", display = L["Unit Name/Realm"], + desc = constants.nameRealmFilterDesc, type = "string", preamble = "local nameRealmChecker = WeakAuras.ParseNameCheck(%q)", test = "nameRealmChecker:Check(name, realm)", @@ -1171,6 +1227,23 @@ Private.event_prototypes = { store = true, conditionType = "select" }, + { + name = "raidMarkIndex", + display = L["Raid Mark"], + type = "select", + values = "raid_mark_check_type", + store = true, + conditionType = "select", + init = "GetRaidTargetIndex(unit) or 0" + }, + { + name = "raidMark", + display = L["Raid Mark Icon"], + store = true, + hidden = true, + test = "true", + init = "raidMarkIndex > 0 and '{rt'..raidMarkIndex..'}' or ''" + }, { name = "ignoreSelf", display = L["Ignore Self"], @@ -1251,6 +1324,22 @@ Private.event_prototypes = { store = true, conditionType = "bool" }, + { + name = "afk", + display = L["Afk"], + type = "tristate", + init = "UnitIsAFK(unit) == 1 and true or false", + store = true, + conditionType = "bool" + }, + { + name = "dnd", + display = L["Do Not Disturb"], + type = "tristate", + init = "UnitIsDND(unit) == 1 and true or false", + store = true, + conditionType = "bool" + }, { hidden = true, test = "WeakAuras.UnitExistsFixed(unit, smart) and specificUnitCheck" @@ -1326,7 +1415,7 @@ Private.event_prototypes = { name = "percentXP", display = L["Experience (%)"], type = "number", - init = "total ~= 0 and (value / total) * 100", + init = "total ~= 0 and (value / total) * 100 or nil", store = true, conditionType = "number" }, @@ -1348,7 +1437,7 @@ Private.event_prototypes = { { name = "percentrested", display = L["Rested Experience (%)"], - init = "total ~= 0 and (restedXP / total) * 100", + init = "total ~= 0 and (restedXP / total) * 100 or nil", type = "number", store = true, conditionType = "number", @@ -1369,11 +1458,13 @@ Private.event_prototypes = { }, ["Health"] = { type = "unit", + includePets = "true", canHaveDuration = true, events = function(trigger) local unit = trigger.unit local result = {} AddUnitEventForEvents(result, unit, "UNIT_HEALTH") + AddUnitEventForEvents(result, unit, "UNIT_MAXHEALTH") AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") if trigger.use_ignoreDead or trigger.use_ignoreDisconnected then AddUnitEventForEvents(result, unit, "UNIT_FLAGS") @@ -1383,10 +1474,11 @@ Private.event_prototypes = { internal_events = function(trigger) local unit = trigger.unit local result = {} - AddUnitChangeInternalEvents(unit, result) + local includePets = trigger.use_includePets == true and trigger.includePets or nil + AddUnitChangeInternalEvents(unit, result, includePets) return result end, - force_events = unitHelperFunctions.UnitChangedForceEvents, + force_events = unitHelperFunctions.UnitChangedForceEventsWithPets, name = L["Health"], init = function(trigger) trigger.unit = trigger.unit or "player"; @@ -1409,6 +1501,7 @@ Private.event_prototypes = { type = "unit", init = "arg", values = "actual_unit_types_cast", + desc = Private.actual_unit_types_cast_tooltip, test = "true", store = true }, @@ -1445,7 +1538,15 @@ Private.event_prototypes = { name = "percenthealth", display = L["Health (%)"], type = "number", - init = "total ~= 0 and (value / total) * 100", + init = "total ~= 0 and (value / total) * 100 or nil", + store = true, + conditionType = "number" + }, + { + name = "deficit", + display = WeakAuras.newFeatureString .. L["Health Deficit"], + type = "number", + init = "total - value", store = true, conditionType = "number" }, @@ -1498,6 +1599,34 @@ Private.event_prototypes = { store = true, conditionType = "select" }, + { + name = "raidMarkIndex", + display = L["Raid Mark"], + type = "select", + values = "raid_mark_check_type", + store = true, + conditionType = "select", + init = "GetRaidTargetIndex(unit) or 0" + }, + { + name = "raidMark", + display = L["Raid Mark Icon"], + store = true, + hidden = true, + test = "true", + init = "raidMarkIndex > 0 and '{rt'..raidMarkIndex..'}' or ''" + }, + { + name = "includePets", + display = WeakAuras.newFeatureString .. L["Include Pets"], + type = "select", + values = "include_pets_types", + width = WeakAuras.normalWidth, + test = "true", + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end + }, { name = "ignoreSelf", display = L["Ignore Self"], @@ -1574,11 +1703,12 @@ Private.event_prototypes = { internal_events = function(trigger) local unit = trigger.unit local result = {} - AddUnitChangeInternalEvents(unit, result) + local includePets = trigger.use_includePets == true and trigger.includePets or nil + AddUnitChangeInternalEvents(unit, result, includePets) return result end, - force_events = unitHelperFunctions.UnitChangedForceEvents, + force_events = unitHelperFunctions.UnitChangedForceEventsWithPets, name = L["Power"], init = function(trigger) trigger.unit = trigger.unit or "player"; @@ -1605,6 +1735,7 @@ Private.event_prototypes = { type = "unit", init = "arg", values = "actual_unit_types_cast", + desc = Private.actual_unit_types_cast_tooltip, test = "true", store = true }, @@ -1668,7 +1799,15 @@ Private.event_prototypes = { name = "percentpower", display = L["Power (%)"], type = "number", - init = "total ~= 0 and (value / total) * 100", + init = "total ~= 0 and (value / total) * 100 or nil", + store = true, + conditionType = "number" + }, + { + name = "deficit", + display = WeakAuras.newFeatureString .. L["Power Deficit"], + type = "number", + init = "total - value", store = true, conditionType = "number" }, @@ -1721,6 +1860,34 @@ Private.event_prototypes = { store = true, conditionType = "select" }, + { + name = "raidMarkIndex", + display = L["Raid Mark"], + type = "select", + values = "raid_mark_check_type", + store = true, + conditionType = "select", + init = "GetRaidTargetIndex(unit) or 0" + }, + { + name = "raidMark", + display = L["Raid Mark Icon"], + store = true, + hidden = true, + test = "true", + init = "raidMarkIndex > 0 and '{rt'..raidMarkIndex..'}' or ''" + }, + { + name = "includePets", + display = WeakAuras.newFeatureString .. L["Include Pets"], + type = "select", + values = "include_pets_types", + width = WeakAuras.normalWidth, + test = "true", + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end + }, { name = "ignoreSelf", display = L["Ignore Self"], @@ -1816,7 +1983,7 @@ Private.event_prototypes = { }, { name = "sourceFlags", - display = L["Source In Group"], + display = L["Source Affiliation"], type = "select", values = "combatlog_flags_check_type", init = "arg", @@ -1969,10 +2136,19 @@ Private.event_prototypes = { conditionType = "string" }, { + name = "spellSchool", + display = WeakAuras.newFeatureString .. L["Spell School"], + type = "select", + values = "combatlog_spell_school_types_for_ui", + test = "spellSchool == %d", + init = "arg", + control = "WeakAurasSortedDropdown", + conditionType = "select", + store = true, enable = function(trigger) return trigger.subeventPrefix and (trigger.subeventPrefix:find("SPELL") or trigger.subeventPrefix == "RANGE" or trigger.subeventPrefix:find("DAMAGE")) end - }, -- spellSchool ignored with _ argument + }, { name = "environmentalType", display = L["Environment Type"], @@ -2191,7 +2367,7 @@ Private.event_prototypes = { init = "spellId and select(3, GetSpellInfo(spellId)) or 'Interface\\\\Icons\\\\INV_Misc_QuestionMark'", store = true, test = "true" - } + }, }, timedrequired = true }, @@ -2445,7 +2621,7 @@ Private.event_prototypes = { conditionEvents = { "PLAYER_TARGET_CHANGED", "WA_SPELL_RANGECHECK", - } + }, }, { hidden = true, @@ -2592,7 +2768,7 @@ Private.event_prototypes = { conditionType = "select", conditionValues = "charges_change_condition_type"; conditionTest = function(state, needle) - return state and state.show and WeakAuras.CheckChargesDirection(state.direction, needle) + return state and state.show and state.direction and WeakAuras.CheckChargesDirection(state.direction, needle) end, }, { @@ -2789,7 +2965,6 @@ Private.event_prototypes = { end, hasItemID = true, automaticrequired = true, - automaticAutoHide = false }, ["Cooldown Progress (Equipment Slot)"] = { type = "item", @@ -4323,10 +4498,12 @@ Private.event_prototypes = { end ]]; + local showOnActive = trigger.showOn == 'showOnActive' or not trigger.showOn + return ret:format(trigger.weapon or "main", trigger.use_enchant and trigger.enchant or "", - trigger.use_stack and tonumber(trigger.stack or 0) or "nil", - trigger.use_remaining and tonumber(trigger.remaining or 0) or "nil", + showOnActive and trigger.use_stack and tonumber(trigger.stack or 0) or "nil", + showOnActive and trigger.use_remaining and tonumber(trigger.remaining or 0) or "nil", trigger.showOn or "showOnActive", trigger.stack_operator or "<", trigger.remaining_operator or "<") @@ -4987,18 +5164,16 @@ Private.event_prototypes = { AddUnitEventForEvents(result, unit, "UNIT_SPELLCAST_INTERRUPTIBLE") AddUnitEventForEvents(result, unit, "UNIT_SPELLCAST_NOT_INTERRUPTIBLE") AddUnitEventForEvents(result, unit, "UNIT_SPELLCAST_INTERRUPTED") - AddUnitEventForEvents(result, unit, "UNIT_TARGET") AddUnitEventForEvents(result, unit, "UNIT_NAME_UPDATE") - if trigger.use_showLatency and unit == "player" then - result.events = result.events or {} - tinsert(result.events, "CAST_LATENCY_UPDATE") - end + AddUnitEventForEvents(result, unit, "UNIT_TARGET") return result end, internal_events = function(trigger) local unit = trigger.unit - local result = {"CAST_REMAINING_CHECK_" .. string.lower(unit)} - AddUnitChangeInternalEvents(unit, result) + local result = {} + AddRemainingCastInternalEvents(unit, result) + local includePets = trigger.use_includePets == true and trigger.includePets or nil + AddUnitChangeInternalEvents(unit, result, includePets) return result end, loadFunc = function(trigger) @@ -5006,7 +5181,7 @@ Private.event_prototypes = { WeakAuras.WatchForCastLatency() end end, - force_events = unitHelperFunctions.UnitChangedForceEvents, + force_events = unitHelperFunctions.UnitChangedForceEventsWithPets, canHaveDuration = "timed", name = L["Cast"], init = function(trigger) @@ -5041,21 +5216,10 @@ Private.event_prototypes = { WeakAuras.ScheduleCastCheck(expirationTime - remainingCheck, unit) end ]=]; - ret = ret:format(trigger.unit == "group" and "true" or "false", trigger.use_remaining and tonumber(trigger.remaining or 0) or "nil", trigger.use_inverse and "true" or "false"); - if trigger.unit == "player" and trigger.use_showLatency then - ret = ret .. [[ - if event == "CAST_LATENCY_UPDATE" then - state.sendTime = GetTime() - elseif event == "UNIT_SPELLCAST_START" and tonumber(state.sendTime) then - state.latency = GetTime() - state.sendTime - state.changed = true - end - ]] - end ret = ret .. unitHelperFunctions.SpecificUnitCheck(trigger) return ret @@ -5191,6 +5355,34 @@ Private.event_prototypes = { return not trigger.use_inverse end }, + { + name = "raidMarkIndex", + display = L["Raid Mark"], + type = "select", + values = "raid_mark_check_type", + store = true, + conditionType = "select", + init = "GetRaidTargetIndex(unit) or 0" + }, + { + name = "raidMark", + display = L["Raid Mark Icon"], + store = true, + hidden = true, + test = "true", + init = "raidMarkIndex > 0 and '{rt'..raidMarkIndex..'}' or ''" + }, + { + name = "includePets", + display = WeakAuras.newFeatureString .. L["Include Pets"], + type = "select", + values = "include_pets_types", + width = WeakAuras.normalWidth, + test = "true", + enable = function(trigger) + return trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party" + end + }, { name = "ignoreSelf", display = L["Ignore Self"], @@ -5325,7 +5517,8 @@ Private.event_prototypes = { { name = L["Latency"], func = function(trigger, state) - return 0, type(state.latency) == "number" and state.latency + if not state.expirationTime or not state.duration then return 0, 0 end + return 0, (state.expirationTime - state.duration) - (Private.LAST_CURRENT_SPELL_CAST_CHANGED or 0) end, enable = function(trigger) return trigger.use_showLatency and trigger.unit == "player" @@ -5733,7 +5926,7 @@ Private.event_prototypes = { }, { name = "ingroup", - display = L["In Group"], + display = L["Group Type"], type = "multiselect", values = "group_types", init = "WeakAuras.GroupType()", @@ -6018,6 +6211,12 @@ end Private.event_prototypes["BigWigs Message"] = nil Private.event_prototypes["BigWigs Timer"] = nil +Private.category_event_prototype = {} +for name, prototype in pairs(Private.event_prototypes) do + Private.category_event_prototype[prototype.type] = Private.category_event_prototype[prototype.type] or {} + Private.category_event_prototype[prototype.type][name] = prototype.name +end + Private.dynamic_texts = { ["p"] = { get = function(state) @@ -6026,6 +6225,10 @@ Private.dynamic_texts = { return state.value or nil end if state.progressType == "timed" then + if state.paused then + return state.remaining and state.remaining >= 0 and state.remaining or nil + end + if not state.expirationTime or not state.duration then return nil end diff --git a/WeakAuras/RegionTypes/AuraBar.lua b/WeakAuras/RegionTypes/AuraBar.lua index 25381ea..82f1a97 100644 --- a/WeakAuras/RegionTypes/AuraBar.lua +++ b/WeakAuras/RegionTypes/AuraBar.lua @@ -35,12 +35,7 @@ local default = { icon_side = "RIGHT", icon_color = {1.0, 1.0, 1.0, 1.0}, frameStrata = 1, - zoom = 0, - subRegions = { - [1] = { - ["type"] = "aurabar_bar" - } - } + zoom = 0 }; WeakAuras.regionPrototype.AddAdjustedDurationToDefault(default); @@ -223,8 +218,6 @@ local anchorAlignment = { ["VERTICAL_INVERSE"] = { "BOTTOMLEFT", "BOTTOMRIGHT", "TOP" } } -local extraTextureWrapMode = "REPEAT"; - -- Emulate blizzard statusbar with advanced features (more grow directions) local barPrototype = { ["UpdateAnchors"] = function(self) @@ -314,7 +307,6 @@ local barPrototype = { for index, additionalBar in ipairs(self.additionalBars) do if (not self.extraTextures[index]) then local extraTexture = self:CreateTexture(nil, "ARTWORK"); - extraTexture:SetTexture(self:GetStatusBarTexture(), extraTextureWrapMode, extraTextureWrapMode); extraTexture:SetDrawLayer("ARTWORK", min(index, 7)); self.extraTextures[index] = extraTexture; end @@ -379,6 +371,14 @@ local barPrototype = { extraTexture:SetVertexColor(1, 1, 1, 1); end + local texture = self.additionalBarsTextures and self.additionalBarsTextures[index]; + if texture then + local texturePath = SharedMedia:Fetch("statusbar", texture) or "" + extraTexture:SetTexture(texturePath) + else + extraTexture:SetTexture(self:GetStatusBarTexture()); + end + local xOffset = 0; local yOffset = 0; local width, height = self:GetRealSize() @@ -461,9 +461,10 @@ local barPrototype = { end end, - ["SetAdditionalBars"] = function(self, additionalBars, colors, min, max, inverse, overlayclip) + ["SetAdditionalBars"] = function(self, additionalBars, colors, textures, min, max, inverse, overlayclip) self.additionalBars = additionalBars; self.additionalBarsColors = colors; + self.additionalBarsTextures = textures; self.additionalBarsMin = min or 0; self.additionalBarsMax = max or 0; self.additionalBarsInverse = inverse; @@ -471,6 +472,15 @@ local barPrototype = { self:UpdateAdditionalBars(); end, + ["GetAdditionalBarsInverse"] = function(self) + return self.additionalBarsInverse + end, + + ["SetAdditionalBarsInverse"] = function(self, value) + self.additionalBarsInverse = value; + self:UpdateAdditionalBars(); + end, + ["SetAdditionalBarColor"] = function(self, id, color) self.additionalBarsColors[id] = color; if self.extraTextures[id] then @@ -515,7 +525,7 @@ local barPrototype = { self.fg:SetTexture(texture); self.bg:SetTexture(texture); for index, extraTexture in ipairs(self.extraTextures) do - extraTexture:SetTexture(texture, extraTextureWrapMode, extraTextureWrapMode); + extraTexture:SetTexture(texture); end end, @@ -854,6 +864,7 @@ local funcs = { else self.bar:SetValue(1 - self.bar:GetValue()); end + self.bar:SetAdditionalBarsInverse(not self.bar:GetAdditionalBarsInverse()) self.subRegionEvents:Notify("InverseChanged") end, SetOrientation = function(self, orientation) @@ -994,6 +1005,7 @@ end local function create(parent) -- Create overall region (containing everything else) local region = CreateFrame("FRAME", nil, parent); + region.regionType = "aurabar" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); @@ -1030,9 +1042,6 @@ local function create(parent) icon._SetTexture = icon.SetTexture icon.SetTexture = setTexture - -- Region variables - region.values = {}; - local oldSetFrameLevel = region.SetFrameLevel; function region.SetFrameLevel(self, frameLevel) oldSetFrameLevel(self, frameLevel); @@ -1100,6 +1109,11 @@ local function modify(parent, region, data) else region.overlays = {} end + if data.overlaysTexture then + region.overlaysTexture = CopyTable(data.overlaysTexture) + else + region.overlaysTexture = {} + end -- Update texture settings local texturePath = SharedMedia:Fetch("statusbar", data.texture) or ""; @@ -1236,15 +1250,33 @@ local function modify(parent, region, data) local state = region.state region:UpdateMinMax() if state.progressType == "timed" then - local expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + local expirationTime + if state.paused == true then + if not region.paused then + region:Pause() + end + if region.TimerTick then + region.TimerTick = nil + region:UpdateRegionHasTimerTick() + end + expirationTime = GetTime() + (state.remaining or 0) + else + if region.paused then + region:Resume() + end + if not region.TimerTick then + region.TimerTick = TimerTick + region:UpdateRegionHasTimerTick() + end + expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + end local duration = state.duration or 0 region:SetTime(region.currentMax - region.currentMin, expirationTime - region.currentMin, state.inverse); - if not region.TimerTick then - region.TimerTick = TimerTick - region:UpdateRegionHasTimerTick() - end elseif state.progressType == "static" then + if region.paused then + region:Resume() + end local value = state.value or 0; local total = state.total or 0; @@ -1254,6 +1286,9 @@ local function modify(parent, region, data) region:UpdateRegionHasTimerTick() end else + if region.paused then + region:Resume() + end region:SetTime(0, math.huge) if region.TimerTick then region.TimerTick = nil @@ -1265,7 +1300,7 @@ local function modify(parent, region, data) local duration = state.duration or 0 local effectiveInverse = (state.inverse and not region.inverseDirection) or (not state.inverse and region.inverseDirection); - region.bar:SetAdditionalBars(state.additionalProgress, region.overlays, region.currentMin, region.currentMax, effectiveInverse, region.overlayclip); + region.bar:SetAdditionalBars(state.additionalProgress, region.overlays, region.overlaysTexture, region.currentMin, region.currentMax, effectiveInverse, region.overlayclip); end -- Scale update function @@ -1322,42 +1357,18 @@ local function modify(parent, region, data) WeakAuras.regionPrototype.modifyFinish(parent, region, data); end -local function ValidateRegion(data) - data.subRegions = data.subRegions or {} - for index, subRegionData in ipairs(data.subRegions) do - if subRegionData.type == "aurabar_bar" then - return +local function validate(data) + -- pre-migration + if data.subRegions then + for _, subRegionData in ipairs(data.subRegions) do + if subRegionData.type == "aurabar_bar" then + subRegionData.type = "subforeground" + end end end - tinsert(data.subRegions, 1, { - ["type"] = "aurabar_bar" - }) + Private.EnforceSubregionExists(data, "subforeground") + Private.EnforceSubregionExists(data, "subbackground") end -- Register new region type with WeakAuras -WeakAuras.RegisterRegionType("aurabar", create, modify, default, GetProperties, ValidateRegion); - -local function subSupports(regionType) - return regionType == "aurabar" -end - -local function noop() -end - -local function SetFrameLevel(self, level) - self.parent.bar:SetFrameLevel(level) - self.parent.iconFrame:SetFrameLevel(level) -end - -local function subCreate() - local result = {} - result.Update = noop - result.SetFrameLevel = SetFrameLevel - return result -end - -local function subModify(parent, region) - region.parent = parent -end - -WeakAuras.RegisterSubRegionType("aurabar_bar", L["Foreground"], subSupports, subCreate, subModify, noop, noop, {}, nil, {}, false); +WeakAuras.RegisterRegionType("aurabar", create, modify, default, GetProperties, validate); diff --git a/WeakAuras/RegionTypes/DynamicGroup.lua b/WeakAuras/RegionTypes/DynamicGroup.lua index 014c716..e029bf1 100644 --- a/WeakAuras/RegionTypes/DynamicGroup.lua +++ b/WeakAuras/RegionTypes/DynamicGroup.lua @@ -104,6 +104,7 @@ end local function create(parent) local region = CreateFrame("FRAME", nil, parent) + region.regionType = "dynamicgroup" region:SetSize(16, 16) region:SetMovable(true) region.sortedChildren = {} @@ -116,6 +117,13 @@ local function create(parent) region.controlPoints.parent = region WeakAuras.regionPrototype.create(region) region.suspended = 0 + + local oldSetFrameLevel = region.SetFrameLevel + region.SetFrameLevel = function(self, level) + oldSetFrameLevel(self, level) + self.background:SetFrameLevel(level) + end + return region end @@ -855,6 +863,7 @@ local function modify(parent, region, data) else childRegion:SetFrameStrata(Private.frame_strata_types[childData.frameStrata]); end + Private.ApplyFrameLevel(childRegion) return regionData end @@ -1191,7 +1200,7 @@ local function modify(parent, region, data) -- if self.dynamicAnchor then self:UpdateBorder(); return end Private.StartProfileSystem("dynamicgroup") Private.StartProfileAura(data.id) - local numVisible, minX, maxX, maxY, minY, minLevel = 0 + local numVisible, minX, maxX, maxY, minY = 0 for active, regionData in ipairs(self.sortedChildren) do if regionData.shown then numVisible = numVisible + 1 @@ -1199,14 +1208,12 @@ local function modify(parent, region, data) local regionLeft, regionRight, regionTop, regionBottom = SafeGetPos(childRegion, childRegion.GetLeft), SafeGetPos(childRegion, childRegion.GetRight), SafeGetPos(childRegion, childRegion.GetTop), SafeGetPos(childRegion, childRegion.GetBottom) - local frameLevel = childRegion:GetFrameLevel() - if(regionLeft and regionRight and regionTop and regionBottom and frameLevel) then + if(regionLeft and regionRight and regionTop and regionBottom) then minX = minX and min(regionLeft, minX) or regionLeft maxX = maxX and max(regionRight, maxX) or regionRight minY = minY and min(regionBottom, minY) or regionBottom maxY = maxY and max(regionTop, maxY) or regionTop - minLevel = minLevel and min(frameLevel, minLevel) or frameLevel end end end @@ -1223,7 +1230,6 @@ local function modify(parent, region, data) self:SetHeight(height) self.currentWidth = width self.currentHeight = height - self.background:SetFrameLevel(minLevel and minLevel - 1 or 0) else self:Hide() end diff --git a/WeakAuras/RegionTypes/Group.lua b/WeakAuras/RegionTypes/Group.lua index 769037f..57f9312 100644 --- a/WeakAuras/RegionTypes/Group.lua +++ b/WeakAuras/RegionTypes/Group.lua @@ -26,6 +26,7 @@ local default = { local function create(parent) -- Main region local region = CreateFrame("FRAME", nil, parent); + region.regionType = "group" region:SetMovable(true); region:SetWidth(2); region:SetHeight(2); @@ -36,6 +37,12 @@ local function create(parent) WeakAuras.regionPrototype.create(region); + local oldSetFrameLevel = region.SetFrameLevel + region.SetFrameLevel = function(self, level) + oldSetFrameLevel(self, level) + self.border:SetFrameLevel(level) + end + return region; end @@ -92,20 +99,14 @@ local function modify(parent, region, data) -- Get overall bounding box local leftest, rightest, lowest, highest = 0, 0, 0, 0; - local minLevel - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - local childRegion = WeakAuras.GetRegion(childId) - if(childData) then - local blx, bly, trx, try = getRect(childData, childRegion); + for child in Private.TraverseLeafs(data) do + local childRegion = WeakAuras.GetRegion(child.id) + if(child) then + local blx, bly, trx, try = getRect(child, childRegion); leftest = math.min(leftest, blx); rightest = math.max(rightest, trx); lowest = math.min(lowest, bly); highest = math.max(highest, try); - local frameLevel = childRegion and childRegion:GetFrameLevel() - if frameLevel then - minLevel = minLevel and math.min(minLevel, frameLevel) or frameLevel - end end end region.blx = leftest; @@ -116,56 +117,67 @@ local function modify(parent, region, data) -- Adjust frame-level sorting Private.FixGroupChildrenOrderForGroup(data); - -- Control children (does not happen with "group") - function region:UpdateBorder(childRegion) - local border = region.border; - -- Apply border settings - if data.border then - -- Initial visibility (of child that originated UpdateBorder(...)) - local childVisible = childRegion and childRegion.toShow or false; + local hasDynamicSubGroups = false + for index, childId in pairs(data.controlledChildren) do + local childData = WeakAuras.GetData(childId); + if childData.regionType == "dynamicgroup" then + hasDynamicSubGroups = true + break; + end + end - -- Scan children for visibility - if not childVisible then - for index, childId in ipairs(data.controlledChildren) do - local childRegion = WeakAuras.regions[childId] and WeakAuras.regions[childId].region; - if childRegion and childRegion.toShow then - childVisible = true; - break; + if not hasDynamicSubGroups then + -- Control children (does not happen with "group") + function region:UpdateBorder(childRegion) + local border = region.border; + -- Apply border settings + if data.border then + -- Initial visibility (of child that originated UpdateBorder(...)) + local childVisible = childRegion and childRegion.toShow or false; + + -- Scan children for visibility + if not childVisible then + for child in Private.TraverseLeafs(data) do + local childRegion = WeakAuras.regions[child.id] and WeakAuras.regions[child.id].region; + if childRegion and childRegion.toShow then + childVisible = true; + break; + end end end - end - -- Show border if child is visible - if childVisible then - border:SetBackdrop({ - edgeFile = data.borderEdge ~= "None" and SharedMedia:Fetch("border", data.borderEdge) or "", - edgeSize = data.borderSize, - bgFile = data.borderBackdrop ~= "None" and SharedMedia:Fetch("background", data.borderBackdrop) or "", - insets = { - left = data.borderInset, - right = data.borderInset, - top = data.borderInset, - bottom = data.borderInset, - }, - }); - border:SetBackdropBorderColor(data.borderColor[1], data.borderColor[2], data.borderColor[3], data.borderColor[4]); - border:SetBackdropColor(data.backdropColor[1], data.backdropColor[2], data.backdropColor[3], data.backdropColor[4]); + -- Show border if child is visible + if childVisible then + border:SetBackdrop({ + edgeFile = data.borderEdge ~= "None" and SharedMedia:Fetch("border", data.borderEdge) or "", + edgeSize = data.borderSize, + bgFile = data.borderBackdrop ~= "None" and SharedMedia:Fetch("background", data.borderBackdrop) or "", + insets = { + left = data.borderInset, + right = data.borderInset, + top = data.borderInset, + bottom = data.borderInset, + }, + }); + border:SetBackdropBorderColor(data.borderColor[1], data.borderColor[2], data.borderColor[3], data.borderColor[4]); + border:SetBackdropColor(data.backdropColor[1], data.backdropColor[2], data.backdropColor[3], data.backdropColor[4]); - border:ClearAllPoints(); - border:SetPoint("bottomleft", region, "bottomleft", leftest-data.borderOffset, lowest-data.borderOffset); - border:SetPoint("topright", region, "topright", rightest+data.borderOffset, highest+data.borderOffset); - if minLevel then - border:SetFrameLevel(minLevel - 1) + border:ClearAllPoints(); + border:SetPoint("bottomleft", region, "bottomleft", leftest-data.borderOffset, lowest-data.borderOffset); + border:SetPoint("topright", region, "topright", rightest+data.borderOffset, highest+data.borderOffset); + border:Show(); + else + border:Hide(); end - border:Show(); else border:Hide(); end - else - border:Hide(); end + region:UpdateBorder() + else + region.UpdateBorder = function() end + region.border:Hide() end - region:UpdateBorder() WeakAuras.regionPrototype.modifyFinish(parent, region, data); end diff --git a/WeakAuras/RegionTypes/Icon.lua b/WeakAuras/RegionTypes/Icon.lua index 8af2082..b89af10 100644 --- a/WeakAuras/RegionTypes/Icon.lua +++ b/WeakAuras/RegionTypes/Icon.lua @@ -33,8 +33,7 @@ local default = { keepAspectRatio = false, frameStrata = 1, cooldown = false, - cooldownEdge = false, - subRegions = {} + cooldownEdge = false }; WeakAuras.regionPrototype.AddAlphaToDefault(default); @@ -187,10 +186,65 @@ local function setTexture(self, ...) return apply end +local function cooldown_onUpdate(self, e) + if self._duration then + if self._delay then + if not self._paused then + self._delay = self._delay - e + end + self:_SetCooldown(GetTime(), self._duration) + if self._delay <= 0 then + self._delay = nil + if not self._paused then + self:SetScript("OnUpdate", nil) + end + end + return + end + if self._paused then + self:_SetCooldown(GetTime() - self._paused, self._duration) + end + end +end + +local function cooldown_pause(self) + if not self._paused then + self._paused = GetTime() - (self._start or 0) + self:SetScript("OnUpdate", cooldown_onUpdate) + end +end + +local function cooldown_resume(self) + if self._paused then + self._start = GetTime() - self._paused + end + self._paused = nil + self:SetScript("OnUpdate", nil) +end + +local function cooldown_setCooldown(self, start, duration, ...) + if self._start == start and self._duration == duration then + return + end + self._start = start + self._duration = duration + local getTime = GetTime() + local delay = start - getTime + if delay > 0 then + self._delay = delay + self:SetScript("OnUpdate", cooldown_onUpdate) + end + if self._paused then + self._paused = getTime - self._start + end + return self._SetCooldown(self, math.min(getTime, start), duration, ...) +end + local function create(parent, data) local font = "GameFontHighlight"; local region = CreateFrame("FRAME", nil, parent); + region.regionType = "icon" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); @@ -262,8 +316,10 @@ local function create(parent, data) region.cooldown = cooldown; cooldown:SetAllPoints(icon); - region.values = {}; - + cooldown._SetCooldown = cooldown.SetCooldown; + cooldown.SetCooldown = cooldown_setCooldown; + cooldown.Pause = cooldown_pause; + cooldown.Resume = cooldown_resume; local SetFrameLevel = region.SetFrameLevel; @@ -504,9 +560,14 @@ local function modify(parent, region, data) cooldown:Hide() if(data.cooldown) then function region:SetValue(value, total) - cooldown.duration = 0 - cooldown.expirationTime = math.huge - cooldown:Hide(); + cooldown.value = value + cooldown.total = total + if (value >= 0 and value <= total) then + cooldown:Show() + cooldown:SetCooldown(GetTime() - (total - value), total) + else + cooldown:Hide(); + end end function region:SetTime(duration, expirationTime) @@ -526,13 +587,28 @@ local function modify(parent, region, data) if (cooldown.duration and cooldown.duration > 0.01) then cooldown:Show(); cooldown:SetCooldown(cooldown.expirationTime - cooldown.duration, cooldown.duration); + cooldown:Resume() end end function region:Update() local state = region.state if state.progressType == "timed" then - local expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + local expirationTime + if state.paused == true then + if not region.paused then + region:Pause() + end + cooldown:Pause() + expirationTime = GetTime() + (state.remaining or 0) + else + if region.paused then + region:Resume() + end + cooldown:Resume() + expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + end + local duration = state.duration or 0 if region.adjustedMinRelPercent then region.adjustedMinRel = region.adjustedMinRelPercent * duration @@ -561,6 +637,7 @@ local function modify(parent, region, data) local adjustMin = region.adjustedMin or region.adjustedMinRel or 0; local max = region.adjustedMax or region.adjustedMaxRel or total; region:SetValue(value - adjustMin, max - adjustMin); + cooldown:Pause() else region:SetTime(0, math.huge) end @@ -595,4 +672,8 @@ local function modify(parent, region, data) region:SetHeight(region:GetHeight()) end -WeakAuras.RegisterRegionType("icon", create, modify, default, GetProperties) +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + +WeakAuras.RegisterRegionType("icon", create, modify, default, GetProperties, validate) diff --git a/WeakAuras/RegionTypes/Model.lua b/WeakAuras/RegionTypes/Model.lua index 9c34b1c..6e6a5f9 100644 --- a/WeakAuras/RegionTypes/Model.lua +++ b/WeakAuras/RegionTypes/Model.lua @@ -30,7 +30,7 @@ local default = { borderOffset = 5, borderInset = 11, borderSize = 16, - borderBackdrop = "Blizzard Tooltip", + borderBackdrop = "Blizzard Tooltip" }; local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ceil(GetScreenHeight() / 20) * 20; @@ -70,6 +70,7 @@ local regionFunctions = { local function create(parent) -- Main region local region = CreateFrame("FRAME", nil, UIParent); + region.regionType = "model" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); @@ -84,6 +85,8 @@ local function create(parent) region[k] = v end + region.AnchorSubRegion = WeakAuras.regionPrototype.AnchorSubRegion + -- Return complete region return region; end @@ -293,5 +296,9 @@ do end end +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + -- Register new region type with WeakAuras -WeakAuras.RegisterRegionType("model", create, modify, default, GetProperties); +WeakAuras.RegisterRegionType("model", create, modify, default, GetProperties, validate); diff --git a/WeakAuras/RegionTypes/ProgressTexture.lua b/WeakAuras/RegionTypes/ProgressTexture.lua index 7cb5a06..20fc4e1 100644 --- a/WeakAuras/RegionTypes/ProgressTexture.lua +++ b/WeakAuras/RegionTypes/ProgressTexture.lua @@ -92,7 +92,6 @@ local default = { height = 200, orientation = "VERTICAL", inverse = false, - alpha = 1.0, foregroundColor = {1, 1, 1, 1}, backgroundColor = {0.5, 0.5, 0.5, 0.5}, startAngle = 0, @@ -101,7 +100,6 @@ local default = { user_y = 0, crop_x = 0.41, crop_y = 0.41, - crop = 0.41, rotation = 0, selfPoint = "CENTER", anchorPoint = "CENTER", @@ -110,9 +108,9 @@ local default = { yOffset = 0, font = defaultFont, fontSize = defaultFontSize, - stickyDuration = false, mirror = false, - frameStrata = 1 + frameStrata = 1, + slantMode = "INSIDE" }; WeakAuras.regionPrototype.AddAlphaToDefault(default); @@ -160,10 +158,46 @@ local properties = { bigStep = 1, default = 32 }, + orientation = { + display = L["Orientation"], + setter = "SetOrientation", + type = "list", + values = Private.orientation_with_circle_types + }, + inverse = { + display = L["Inverse"], + setter = "SetInverse", + type = "bool" + }, + mirror = { + display = L["Mirror"], + setter = "SetMirror", + type = "bool" + } } WeakAuras.regionPrototype.AddProperties(properties, default); +local function GetProperties(data) + local overlayInfo = Private.GetOverlayInfo(data); + if (overlayInfo and next(overlayInfo)) then + local auraProperties = CopyTable(properties); + + for id, display in ipairs(overlayInfo) do + auraProperties["overlays." .. id] = { + display = string.format(L["%s Overlay Color"], display), + setter = "SetOverlayColor", + arg1 = id, + type = "color", + } + end + + return auraProperties; + else + return CopyTable(properties); + end +end + local spinnerFunctions = {}; function spinnerFunctions.SetTexture(self, texture) @@ -391,6 +425,7 @@ local function create(parent) local font = "GameFontHighlight"; local region = CreateFrame("FRAME", nil, parent); + region.regionType = "progresstexture" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); @@ -405,15 +440,30 @@ local function create(parent) region.foregroundSpinner = createSpinner(region, "ARTWORK", parent:GetFrameLevel() + 2); region.backgroundSpinner = createSpinner(region, "BACKGROUND", parent:GetFrameLevel() + 1); - region.values = {}; + region.extraTextures = {}; + region.extraSpinners = {}; + + -- Use a dummy object for the SmoothStatusBarMixin, because our SetValue + -- is used for a different purpose + region.smoothProgress = {}; + WeakAuras.Mixin(region.smoothProgress, SmoothStatusBarMixin); + region.smoothProgress.SetValue = function(self, progress) + region:SetValueOnTexture(progress); + end + + region.smoothProgress.GetValue = function(self) + return region.progress; + end + + region.smoothProgress.GetMinMaxValues = function(self) + return 0, 1; + end region.duration = 0; region.expirationTime = math.huge; WeakAuras.regionPrototype.create(region); - region.AnchorSubRegion = WeakAuras.regionPrototype.AnchorSubRegion - return region; end @@ -805,7 +855,11 @@ local function modify(parent, region, data) end progress = progress > 0.0001 and progress or 0.0001; - region:SetValueOnTexture(progress); + if (data.smoothProgress) then + region.smoothProgress:SetSmoothedValue(progress); + else + region:SetValueOnTexture(progress); + end end function region:SetValue(value, total) @@ -817,7 +871,11 @@ local function modify(parent, region, data) end end progress = progress > 0.0001 and progress or 0.0001; - region:SetValueOnTexture(progress); + if (data.smoothProgress) then + region.smoothProgress:SetSmoothedValue(progress); + else + region:SetValueOnTexture(progress); + end end function region:Update() @@ -825,7 +883,27 @@ local function modify(parent, region, data) local max if state.progressType == "timed" then - local expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + local expirationTime + if state.paused == true then + if not region.paused then + region:Pause() + end + if region.TimerTick then + region.TimerTick = nil + region:UpdateRegionHasTimerTick() + end + expirationTime = GetTime() + (state.remaining or 0) + else + if region.paused then + region:Resume() + end + if not region.TimerTick then + region.TimerTick = TimerTick + region:UpdateRegionHasTimerTick() + end + expirationTime = state.expirationTime and state.expirationTime > 0 and state.expirationTime or math.huge; + end + local duration = state.duration or 0 if region.adjustedMinRelPercent then region.adjustedMinRel = region.adjustedMinRelPercent * duration @@ -843,11 +921,11 @@ local function modify(parent, region, data) end region:SetTime(max - adjustMin, expirationTime - adjustMin, state.inverse); - if not region.TimerTick then - region.TimerTick = TimerTick - region:UpdateRegionHasTimerTick() - end elseif state.progressType == "static" then + if region.paused then + region:Resume() + end + local value = state.value or 0; local total = state.total or 0; if region.adjustedMinRelPercent then @@ -870,6 +948,9 @@ local function modify(parent, region, data) region:UpdateRegionHasTimerTick() end else + if region.paused then + region:Resume() + end region:SetTime(0, math.huge) if region.TimerTick then region.TimerTick = nil @@ -892,6 +973,14 @@ local function modify(parent, region, data) background:SetTexture(texture); backgroundSpinner:SetTexture(texture); end + + for _, extraTexture in ipairs(region.extraTextures) do + extraTexture:SetTexture(texture); + end + + for _, extraSpinner in ipairs(region.extraSpinners) do + extraSpinner:SetTexture(texture); + end end function region:SetForegroundDesaturated(b) @@ -929,7 +1018,21 @@ local function modify(parent, region, data) region:SetValueOnTexture(progress); end + function region:SetOverlayColor(id, r, g, b, a) + self.overlays[id] = { r, g, b, a}; + if (self.extraTextures[id]) then + self.extraTextures[id]:SetVertexColor(r, g, b, a); + end + if (self.extraSpinners[id]) then + self.extraSpinners[id]:Color(r, g, b, a); + end + end + WeakAuras.regionPrototype.modifyFinish(parent, region, data); end -WeakAuras.RegisterRegionType("progresstexture", create, modify, default, properties); +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + +WeakAuras.RegisterRegionType("progresstexture", create, modify, default, GetProperties, validate); diff --git a/WeakAuras/RegionTypes/RegionPrototype.lua b/WeakAuras/RegionTypes/RegionPrototype.lua index bdf37d7..f5f2933 100644 --- a/WeakAuras/RegionTypes/RegionPrototype.lua +++ b/WeakAuras/RegionTypes/RegionPrototype.lua @@ -118,11 +118,16 @@ local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ce function Private.GetAnchorsForData(parentData, type) local result if not parentData.controlledChildren then - if not WeakAuras.regionOptions[parentData.regionType] or not WeakAuras.regionOptions[parentData.regionType].getAnchors then + if not WeakAuras.regionOptions[parentData.regionType] then return end - local anchors = WeakAuras.regionOptions[parentData.regionType].getAnchors(parentData) + local anchors + if WeakAuras.regionOptions[parentData.regionType].getAnchors then + anchors = WeakAuras.regionOptions[parentData.regionType].getAnchors(parentData) + else + anchors = Private.default_types_for_anchor + end for anchorId, anchorData in pairs(anchors) do if anchorData.type == type then result = result or {} @@ -133,23 +138,6 @@ function Private.GetAnchorsForData(parentData, type) return result end -function WeakAuras.regionPrototype:AnchorSubRegion(subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) - subRegion:ClearAllPoints() - - if anchorType == "point" then - local xOffset = anchorXOffset or 0 - local yOffset = anchorYOffset or 0 - subRegion:SetPoint(Private.point_types[selfPoint] and selfPoint or "CENTER", - self, Private.point_types[anchorPoint] and anchorPoint or "CENTER", - xOffset, yOffset) - else - anchorXOffset = anchorXOffset or 0 - anchorYOffset = anchorYOffset or 0 - subRegion:SetPoint("bottomleft", self, "bottomleft", -anchorXOffset, -anchorYOffset) - subRegion:SetPoint("topright", self, "topright", anchorXOffset, anchorYOffset) - end -end - -- Sound / Chat Message / Custom Code function WeakAuras.regionPrototype.AddProperties(properties, defaultsForRegion) properties["sound"] = { @@ -259,8 +247,7 @@ local function SendChat(self, options) if (not options or WeakAuras.IsOptionsOpen()) then return end - - Private.HandleChatAction(options.message_type, options.message, options.message_dest, options.message_channel, options.r, options.g, options.b, self, options.message_custom, nil, options.message_formaters); + Private.HandleChatAction(options.message_type, options.message, options.message_dest, options.message_dest_isunit, options.message_channel, options.r, options.g, options.b, self, options.message_custom, nil, options.message_formaters); end local function RunCode(self, func) @@ -432,7 +419,7 @@ local function TimerTickForRegion(region) end local function UpdateTimerTick(self) - if self.triggerProvidesTimer and self.regionHasTimer then + if self.triggerProvidesTimer and self.regionHasTimer and self.toShow then if not self:GetScript("OnUpdate") then self:SetScript("OnUpdate", function() TimerTickForRegion(self) @@ -445,7 +432,27 @@ local function UpdateTimerTick(self) end end +local function AnchorSubRegion(self, subRegion, anchorType, selfPoint, anchorPoint, anchorXOffset, anchorYOffset) + subRegion:ClearAllPoints() + + if anchorType == "point" then + local xOffset = anchorXOffset or 0 + local yOffset = anchorYOffset or 0 + subRegion:SetPoint(Private.point_types[selfPoint] and selfPoint or "CENTER", + self, Private.point_types[anchorPoint] and anchorPoint or "CENTER", + xOffset, yOffset) + else + anchorXOffset = anchorXOffset or 0 + anchorYOffset = anchorYOffset or 0 + subRegion:SetPoint("bottomleft", self, "bottomleft", -anchorXOffset, -anchorYOffset) + subRegion:SetPoint("topright", self, "topright", anchorXOffset, anchorYOffset) + end +end + +WeakAuras.regionPrototype.AnchorSubRegion = AnchorSubRegion + function WeakAuras.regionPrototype.create(region) + local defaultsForRegion = WeakAuras.regionTypes[region.regionType] and WeakAuras.regionTypes[region.regionType].default; region.SoundPlay = SoundPlay; region.SoundRepeatStop = SoundRepeatStop; region.SendChat = SendChat; @@ -470,8 +477,10 @@ function WeakAuras.regionPrototype.create(region) region:RealClearAllPoints(); region:ResetPosition(); end - region.SetRegionAlpha = SetRegionAlpha; - region.GetRegionAlpha = GetRegionAlpha; + if (defaultsForRegion and defaultsForRegion.alpha) then + region.SetRegionAlpha = SetRegionAlpha; + region.GetRegionAlpha = GetRegionAlpha; + end region.SetAnimAlpha = SetAnimAlpha; region.SetTriggerProvidesTimer = SetTriggerProvidesTimer @@ -479,6 +488,8 @@ function WeakAuras.regionPrototype.create(region) region.UpdateTimerTick = UpdateTimerTick region.subRegionEvents = CreateSubRegionEventSystem() + region.AnchorSubRegion = AnchorSubRegion + region.values = {} -- For SubText region:SetPoint("CENTER", UIParent, "CENTER") end @@ -486,12 +497,12 @@ end -- SetDurationInfo function WeakAuras.regionPrototype.modify(parent, region, data) + region.state = nil + region.states = nil region.subRegionEvents:ClearSubscribers() local defaultsForRegion = WeakAuras.regionTypes[data.regionType] and WeakAuras.regionTypes[data.regionType].default; - if (defaultsForRegion and defaultsForRegion.alpha) then - region:SetRegionAlpha(data.alpha); - end + if region.SetRegionAlpha then region:SetRegionAlpha(data.alpha) end @@ -558,7 +569,7 @@ function WeakAuras.regionPrototype.modify(parent, region, data) data.actions.start[fullKey] = default end return data.actions.start[fullKey] - end) + end, true) region.finishFormatters = Private.CreateFormatters(data.actions.finish.message, function(key, default) local fullKey = "message_format_" .. key @@ -566,7 +577,8 @@ function WeakAuras.regionPrototype.modify(parent, region, data) data.actions.finish[fullKey] = default end return data.actions.finish[fullKey] - end) + end, true) + end function WeakAuras.regionPrototype.modifyFinish(parent, region, data) @@ -742,7 +754,20 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare Private.RunConditions(region, uid, true) region.subRegionEvents:Notify("PreHide") - region:Hide(); + if region:IsProtected() then + if InCombatLockdown() then + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "error", + L["Cannot hide secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], + true) + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", + L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) + region:Hide() + end + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame") + region:Hide() + end region.states = nil region.state = nil if (cloneId) then @@ -759,7 +784,22 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare end Private.RunConditions(region, uid, true) region.subRegionEvents:Notify("PreHide") - region:Hide(); + + if region:IsProtected() then + if InCombatLockdown() then + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "error", + L["Cannot hide secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], + true) + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", + L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) + region:Hide() + end + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame") + region:Hide() + end + region.states = nil region.state = nil if (cloneId) then @@ -786,6 +826,7 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare end UnRegisterForFrameTick(region) + region:UpdateTimerTick() end function region:Expand() if (region.toShow) then @@ -799,7 +840,21 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare region.subRegionEvents:Notify("PreShow") Private.ApplyFrameLevel(region) - region:Show(); + if region:IsProtected() then + if InCombatLockdown() then + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "error", + L["Cannot show secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], + true) + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", + L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) + region:Show() + end + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame") + region:Show() + end + Private.PerformActions(data, "start", region); if not(Private.Animate("display", data.uid, "start", data.animation.start, region, true, startMainAnimation, nil, cloneId)) then startMainAnimation(); @@ -831,6 +886,7 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare end UnRegisterForFrameTick(region) + region:UpdateTimerTick() end function region:Expand() if data.anchorFrameType == "SELECTFRAME" @@ -843,16 +899,30 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare if (region.toShow) then return; end - region.toShow = true; + region.toShow = true if(region.PreShow) then region:PreShow(); end region.subRegionEvents:Notify("PreShow") - Private.ApplyFrameLevel(region) - region:Show(); + + if region:IsProtected() then + if InCombatLockdown() then + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "error", + L["Cannot show secure frame in combat lockdown. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"], + true) + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame", "warning", + L["Secure frame detected. Find more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]) + region:Show() + end + else + Private.AuraWarnings.UpdateWarning(uid, "protected_frame") + region:Show() + end + Private.PerformActions(data, "start", region); if not(Private.Animate("display", data.uid, "start", data.animation.start, region, true, startMainAnimation, nil, cloneId)) then startMainAnimation(); @@ -873,4 +943,60 @@ function WeakAuras.regionPrototype.AddExpandFunction(data, region, cloneId, pare if not region.Expand then function region:Expand() end end + if not region.Pause then + function region:Pause() + self.paused = true + end + end + if not region.Resume then + function region:Resume() + self.paused = nil + end + end +end + +do + local function move_condition_subregions(data, offset, afterPos) + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + if type(condition.changes) == "table" then + for changeIndex, change in ipairs(condition.changes) do + if change.property then + local subRegionIndex, property = change.property:match("^sub%.(%d+)%.(.*)") + subRegionIndex = tonumber(subRegionIndex) + if subRegionIndex and property then + if (subRegionIndex >= afterPos) then + change.property = "sub." .. subRegionIndex + offset .. "." .. property + end + end + end + end + end + end + end + end + + function Private.EnforceSubregionExists(data, subregionType) + data.subRegions = data.subRegions or {} + -- search and save indexes of matching subregions + local indexes = {} + for index, subRegionData in ipairs(data.subRegions) do + if subRegionData.type == subregionType then + table.insert(indexes, index) + end + end + -- add if missing + if #indexes == 0 then + tinsert(data.subRegions, 1, { + ["type"] = subregionType + }) + move_condition_subregions(data, 1, 1) + -- delete duplicate + elseif #indexes > 1 then + for i = #indexes, 2, -1 do + table.remove(data.subRegions, indexes[i]) + move_condition_subregions(data, -1, indexes[i]) + end + end + end end diff --git a/WeakAuras/RegionTypes/StopMotion.lua b/WeakAuras/RegionTypes/StopMotion.lua index a6a49c3..5863394 100644 --- a/WeakAuras/RegionTypes/StopMotion.lua +++ b/WeakAuras/RegionTypes/StopMotion.lua @@ -37,6 +37,10 @@ local default = { customForegroundRows = 16, customForegroundColumns = 16, customBackgroundFrames = 0, + customForegroundFileWidth = 0, + customForegroundFileHeight = 0, + customForegroundFrameWidth = 0, + customForegroundFrameHeight = 0, customBackgroundRows = 16, customBackgroundColumns = 16, hideBackground = true @@ -87,6 +91,7 @@ WeakAuras.regionPrototype.AddProperties(properties, default); local function create(parent) local frame = CreateFrame("FRAME", nil, UIParent); + frame.regionType = "stopmotion" frame:SetMovable(true); frame:SetResizable(true); frame:SetMinResize(1, 1); @@ -108,27 +113,34 @@ local function SetTextureViaAtlas(self, texture) self:SetTexture(texture); end -local function setTile(texture, frame, rows, columns ) +local function setTile(texture, frame, rows, columns, frameScaleW, frameScaleH) frame = frame - 1; local row = floor(frame / columns); local column = frame % columns; - local deltaX = 1 / columns; - local deltaY = 1 / rows; + local deltaX = frameScaleW / columns + local deltaY = frameScaleH / rows local left = deltaX * column; local right = left + deltaX; local top = deltaY * row; local bottom = top + deltaY; - - texture:SetTexCoord(left, right, top, bottom); + pcall(function() texture:SetTexCoord(left, right, top, bottom) end) end WeakAuras.setTile = setTile; local function SetFrameViaAtlas(self, texture, frame) - setTile(self, frame, self.rows, self.columns); + local frameScaleW = 1 + local frameScaleH = 1 + if self.fileWidth and self.frameWidth and self.fileWidth > 0 and self.frameWidth > 0 then + frameScaleW = (self.frameWidth * self.columns) / self.fileWidth + end + if self.fileHeight and self.frameHeight and self.fileHeight > 0 and self.frameHeight > 0 then + frameScaleH = (self.frameHeight * self.rows) / self.fileHeight + end + setTile(self, frame, self.rows, self.columns, frameScaleW, frameScaleH); end local function SetTextureViaFrames(self, texture) @@ -142,29 +154,63 @@ end local function modify(parent, region, data) WeakAuras.regionPrototype.modify(parent, region, data); - + region.foreground = region.foreground or {} + region.background = region.background or {} local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]" - local tdata = texture_data[data.foregroundTexture]; - if (tdata) then - local lastFrame = tdata.count - 1; - region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; - region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foreground.rows = tdata.rows; - region.foreground.columns = tdata.columns; - else - local rows, columns, frames = data.foregroundTexture:lower():match(pattern) - if rows and columns and frames then - local lastFrame = frames - 1; + local pattern2 = "%.x(%d+)y(%d+)f(%d+)w(%d+)h(%d+)W(%d+)H(%d+)%.[tb][gl][ap]" + + do + local tdata = texture_data[data.foregroundTexture]; + if (tdata) then + local lastFrame = tdata.count - 1; + region.foreground.lastFrame = lastFrame region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foreground.rows = rows; - region.foreground.columns = columns; + region.foreground.rows = tdata.rows; + region.foreground.columns = tdata.columns; + region.foreground.fileWidth = 0 + region.foreground.fileHeight = 0 + region.foreground.frameWidth = 0 + region.foreground.frameHeight = 0 else - local lastFrame = (data.customForegroundFrames or 256) - 1; - region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; - region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foreground.rows = data.customForegroundRows; - region.foreground.columns = data.customForegroundColumns; + local rows, columns, frames = data.foregroundTexture:lower():match(pattern) + if rows then + local lastFrame = tonumber(frames) - 1; + region.foreground.lastFrame = lastFrame + region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; + region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; + region.foreground.rows = tonumber(rows) + region.foreground.columns = tonumber(columns) + region.foreground.fileWidth = 0 + region.foreground.fileHeight = 0 + region.foreground.frameWidth = 0 + region.foreground.frameHeight = 0 + else + local rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight = data.foregroundTexture:match(pattern2) + if rows then + local lastFrame = tonumber(frames) - 1; + region.foreground.lastFrame = lastFrame + region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; + region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; + region.foreground.rows = tonumber(rows) + region.foreground.columns = tonumber(columns) + region.foreground.fileWidth = tonumber(fileWidth) + region.foreground.fileHeight = tonumber(fileHeight) + region.foreground.frameWidth = tonumber(frameWidth) + region.foreground.frameHeight = tonumber(frameHeight) + else + local lastFrame = (data.customForegroundFrames or 256) - 1; + region.foreground.lastFrame = lastFrame + region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; + region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; + region.foreground.rows = data.customForegroundRows; + region.foreground.columns = data.customForegroundColumns; + region.foreground.fileWidth = data.customForegroundFileWidth + region.foreground.fileHeight = data.customForegroundFileHeight + region.foreground.frameWidth = data.customForegroundFrameWidth + region.foreground.frameHeight = data.customForegroundFrameHeight + end + end end end @@ -172,27 +218,60 @@ local function modify(parent, region, data) and data.foregroundTexture or data.backgroundTexture; - local tbdata = texture_data[backgroundTexture]; - if (tbdata) then - local lastFrame = tbdata.count - 1; - region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); - region.background.rows = tbdata.rows; - region.background.columns = tbdata.columns; - else - local rows, columns, frames = backgroundTexture:lower():match(pattern) - if rows and columns and frames then - local lastFrame = frames - 1; - region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); - region.background.rows = rows; - region.background.columns = columns; + do + if data.sameTexture then + region.backgroundFrame = floor( (data.backgroundPercent or 1) * region.foreground.lastFrame + 1); + region.background.rows = region.foreground.rows + region.background.columns = region.foreground.columns + region.background.fileWidth = region.foreground.fileWidth + region.background.fileHeight = region.foreground.fileHeight + region.background.frameWidth = region.foreground.frameWidth + region.background.frameHeight = region.foreground.frameHeight else - local lastFrame = (data.sameTexture and data.customForegroundFrames - or data.customBackgroundFrames or 256) - 1; - region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); - region.background.rows = data.sameTexture and data.customForegroundRows - or data.customBackgroundRows; - region.background.columns = data.sameTexture and data.customForegroundColumns - or data.customBackgroundColumns; + local tdata = texture_data[data.backgroundTexture]; + if (tdata) then + local lastFrame = tdata.count - 1; + region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); + region.background.rows = tdata.rows; + region.background.columns = tdata.columns; + region.background.fileWidth = 0 + region.background.fileHeight = 0 + region.background.frameWidth = 0 + region.background.frameHeight = 0 + else + local rows, columns, frames = data.backgroundTexture:lower():match(pattern) + if rows then + local lastFrame = frames - 1; + region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); + region.background.rows = tonumber(rows) + region.background.columns = tonumber(columns) + region.background.fileWidth = 0 + region.background.fileHeight = 0 + region.background.frameWidth = 0 + region.background.frameHeight = 0 + else + local rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight = data.backgroundTexture:match(pattern2) + if rows then + local lastFrame = frames - 1; + region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); + region.background.rows = tonumber(rows) + region.background.columns = tonumber(columns) + region.background.fileWidth = tonumber(fileWidth) + region.background.fileHeight = tonumber(fileHeight) + region.background.frameWidth = tonumber(frameWidth) + region.background.frameHeight = tonumber(frameHeight) + else + local lastFrame = (data.customBackgroundFrames or 256) - 1; + region.backgroundFrame = floor( (data.backgroundPercent or 1) * lastFrame + 1); + region.background.rows = data.customBackgroundRows; + region.background.columns = data.customBackgroundColumns; + region.background.fileWidth = data.customBackgroundFileWidth + region.background.fileHeight = data.customBackgroundFileHeight + region.background.frameWidth = data.customBackgroundFrameWidth + region.background.frameHeight = data.customBackgroundFrameHeight + end + end + end end end @@ -339,7 +418,12 @@ local function modify(parent, region, data) local total = region.state.total ~= 0 and region.state.total or 1 frame = floor((frames - 1) * value / total) + startFrame; else - local remaining = region.state.expirationTime and (region.state.expirationTime - GetTime()) or 0; + local remaining + if region.state.paused then + remaining = region.state.remaining or 0; + else + remaining = region.state.expirationTime and (region.state.expirationTime - GetTime()) or 0; + end local progress = region.state.duration and region.state.duration > 0 and (1 - (remaining / region.state.duration)) or 0; frame = floor( (frames - 1) * progress) + startFrame; end @@ -364,6 +448,15 @@ local function modify(parent, region, data) region.FrameTick = onUpdate; function region:Update() + if region.state.paused then + if not region.paused then + region:Pause() + end + else + if region.paused then + region:Resume() + end + end onUpdate(); end @@ -390,4 +483,8 @@ local function modify(parent, region, data) end end -WeakAuras.RegisterRegionType("stopmotion", create, modify, default, properties); +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + +WeakAuras.RegisterRegionType("stopmotion", create, modify, default, properties, validate); diff --git a/WeakAuras/RegionTypes/Text.lua b/WeakAuras/RegionTypes/Text.lua index 561918a..779be35 100644 --- a/WeakAuras/RegionTypes/Text.lua +++ b/WeakAuras/RegionTypes/Text.lua @@ -27,7 +27,7 @@ local default = { shadowColor = { 0, 0, 0, 1}, shadowXOffset = 1, - shadowYOffset = -1, + shadowYOffset = -1 }; local properties = { @@ -55,6 +55,7 @@ end local function create(parent) local region = CreateFrame("FRAME", nil, parent); + region.regionType = "text" region:SetMovable(true); local text = region:CreateFontString(nil, "OVERLAY"); @@ -62,7 +63,6 @@ local function create(parent) text:SetWordWrap(true); text:SetNonSpaceWrap(true); - region.values = {}; region.duration = 0; region.expirationTime = math.huge; @@ -268,7 +268,11 @@ local function modify(parent, region, data) WeakAuras.regionPrototype.modifyFinish(parent, region, data); end -WeakAuras.RegisterRegionType("text", create, modify, default, GetProperties); +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + +WeakAuras.RegisterRegionType("text", create, modify, default, GetProperties, validate); -- Fallback region type diff --git a/WeakAuras/RegionTypes/Texture.lua b/WeakAuras/RegionTypes/Texture.lua index ac2f834..4bb3e38 100644 --- a/WeakAuras/RegionTypes/Texture.lua +++ b/WeakAuras/RegionTypes/Texture.lua @@ -69,6 +69,7 @@ WeakAuras.regionPrototype.AddProperties(properties, default); local function create(parent) local region = CreateFrame("FRAME", nil, UIParent); + region.regionType = "texture" region:SetMovable(true); region:SetResizable(true); region:SetMinResize(1, 1); @@ -78,9 +79,6 @@ local function create(parent) texture:SetAllPoints(region); WeakAuras.regionPrototype.create(region); - region.values = {}; - - region.AnchorSubRegion = WeakAuras.regionPrototype.AnchorSubRegion return region; end @@ -237,4 +235,8 @@ local function modify(parent, region, data) WeakAuras.regionPrototype.modifyFinish(parent, region, data); end -WeakAuras.RegisterRegionType("texture", create, modify, default, properties); +local function validate(data) + Private.EnforceSubregionExists(data, "subbackground") +end + +WeakAuras.RegisterRegionType("texture", create, modify, default, properties, validate); diff --git a/WeakAuras/SubRegionTypes/Background.lua b/WeakAuras/SubRegionTypes/Background.lua new file mode 100644 index 0000000..3320c40 --- /dev/null +++ b/WeakAuras/SubRegionTypes/Background.lua @@ -0,0 +1,56 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, Private = ... +local L = WeakAuras.L; + +do + local function subSupports(regionType) + return regionType ~= "group" and regionType ~= "dynamicgroup" + end + + local function noop() + end + + local function subSetFrameLevel(self, level) + self.parent:SetFrameLevel(level) + end + + local function subCreate() + return { Update = noop, SetFrameLevel = subSetFrameLevel} + end + + local function subModify(parent, region) + region.parent = parent + end + + WeakAuras.RegisterSubRegionType("subbackground", L["Background"], subSupports, subCreate, subModify, noop, noop, {}, nil, {}, false); +end + +-- Foreground for aurabar + +do + local function subSupports(regionType) + return regionType == "aurabar" + end + + local function noop() + end + + local function subSetFrameLevel(self, level) + if self.parent.bar then + self.parent.bar:SetFrameLevel(level) + end + if self.parent.iconFrame then + self.parent.iconFrame:SetFrameLevel(level) + end + end + + local function subCreate() + return { Update = noop, SetFrameLevel = subSetFrameLevel} + end + + local function subModify(parent, region) + region.parent = parent + end + + WeakAuras.RegisterSubRegionType("subforeground", L["Foreground"], subSupports, subCreate, subModify, noop, noop, {}, nil, {}, false); +end diff --git a/WeakAuras/SubRegionTypes/Glow.lua b/WeakAuras/SubRegionTypes/Glow.lua index 5f07dc8..8c93d4b 100644 --- a/WeakAuras/SubRegionTypes/Glow.lua +++ b/WeakAuras/SubRegionTypes/Glow.lua @@ -177,18 +177,17 @@ local funcs = { if MSQ and self.parentType == "icon" then if (visible) then self.__MSQ_Shape = self:GetParent().button.__MSQ_Shape + self:Show() glowStart(self, self, color); else self.glowStop(self); + self:Hide() end elseif (visible) then + self:Show() glowStart(self, self, color); else self.glowStop(self); - end - if visible then - self:Show() - else self:Hide() end end, diff --git a/WeakAuras/SubRegionTypes/BarModel.lua b/WeakAuras/SubRegionTypes/Model.lua similarity index 74% rename from WeakAuras/SubRegionTypes/BarModel.lua rename to WeakAuras/SubRegionTypes/Model.lua index 84e2c7a..21eb44e 100644 --- a/WeakAuras/SubRegionTypes/BarModel.lua +++ b/WeakAuras/SubRegionTypes/Model.lua @@ -8,8 +8,9 @@ Private.barmodels = {} local default = function(parentType) return { - bar_model_visible = true, - bar_model_alpha = 1, + model_visible = true, + model_alpha = 1, + model_x = 0, model_y = 0, model_z = 0, @@ -21,13 +22,13 @@ local default = function(parentType) end local properties = { - bar_model_visible = { + model_visible = { display = L["Visibility"], setter = "SetVisible", type = "bool", defaultProperty = true }, - bar_model_alpha = { + model_alpha = { display = L["Alpha"], setter = "SetAlpha", type = "number", @@ -66,11 +67,22 @@ local function AcquireModel(region, data) model:ClearAllPoints() + local anchor if region.parentType == "aurabar" then - model:SetAllPoints(region.parent.bar) + anchor = region.parent.bar else - model:SetAllPoints(region.parent) + anchor = region.parent end + + local extra_width, extra_height = 0, 0 + if not(data.bar_model_clip and region.parentType == "aurabar") then + extra_width = data.extra_width or 0 + extra_height = data.extra_height or 0 + end + + model:SetPoint("TOPLEFT", anchor ,"TOPLEFT", -extra_width/2, extra_height/2) + model:SetPoint("BOTTOMRIGHT", anchor ,"BOTTOMRIGHT", extra_width/2, -extra_height/2) + model:SetParent(region) --model:SetKeepModelOnHide(true) model:Show() @@ -162,6 +174,8 @@ local function onRelease(subRegion) subRegion:Hide() end + + local function modify(parent, region, parentData, data, first) if region.model then ReleaseModel(region.model) @@ -174,18 +188,28 @@ local function modify(parent, region, parentData, data, first) region:SetParent(parent) + local anchor if parentData.regionType == "aurabar" then if data.bar_model_clip then - region:SetAllPoints(parent.bar.fgFrame) + anchor = parent.bar.fgFrame else - region:SetAllPoints(parent.bar) + anchor = parent.bar end else - region:SetAllPoints(parent) + anchor = parent end - region:SetAlpha(data.bar_model_alpha) - region:SetVisible(data.bar_model_visible) + local extra_width, extra_height = 0, 0 + if not(data.bar_model_clip and parentData.regionType == "aurabar") then + extra_width = data.extra_width or 0 + extra_height = data.extra_height or 0 + end + + region:SetPoint("TOPLEFT", anchor ,"TOPLEFT", -extra_width/2, extra_height/2) + region:SetPoint("BOTTOMRIGHT", anchor ,"BOTTOMRIGHT", extra_width/2, -extra_height/2) + + region:SetAlpha(data.model_alpha) + region:SetVisible(data.model_visible) parent.subRegionEvents:AddSubscriber("AlphaChanged", region) parent.subRegionEvents:AddSubscriber("PreShow", region) @@ -193,7 +217,11 @@ local function modify(parent, region, parentData, data, first) end local function supports(regionType) - return regionType == "aurabar" or regionType == "icon" + return regionType == "texture" + or regionType == "progresstexture" + or regionType == "icon" + or regionType == "aurabar" + or regionType == "text" end -WeakAuras.RegisterSubRegionType("subbarmodel", L["Model"], supports, create, modify, onAcquire, onRelease, default, nil, properties); +WeakAuras.RegisterSubRegionType("submodel", L["Model"], supports, create, modify, onAcquire, onRelease, default, nil, properties); diff --git a/WeakAuras/SubRegionTypes/Tick.lua b/WeakAuras/SubRegionTypes/Tick.lua index dddaca3..8aaae83 100644 --- a/WeakAuras/SubRegionTypes/Tick.lua +++ b/WeakAuras/SubRegionTypes/Tick.lua @@ -197,7 +197,7 @@ local funcs = { end end, UpdateTimerTick = function(self) - if self.tick_placement_mode == "ValueOffset" and self.state and self.state.progressType == "timed" then + if self.tick_placement_mode == "ValueOffset" and self.state and self.state.progressType == "timed" and not self.paused then if not self.TimerTick then self.TimerTick = self.UpdateTickPlacement self.parent:UpdateRegionHasTimerTick() @@ -237,7 +237,13 @@ local funcs = { elseif self.tick_placement_mode == "ValueOffset" then if self.trigger_total and self.trigger_total ~= 0 then if self.state.progressType == "timed" then - tick_placement = self.state.expirationTime - GetTime() + self.tick_placement + if self.state.paused then + if self.state.remaining then + tick_placement = self.state.remaining + self.tick_placement + end + else + tick_placement = self.state.expirationTime - GetTime() + self.tick_placement + end else tick_placement = self.state.value + self.tick_placement end diff --git a/WeakAuras/Transmission.lua b/WeakAuras/Transmission.lua index 622b41a..761689e 100644 --- a/WeakAuras/Transmission.lua +++ b/WeakAuras/Transmission.lua @@ -2,7 +2,7 @@ This file contains all transmission related functionality, e.g. import/export and chat links. For that it hooks into the chat frame and addon message channels. -This file adds the following API to the WeakAuras object: +Noteable functions in this file are: DisplayToString(id, forChat) Converts the display id to a plain text string @@ -13,9 +13,6 @@ Converts the display id to a formatted table SerializeTable(data) Converts the table data to a formatted table -ShowDisplayTooltip(data, children, icon, icons, import, compressed) -Shows a tooltip frame for an aura, which allows for importing if import is true - Import(str, [target]) Imports an aura from a table, which may or may not be encoded as a B64 string. If target is installed data, or is a uid which points to installed data, then the import will be an update to that aura @@ -117,7 +114,7 @@ local function stripNonTransmissableFields(datum, fieldMap) end end -function CompressDisplay(data) +function CompressDisplay(data, version) -- Clean up custom trigger fields that are unused -- Those can contain lots of unnecessary data. -- Also we warn about any custom code, so removing unnecessary @@ -139,7 +136,8 @@ function CompressDisplay(data) end local copiedData = CopyTable(data) - stripNonTransmissableFields(copiedData, Private.non_transmissable_fields) + local non_transmissable_fields = version >= 2000 and Private.non_transmissable_fields_v2000 or Private.non_transmissable_fields + stripNonTransmissableFields(copiedData, non_transmissable_fields) copiedData.tocversion = WeakAuras.BuildInfo return copiedData; end @@ -153,12 +151,12 @@ local function filterFunc(_, event, msg, player, l, cs, t, flag, channelId, ...) local remaining = msg; local done; repeat - local start, finish, characterName, displayName = remaining:find("%[WeakAuras: ([^%s]+) %- (.+)%]"); + local start, finish, characterName, displayName = remaining:find("%[WeakAuras: ([^%s]+) %- (.*)%]"); if(characterName and displayName) then characterName = characterName:gsub("|c[Ff][Ff]......", ""):gsub("|r", ""); displayName = displayName:gsub("|c[Ff][Ff]......", ""):gsub("|r", ""); newMsg = newMsg..remaining:sub(1, start-1); - newMsg = newMsg.."|Hgarrmission:weakauras|h|cFF8800FF["..characterName.." |r|cFF8800FF- "..displayName.."]|h|r"; + newMsg = newMsg.."|HBNplayer::weakauras|h|cFF8800FF["..characterName.." |r|cFF8800FF- "..displayName.."]|h|r"; remaining = remaining:sub(finish + 1); else done = true; @@ -199,405 +197,6 @@ ChatFrame_AddMessageEventFilter("CHAT_MSG_BN_WHISPER_INFORM", filterFunc) ChatFrame_AddMessageEventFilter("CHAT_MSG_BATTLEGROUND", filterFunc) ChatFrame_AddMessageEventFilter("CHAT_MSG_BATTLEGROUND_LEADER", filterFunc) -local buttonAnchor = CreateFrame("FRAME", "WeakAurasTooltipAnchor", ItemRefTooltip) -buttonAnchor:SetAllPoints() -buttonAnchor:Hide() -buttonAnchor:RegisterEvent("PLAYER_REGEN_ENABLED") -buttonAnchor:RegisterEvent("PLAYER_REGEN_DISABLED") - -local thumbnailAnchor = CreateFrame("FRAME", "WeakAurasTooltipThumbnailFrame", buttonAnchor) -thumbnailAnchor:SetSize(40,40) -thumbnailAnchor:SetPoint("TOPRIGHT", -27, -8) - -local importButton = CreateFrame("BUTTON", "WeakAurasTooltipImportButton", buttonAnchor, "UIPanelButtonTemplate") -importButton:SetPoint("BOTTOMRIGHT", -8, 8) -importButton:SetSize(90, 22) - - -local showCodeButton = CreateFrame("BUTTON", "WeakAurasTooltipShowCodeButton", buttonAnchor, "UIPanelButtonTemplate") -showCodeButton:SetPoint("BOTTOMLEFT", 8, 8) -showCodeButton:SetText(L["Show Code"]) -showCodeButton:SetSize(90, 22) - -local checkButtons, radioButtons, keyToButton, pendingData = {}, {}, {}, {} - -for _, key in pairs(Private.internal_fields) do - keyToButton[key] = false -end - -for index,category in pairs(Private.update_categories) do - local button = CreateFrame("checkButton", "WeakAurasTooltipCheckButton"..index, buttonAnchor, "ChatConfigCheckButtonTemplate") - for k, v in pairs(category) do - button[k] = v - end - button.text = _G[button:GetName().."Text"] - local point, relativeFrame, relativePoint, xOff, yOff = button.text:GetPoint(1) - xOff = xOff + 4 - yOff = yOff - 2 - button.text:SetPoint(point, relativeFrame, relativePoint, xOff, yOff) - button.text:SetText(button.label) - button.text:SetTextColor(1, 0.82, 0) - button.func = function() - if button:GetChecked() then - pendingData.buttonsChecked = min(pendingData.buttonsChecked + 1, #checkButtons) - else - pendingData.buttonsChecked = max(pendingData.buttonsChecked - 1, 0) - end - radioButtons[#radioButtons]:Click() - end - checkButtons[index] = button - checkButtons[button.name] = button - for _, key in pairs(category.fields) do - keyToButton[key] = button - end -end - -radioButtons = { - CreateFrame("CHECKBUTTON", "WeakAurasTooltipRadioButtonCopy", buttonAnchor, "UIRadioButtonTemplate"), - -- CreateFrame("CHECKBUTTON", "WeakAurasTooltipRadioButtonReplace", buttonAnchor, "UIRadioButtonTemplate"), - CreateFrame("CHECKBUTTON", "WeakAurasTooltipRadioButtonUpdate", buttonAnchor, "UIRadioButtonTemplate"), -} - -local radioButtonLabels = { - L["Create a Copy"], - L["Update Auras"] -} - -for index, button in ipairs(radioButtons) do - button.text = _G[button:GetName().."Text"] - button.text:SetFontObject("GameFontNormal") - button.text:SetText(radioButtonLabels[index]) - button:SetScript("OnClick", function(self) - for _, button in ipairs(radioButtons) do - button:SetChecked(button == self) - end - pendingData.mode = index - Private.RefreshTooltipButtons() - end) -end - --- Ideally, we would not add an __index method to this --- But that would greatly bloat the update_category for display --- A better solution would be to refactor the data --- Such that all of the display tab settings are in their own subtable -setmetatable(keyToButton,{__index = function(_, key) return key and checkButtons.display end,}) - -local deleted = {} -- magic value -local function recurseUpdate(data, chunk) - for k,v in pairs(chunk) do - if v == deleted then - data[k] = nil - elseif type(v) == 'table' and type(data[k]) == 'table' then - recurseUpdate(data[k], v) - else - data[k] = v - end - end -end - -local function Update(data, diff) - -- modifies the installed data such that it matches the pending import, sans user-specified options - -- diff is expected to be output generated by diff - if not diff then - WeakAuras.Add(data) - return - end - if not data then - return - end - recurseUpdate(data, diff) - WeakAuras.Add(data) - return data -end - -local function install(data, oldData, patch, mode, isParent) - -- munch the provided data and add, update, or delete as appropriate - -- return the data which the SV knows about afterwards (if there is any) - local installedUID, imported - if mode == 1 then - -- always import as a new thing, set preferToUpdate = false - if data then - imported = CopyTable(data) - data.id = WeakAuras.FindUnusedId(data.id) - WeakAuras.Add(data) - installedUID = data.uid - data.preferToUpdate = false - if oldData then - oldData.preferToUpdate = false - end - else - -- nothing to add - return - end - elseif not oldData then - -- this is a new thing - if not checkButtons.newchildren:GetChecked() then - -- user has chosen to not add new children, so do nothing - return - end - imported = CopyTable(data) - data.id = WeakAuras.FindUnusedId(data.id) - data.preferToUpdate = true - WeakAuras.Add(data) - installedUID = data.uid - elseif not data then - -- this is an old thing - if checkButtons.oldchildren:GetChecked() then - WeakAuras.Delete(oldData) - return - else - -- user has chosen to not delete obsolete auras, so do nothing - return Private.GetDataByUID(oldData.uid) - end - else - -- something to update - if patch then -- if there's no result from Diff, then there's nothing to do - for key in pairs(patch) do - local checkButton = keyToButton[key] - if not isParent and checkButton == checkButtons.anchor then - checkButton = checkButtons.arrangement - end - if checkButton and not checkButton:GetChecked() then - patch[key] = nil - end - end - if patch.id then - patch.id = WeakAuras.FindUnusedId(patch.id) - end - end - WeakAuras.Delete(oldData) - if data.uid and data.uid ~= oldData.uid then - oldData.uid = data.uid - end - Update(oldData, patch) - oldData.preferToUpdate = true - installedUID = oldData.uid - imported = data - end - -- if at this point, then some change has been made in the db. Update History to reflect the change - Private.SetHistory(installedUID, imported, "import") - return Private.GetDataByUID(installedUID) -end - -local function importPendingData() - -- get necessary info - local imports, old, diffs = pendingData.newData, pendingData.oldData or {}, pendingData.diffs or {} - local addedChildren, deletedChildren = pendingData.added or 0, pendingData.deleted or 0 - local mode = pendingData.mode or 1 - local indexMap = pendingData.indexMap - - -- cleanup the mess - ItemRefTooltip:Hide()-- this also wipes pendingData via the hook on L521 - buttonAnchor:Hide() - if thumbnailAnchor.currentThumbnail then - regionOptions[thumbnailAnchor.currentThumbnailType].releaseThumbnail(thumbnailAnchor.currentThumbnail) - thumbnailAnchor.currentThumbnail = nil - end - if imports and Private.LoadOptions() then - if not WeakAuras.IsOptionsOpen() then - WeakAuras.OpenOptions() - end - else - return - end - WeakAuras.CloseImportExport() - WeakAuras.SetImporting(true) - - -- import parent/single aura - local data, oldData, patch = imports[0], old[0], diffs[0] - data.parent = nil - -- handle sortHybridTable - local hybridTables - if (data and data.sortHybridTable) or (mode ~= 1 and oldData and oldData.sortHybridTable) then - hybridTables = { - new = data and data.sortHybridTable or {}, - old = mode ~= 1 and oldData and oldData.sortHybridTable or {}, - merged = {}, - } - end - if data then - data.authorMode = nil - end - if oldData then - oldData.authorMode = nil - end - local installedData = {[0] = install(data, oldData, patch, mode, true)} - WeakAuras.NewDisplayButton(installedData[0]) - coroutine.yield() - - -- handle children, if there are any - if #imports > 0 then - local parentData = installedData[0] - local preserveOldArrangement = mode ~= 1 and not checkButtons.arrangement:GetChecked() - - local map - if not indexMap then - map = {} - else - map = preserveOldArrangement and indexMap.oldToNew or indexMap.newToOld - end - for i = 1, preserveOldArrangement and #old or #imports do - local j = map[i] - if preserveOldArrangement then - oldData = old[i] - if j then - data, patch = imports[j], diffs[j] - else - data, patch = nil, nil - end - else - data, patch = imports[i], diffs[i] - if j then - oldData = old[j] - else - oldData = nil - end - end - local oldID, newID = oldData and oldData.id, data and data.id - if oldData and mode ~= 1 then - oldData.parent = parentData.id - end - if data then - data.parent = parentData.id - data.authorMode = nil - end - if oldData then - oldData.authorMode = nil - end - local childData = install(data, oldData, patch, mode) - if childData then - tinsert(installedData, childData) - if hybridTables then - if preserveOldArrangement then - hybridTables.merged[childData.id] = oldID and hybridTables.old[oldID] - else - hybridTables.merged[childData.id] = newID and hybridTables.new[newID] - end - end - end - coroutine.yield() - end - - -- now, the other side of the data may have some children which need to be handled - if mode ~= 1 then - if preserveOldArrangement then - if checkButtons.newchildren:GetChecked() and addedChildren > 0 then - -- we have children which need to be added - local nextInsert, highestInsert, toInsert, newToOld = 1, 0, {}, indexMap.newToOld - for newIndex, data in ipairs(imports) do - local oldIndex = newToOld[newIndex] - if oldIndex then - nextInsert = oldIndex + 1 - else - if nextInsert > highestInsert then - highestInsert = nextInsert - end - toInsert[nextInsert] = toInsert[nextInsert] or {} - tinsert(toInsert[nextInsert], data) - end - coroutine.yield() - end - for index = highestInsert, 1, -1 do -- go backwards so that tinsert doesn't screw things up - if toInsert[index] then - for i = #toInsert[index], 1, -1 do - local data = toInsert[index][i] - local id = data.id - data.id = WeakAuras.FindUnusedId(id) - data.parent = parentData.id; - WeakAuras.Add(data) - tinsert(installedData, index, data) - if hybridTables and hybridTables.new[id] then - hybridTables.merged[data.id] = true - end - coroutine.yield() - end - end - end - end - elseif deletedChildren > 0 then - if not checkButtons.oldchildren:GetChecked() then - -- we have existing children which need migrating - -- same algorithm as before, but mirror image - local nextInsert, highestInsert, toInsert, oldToNew = 1, 0, {}, indexMap.oldToNew - for oldIndex, data in ipairs(old) do - local newIndex = oldToNew[oldIndex] - if newIndex then - nextInsert = newIndex + 1 - else - if nextInsert > highestInsert then - highestInsert = nextInsert - end - toInsert[nextInsert] = toInsert[nextInsert] or {} - tinsert(toInsert[nextInsert], data) - end - end - coroutine.yield() - for index = highestInsert, 1, -1 do -- go backwards so that tinsert doesn't screw things up - if toInsert[index] then - for i = #toInsert[index], 1, -1 do - local data = toInsert[index][i] - if hybridTables and hybridTables.old[data.id] then - hybridTables.merged[data.id] = true - end - tinsert(installedData, index, data) - end - end - end - coroutine.yield() - else-- we have children which need deleting - local oldToNew = indexMap.oldToNew - for oldIndex, oldData in ipairs(old) do - if not oldToNew[oldIndex] and WeakAuras.GetData(oldData.id) then - WeakAuras.Delete(oldData) - coroutine.yield() - end - end - end - end - end - - parentData.controlledChildren = {} - parentData.sortHybridTable = hybridTables and hybridTables.merged - for index, installedChild in ipairs(installedData) do - installedChild.parent = parentData.id - tinsert(parentData.controlledChildren, installedChild.id) - WeakAuras.NewDisplayButton(installedChild) - local childButton = WeakAuras.GetDisplayButton(installedChild.id) - childButton:SetGroup(parentData.id, parentData.regionType == "dynamicgroup") - childButton:SetGroupOrder(index, #parentData.controlledChildren) - coroutine.yield() - end - - WeakAuras.Add(parentData); - local button = WeakAuras.GetDisplayButton(parentData.id) - button.callbacks.UpdateExpandButton() - WeakAuras.UpdateGroupOrders(parentData) - WeakAuras.UpdateDisplayButton(parentData) - WeakAuras.ClearAndUpdateOptions(parentData.id) - WeakAuras.SortDisplayButtons() - end - WeakAuras.SetImporting(false) - return WeakAuras.PickDisplay(installedData[0].id) -end - -ItemRefTooltip:HookScript("OnHide", function(self) - buttonAnchor:Hide() - wipe(pendingData) - if (ItemRefTooltip.WeakAuras_Desc_Box) then - ItemRefTooltip.WeakAuras_Desc_Box:Hide(); - end -end) - -importButton:SetScript("OnClick", function() - Private.dynFrame:AddAction("import", coroutine.create(importPendingData)) -end) - -showCodeButton:SetScript("OnClick", function() - WeakAuras.OpenOptions() - WeakAuras.OpenCodeReview(pendingData.codes) -end) - - local Compresser = LibStub:GetLibrary("LibCompress") local LibDeflate = LibStub:GetLibrary("LibDeflate") local Serializer = LibStub:GetLibrary("AceSerializer-3.0") @@ -612,9 +211,8 @@ local tooltipLoading; local receivedData; hooksecurefunc("SetItemRef", function(link, text) - buttonAnchor:Hide() - if(link == "garrmission:weakauras") then - local _, _, characterName, displayName = text:find("|Hgarrmission:weakauras|h|cFF8800FF%[([^%s]+) |r|cFF8800FF%- (.+)%]|h"); + if(link == "BNplayer::weakauras") then + local _, _, characterName, displayName = text:find("|HBNplayer::weakauras|h|cFF8800FF%[([^%s]+) |r|cFF8800FF%- ([^%]]+)%]|h"); if(characterName and displayName) then characterName = characterName:gsub("|c[Ff][Ff]......", ""):gsub("|r", ""); displayName = displayName:gsub("|c[Ff][Ff]......", ""):gsub("|r", ""); @@ -652,14 +250,6 @@ hooksecurefunc("SetItemRef", function(link, text) end end); -local OriginalSetHyperlink = ItemRefTooltip.SetHyperlink -function ItemRefTooltip:SetHyperlink(link, ...) - if(link and link:sub(0, 11) == "garrmission") then - return; - end - return OriginalSetHyperlink(self, link, ...); -end - local compressedTablesCache = {} function TableToString(inTable, forChat) @@ -740,43 +330,41 @@ function StringToTable(inString, fromChat) end return deserialized end +Private.StringToTable = StringToTable -function WeakAuras.DisplayToString(id, forChat) +function Private.DisplayToString(id, forChat) local data = WeakAuras.GetData(id); if(data) then data.uid = data.uid or GenerateUniqueID() - local transmitData = CompressDisplay(data); - local children = data.controlledChildren; - local transmit = { + -- Check which transmission version we want to use + local version = 1421 + for child in Private.TraverseSubGroups(data) do -- luacheck: ignore + version = 2000 + break; + end + local transmitData = CompressDisplay(data, version); + local transmit = { m = "d", d = transmitData, - v = 1421, -- Version of Transmisson, won't change anymore. + v = version, s = versionString }; - local firstTrigger = data.triggers[1].trigger - if(firstTrigger.type == "aura" and WeakAurasOptionsSaved and WeakAurasOptionsSaved.spellCache) then - transmit.a = {}; - for i,v in pairs(firstTrigger.names) do - transmit.a[v] = WeakAuras.spellCache.GetIcon(v); - end - end - if(children) then + if(data.controlledChildren) then transmit.c = {}; local uids = {} - for index,childID in pairs(children) do - local childData = WeakAuras.GetData(childID); - if(childData) then - if childData.uid then - if uids[childData.uid] then - childData.uid = GenerateUniqueID() - else - uids[childData.uid] = true - end + local index = 1 + for child in Private.TraverseAllChildren(data) do + if child.uid then + if uids[child.uid] then + child.uid = GenerateUniqueID() else - childData.uid = GenerateUniqueID() + uids[child.uid] = true end - transmit.c[index] = CompressDisplay(childData); + else + child.uid = GenerateUniqueID() end + transmit.c[index] = CompressDisplay(child, version); + index = index + 1 end end return TableToString(transmit, forChat); @@ -831,96 +419,7 @@ function Private.SerializeTable(data) return table.concat(lines, "\n") end -function Private.RefreshTooltipButtons() - importButton:Disable() - importButton.tooltipText = nil - showCodeButton:Enable() - if not pendingData.codes or #pendingData.codes == 0 then - showCodeButton:Hide() - else - showCodeButton:Show() - end - if InCombatLockdown() then - importButton:SetText(L["In Combat"]) - importButton.tooltipText = L["Importing is disabled while in combat"] - showCodeButton:Disable() - elseif WeakAuras.IsImporting() then - importButton:SetText(L["Import in progress"]) - elseif pendingData.newData then - if pendingData.activeCategories then - if pendingData.mode == 1 then - for button in pairs(pendingData.activeCategories) do - button.text:SetTextColor(.5, .5, .5) - end - else - for button in pairs(pendingData.activeCategories) do - button.text:SetTextColor(1, 0.82, 0) - end - end - end - if pendingData.mode == #radioButtons and pendingData.buttonsChecked == 0 then - importButton:SetText(L["Choose a category"]) - else - importButton:Enable() - if pendingData.diffs then - if pendingData.mode ~= 1 then - importButton:SetText(L["Import as Update"]) - else - importButton:SetText(L["Import as Copy"]) - end - else - if pendingData.mode == 1 then - importButton:SetText(L["Import as Copy"]) - elseif #pendingData.newData > 0 then - importButton:SetText(L["Import Group"]) - else - importButton:SetText(L["Import"]) - end - end - end - end - local importWidth = WeakAurasTooltipImportButtonText:GetStringWidth() - importButton:SetWidth(importWidth + 30) -end -buttonAnchor:SetScript("OnEvent", Private.RefreshTooltipButtons) - -local function SetCheckButtonStates(radioButtonAnchor, activeCategories) - if activeCategories then - for i, button in ipairs(radioButtons) do - button:Show() - button:Enable() - button:SetChecked(i == pendingData.mode) - button:SetPoint("TOPLEFT", radioButtonAnchor, "TOPLEFT", 0, -20 * (i - .7)) - end - local checkButtonAnchor = radioButtons[#radioButtons] - local buttonsChecked, buttonsShown = 0, 0 - for i, button in ipairs(checkButtons) do - if activeCategories[button] then - button:Show() - button:Enable() - button:SetPoint("TOPLEFT", checkButtonAnchor, "TOPLEFT", 17, -27*(0.6 + buttonsShown)) - button:SetChecked(button.default) - buttonsShown = buttonsShown + 1 - buttonsChecked = buttonsChecked + (button:GetChecked() and 1 or 0) - else - button:Hide() - button:Disable() - end - end - pendingData.buttonsChecked = buttonsChecked - else - for _, button in ipairs(radioButtons) do - button:Hide() - button:Disable() - end - for _, button in ipairs(checkButtons) do - button:Disable() - button:Hide() - end - end -end - -function ShowTooltip(lines, linesFromTop, activeCategories) +function ShowTooltip(lines) ItemRefTooltip:Show(); if not ItemRefTooltip:IsVisible() then ItemRefTooltip:SetOwner(UIParent, "ANCHOR_PRESERVE"); @@ -935,718 +434,32 @@ function ShowTooltip(lines, linesFromTop, activeCategories) end end ItemRefTooltip:Show() - if pendingData.newData then - local checkButtonAnchor = _G["ItemRefTooltipTextLeft" .. (linesFromTop or 1)] - SetCheckButtonStates(checkButtonAnchor, activeCategories) - Private.RefreshTooltipButtons() - buttonAnchor:Show() - else - buttonAnchor:Hide() - end end --- TODO: Should savedvariables data ever be refactored, then shunting the custom scripts --- into their own special subtable will allow us to simplify the scam check significantly. -local function checkTrigger(codes, id, trigger, untrigger) - if (not trigger) then return end; - local t = {}; - if (trigger.custom) then - t.text = L["%s Trigger Function"]:format(id); - t.value = t.text; - t.code = trigger.custom; - tinsert(codes, t); +local delayedImport = CreateFrame("FRAME") + +local function ImportNow(data, children, target, sender) + if InCombatLockdown() then + WeakAuras.prettyPrint(L["Importing will start after combat ends."]) + + delayedImport:RegisterEvent("PLAYER_REGEN_ENABLED") + delayedImport:SetScript("OnEvent", function() + delayedImport:UnregisterEvent("PLAYER_REGEN_ENABLED") + ImportNow(data, children, target, sender) + end) + return end - if (untrigger and untrigger.custom) then - t = {} - t.text = L["%s Untrigger Function"]:format(id); - t.value = t.text; - t.code = untrigger.custom; - tinsert(codes, t); - end - - if (trigger.customDuration) then - t = {} - t.text = L["%s Duration Function"]:format(id); - t.value = t.text; - t.code = trigger.customDuration - tinsert(codes, t); - end - - if (trigger.customName) then - t = {} - t.text = L["%s Name Function"]:format(id); - t.value = t.text; - t.code = trigger.customName - tinsert(codes, t); - end - - if (trigger.customIcon) then - t = {} - t.text = L["%s Icon Function"]:format(id); - t.value = t.text; - t.code = trigger.customIcon - tinsert(codes, t); - end - - if (trigger.customTexture) then - t = {} - t.text = L["%s Texture Function"]:format(id); - t.value = t.text; - t.code = trigger.customTexture - tinsert(codes, t); - end - - if (trigger.customStacks) then - t = {} - t.text = L["%s Stacks Function"]:format(id); - t.value = t.text; - t.code = trigger.customStacks - tinsert(codes, t); - end -end - -local function checkCustom(codes, id, base) - if (not base) then return end - if (base.custom) then - local t = {}; - t.text = id; - t.value = id; - t.code = base.custom - tinsert(codes, t); - end -end - -local function checkActionCustomText(codes, id, base) - if (not base) then return end - if (base.do_message and base.message_custom) then - local t = {}; - t.text = id; - t.value = id; - t.code = base.message_custom - tinsert(codes, t); - end -end - -local function checkAnimation(codes, id, a) - if (not a) then return end - if (a.alphaType == "custom" and a.use_alpha and a.alphaFunc) then - local t = {}; - t.text = L["%s - Alpha Animation"]:format(id); - t.value = t.text; - t.code = a.alphaFunc; - tinsert(codes, t); - end - - if (a.translateType == "custom" and a.use_translate and a.translateFunc) then - local t = {}; - t.text = L["%s - Translate Animation"]:format(id); - t.value = t.text; - t.code = a.translateFunc; - tinsert(codes, t); - end - - if (a.scaleType == "custom" and a.use_scale and a.scaleFunc) then - local t = {}; - t.text = L["%s - Scale Animation"]:format(id); - t.value = t.text; - t.code = a.scaleFunc; - tinsert(codes, t); - end - - if (a.rotateType == "custom" and a.use_rotate and a.rotateFunc) then - local t = {}; - t.text = L["%s - Rotate Animation"]:format(id); - t.value = t.text; - t.code = a.rotateFunc; - tinsert(codes, t); - end - - if (a.colorType == "custom" and a.use_color and a.colorFunc) then - local t = {}; - t.text = L["%s - Color Animation"]:format(id); - t.value = t.text; - t.code = a.colorFunc - tinsert(codes, t); - end -end - -local function checkTriggerLogic(codes, id, logic) - if (not logic) then return end - local t = {}; - t.text = id; - t.value = id; - t.code = logic; - tinsert(codes, t); -end - -local function checkText(codes, id, customText) - if (not customText) then return end - local t = {}; - t.text = id; - t.value = id; - t.code = customText; - tinsert(codes, t); -end - -local function checkCustomCondition(codes, id, customText) - if (not customText) then return end - local t = {}; - t.text = id; - t.value = id; - t.code = customText; - tinsert(codes, t); -end - -local function scamCheck(codes, data) - for i, v in ipairs(data.triggers) do - checkTrigger(codes, L["%s - %i. Trigger"]:format(data.id, i), v.trigger, v.untrigger); - end - - if (data.actions) then - checkCustom(codes, L["%s - Init Action"]:format(data.id), data.actions.init); - checkCustom(codes, L["%s - Start Action"]:format(data.id), data.actions.start); - checkCustom(codes, L["%s - Finish Action"]:format(data.id), data.actions.finish); - checkActionCustomText(codes, L["%s - Start Custom Text"]:format(data.id), data.actions.start); - checkActionCustomText(codes, L["%s - Finish Custom Text"]:format(data.id), data.actions.finish); - end - - if (data.animation) then - checkAnimation(codes, L["%s - Start"]:format(data.id), data.animation.start); - checkAnimation(codes, L["%s - Main"]:format(data.id), data.animation.main); - checkAnimation(codes, L["%s - Finish"]:format(data.id), data.animation.finish); - end - - if(data.triggers.customTriggerLogic) then - checkTriggerLogic(codes, L["%s - Trigger Logic"]:format(data.id), data.triggers.customTriggerLogic); - end - - if(data.customText) then - checkText(codes, L["%s - Custom Text"]:format(data.id), data.customText); - end - - if (data.conditions) then - for _, condition in ipairs(data.conditions) do - if (condition and condition.changes) then - for _, property in ipairs(condition.changes) do - if ((property.property == "chat" or property.property == "customcode") and type(property.value) == "table" and property.value.custom) then - checkCustomCondition(codes, L["%s - Condition Custom Chat"]:format(data.id), property.value.custom); - end - end - end + if Private.LoadOptions() then + if not WeakAuras.IsOptionsOpen() then + WeakAuras.OpenOptions() end + Private.OpenUpdate(data, children, target, sender) end end -local internalFields = Private.internal_fields -local nonTransmissableFields = Private.non_transmissable_fields -local function recurseDiff(ours, theirs) - local diff, seen, same = {}, {}, true - for key, ourVal in pairs(ours) do - if not (internalFields[key] or nonTransmissableFields[key]) then - seen[key] = true - local theirVal = theirs[key] - if type(ourVal) == "table" and type(theirVal) == "table" then - local diffVal = recurseDiff(ourVal, theirVal) - if diffVal then - diff[key] = diffVal - same = false - end - elseif ourVal ~= theirVal and -- of course, floating points can be nonequal by less than we could possibly care - not(type(ourVal) == "number" and type(theirVal) == "number" and math.abs(ourVal - theirVal) < 1e-6) then - if (theirVal == nil) then - diff[key] = deleted - else - diff[key] = theirVal; - end - same = false - end - end - end - for key, theirVal in pairs(theirs) do - if not seen[key] and not (internalFields[key] or nonTransmissableFields[key]) then - diff[key] = theirVal - same = false - end - end - if not same then return diff end -end - --- for debug purposes -local function recurseSerial(lines, depth, chunk) - for k, v in pairs(chunk) do - if v == deleted then - tinsert(lines, string.rep(" ", depth) .. "|cFFFF0000" .. k .. " -> deleted|r") - elseif type(v) == "table" then - tinsert(lines, string.rep(" ", depth) .. k .. " -> {") - recurseSerial(lines, depth + 1, v) - tinsert(lines, string.rep(" ", depth) .. "}") - else - tinsert(lines, string.rep(" ", depth) .. k .. " -> " .. tostring(v)) - end - end -end - -local function diff(ours, theirs) - -- generates a diff which WeakAuras.Update can use - local debug = false - if not ours or not theirs then return end - local diff = recurseDiff(ours, theirs) - if diff then - if debug then - local lines = { - "==========================", - "Diff detected: ", - "ours: " .. (ours.id or "unknown") .. "," .. tostring(ours.uid), - "theirs: " .. (theirs.id or "unknown") .. "," .. tostring(theirs.uid), - "{", - } - recurseSerial(lines, 1, diff) - tinsert(lines, "}") - tinsert(lines, "==========================") - print(table.concat(lines, "\n")) - end - return diff - end -end - -local function findMatch(data, children) - local function isParentMatch(old, new) - if old.parent then return end - if old.uid and new.uid then - if old.uid ~= new.uid then - return - else - if (children == nil) ~= (old.controlledChildren == nil) then - return - end - return true - end - else - if children then - if not old.controlledChildren then return end - return old.id == new.id - else - if old.controlledChildren then return end - return old.id == new.id - end - end - end - local oldParent = WeakAuras.GetData(data.id) - if oldParent and not isParentMatch(oldParent, data) then - oldParent = nil - end - if not oldParent or not isParentMatch(oldParent, data) then - oldParent = nil - if data.uid then - for id, existingData in pairs(WeakAurasSaved.displays) do - if isParentMatch(existingData, data) then - oldParent = existingData - break - end - end - else - return nil - end - end - return oldParent -end - -local function MatchInfo(data, children, oldParent) - if oldParent then - -- Either both have children, or both don't have - if type(children) ~= type(oldParent.controlledChildren) then - return nil - end - else - -- match the parent/single aura (if no children) - oldParent = findMatch(data, children) - end - - if not oldParent then - return nil - end - - - -- setup - local info = { - mode = 1, - unmodified = 0, - modified = 0, - added = 0, - deleted = 0, - oldData = {}, - newData = {}, - indexMap = { - oldToNew = {}, - newToOld = {}, - }, - diffs = {}, - activeCategories = {}, - } - - info.oldData[0] = oldParent - if info.oldData[0].preferToUpdate then - info.mode = 2 - end - info.newData[0] = data - info.diffs[0] = diff(oldParent, data) - if info.diffs[0] then - info.modified = info.modified + 1 - end - local hybridTables - if oldParent.sortHybridTable or data.sortHybridTable then - hybridTables = { - old = {}, - new = {}, - } - if oldParent.sortHybridTable then - for id, isHybrid in pairs(oldParent.sortHybridTable) do - hybridTables.old[id] = isHybrid - end - end - if data.sortHybridTable then - for id, isHybrid in pairs(data.sortHybridTable) do - hybridTables.new[id] = isHybrid - end - end - end - - if children then - -- setup - info.deleted = #oldParent.controlledChildren - info.added = #children - local UIDtoID, IDtoIndex = {}, {} - for index, oldChildID in ipairs(oldParent.controlledChildren) do - local oldChild = WeakAuras.GetData(oldChildID) - info.oldData[index] = oldChild - IDtoIndex[oldChildID] = index - if oldChild.uid and not UIDtoID[oldChild.uid] then - UIDtoID[oldChild.uid] = oldChildID - end - end - - -- confirm uid matches, find tentative matches - local tentativeMatches = {} - for newIndex, newChild in ipairs(children) do - info.newData[newIndex] = newChild - local oldIndex = IDtoIndex[newChild.id] - local matchedID = newChild.uid and UIDtoID[newChild.uid] - if matchedID then - oldIndex = IDtoIndex[matchedID] - info.indexMap.oldToNew[oldIndex] = newIndex - info.indexMap.newToOld[newIndex] = oldIndex - info.deleted = info.deleted - 1 - info.added = info.added - 1 - if hybridTables then - if (hybridTables.new[newChild.id] and not hybridTables.old[matchedID]) - or (not hybridTables.new[newChild.id] and hybridTables.old[matchedID]) then - info.activeCategories[checkButtons.arrangement] = true - hybridTables = nil - else - hybridTables.new[newChild.id] = nil - hybridTables.old[matchedID] = nil - end - end - UIDtoID[newChild.uid] = nil - tentativeMatches[oldIndex] = nil - elseif oldIndex then - tentativeMatches[oldIndex] = newIndex - end - end - - -- confirm remaining tentative matches - for oldIndex, newIndex in pairs(tentativeMatches) do - local newChild, oldChild = info.newData[newIndex], info.oldData[oldIndex] - if not newChild.uid or not oldChild.uid or newChild.uid == oldChild.uid then - if hybridTables then - if (hybridTables.new[newChild.id] and not hybridTables.old[oldChild.id]) - or (not hybridTables.new[newChild.id] and hybridTables.old[oldChild.id]) then - info.activeCategories[checkButtons.arrangement] = true - hybridTables = nil - else - hybridTables.new[newChild.id] = nil - hybridTables.old[oldChild.id] = nil - end - end - info.indexMap.oldToNew[oldIndex] = newIndex - info.indexMap.newToOld[newIndex] = oldIndex - info.deleted = info.deleted - 1 - info.added = info.added - 1 - end - end - - if hybridTables then - if next(hybridTables.new) ~= nil or next(hybridTables.old) ~= nil then - info.activeCategories[checkButtons.arrangement] = true - end - end - -- detect order mismatch and find diffs - local lastMatch = 0 - for newIndex, newChild in ipairs(info.newData) do - local oldIndex = info.indexMap.newToOld[newIndex] - if oldIndex then - local oldChild = info.oldData[oldIndex] - if lastMatch and lastMatch > oldIndex then - lastMatch = nil - info.activeCategories[checkButtons.arrangement] = true - else - lastMatch = oldIndex - end - local childDiff = diff(oldChild, newChild, true) - if childDiff then - info.modified = info.modified + 1 - info.diffs[newIndex] = childDiff - end - end - end - end - - if info.added ~= 0 then - info.activeCategories[checkButtons.newchildren] = true - end - - if info.deleted ~= 0 then - info.activeCategories[checkButtons.oldchildren] = true - end - - if info.modified ~= 0 then - for i, diff in pairs(info.diffs) do - for key in pairs(diff) do - local button = keyToButton[key] - if button then - if i > 0 and button == checkButtons.anchor then - button = checkButtons.arrangement - end - info.activeCategories[button] = true - end - end - end - end - - local modified = next(info.activeCategories) ~= nil - return modified and info or false -end - -local function ShowDisplayTooltip(data, children, matchInfo, icon, icons, import, compressed) - -- since we have new data, wipe the old pending data - wipe(pendingData) - - local regionType = data.regionType; - local regionData = regionOptions[regionType or ""] - local displayName = regionData and regionData.displayName or regionType or ""; - - local tooltip = { - {2, data.id, " ", 0.5333, 0, 1}, - {2, displayName, " ", 1, 0.82, 0}, - {1, " ", 1, 1, 1} - }; - - pendingData.codes = {}; - local highestVersion = data.internalVersion or 1; - local hasDescription = data.desc and data.desc ~= ""; - local hasUrl = data.url and data.url ~= ""; - local hasVersion = (data.semver and data.semver ~= "") or (data.version and data.version ~= ""); - - if hasDescription or hasUrl or hasVersion then - tinsert(tooltip, {1, " "}); - end - - if hasDescription then - tinsert(tooltip, {1, "\""..data.desc.."\"", 1, 0.82, 0, 1}); - end - - if hasUrl then - tinsert(tooltip, {1, L["Source: "] .. data.url, 1, 0.82, 0, 1}); - end - - if hasVersion then - tinsert(tooltip, {1, L["Version: "] .. (data.semver or data.version), 1, 0.82, 0, 1}); - end - - -- WeakAuras.GetData needs to be replaced temporarily so that when the subsequent code constructs the thumbnail for - -- the tooltip, it will look to the incoming transmission data for child data. This allows thumbnails of incoming - -- groups to be constructed properly. - local RegularGetData = WeakAuras.GetData - if children then - WeakAuras.GetData = function(id) - if(children) then - for index, childData in pairs(children) do - if(childData.id == id) then - return childData; - end - end - end - end - end - - local linesFromTop - if import then - -- import description - pendingData.newData = {[0] = data} - if children then - for index,child in ipairs(children) do - pendingData.newData[index] = child - end - end - --TODO: only scamCheck codes that have actually changed - for i = 0, #pendingData.newData do - scamCheck(pendingData.codes, pendingData.newData[i]) - end - if matchInfo ~= nil then - -- we know of at least one match - tinsert(tooltip, {1, " "}) - if type(matchInfo) ~= "table" then - -- there is no difference whatsoever - tinsert(tooltip, {1, L["You already have this group/aura. Importing will create a duplicate."], 1, 0.82, 0,}) - else - -- load pendingData - for k,v in pairs(matchInfo) do - pendingData[k] = v - end - -- tally up changes - if children and #children > 0 then - tinsert(tooltip, {1, L["This is a modified version of your group, |cff9900FF%s.|r"]:format(pendingData.oldData[0].id), 1, 0.82, 0,}) - tinsert(tooltip, {1, " "}) - if matchInfo.added ~= 0 then - tinsert(tooltip, {1, L[" • %d auras added"]:format(matchInfo.added), 1, 0.82, 0}) - end - if matchInfo.modified ~= 0 then - tinsert(tooltip, {1, L[" • %d auras modified"]:format(matchInfo.modified), 1, 0.82, 0}) - end - if matchInfo.deleted ~= 0 then - tinsert(tooltip, {1, L[" • %d auras deleted"]:format(matchInfo.deleted), 1, 0.82, 0}) - end - tinsert(tooltip, {1, " "}) - tinsert(tooltip, {1, L["What do you want to do?"], 1, 0.82, 0}) - tinsert(tooltip, {1, " "}) - else - local oldID = type(matchInfo.oldData[0]) == "table" and matchInfo.oldData[0].id or "unknown" - tinsert(tooltip, {1, L["This is a modified version of your aura, |cff9900FF%s.|r"]:format(pendingData.oldData[0].id), 1, 0.82, 0}) - tinsert(tooltip, {1, L["What do you want to do?"], 1, 0.82, 0}) - tinsert(tooltip, {1, " "}) - end - linesFromTop = #tooltip - for _ in ipairs(radioButtons) do - tinsert(tooltip, {1, " "}) - end - for _, button in ipairs(checkButtons) do - if pendingData.activeCategories[button] then - tinsert(tooltip, {1, " "}) - tinsert(tooltip, {1, " "}) - end - end - end - else - if not children and (not data.controlledChildren or #data.controlledChildren == 0) then - tinsert(tooltip, {1, L["No Children"], 1, 1, 1}) - for triggernum = 1, min(#data.triggers, 10) do - local trigger = data.triggers[triggernum].trigger - if(trigger) then - if(trigger.type == "aura") then - for index, name in pairs(trigger.names) do - local left = " "; - if(index == 1) then - if(#trigger.names > 0) then - if(#trigger.names > 1) then - left = L["Auras:"]; - else - left = L["Aura:"]; - end - end - end - if (icons) then - tinsert(tooltip, {2, left, name..(icons[name] and (" |T"..icons[name]..":12:12:0:0:64:64:4:60:4:60|t") or ""), 1, 1, 1, 1, 1, 1}); - else - local icon = WeakAuras.spellCache and WeakAuras.spellCache.GetIcon(name) or "Interface\\Icons\\INV_Misc_QuestionMark"; - tinsert(tooltip, {2, left, name.." |T"..icon..":12:12:0:0:64:64:4:60:4:60|t", 1, 1, 1, 1, 1, 1}); - end - end - elseif(trigger.type == "aura2") then - tinsert(tooltip, {2, L["Trigger:"], L["Aura"], 1, 1, 1, 1, 1, 1}); - elseif(Private.category_event_prototype[trigger.type]) then - tinsert(tooltip, {2, L["Trigger:"], (Private.event_prototypes[trigger.event].name or L["Undefined"]), 1, 1, 1, 1, 1, 1}); - if(trigger.event == "Combat Log" and trigger.subeventPrefix and trigger.subeventSuffix) then - tinsert(tooltip, {2, L["Message type:"], (Private.subevent_prefix_types[trigger.subeventPrefix] or L["Undefined"]).." "..(Private.subevent_suffix_types[trigger.subeventSuffix] or L["Undefined"]), 1, 1, 1, 1, 1, 1}); - end - else - tinsert(tooltip, {2, L["Trigger:"], L["Custom"], 1, 1, 1, 1, 1, 1}); - end - end - end - else - tinsert(tooltip, {1, L["Children:"], 1, 1, 1}) - local excessChildren = -30 - for _, child in pairs(children or data.controlledChildren) do - excessChildren = excessChildren + 1 - if children then - highestVersion = max(highestVersion, child.internalVersion or 1) - end - if excessChildren <= 0 then - tinsert(tooltip, {2, " ", child.id, 1, 1, 1, 1, 1, 1}) - end - end - if excessChildren > 0 then - tinsert(tooltip, {2, " ", "[...]", 1, 1, 1, 1, 1, 1}) - tinsert(tooltip, {1, L["%s total auras"]:format(excessChildren + 30), "", 1, 1, 1, 1}) - end - end - end - - tinsert(tooltip, {1, " "}) - if(type(import) == "string" and import ~= "unknown") then - tinsert(tooltip, {2, L["From"]..": "..import, " ", 0, 1, 0}) - end - - if #pendingData.codes > 0 then - tinsert(tooltip, {1, L["This aura contains custom Lua code."], 1, 0, 0}) - tinsert(tooltip, {1, L["Make sure you can trust the person who sent it!"], 1, 0, 0}) - end - - if (highestVersion > WeakAuras.InternalVersion()) then - tinsert(tooltip, {1, L["This aura was created with a newer version of WeakAuras."], 1, 0, 0}) - tinsert(tooltip, {1, L["It might not work correctly with your version!"], 1, 0, 0}) - end - - tinsert(tooltip, {2, " ", " ", 0, 1, 0}) - tinsert(tooltip, {2, " ", " ", 0, 1, 0}) - - end - - if not IsAddOnLoaded('WeakAurasOptions') then - LoadAddOn('WeakAurasOptions') - end - if thumbnailAnchor.currentThumbnail then - regionOptions[thumbnailAnchor.currentThumbnailType].releaseThumbnail(thumbnailAnchor.currentThumbnail) - thumbnailAnchor.currentThumbnail = nil - end - if regionOptions[regionType] then - local ok, thumbnail = pcall(regionOptions[regionType].acquireThumbnail, thumbnailAnchor, data); - if not ok then - error(string.format("Error creating thumbnail for %s %s", regionType, thumbnail), 2) - end - thumbnailAnchor.currentThumbnail = thumbnail - thumbnailAnchor.currentThumbnailType = regionType - thumbnail:SetAllPoints(thumbnailAnchor); - thumbnail:SetParent(thumbnailAnchor) - if (thumbnail.SetIcon) then - local i; - if(icon) then - i = icon; - end - if (i) then - thumbnail:SetIcon(i); - else - thumbnail:SetIcon(); - end - end - end - WeakAuras.GetData = RegularGetData or WeakAuras.GetData - ShowTooltip(tooltip, linesFromTop, matchInfo and matchInfo.activeCategories) -end - function WeakAuras.Import(inData, target) - local data, children, icon, icons + local data, children, version if type(inData) == 'string' then -- encoded data local received = StringToTable(inData, true) @@ -1660,34 +473,33 @@ function WeakAuras.Import(inData, target) elseif received.m == "d" then data = received.d children = received.c - icon = received.i - icons = received.a + version = received.v end elseif type(inData.d) == 'table' then data = inData.d children = inData.c - icon = inData.i - icons = inData.a + version = inData.v end if type(data) ~= "table" then return nil, "Invalid import data." end - local status, msg = true, "" - if type(target) ~= 'nil' then - local targetData - local uid = type(target) == 'table' and target.uid or target - if type(uid) == 'string' then - for _, installedData in pairs(WeakAurasSaved.displays) do - if installedData.uid == uid then - targetData = installedData - break - end + + if version < 2000 then + if children then + data.controlledChildren = {} + for i, child in ipairs(children) do + tinsert(data.controlledChildren, child.id) + child.parent = data.id end end + end + + local status, msg = true, "" + if type(target) ~= 'nil' then + local uid = type(target) == 'table' and target.uid or target + local targetData = Private.GetDataByUID(uid) if not targetData then - status = false - msg = "Invalid update target. Importing as an unknown payload." - target = nil + return false, "Invalid update target." else target = targetData end @@ -1698,17 +510,10 @@ function WeakAuras.Import(inData, target) WeakAuras.PreAdd(child) end end - tooltipLoading = nil; - local matchInfo = MatchInfo(data, children, target) - if matchInfo == nil and target then - return false, "Import data did not match to target" - end - ShowDisplayTooltip(data, children, matchInfo, icon, icons, "unknown") - return status, msg -end --- backwards compatibility -WeakAuras.ImportString = WeakAuras.Import + tooltipLoading = nil; + return ImportNow(data, children, target) +end local function crossRealmSendCommMessage(prefix, text, target, queueName, callbackFn, callbackArg) local chattype = "WHISPER" @@ -1746,7 +551,7 @@ function TransmitError(errorMsg, characterName) end function TransmitDisplay(id, characterName) - local encoded = WeakAuras.DisplayToString(id); + local encoded = Private.DisplayToString(id); if(encoded ~= "") then crossRealmSendCommMessage("WeakAuras", encoded, characterName, "BULK", function(displayName, done, total) crossRealmSendCommMessage("WeakAurasProg", done.." "..total.." "..displayName, characterName, "ALERT"); @@ -1799,21 +604,47 @@ Comm:RegisterComm("WeakAuras", function(prefix, message, distribution, sender) end end end + + local linkValidityDuration = 60 * 5 + local safeSender = safeSenders[sender] + local validLink = false + if Private.linked then + local expiredLinkTime = GetTime() - linkValidityDuration + for id, time in pairs(Private.linked) do + if time > expiredLinkTime then + validLink = true + end + end + end + if not safeSender and not validLink then + return + end + local received = StringToTable(message); if(received and type(received) == "table" and received.m) then - if(received.m == "d") and safeSenders[sender] then + if(received.m == "d") then tooltipLoading = nil; - local data, children, icon, icons = received.d, received.c, received.i, received.a + local data, children, version = received.d, received.c, received.v WeakAuras.PreAdd(data) if children then for _, child in ipairs(children) do WeakAuras.PreAdd(child) end end - local matchInfo = MatchInfo(data, children) - ShowDisplayTooltip(data, children, matchInfo, icon, icons, sender, true) + if version < 2000 then + if children then + data.controlledChildren = {} + for i, child in ipairs(children) do + tinsert(data.controlledChildren, child.id) + child.parent = data.id + end + end + end + + ItemRefTooltip:Hide() + ImportNow(data, children, nil, sender) elseif(received.m == "dR") then - if(Private.linked and Private.linked[received.d]) then + if(Private.linked and Private.linked[received.d] and Private.linked[received.d] > GetTime() - linkValidityDuration) then TransmitDisplay(received.d, sender); end elseif(received.m == "dE") then diff --git a/WeakAuras/Types.lua b/WeakAuras/Types.lua index 7c9324f..a00dab1 100644 --- a/WeakAuras/Types.lua +++ b/WeakAuras/Types.lua @@ -337,14 +337,16 @@ Private.format_types = { }, Unit = { display = L["Formats |cFFFF0000%unit|r"], - AddOptions = function(symbol, hidden, addOption, get) - addOption(symbol .. "_color", { - type = "select", - name = L["Color"], - width = WeakAuras.normalWidth, - values = Private.unit_color_types, - hidden = hidden, - }) + AddOptions = function(symbol, hidden, addOption, get, withoutColor) + if not withoutColor then + addOption(symbol .. "_color", { + type = "select", + name = L["Color"], + width = WeakAuras.normalWidth, + values = Private.unit_color_types, + hidden = hidden, + }) + end addOption(symbol .. "_realm_name", { type = "select", name = L["Realm Name"], @@ -371,8 +373,8 @@ Private.format_types = { end }) end, - CreateFormatter = function(symbol, get) - local color = get(symbol .. "_color", true) + CreateFormatter = function(symbol, get, withoutColor) + local color = not withoutColor and get(symbol .. "_color", true) local realm = get(symbol .. "_realm_name", "never") local abbreviate = get(symbol .. "_abbreviate", false) local abbreviateMax = get(symbol .. "_abbreviate_max", 8) @@ -383,7 +385,10 @@ Private.format_types = { if color == "class" then colorFunc = function(unit, text) if unit and UnitPlayerControlled(unit) then - return string.format("|c%s%s|r", WA_GetClassColor(select(2, UnitClass(unit))), text) + local classFilename = select(2, UnitClass(unit)) + if classFilename then + return string.format("|c%s%s|r", WA_GetClassColor(classFilename), text) + end end return text end @@ -459,14 +464,16 @@ Private.format_types = { }, guid = { display = L["Formats Player's |cFFFF0000%guid|r"], - AddOptions = function(symbol, hidden, addOption, get) - addOption(symbol .. "_color", { - type = "select", - name = L["Color"], - width = WeakAuras.normalWidth, - values = Private.unit_color_types, - hidden = hidden, - }) + AddOptions = function(symbol, hidden, addOption, get, withoutColor) + if not withoutColor then + addOption(symbol .. "_color", { + type = "select", + name = L["Color"], + width = WeakAuras.normalWidth, + values = Private.unit_color_types, + hidden = hidden, + }) + end addOption(symbol .. "_realm_name", { type = "select", name = L["Realm Name"], @@ -492,8 +499,8 @@ Private.format_types = { end }) end, - CreateFormatter = function(symbol, get) - local color = get(symbol .. "_color", true) + CreateFormatter = function(symbol, get, withoutColor) + local color = not withoutColor and get(symbol .. "_color", true) local realm = get(symbol .. "_realm_name", "never") local abbreviate = get(symbol .. "_abbreviate", false) local abbreviateMax = get(symbol .. "_abbreviate_max", 8) @@ -503,7 +510,11 @@ Private.format_types = { local abbreviateFunc if color == "class" then colorFunc = function(class, text) - return string.format("|c%s%s|r", WA_GetClassColor(class), text) + if class then + return string.format("|c%s%s|r", WA_GetClassColor(class), text) + else + return text + end end end @@ -717,7 +728,8 @@ Private.trigger_modes = { Private.debuff_types = { HELPFUL = L["Buff"], - HARMFUL = L["Debuff"] + HARMFUL = L["Debuff"], + BOTH = L["Buff/Debuff"] } Private.tooltip_count = { @@ -728,9 +740,10 @@ Private.tooltip_count = { Private.aura_types = { BUFF = L["Buff"], - DEBUFF = L["Debuff"] + DEBUFF = L["Debuff"], } + Private.debuff_class_types = { magic = L["Magic"], curse = L["Curse"], @@ -785,16 +798,12 @@ Private.actual_unit_types_cast = { member = L["Specific Unit"], } -Private.actual_unit_types = { -- TODO - player = L["Player"], - target = L["Target"], - focus = L["Focus"], - pet = L["Pet"] -} +Private.actual_unit_types_cast_tooltip = L["• |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs.\n• |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch.\n|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger.\n• |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs.\n• |cffffff00Smart Group|r adjusts to your current group type, matching just the \"player\" when solo, \"party\" units (including \"player\") in a party or \"raid\" units in a raid.\n\n|cffffff00*|r Yellow Unit settings will create clones for each matching unit while this trigger is providing Dynamic Info to the Aura."] Private.threat_unit_types = { target = L["Target"], focus = L["Focus"], + boss = L["Boss"], member = L["Specific Unit"], none = L["At Least One Enemy"] } @@ -808,10 +817,10 @@ Private.unit_types_range_check = { Private.unit_threat_situation_types = { [-1] = L["Not On Threat Table"], - [0] = "|cFFB0B0B0"..L["Lower Than Tank"], - [1] = "|cFFFFFF77"..L["Higher Than Tank"], - [2] = "|cFFFF9900"..L["Tanking But Not Highest"], - [3] = "|cFFFF0000"..L["Tanking And Highest"] + [0] = "|cFFB0B0B0"..L["Lower Than Tank"].."|r", + [1] = "|cFFFFFF77"..L["Higher Than Tank"].."|r", + [2] = "|cFFFF9900"..L["Tanking But Not Highest"].."|r", + [3] = "|cFFFF0000"..L["Tanking And Highest"].."|r" } WeakAuras.class_types = {} @@ -939,11 +948,6 @@ Private.tick_placement_modes = { ValueOffset = L["Offset from progress"] } -Private.containment_types = { -- TODO - OUTSIDE = L["Outside"], - INSIDE = L["Inside"] -} - Private.font_flags = { None = L["None"], MONOCHROME = L["Monochrome"], @@ -963,11 +967,10 @@ Private.text_word_wrap = { Elide = L["Elide"] } -Private.category_event_prototype = {} -for name, prototype in pairs(Private.event_prototypes) do - Private.category_event_prototype[prototype.type] = Private.category_event_prototype[prototype.type] or {} - Private.category_event_prototype[prototype.type][name] = prototype.name -end +Private.include_pets_types = { + PlayersAndPets = L["Players and Pets"], + PetsOnly = L["Pets only"] +} Private.subevent_prefix_types = { SWING = L["Swing"], @@ -1060,7 +1063,8 @@ Private.environmental_types = { Private.combatlog_flags_check_type = { Mine = L["Mine"], InGroup = L["In Group"], - NotInGroup = L["Not in Group"] + InParty = L["In Party"], + NotInGroup = L["Not in Smart Group"] } Private.combatlog_flags_check_reaction = { @@ -1077,6 +1081,48 @@ Private.combatlog_flags_check_object_type = { Player = L["Player"] } +Private.combatlog_spell_school_types = { + [1] = STRING_SCHOOL_PHYSICAL, + [2] = STRING_SCHOOL_HOLY, + [4] = STRING_SCHOOL_FIRE, + [8] = STRING_SCHOOL_NATURE, + [16] = STRING_SCHOOL_FROST, + [32] = STRING_SCHOOL_SHADOW, + [64] = STRING_SCHOOL_ARCANE, + [3] = STRING_SCHOOL_HOLYSTRIKE, + [5] = STRING_SCHOOL_FLAMESTRIKE, + [6] = STRING_SCHOOL_HOLYFIRE, + [9] = STRING_SCHOOL_STORMSTRIKE, + [10] = STRING_SCHOOL_HOLYSTORM, + [12] = STRING_SCHOOL_FIRESTORM, + [17] = STRING_SCHOOL_FROSTSTRIKE, + [18] = STRING_SCHOOL_HOLYFROST, + [20] = STRING_SCHOOL_FROSTFIRE, + [24] = STRING_SCHOOL_FROSTSTORM, + [33] = STRING_SCHOOL_SHADOWSTRIKE, + [34] = STRING_SCHOOL_SHADOWLIGHT, + [36] = STRING_SCHOOL_SHADOWFLAME, + [40] = STRING_SCHOOL_SHADOWSTORM, + [48] = STRING_SCHOOL_SHADOWFROST, + [65] = STRING_SCHOOL_SPELLSTRIKE, + [66] = STRING_SCHOOL_DIVINE, + [68] = STRING_SCHOOL_SPELLFIRE, + [72] = STRING_SCHOOL_SPELLSTORM, + [80] = STRING_SCHOOL_SPELLFROST, + [96] = STRING_SCHOOL_SPELLSHADOW, + [28] = STRING_SCHOOL_ELEMENTAL, + [62] = STRING_SCHOOL_CHROMATIC, + [106] = STRING_SCHOOL_COSMIC, + [124] = STRING_SCHOOL_CHAOS, + [126] = STRING_SCHOOL_MAGIC, + [127] = STRING_SCHOOL_CHAOS, +} + +Private.combatlog_spell_school_types_for_ui = {} +for id, str in pairs(Private.combatlog_spell_school_types) do + Private.combatlog_spell_school_types_for_ui[id] = ("%.3d - %s"):format(id, str) +end + Private.combatlog_raid_mark_check_type = { [0] = RAID_TARGET_NONE, "|TInterface\\TARGETINGFRAME\\UI-RaidTargetingIcon_1:14|t " .. RAID_TARGET_1, -- Star @@ -1090,6 +1136,9 @@ Private.combatlog_raid_mark_check_type = { L["Any"] } +Private.raid_mark_check_type = CopyTable(Private.combatlog_raid_mark_check_type) +Private.raid_mark_check_type[9] = nil + Private.orientation_types = { HORIZONTAL_INVERSE = L["Left to Right"], HORIZONTAL = L["Right to Left"], @@ -1468,6 +1517,10 @@ Private.texture_types = { ["Interface\\TargetingFrame\\UI-RaidTargetingIcon_6"] = RAID_TARGET_6, ["Interface\\TargetingFrame\\UI-RaidTargetingIcon_7"] = RAID_TARGET_7, ["Interface\\TargetingFrame\\UI-RaidTargetingIcon_8"] = RAID_TARGET_8, + }, + ["WeakAuras"] = { + ["Interface\\AddOns\\WeakAuras\\Media\\Textures\\logo_64.tga"] = "WeakAuras logo 64px", + ["Interface\\AddOns\\WeakAuras\\Media\\Textures\\logo_256.tga"] = "WeakAuras logo 256px" } } @@ -1835,9 +1888,22 @@ Private.instance_types = { arena = L["Arena"] } +Private.TocToExpansion = { + [1] = L["Classic"], + [2] = L["Burning Crusade"], + [3] = L["Wrath of the Lich King"], + [4] = L["Cataclysm"], + [5] = L["Mists of Pandaria"], + [6] = L["Warlords of Draenor"], + [7] = L["Legion"], + [8] = L["Battle for Azeroth"], + [9] = L["Shadowlands"], + [10] = L["Dragonflight"] +} + Private.group_types = { solo = L["Not in Group"], - group = L["In Group"], + group = L["In Party"], raid = L["In Raid"] } @@ -2205,6 +2271,7 @@ Private.bool_types = { Private.update_categories = { { name = "anchor", + -- Note, these are special cased for child auras and considered arrangment fields = { "xOffset", "yOffset", @@ -2297,12 +2364,14 @@ Private.update_categories = { fields = {}, default = true, label = L["Remove Obsolete Auras"], + skipInSummary = true }, { name = "newchildren", fields = {}, default = true, label = L["Add Missing Auras"], + skipInSummary = true }, { name = "metadata", @@ -2322,6 +2391,10 @@ Private.internal_fields = { uid = true, internalVersion = true, sortHybridTable = true, + tocversion = true, + parent = true, + controlledChildren = true, + source = true } -- fields that are not included in exported data @@ -2336,6 +2409,14 @@ Private.non_transmissable_fields = { preferToUpdate = true, } +-- For nested groups, we do transmit parent + controlledChildren +Private.non_transmissable_fields_v2000 = { + authorMode = true, + skipWagoUpdate = true, + ignoreWagoUpdate = true, + preferToUpdate = true, +} + WeakAuras.data_stub = { -- note: this is the minimal data stub which prevents false positives in diff upon reimporting an aura. -- pending a refactor of other code which adds unnecessary fields, it is possible to shrink it @@ -2544,7 +2625,11 @@ Private.multiUnitId = { ["boss"] = true, ["arena"] = true, ["group"] = true, + ["grouppets"] = true, + ["grouppetsonly"] = true, ["party"] = true, + ["partypets"] = true, + ["partypetsonly"] = true, ["raid"] = true, } @@ -2559,11 +2644,16 @@ Private.multiUnitUnits = { Private.multiUnitUnits.group["player"] = true Private.multiUnitUnits.party["player"] = true +Private.multiUnitUnits.group["pet"] = true +Private.multiUnitUnits.party["pet"] = true + for i = 1, 4 do Private.baseUnitId["party"..i] = true Private.baseUnitId["partypet"..i] = true Private.multiUnitUnits.group["party"..i] = true Private.multiUnitUnits.party["party"..i] = true + Private.multiUnitUnits.group["partypet"..i] = true + Private.multiUnitUnits.party["partypet"..i] = true end for i = 1, MAX_BOSS_FRAMES do @@ -2581,6 +2671,8 @@ for i = 1, 40 do Private.baseUnitId["raidpet"..i] = true Private.multiUnitUnits.group["raid"..i] = true Private.multiUnitUnits.raid["raid"..i] = true + Private.multiUnitUnits.group["raidpet"..i] = true + Private.multiUnitUnits.raid["raidpet"..i] = true end Private.dbm_types = { @@ -2697,15 +2789,6 @@ WeakAuras.StopMotion.texture_data["Interface\\AddOns\\WeakAurasStopMotion\\Textu ["columns"] = 8 } -WeakAuras.StopMotion.texture_types.Kaitan = { - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\CellRing"] = "CellRing", - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\Gadget"] = "Gadget", - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\Radar"] = "Radar", - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\RadarComplex"] = "RadarComplex", - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\Saber"] = "Saber", - ["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\Waveform"] = "Waveform", -} - WeakAuras.StopMotion.texture_data["Interface\\AddOns\\WeakAurasStopMotion\\Textures\\Kaitan\\CellRing"] = { ["count"] = 32, ["rows"] = 8, diff --git a/WeakAuras/WeakAuras.lua b/WeakAuras/WeakAuras.lua index 10363e7..f38b7f8 100644 --- a/WeakAuras/WeakAuras.lua +++ b/WeakAuras/WeakAuras.lua @@ -1,6 +1,6 @@ local AddonName, Private = ... -local internalVersion = 44 +local internalVersion = 52 -- Lua APIs local insert = table.insert @@ -28,7 +28,23 @@ local prettyPrint = WeakAuras.prettyPrint WeakAurasTimers = setmetatable({}, {__tostring=function() return "WeakAuras" end}) LibStub("AceTimer-3.0"):Embed(WeakAurasTimers) -Private.callbacks = LibStub("CallbackHandler-1.0"):New(Private) +-- The worlds simplest callback system +-- That supports 1:N, but no deregistration and breaks if registrating in a callback +Private.callbacks = {} +Private.callbacks.events = {} + +function Private.callbacks:RegisterCallback(event, handler) + self.events[event] = self.events[event] or {} + tinsert(self.events[event], handler) +end + +function Private.callbacks:Fire(event, ...) + if self.events[event] then + for index, f in ipairs(self.events[event]) do + f(event, ...) + end + end +end local LDB = LibStub("LibDataBroker-1.1") local LDBIcon = LibStub("LibDBIcon-1.0") @@ -60,7 +76,7 @@ function Private.LoadOptions(msg) local loaded, reason = LoadAddOn("WeakAurasOptions"); if not(loaded) then reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") - WeakAuras.prettyPrint("Options could not be loaded, the addon is " .. reason); + WeakAuras.prettyPrint(string.format(L["Options could not be loaded, the addon is %s"], reason)); return false; end end @@ -89,6 +105,11 @@ end SLASH_WEAKAURAS1, SLASH_WEAKAURAS2 = "/weakauras", "/wa"; function SlashCmdList.WEAKAURAS(input) + if not WeakAuras.IsCorrectVersion() then + prettyPrint(Private.wrongTargetMessage) + return + end + local args, msg = {}, nil for v in string.gmatch(input, "%S+") do @@ -120,18 +141,6 @@ end if not WeakAuras.IsCorrectVersion() then return end -function Private.ApplyToDataOrChildData(data, func, ...) - if data.controlledChildren then - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - func(childData, ...) - end - return true - else - func(data, ...) - end -end - function Private.ToggleMinimap() WeakAurasSaved.minimap.hide = not WeakAurasSaved.minimap.hide if WeakAurasSaved.minimap.hide then @@ -153,15 +162,6 @@ BINDING_NAME_WEAKAURASPRINTPROFILING = L["Print Profiling Results"] -- displays: All aura settings, keyed on their id local db; -local registeredFromAddons; --- List of addons that registered displays -Private.addons = {}; -local addons = Private.addons; - --- used if an addon tries to register a display under an id that the user already has a display with that id -Private.collisions = {}; -local collisions = Private.collisions; - -- While true no events are handled. E.g. WeakAuras is paused while the Options dialog is open local paused = true; local importing = false; @@ -250,16 +250,34 @@ local timers = {}; -- Timers for autohiding, keyed on id, triggernum, cloneid WeakAuras.timers = timers; WeakAuras.raidUnits = {}; +WeakAuras.raidpetUnits = {}; WeakAuras.partyUnits = {}; +WeakAuras.partypetUnits = {}; +WeakAuras.petUnitToUnit = { + pet = "player" +} +WeakAuras.unitToPetUnit = { + player = "pet" +} do for i=1,40 do WeakAuras.raidUnits[i] = "raid"..i + WeakAuras.raidpetUnits[i] = "raidpet"..i + WeakAuras.petUnitToUnit["raidpet"..i] = "raid"..i + WeakAuras.unitToPetUnit["raid"..i] = "raidpet"..i end for i=1,4 do WeakAuras.partyUnits[i] = "party"..i + WeakAuras.partypetUnits[i] = "partypet"..i + WeakAuras.petUnitToUnit["partypet"..i] = "party"..i + WeakAuras.unitToPetUnit["party"..i] = "partypet"..i end end +WeakAuras.UnitIsPet = function(unit) + return WeakAuras.petUnitToUnit[unit] ~= nil +end + local playerLevel = UnitLevel("player"); -- Custom Action Functions, keyed on id, "init" / "start" / "finish" @@ -412,10 +430,15 @@ function WeakAuras.RegisterRegionOptions(name, createFunction, icon, displayName elseif(regionOptions[name]) then error("Improper arguments to WeakAuras.RegisterRegionOptions - region type \""..name.."\" already defined", 2); else + local templateIcon if (type(icon) == "function") then - -- We only want to create one icon and reparent it as needed + -- We only want to create two icons and reparent it as needed + templateIcon = icon() + templateIcon:Hide() icon = icon() icon:Hide() + else + templateIcon = icon end local acquireThumbnail, releaseThumbnail @@ -435,6 +458,7 @@ function WeakAuras.RegisterRegionOptions(name, createFunction, icon, displayName regionOptions[name] = { create = createFunction, icon = icon, + templateIcon = templateIcon, displayName = displayName, createThumbnail = createThumbnail, modifyThumbnail = modifyThumbnail, @@ -722,13 +746,41 @@ local function LoadCustomActionFunctions(data) end end -function WeakAuras.CountWagoUpdates() - if not (WeakAurasCompanion and WeakAurasCompanion.slugs) then +Private.CompanionData = {} +-- use this function to not overwrite data from other companion compatible addons +-- when using this function, do not name your global data table "WeakAurasCompanion" +function WeakAuras.AddCompanionData(data) + WeakAuras.DeepMixin(Private.CompanionData, data) +end + +-- add data from versions of companion compatible addon that does not use WeakAuras.AddCompanionData yet +local function AddLegacyCompanionData() + local CompanionData = WeakAurasCompanion and WeakAurasCompanion.WeakAuras or WeakAurasCompanion + if CompanionData then + WeakAuras.AddCompanionData(CompanionData) + end +end + +function Private.PostAddCompanion() + -- add data from older verion of companion addons + AddLegacyCompanionData() + -- nag if updates + local count = Private.CountWagoUpdates() + if count and count > 0 then + WeakAuras.prettyPrint(L["There are %i updates to your auras ready to be installed!"]:format(count)) + end + -- nag if new installs + if Private.CompanionData.stash and next(Private.CompanionData.stash) then + WeakAuras.prettyPrint(L["You have new auras ready to be installed!"]) + end +end + +function Private.CountWagoUpdates() + if not (Private.CompanionData.slugs) then return 0 end - local WeakAurasSaved = WeakAurasSaved local updatedSlugs, updatedSlugsCount = {}, 0 - for id, aura in pairs(WeakAurasSaved.displays) do + for id, aura in pairs(db.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 @@ -736,12 +788,8 @@ function WeakAuras.CountWagoUpdates() version = 1 end if slug and version then - local wago = WeakAurasCompanion.slugs[slug] - if wago and wago.wagoVersion - and tonumber(wago.wagoVersion) > ( - aura.skipWagoUpdate and tonumber(aura.skipWagoUpdate) or tonumber(version) - ) - then + local wago = Private.CompanionData.slugs[slug] + if wago and wago.wagoVersion and tonumber(wago.wagoVersion) > tonumber(version) then if not updatedSlugs[slug] then updatedSlugs[slug] = true updatedSlugsCount = updatedSlugsCount + 1 @@ -758,8 +806,8 @@ local function tooltip_draw() local tooltip = GameTooltip; tooltip:ClearLines(); tooltip:AddDoubleLine("WeakAuras", versionString); - if WeakAurasCompanion then - local count = WeakAuras.CountWagoUpdates() + if Private.CompanionData.slugs then + local count = Private.CountWagoUpdates() if count > 0 then tooltip:AddLine(" "); tooltip:AddLine((L["There are %i updates to your auras ready to be installed!"]):format(count)); @@ -864,7 +912,8 @@ do -- Archive stuff if not IsAddOnLoaded("WeakAurasArchive") then local ok, reason = LoadAddOn("WeakAurasArchive") if not ok then - error("Could not load WeakAuras Archive, reason: |cFFFF00" .. (reason or "UNKNOWN")) + reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") + error(string.format(L["Could not load WeakAuras Archive, the addon is %s"], reason)) end end Archivist:Initialize(WeakAurasArchive) @@ -919,11 +968,6 @@ function Private.Login(initialTime, takeNewSnapshots) WeakAuras.AddMany(toAdd, takeNewSnapshots); coroutine.yield(); - WeakAuras.AddManyFromAddons(from_files); - WeakAuras.RegisterDisplay = WeakAuras.AddFromAddon; - coroutine.yield(); - Private.ResolveCollisions(function() registeredFromAddons = true; end); - coroutine.yield(); Private.RegisterLoadEvents(); Private.Resume(); @@ -1041,6 +1085,7 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) timer:ScheduleTimer(function() squelch_actions = false; end, remainingSquelch); -- No sounds while loading end end + Private.PostAddCompanion() elseif(event == "PLAYER_REGEN_ENABLED") then callback = function() if (queueshowooc) then @@ -1058,9 +1103,8 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) end end) -function WeakAuras.SetImporting(b) +function Private.SetImporting(b) importing = b; - Private.RefreshTooltipButtons() end function WeakAuras.IsImporting() @@ -1210,6 +1254,7 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) local changed = 0; local shouldBeLoaded, couldBeLoaded; + local parentsToCheck = {} wipe(toLoad); wipe(toUnload); @@ -1224,11 +1269,17 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) if(shouldBeLoaded and not loaded[id]) then changed = changed + 1; toLoad[id] = true; + for parent in Private.TraverseParents(data) do + parentsToCheck[parent.id] = true + end end if(loaded[id] and not shouldBeLoaded) then toUnload[id] = true; changed = changed + 1; + for parent in Private.TraverseParents(data) do + parentsToCheck[parent.id] = true + end end if(shouldBeLoaded) then loaded[id] = true; @@ -1246,14 +1297,25 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) Private.FinishLoadUnload(); end - for id, data in pairs(db.displays) do + Private.ScanForLoadsGroup(parentsToCheck) + Private.callbacks:Fire("ScanForLoads") + + wipe(toLoad); + wipe(toUnload) +end + +function Private.ScanForLoadsGroup(toCheck) + for id in pairs(toCheck) do + local data = WeakAuras.GetData(id) if(data.controlledChildren) then if(#data.controlledChildren > 0) then - local any_loaded; - for index, childId in pairs(data.controlledChildren) do - if(loaded[childId] ~= nil) then + local any_loaded = false; + for child in Private.TraverseLeafs(data) do + if(loaded[child.id] ~= nil) then any_loaded = true; break; + else + any_loaded = nil end end loaded[id] = any_loaded; @@ -1262,11 +1324,6 @@ local function scanForLoadsImpl(toCheck, event, arg1, ...) end end end - - Private.callbacks:Fire("ScanForLoads") - - wipe(toLoad); - wipe(toUnload) end function Private.ScanForLoads(toCheck, event, arg1, ...) @@ -1379,8 +1436,12 @@ function Private.Resume() end end + UnloadAll(); scanForLoadsImpl(); + if loadEvents["GROUP"] then + Private.ScanForLoadsGroup(loadEvents["GROUP"]) + end Private.ResumeAllDynamicGroups(); end @@ -1406,13 +1467,6 @@ function Private.UnloadDisplays(toUnload, ...) end for id in pairs(toUnload) do - -- Even though auras are collapsed, their finish animation can be running - Private.CancelAnimation(WeakAuras.regions[id].region, true, true, true, true, true, true) - if clones[id] then - for cloneId, region in pairs(clones[id]) do - Private.CancelAnimation(region, true, true, true, true, true, true) - end - end for i = 1, triggerState[id].numTriggers do if (triggerState[id][i]) then @@ -1438,6 +1492,14 @@ function Private.UnloadDisplays(toUnload, ...) WeakAuras.regions[id].region:Collapse(); Private.CollapseAllClones(id); + + -- Even though auras are collapsed, their finish animation can be running + Private.CancelAnimation(WeakAuras.regions[id].region, true, true, true, true, true, true) + if clones[id] then + for cloneId, region in pairs(clones[id]) do + Private.CancelAnimation(region, true, true, true, true, true, true) + end + end end end @@ -1480,7 +1542,9 @@ function WeakAuras.Delete(data) if parentData.sortHybridTable then parentData.sortHybridTable[id] = nil end - Private.ClearAuraEnvironment(data.parent); + for parent in Private.TraverseParents(data) do + Private.ClearAuraEnvironment(parent.id); + end end end @@ -1512,13 +1576,6 @@ function WeakAuras.Delete(data) regions[id].region:Hide(); db.registered[id] = nil; - if(WeakAuras.importDisplayButtons and WeakAuras.importDisplayButtons[id]) then - local button = WeakAuras.importDisplayButtons[id]; - button.checkbox:SetChecked(false); - if(button.updateChecked) then - button.updateChecked(); - end - end for _, triggerSystem in pairs(triggerSystems) do triggerSystem.Delete(id); @@ -1550,11 +1607,7 @@ function WeakAuras.Delete(data) Private.RemoveHistory(data.uid) - -- Add the parent - if parentUid then - WeakAuras.Add(Private.GetDataByUID(parentUid)) - end - + Private.AddParents(data) Private.callbacks:Fire("Delete", uid, id, parentUid, parentId) end @@ -1670,6 +1723,44 @@ function Private.Convert(data, newType) regions[id] = nil; data.regionType = newType; + + -- Clean up sub regions + if data.subRegions then + for index, subRegionData in ipairs_reverse(data.subRegions) do + local subType = subRegionData.type + local removeSubRegion = true + if subType and Private.subRegionTypes[subType] then + if Private.subRegionTypes[subType].supports(data.regionType) then + removeSubRegion = false + end + end + if removeSubRegion then + tremove(data.subRegions, index) + -- Adjust conditions! + if data.conditions then + for conditionIndex, condition in ipairs(data.conditions) do + if type(condition.changes) == "table" then + for changeIndex, change in ipairs(condition.changes) do + if change.property then + local subRegionIndex, property = change.property:match("^sub%.(%d+)%.(.*)") + subRegionIndex = tonumber(subRegionIndex) + if subRegionIndex and property then + if subRegionIndex == index then + change.property = nil + elseif subRegionIndex > index then + change.property = "sub." .. subRegionIndex -1 .. "." .. property + end + end + end + end + end + end + end + end + end + end + + WeakAuras.Add(data); Private.FakeStatesFor(id, true) @@ -1695,163 +1786,6 @@ function WeakAuras.DeepMixin(dest, source) recurse(source, dest); end -function WeakAuras.RegisterAddon(addon, displayName, description, icon) - if(addons[addon]) then - addons[addon].displayName = displayName; - addons[addon].description = description; - addons[addon].icon = icon; - addons[addon].displays = addons[addon].displays or {}; - else - addons[addon] = { - displayName = displayName, - description = description, - icon = icon, - displays = {} - }; - end -end - -function WeakAuras.RegisterDisplay(addon, data, force) - tinsert(from_files, {addon, data, force}); -end - -function WeakAuras.AddManyFromAddons(table) - for _, addData in ipairs(table) do - WeakAuras.AddFromAddon(addData[1], addData[2], addData[3]); - end -end - -function WeakAuras.AddFromAddon(addon, data, force) - local id = data.id; - if(id and addons[addon]) then - addons[addon].displays[id] = data; - if(db.registered[id]) then - -- This display was already registered - -- It is unnecessary to add it again - elseif(force and not db.registered[id] == false) then - if(db.displays[id]) then - -- ID collision - collisions[id] = {addon, data}; - else - db.registered[id] = addon; - WeakAuras.Add(data); - end - end - end -end - -function WeakAuras.CollisionResolved(addon, data, force) - WeakAuras.AddFromAddon(addon, data, force); -end - -function Private.IsDefinedByAddon(id) - return db.registered[id]; -end - -function Private.ResolveCollisions(onFinished) - local num = 0; - for id, _ in pairs(collisions) do - num = num + 1; - end - - if(num > 0) then - local baseText; - local buttonText; - if(registeredFromAddons) then - if(num == 1) then - baseText = L["Resolve collisions dialog singular"]; - buttonText = L["Done"]; - else - baseText = L["Resolve collisions dialog"]; - buttonText = L["Next"]; - end - else - if(num == 1) then - baseText = L["Resolve collisions dialog startup singular"]; - buttonText = L["Done"]; - else - baseText = L["Resolve collisions dialog startup"]; - buttonText = L["Next"]; - end - end - - local numResolved = 0; - local currentId = next(collisions); - - local function UpdateText(popup) - popup.text:SetText(baseText..(numResolved or "error").."/"..(num or "error")); - end - - StaticPopupDialogs["WEAKAURAS_RESOLVE_COLLISIONS"] = { - text = baseText, - button1 = buttonText, - OnAccept = function(self) - -- Do the collision resolution - local newId = self.editBox:GetText(); - if(WeakAuras.OptionsFrame and WeakAuras.OptionsFrame() and WeakAuras.displayButtons and WeakAuras.displayButtons[currentId]) then - WeakAuras.displayButtons[currentId].callbacks.OnRenameAction(newId) - else - local data = WeakAuras.GetData(currentId); - if(data) then - WeakAuras.Rename(data, newId); - else - print("|cFF8800FFWeakAuras|r: Data not found"); - end - end - - WeakAuras.CollisionResolved(collisions[currentId][1], collisions[currentId][2], true); - numResolved = numResolved + 1; - - -- Get the next id to resolve - currentId = next(collisions, currentId); - if(currentId) then - -- There is another conflict to resolve - hook OnHide to reshow the dialog as soon as it hides - self:SetScript("OnHide", function(self) - self:Show(); - UpdateText(self); - self.editBox:SetText(currentId); - self:SetScript("OnHide", nil); - if not(next(collisions, currentId)) then - self.button1:SetText(L["Done"]); - end - end); - else - self.editBox:SetScript("OnTextChanged", nil); - wipe(collisions); - if(onFinished) then - onFinished(); - end - end - end, - hasEditBox = true, - hasWideEditBox = true, - hideOnEscape = true, - whileDead = true, - showAlert = true, - timeout = 0, - preferredindex = STATICPOPUP_NUMDIALOGS - }; - - local popup = StaticPopup_Show("WEAKAURAS_RESOLVE_COLLISIONS"); - popup.editBox:SetScript("OnTextChanged", function(self) - local newid = self:GetText(); - if(collisions[newid] or db.displays[newid]) then - popup.button1:Disable(); - else - popup.button1:Enable(); - end - end); - popup.editBox:SetText(currentId); - popup.text:SetJustifyH("left"); - popup.icon:SetTexture("Interface\\Addons\\WeakAuras\\Media\\Textures\\icon.blp"); - popup.icon:SetVertexColor(0.833, 0, 1); - - UpdateText(popup); - elseif(onFinished) then - onFinished(); - end -end - local function LastUpgrade() return db.lastUpgrade and date(nil, db.lastUpgrade) or "unknown" end @@ -1862,7 +1796,7 @@ end local function RepairDatabase(loginAfter) local coro = coroutine.create(function() - WeakAuras.SetImporting(true) + Private.SetImporting(true) -- set db version to current code version db.dbVersion = WeakAuras.InternalVersion() -- reinstall snapshots from history @@ -1877,7 +1811,7 @@ local function RepairDatabase(loginAfter) end end db.displays = newDB - WeakAuras.SetImporting(false) + Private.SetImporting(false) -- finally, login Private.Login() end) @@ -1892,10 +1826,13 @@ StaticPopupDialogs["WEAKAURAS_CONFIRM_REPAIR"] = { RepairDatabase() end, OnShow = function(self) + local AutomaticRepairText = L["WeakAuras has detected that it has been downgraded.\nYour saved auras may no longer work properly.\nWould you like to run the |cffff0000EXPERIMENTAL|r repair tool? This will overwrite any changes you have made since the last database upgrade.\nLast upgrade: %s"] + local ManualRepairText = L["Are you sure you want to run the |cffff0000EXPERIMENTAL|r repair tool?\nThis will overwrite any changes you have made since the last database upgrade.\nLast upgrade: %s"] + if self.data.reason == "user" then - self.text:SetText(L["Manual Repair Confirmation Dialog"]:format(LastUpgrade())) + self.text:SetText(ManualRepairText:format(LastUpgrade())) else - self.text:SetText(L["Automatic Repair Confirmation Dialog"]:format(LastUpgrade())) + self.text:SetText(AutomaticRepairText:format(LastUpgrade())) end end, OnCancel = function(self) @@ -1938,13 +1875,13 @@ end function Private.SyncParentChildRelationships(silent) -- 1. Find all auras where data.parent ~= nil or data.controlledChildren ~= nil - -- If an aura has both, then remove data.parent -- If an aura has data.parent which doesn't exist, then remove data.parent -- If an aura has data.parent which doesn't have data.controlledChildren, then remove data.parent -- 2. For each aura with data.controlledChildren, iterate through the list of children and remove entries where: -- The child doesn't exist in the database -- The child ID is duplicated in data.controlledChildren (only the first will be kept) -- The child's data.parent points to a different parent + -- The parent is a dynamic group and the child is a group/dynamic group -- Otherwise, mark the child as having a valid parent relationship -- 3. For each aura with data.parent, remove data.parent if it was not marked to have a valid relationship in 2. local parents = {} @@ -1952,14 +1889,7 @@ function Private.SyncParentChildRelationships(silent) local childHasParent = {} for id, data in pairs(db.displays) do if data.parent then - if data.controlledChildren then - if not silent then - prettyPrint("Detected corruption in saved variables: "..id.." is a group that thinks it's a parent.") - end - -- A display cannot have both children and a parent - data.parent = nil - parents[id] = data - elseif not db.displays[data.parent] then + if not db.displays[data.parent] then if not(silent) then prettyPrint("Detected corruption in saved variables: "..id.." has a nonexistent parent.") end @@ -1973,7 +1903,8 @@ function Private.SyncParentChildRelationships(silent) else children[id] = data end - elseif data.controlledChildren then + end + if data.controlledChildren then parents[id] = data end end @@ -1981,6 +1912,7 @@ function Private.SyncParentChildRelationships(silent) for id, data in pairs(parents) do local groupChildren = {} local childrenToRemove = {} + local dynamicGroup = data.regionType == "dynamicgroup" for index, childID in ipairs(data.controlledChildren) do local child = children[childID] if not child then @@ -1993,6 +1925,13 @@ function Private.SyncParentChildRelationships(silent) prettyPrint("Detected corruption in saved variables: "..id.." thinks it controls "..childID.." which it does not.") end childrenToRemove[index] = true + elseif dynamicGroup and child.controlledChildren then + if not silent then + prettyPrint("Detected corruption in saved variables: "..id.." is a dynamic group and controls "..childID.." which is a group/dynamicgroup.") + end + child.parent = nil + children[child.id] = nil + childrenToRemove[index] = true elseif groupChildren[childID] then if not silent then prettyPrint("Detected corruption in saved variables: "..id.." has "..childID.." as a child in multiple positions.") @@ -2095,16 +2034,18 @@ end local function validateUserConfig(data, options, config) local authorOptionKeys, corruptOptions = {}, {} for index, option in ipairs(options) do - if not customOptionIsValid(option) then + if not customOptionIsValid(option) or authorOptionKeys[option.key] then prettyPrint(data.id .. " Custom Option #" .. index .. " in " .. data.id .. " has been detected as corrupt, and has been deleted.") corruptOptions[index] = true else local optionClass = Private.author_option_classes[option.type] + if option.key then + authorOptionKeys[option.key] = index + end if optionClass == "simple" then if not option.key then option.key = WeakAuras.GenerateUniqueID() end - authorOptionKeys[option.key] = index if config[option.key] == nil then if type(option.default) ~= "table" then config[option.key] = option.default @@ -2113,7 +2054,6 @@ local function validateUserConfig(data, options, config) end end elseif optionClass == "group" then - authorOptionKeys[option.key] = "group" local subOptions = option.subOptions if type(config[option.key]) ~= "table" then config[option.key] = {} @@ -2154,65 +2094,69 @@ local function validateUserConfig(data, options, config) end end end - for i = #options, 1, -1 do - if corruptOptions[i] then - tremove(options, i) - end - end for key, value in pairs(config) do if not authorOptionKeys[key] then config[key] = nil - elseif authorOptionKeys[key] ~= "group" then + else local option = options[authorOptionKeys[key]] - if type(value) ~= type(option.default) then - -- if type mismatch then we know that it can't be right - if type(option.default) ~= "table" then - config[key] = option.default - else - config[key] = CopyTable(option.default) - end - elseif option.type == "input" and option.useLength then - config[key] = config[key]:sub(1, option.length) - elseif option.type == "number" or option.type == "range" then - if (option.max and option.max < value) or (option.min and option.min > value) then - config[key] = option.default - else - if option.type == "number" and option.step then - local min = option.min or 0 - config[key] = option.step * Round((value - min)/option.step) + min - end - end - elseif option.type == "select" then - if value < 1 or value > #option.values then - config[key] = option.default - end - elseif option.type == "multiselect" then - local multiselect = config[key] - for i, v in ipairs(multiselect) do - if option.default[i] ~= nil then - if type(v) ~= "boolean" then - multiselect[i] = option.default[i] - end - else - multiselect[i] = nil - end - end - for i, v in ipairs(option.default) do - if type(multiselect[i]) ~= "boolean" then - multiselect[i] = v - end - end - elseif option.type == "color" then - for i = 1, 4 do - local c = config[key][i] - if type(c) ~= "number" or c < 0 or c > 1 then + local optionClass = Private.author_option_classes[option.type] + if optionClass ~= "group" then + local option = options[authorOptionKeys[key]] + if type(value) ~= type(option.default) then + -- if type mismatch then we know that it can't be right + if type(option.default) ~= "table" then config[key] = option.default - break + else + config[key] = CopyTable(option.default) + end + elseif option.type == "input" and option.useLength then + config[key] = config[key]:sub(1, option.length) + elseif option.type == "number" or option.type == "range" then + if (option.max and option.max < value) or (option.min and option.min > value) then + config[key] = option.default + else + if option.type == "number" and option.step then + local min = option.min or 0 + config[key] = option.step * Round((value - min)/option.step) + min + end + end + elseif option.type == "select" then + if value < 1 or value > #option.values then + config[key] = option.default + end + elseif option.type == "multiselect" then + local multiselect = config[key] + for i, v in ipairs(multiselect) do + if option.default[i] ~= nil then + if type(v) ~= "boolean" then + multiselect[i] = option.default[i] + end + else + multiselect[i] = nil + end + end + for i, v in ipairs(option.default) do + if type(multiselect[i]) ~= "boolean" then + multiselect[i] = v + end + end + elseif option.type == "color" then + for i = 1, 4 do + local c = config[key][i] + if type(c) ~= "number" or c < 0 or c > 1 then + config[key] = option.default + break + end end end end end end + for i = #options, 1, -1 do + if corruptOptions[i] then + tremove(options, i) + end + end end local function removeSpellNames(data) @@ -2337,6 +2281,33 @@ local oldDataStub2 = { conditions = {}, } +function Private.UpdateSoundIcon(data) + local function anySoundCondition() + if data.conditions then + for _, condition in ipairs(data.conditions) do + for changeIndex, change in ipairs(condition.changes) do + if change.property == "sound" then + return true + end + end + end + end + end + + if data.actions.start.do_sound or data.actions.finish.do_sound then + Private.AuraWarnings.UpdateWarning(data.uid, "sound_action", "sound", L["This aura plays a sound via an action."]) + else + Private.AuraWarnings.UpdateWarning(data.uid, "sound_action") + end + + if anySoundCondition() then + Private.AuraWarnings.UpdateWarning(data.uid, "sound_condition", "sound", L["This aura plays a sound via a condition."]) + else + Private.AuraWarnings.UpdateWarning(data.uid, "sound_condition") + end + +end + function WeakAuras.PreAdd(data) -- Readd what Compress removed before version 8 if (not data.internalVersion or data.internalVersion < 7) then @@ -2358,11 +2329,9 @@ function WeakAuras.PreAdd(data) Private.Modernize(data); WeakAuras.validate(data, WeakAuras.data_stub); if data.subRegions then - local result = {} for index, subRegionData in ipairs(data.subRegions) do local subType = subRegionData.type if subType and Private.subRegionTypes[subType] then - -- If it is not supported, then drop it if Private.subRegionTypes[subType].supports(data.regionType) then local default = Private.subRegionTypes[subType].default if type(default) == "function" then @@ -2371,16 +2340,11 @@ function WeakAuras.PreAdd(data) if default then WeakAuras.validate(subRegionData, default) end - - tinsert(result, subRegionData) + else + WeakAuras.prettyPrint(L["ERROR in '%s' unknown or incompatible sub element type '%s'"]:format(data.id, subType)) end - else - -- Unknown sub type is because the user didn't restart their client - -- so keep it - tinsert(result, subRegionData) end end - data.subRegions = result end validateUserConfig(data, data.authorOptions, data.config) removeSpellNames(data) @@ -2397,6 +2361,13 @@ local function pAdd(data, simpleChange) end data.uid = data.uid or WeakAuras.GenerateUniqueID() + if db.displays[id] and db.displays[id].uid ~= data.uid then + print("Improper? arguments to WeakAuras.Add - id", id, "is assigned to a different uid.", data.uid, db.displays[id].uid) + end + if UIDtoID[data.uid] and UIDtoID[data.uid] ~= id then + print("Improper? arguments to WeakAuras.Add - uid is assigned to a id. Uid:", data.uid, "assigned too:", UIDtoID[data.uid], "assigning now to", data.id) + end + local otherID = UIDtoID[data.uid] if not otherID then UIDtoID[data.uid] = id @@ -2409,16 +2380,24 @@ local function pAdd(data, simpleChange) if simpleChange then db.displays[id] = data WeakAuras.SetRegion(data) + if clones[id] then + for cloneId, region in pairs(clones[id]) do + WeakAuras.SetRegion(data, cloneId) + end + end Private.UpdatedTriggerState(id) else if (data.controlledChildren) then Private.ClearAuraEnvironment(id); - if data.parent then - Private.ClearAuraEnvironment(data.parent); + for parent in Private.TraverseParents(data) do + Private.ClearAuraEnvironment(parent.id); end db.displays[id] = data; WeakAuras.SetRegion(data); - else + Private.ScanForLoadsGroup({[id] = true}); + loadEvents["GROUP"] = loadEvents["GROUP"] or {} + loadEvents["GROUP"][id] = true + else -- Non group aura local visible if (WeakAuras.IsOptionsOpen()) then visible = Private.FakeStatesFor(id, false) @@ -2431,8 +2410,8 @@ local function pAdd(data, simpleChange) end Private.ClearAuraEnvironment(id); - if data.parent then - Private.ClearAuraEnvironment(data.parent); + for parent in Private.TraverseParents(data) do + Private.ClearAuraEnvironment(parent.id); end db.displays[id] = data; @@ -2503,6 +2482,8 @@ local function pAdd(data, simpleChange) Private.ScanForLoads({[id] = true}); end end + + Private.UpdateSoundIcon(data) end end @@ -2522,6 +2503,15 @@ function WeakAuras.Add(data, takeSnapshot, simpleChange) end end +function Private.AddParents(data) + local parent = data.parent + if (parent) then + local parentData = WeakAuras.GetData(parent) + WeakAuras.Add(parentData) + Private.AddParents(parentData) + end +end + function WeakAuras.SetRegion(data, cloneId) local regionType = data.regionType; if not(regionType) then @@ -2558,7 +2548,6 @@ function WeakAuras.SetRegion(data, cloneId) else if((not regions[id]) or (not regions[id].region) or regions[id].regionType ~= regionType) then region = regionTypes[regionType].create(frame, data); - region.regionType = regionType; regions[id] = { regionType = regionType, region = region @@ -2677,10 +2666,14 @@ function Private.ReleaseClone(id, cloneId, regionType) end local region = clones[id][cloneId]; clones[id][cloneId] = nil; - clonePool[regionType][#clonePool[regionType] + 1] = region; + if region:IsProtected() then + WeakAuras.prettyPrint(L["Error '%s' created a secure clone. We advise deleting the aura. For more information:\nhttps://github.com/WeakAuras/WeakAuras2/wiki/Protected-Frames"]:format(id)) + else + clonePool[regionType][#clonePool[regionType] + 1] = region; + end end -function Private.HandleChatAction(message_type, message, message_dest, message_channel, r, g, b, region, customFunc, when, formatters) +function Private.HandleChatAction(message_type, message, message_dest, message_dest_isunit, message_channel, r, g, b, region, customFunc, when, formatters) local useHiddenStates = when == "finish" if (message:find('%%')) then message = Private.ReplacePlaceHolders(message, region, customFunc, useHiddenStates, formatters); @@ -2695,11 +2688,13 @@ function Private.HandleChatAction(message_type, message, message_dest, message_c end elseif(message_type == "WHISPER") then if(message_dest) then - if(message_dest == "target" or message_dest == "'target'" or message_dest == "\"target\"" or message_dest == "%t" or message_dest == "'%t'" or message_dest == "\"%t\"") then - pcall(function() SendChatMessage(message, "WHISPER", nil, UnitName("target")) end); - else - pcall(function() SendChatMessage(message, "WHISPER", nil, message_dest) end); + if (message_dest:find('%%')) then + message_dest = Private.ReplacePlaceHolders(message_dest, region, customFunc, useHiddenStates, formatters); end + if message_dest_isunit == true then + message_dest = UnitName(message_dest) + end + pcall(function() SendChatMessage(message, "WHISPER", nil, message_dest) end); end elseif(message_type == "SMARTRAID") then if UnitInBattleground("player") then @@ -2919,7 +2914,7 @@ function Private.PerformActions(data, when, region) if(actions.do_message and actions.message_type and actions.message) then local customFunc = Private.customActionsFunctions[data.id][when .. "_message"]; - Private.HandleChatAction(actions.message_type, actions.message, actions.message_dest, actions.message_channel, actions.r, actions.g, actions.b, region, customFunc, when, formatters); + Private.HandleChatAction(actions.message_type, actions.message, actions.message_dest, actions.message_dest_isunit, actions.message_channel, actions.r, actions.g, actions.b, region, customFunc, when, formatters); end if(actions.do_sound and actions.sound) then @@ -3060,9 +3055,8 @@ function Private.GetOverlayInfo(data, triggernum) local overlayInfo; if (data.controlledChildren) then overlayInfo = {}; - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - local tmp = wrappedGetOverlayInfo(childData, triggernum); + for child in Private.TraverseLeafs(data) do + local tmp = wrappedGetOverlayInfo(child, triggernum); if (tmp) then for k, v in pairs(tmp) do overlayInfo[k] = v; @@ -3280,10 +3274,12 @@ local function SetFrameLevel(id, frameLevel) end function Private.FixGroupChildrenOrderForGroup(data) - local frameLevel = 5; - for i=1, #data.controlledChildren do - SetFrameLevel(data.controlledChildren[i], frameLevel); - frameLevel = frameLevel + 4; + local frameLevel = 1; + if data.parent == nil then + for child in Private.TraverseAll(data) do + SetFrameLevel(child.id, frameLevel); + frameLevel = frameLevel + 4; + end end end @@ -3293,10 +3289,18 @@ end function Private.ApplyFrameLevel(region, frameLevel) frameLevel = frameLevel or GetFrameLevelFor(region.id) - region:SetFrameLevel(frameLevel) + local subforegroundIndex = 0 if region.subRegions then for index, subRegion in pairs(region.subRegions) do - subRegion:SetFrameLevel(frameLevel + index + 1) + if subRegion.type == "subbackground" then + subRegion:SetFrameLevel(frameLevel + index) + end + end + + for index, subRegion in pairs(region.subRegions) do + if subRegion.type ~= "subbackground" then + subRegion:SetFrameLevel(frameLevel + index) + end end end end @@ -3414,7 +3418,7 @@ do for triggernum, triggerData in ipairs(triggers) do for id, state in pairs(triggerData) do if state.progressType == "timed" and state.expirationTime and state.expirationTime < t and state.duration and state.duration > 0 then - state.expirationTime = state.expirationTime + state.duration + state.expirationTime = t + state.duration state.changed = true changed = true end @@ -3495,7 +3499,7 @@ end local function startStopTimers(id, cloneId, triggernum, state) if (state.show) then - if (state.autoHide and state.duration and state.duration > 0) then -- autohide, update timer + if (state.autoHide and state.duration and state.duration > 0 and not state.paused) then -- autohide, update timer timers[id] = timers[id] or {}; timers[id][triggernum] = timers[id][triggernum] or {}; timers[id][triggernum][cloneId] = timers[id][triggernum][cloneId] or {}; @@ -3631,7 +3635,12 @@ local function ApplyStatesToRegions(id, activeTrigger, states) local triggerStates = WeakAuras.GetTriggerStateForTrigger(id, triggernum) triggerState = triggerStates[cloneId] or triggerStates[""] or {} end - applyChanges = applyChanges or region.states[triggernum] ~= triggerState or (triggerState and triggerState.changed) + if triggernum > 0 then + applyChanges = applyChanges or region.states[triggernum] ~= triggerState or (triggerState and triggerState.changed) + or region.states[triggernum] ~= triggerState + or (triggerState and triggerState.changed) + end + region.states[triggernum] = triggerState needsTimerTick = needsTimerTick or (triggerState and triggerState.show and triggerState.progressType == "timed") end @@ -3808,6 +3817,10 @@ local function ReplaceValuePlaceHolders(textStr, region, customFunc, state, form if custom then value = WeakAuras.EnsureString(custom[index]) end + + if formatter then + value = formatter(value, state) + end else local variable = Private.dynamic_texts[textStr]; if (not variable) then @@ -3942,7 +3955,7 @@ local function ValueForSymbol(symbol, region, customFunc, regionState, regionSta if regionStates[triggerNum][sym] then local value = regionStates[triggerNum][sym] if formatters[symbol] then - return tostring(formatters[symbol](value, regionStates[triggerNum])) or "" + return tostring(formatters[symbol](value, regionStates[triggerNum]) or "") or "" else return tostring(value) or "" end @@ -3984,7 +3997,7 @@ function Private.ReplacePlaceHolders(textStr, region, customFunc, useHiddenState if (endPos == 2) then if string.byte(textStr, 1) == 37 then local symbol = string.sub(textStr, 2) - local value = (regionState.show or useHiddenStates) and ReplaceValuePlaceHolders(symbol, region, customFunc, regionState, formatters[symbol]); + local value = ValueForSymbol(symbol, region, customFunc, regionState, regionStates, useHiddenStates, formatters); if (value) then textStr = tostring(value); end @@ -4096,20 +4109,20 @@ function Private.ParseTextStr(textStr, symbolCallback) end end -function Private.CreateFormatters(input, getter) +function Private.CreateFormatters(input, getter, withoutColor) local seenSymbols = {} local formatters = {} Private.ParseTextStr(input, function(symbol) if not seenSymbols[symbol] then local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") sym = sym or symbol - if sym == "c" or sym == "i" then + if sym == "i" then -- Do nothing else local default = (sym == "p" or sym == "t") and "timed" or "none" local selectedFormat = getter(symbol .. "_format", default) if (Private.format_types[selectedFormat]) then - formatters[symbol] = Private.format_types[selectedFormat].CreateFormatter(symbol, getter) + formatters[symbol] = Private.format_types[selectedFormat].CreateFormatter(symbol, getter, withoutColor) end end end @@ -4523,13 +4536,21 @@ end local textSymbols = { ["{rt1}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_1:0|t", - ["{rt2}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2:0|t ", - ["{rt3}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3:0|t ", - ["{rt4}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4:0|t ", - ["{rt5}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5:0|t ", - ["{rt6}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6:0|t ", - ["{rt7}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7:0|t ", - ["{rt8}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8:0|t " + ["{rt2}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_2:0|t", + ["{rt3}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_3:0|t", + ["{rt4}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_4:0|t", + ["{rt5}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_5:0|t", + ["{rt6}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_6:0|t", + ["{rt7}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_7:0|t", + ["{rt8}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcon_8:0|t", + ["{rt9}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:0:16:32:48|t", + ["{rt10}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:16:32:32:48|t", + ["{rt11}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:32:48:32:48|t", + ["{rt12}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:48:64:32:48|t", + ["{rt13}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:0:16:48:64|t", + ["{rt14}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:16:32:48:64|t", + ["{rt15}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:32:48:48:64|t", + ["{rt16}"] = "|TInterface\\TargetingFrame\\UI-RaidTargetingIcons.blp:0:0:0:0:64:64:48:64:48:64|t" } function WeakAuras.ReplaceRaidMarkerSymbols(txt) @@ -4627,15 +4648,57 @@ function WeakAuras.ParseNameCheck(name) name = {}, realm = {}, full = {}, - AddMatch = function(self, name, start, last) - local match = strtrim(name:sub(start, last)) + AddMatch = function(self, input, start, last) + local match = strtrim(input:sub(start, last)) - if (match:sub(1, 1) == "-") then - self.realm[match:sub(2)] = true - elseif match:find("-", 1, true) then - self.full[match] = true + -- state: 1: In name + -- state: 2: In Realm + -- state: -1: Escape Name + -- state: -2: In Escape Realm + local index = 1 + local state = 1 + local name = "" + local realm = "" + + + for index = 1, #match do + local c = match:sub(index, index) + + if state == -1 then + name = name .. c + state = 1 + elseif state == -2 then + realm = realm .. c + state = 2 + elseif state == 1 then + if c == "\\" then + state = -1 + elseif c == "-" then + state = 2 + else + name = name .. c + end + elseif state == 2 then + if c == "\\" then + state = -2 + else + realm = realm .. c + end + end + end + + if name == "" then + if realm == "" then + -- Do nothing + else + self.realm[realm] = true + end else - self.name[match] = true + if realm == "" then + self.name[name] = true + else + self.full[name .. "-" .. realm] = true + end end end, Check = function(self, name, realm) @@ -4693,3 +4756,93 @@ function WeakAuras.GetTriggerCategoryFor(triggerType) local prototype = Private.event_prototypes[triggerType] return prototype and prototype.type end + +do + local function shouldInclude(data, includeGroups, includeLeafs) + if data.controlledChildren then + return includeGroups + else + return includeLeafs + end + end + + local function Traverse(data, includeSelf, includeGroups, includeLeafs) + if includeSelf and shouldInclude(data, includeGroups, includeLeafs) then + coroutine.yield(data) + end + + if data.controlledChildren then + for _, child in ipairs(data.controlledChildren) do + Traverse(WeakAuras.GetData(child), true, includeGroups, includeLeafs) + end + end + end + + local function TraverseLeafs(data) + return Traverse(data, false, false, true) + end + + local function TraverseLeafsOrAura(data) + return Traverse(data, true, false, true) + end + + local function TraverseGroups(data) + return Traverse(data, true, true, false) + end + + local function TraverseSubGroups(data) + return Traverse(data, false, true, false) + end + + local function TraverseAllChildren(data) + return Traverse(data, false, true, true) + end + + local function TraverseAll(data) + return Traverse(data, true, true, true) + end + + local function TraverseParents(data) + while data.parent do + local parentData = WeakAuras.GetData(data.parent) + coroutine.yield(parentData) + data = parentData + end + end + + -- Only non-group auras, not include self + function Private.TraverseLeafs(data) + return coroutine.wrap(TraverseLeafs), data + end + + -- The root if it is a non-group, otherwise non-group childrens + function Private.TraverseLeafsOrAura(data) + return coroutine.wrap(TraverseLeafsOrAura), data + end + + -- All groups, includes self + function Private.TraverseGroups(data) + return coroutine.wrap(TraverseGroups), data + end + + -- All groups, excludes self + function Private.TraverseSubGroups(data) + return coroutine.wrap(TraverseSubGroups), data + end + + -- All Children, excludes self + function Private.TraverseAllChildren(data) + return coroutine.wrap(TraverseAllChildren), data + end + + -- All Children and self + function Private.TraverseAll(data) + return coroutine.wrap(TraverseAll), data + end + + function Private.TraverseParents(data) + return coroutine.wrap(TraverseParents), data + end +end + + diff --git a/WeakAuras/WeakAuras.toc b/WeakAuras/WeakAuras.toc index a91855d..5259dbf 100644 --- a/WeakAuras/WeakAuras.toc +++ b/WeakAuras/WeakAuras.toc @@ -1,7 +1,7 @@ ## Interface: 30300 ## Title: WeakAuras ## Author: The WeakAuras Team -## Version: 3.2.3 +## Version: 3.7.9 ## Notes: A powerful, comprehensive utility for displaying graphics and information based on buffs, debuffs, and other triggers. ## Notes-esES: Potente y completa aplicación que te permitirá mostrar por pantalla múltiples diseños, basados en beneficios, perjuicios y otros activadores. ## Notes-deDE: Ein leistungsfähiges, umfassendes Addon zur grafischen Darstellung von Informationen von Auren, Cooldowns, Timern und vielem mehr. @@ -20,16 +20,16 @@ compat.lua Pools.lua -# external code + initialization +# External code + initialization embeds.xml Init.lua locales.xml ArchiveTypes\Repository.lua DefaultOptions.lua -# core files -Prototypes.lua +# Core files Types.lua +Prototypes.lua Profiling.lua WeakAuras.lua History.lua @@ -38,14 +38,14 @@ Modernize.lua Animations.lua Conditions.lua -# trigger systems +# Trigger systems BuffTrigger.lua BuffTrigger2.lua GenericTrigger.lua AuraWarnings.lua AuraEnvironment.lua -# region support +# Region support RegionTypes\RegionPrototype.lua RegionTypes\ProgressTexture.lua RegionTypes\Texture.lua @@ -57,9 +57,10 @@ RegionTypes\DynamicGroup.lua RegionTypes\StopMotion.lua RegionTypes\Model.lua -# sub region support +# Sub-region support +SubRegionTypes\Background.lua SubRegionTypes\SubText.lua SubRegionTypes\Border.lua SubRegionTypes\Glow.lua SubRegionTypes\Tick.lua -SubRegionTypes\BarModel.lua +SubRegionTypes\Model.lua diff --git a/WeakAuras/compat.lua b/WeakAuras/compat.lua index 9801e0c..801e333 100644 --- a/WeakAuras/compat.lua +++ b/WeakAuras/compat.lua @@ -1,11 +1,26 @@ local ipairs = ipairs local pairs = pairs -local ceil, floor = math.ceil, math.floor +local abs, ceil, floor = math.abs, math.ceil, math.floor local GetInstanceInfo = GetInstanceInfo local GetNumPartyMembers = GetNumPartyMembers local GetNumRaidMembers = GetNumRaidMembers +function noop() + +end + +function ipairs_reverse(table) + local function Enumerator(table, index) + index = index - 1; + local value = table[index]; + if value ~= nil then + return index, value; + end + end + return Enumerator, table, #table + 1; +end + function tInvert(tbl) local inverted = {}; for k, v in pairs(tbl) do @@ -89,7 +104,7 @@ if not SmoothStatusBarMixin then local min, max = bar:GetMinMaxValues(); local range = max - min; if range > 0 then - return math.abs((newValue - targetValue) / range) < .00001; + return abs((newValue - targetValue) / range) < .00001; end return true; diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIContainer-WeakAurasInlineGroup.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIContainer-WeakAurasInlineGroup.lua index b2466b6..4dce5a5 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIContainer-WeakAurasInlineGroup.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIContainer-WeakAurasInlineGroup.lua @@ -32,7 +32,7 @@ local methods = { ["LayoutFinished"] = function(self, width, height) if self.noAutoHeight then return end - self:SetHeight((height or 0) + 40) + self:SetHeight(height or 0) end, ["OnWidthSet"] = function(self, width) diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasDisplayButton.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasDisplayButton.lua index bcff158..c630c80 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasDisplayButton.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasDisplayButton.lua @@ -5,7 +5,7 @@ local tinsert, tconcat, tremove, wipe = table.insert, table.concat, table.remove local select, pairs, next, type, unpack = select, pairs, next, type, unpack local tostring, error = tostring, error -local Type, Version = "WeakAurasDisplayButton", 56 +local Type, Version = "WeakAurasDisplayButton", 57 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -85,11 +85,10 @@ clipboard.pasteMenuEntry = { func = function() if (not IsRegionAGroup(clipboard.source) and IsRegionAGroup(clipboard.current)) then -- Copy from a single aura to a group => paste it to each individual aura - for index, childId in pairs(clipboard.current.controlledChildren) do - local childData = WeakAuras.GetData(childId); - copyAuraPart(clipboard.source, childData, clipboard.part); - WeakAuras.Add(childData) - WeakAuras.ClearAndUpdateOptions(childData.id) + for child in OptionsPrivate.Private.TraverseLeafs(clipboard.current) do + copyAuraPart(clipboard.source, child, clipboard.part); + WeakAuras.Add(child) + WeakAuras.ClearAndUpdateOptions(child.id) end else copyAuraPart(clipboard.source, clipboard.current, clipboard.part); @@ -99,9 +98,9 @@ clipboard.pasteMenuEntry = { WeakAuras.FillOptions() OptionsPrivate.Private.ScanForLoads({[clipboard.current.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); WeakAuras.PickDisplay(clipboard.current.id); - WeakAuras.UpdateDisplayButton(clipboard.current.id); + WeakAuras.UpdateThumbnail(clipboard.current.id); WeakAuras.ClearAndUpdateOptions(clipboard.current.id); end } @@ -260,7 +259,8 @@ local function Show_Long_Tooltip(owner, description) if(i == 1) then GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or "")); else - GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or ""), 1, 1, 1, 1, 1, 1, 1, 1); + GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or ""), + 1, 1, 1, 1, 1, 1, 1, 1); end end line = line + 1; @@ -299,10 +299,10 @@ local Actions = { source.data.parent = groupId WeakAuras.Add(source.data) WeakAuras.Add(group.data) + OptionsPrivate.Private.AddParents(group.data) WeakAuras.UpdateGroupOrders(group.data) WeakAuras.ClearAndUpdateOptions(group.data.id) WeakAuras.ClearAndUpdateOptions(source.data.id) - WeakAuras.UpdateDisplayButton(group.data) WeakAuras.FillOptions() group.callbacks.UpdateExpandButton(); group:ReloadTooltip() @@ -332,9 +332,9 @@ local Actions = { source:SetGroup() source.data.parent = nil WeakAuras.Add(parent); + OptionsPrivate.Private.AddParents(parent) WeakAuras.UpdateGroupOrders(parent); WeakAuras.ClearAndUpdateOptions(parent.id); - WeakAuras.UpdateDisplayButton(parent); local group = WeakAuras.GetDisplayButton(parent.id) group.callbacks.UpdateExpandButton(); group:ReloadTooltip() @@ -344,96 +344,27 @@ local Actions = { else error("Calling 'Ungroup' with invalid source. Reload your UI to fix the display list.") end - end, - -- move source inside its own group before or after target - ["Move"] = function(source, target, before ) - if source and source.data.parent then - local parent = WeakAuras.GetData(source.data.parent) - local children = parent.controlledChildren - local i = source:GetGroupOrder() - if ensure(children, i, source.data.id) then - if target and target.data.parent then - local j = target:GetGroupOrder() - if ensure(children, j, target.data.id) then - -- account for possible reorder - j = i < j and j-1 or j - -- account for insert position - j = before and j or j+1 - tremove(children, i) - tinsert(children, j, source.data.id) - else - error("Calling 'Move' with invalid target. Reload your UI to fix the display list.") - end - else - tremove(children, i) - tinsert(children, 1, source.data.id) - end - WeakAuras.Add(parent) - WeakAuras.ClearAndUpdateOptions(parent.id) - WeakAuras.FillOptions() - WeakAuras.UpdateGroupOrders(parent) - WeakAuras.UpdateDisplayButton(parent) - else - error("Calling 'Move' with invalid source. Reload your UI to fix the display list.") - end - else - error("Calling 'Move' with invalid source. Reload your UI to fix the display list.") - end - end, + end } -local Icons = { - ["Group"] = "Interface\\GossipFrame\\TrainerGossipIcon", - ["Ungroup"] = "Interface\\GossipFrame\\UnlearnGossipIcon", - ["Move"] = nil -} - -local function GetAction(target, area, source) - if target and source and (area == "TOP" or area == "BOTTOM")then - if target.data.parent and source.data.parent then - if source.data.parent == target.data.parent then - return function(_source, _target) - Actions["Move"](_source, _target, area=="TOP") - end, - Icons["Move"] - else - return function(_source, _target) - Actions["Ungroup"](_source) - Actions["Group"](_source, _target.data.parent, _target, area == "TOP") - end, - Icons["Group"] - end - elseif target.data.parent then -- and not source.data.parent +local function GetAction(target, area) + if target and area then + if area == "GROUP" then return function(_source, _target) - Actions["Group"](_source, _target.data.parent, _target, area == "TOP") - end, - Icons["Group"] - elseif source.data.parent then -- and not target.data.parent - if area == "TOP" then - return function(_source, _target) + if _source.data.parent then Actions["Ungroup"](_source) - Actions["Group"](_source) - end, - Icons["Ungroup"] - else -- area == "BOTTOM" - if source.data.parent == target.data.id then - return Actions["Move"], Icons["Move"] - else - return function(_source, _target) - Actions["Ungroup"](_source) - Actions["Group"](_source, _target.data.id) - end, - Icons["Group"] end + Actions["Group"](_source, _target.data.id) end - else -- not target.data.parent and not source.data.parent - if target:IsGroup() and area == "BOTTOM" then + else -- BEFORE or AFTER + -- Insert into target's parent, at the right position + if target.data.parent then return function(_source, _target) - Actions["Group"](_source, _target.data.id) - end, - Icons["Group"] - else - return nil + if _source.data.parent then + Actions["Ungroup"](_source) + end + Actions["Group"](_source, _target.data.parent, _target, area == "BEFORE") + end end end end @@ -443,18 +374,30 @@ end local function GetDropTarget() local buttonList = WeakAuras.displayButtons - local id, button, pos, offset - repeat - repeat - id, button = next(buttonList, id) - until not id or not button.dragging and button:IsEnabled() and button:IsShown() - if id and button then - offset = (button.frame.height or button.frame:GetHeight() or 16) / 2 - pos = button.frame:IsMouseOver(1,offset) and "TOP" - or button.frame:IsMouseOver(-offset,-1) and "BOTTOM" + + for id, button in pairs(buttonList) do + if not button.dragging and button:IsEnabled() and button:IsShown() then + local halfHeight = button.frame:GetHeight() / 2 + local height = button.frame:GetHeight() + if button.data.controlledChildren then + if button.data.parent == nil and button.frame:IsMouseOver(1, -1) then + -- Top level group, always group into + return id, button, "GROUP" + end + + -- For sub groups, middle third is for grouping + if button.frame:IsMouseOver(-height / 3, height / 3) then + return id, button, "GROUP" + end + end + + if button.frame:IsMouseOver(1, height / 2) then + return id, button, "BEFORE" + elseif button.frame:IsMouseOver(-height / 2, -1) then + return id, button, "AFTER" + end end - until not id or pos - return id, button, pos + end end local function Show_DropIndicator(id) @@ -464,36 +407,24 @@ local function Show_DropIndicator(id) if source then target, pos = select(2, GetDropTarget()) end - indicator:ClearAllPoints() - local action, icon = GetAction(target, pos, source) + local action = GetAction(target, pos) if action then - -- show line - if pos == "TOP" then - indicator:SetPoint("BOTTOMLEFT", target.frame, "TOPLEFT", 0, -1) - indicator:SetPoint("BOTTOMRIGHT", target.frame, "TOPRIGHT", 0, -1) - indicator:Show() - elseif pos == "BOTTOM" then - indicator:SetPoint("TOPLEFT", target.frame, "BOTTOMLEFT", 0, 1) - indicator:SetPoint("TOPRIGHT", target.frame, "BOTTOMRIGHT", 0, 1) - indicator:Show() - else - error("Invalid value pos '"..tostring(pos)) - end - -- show icon - if icon then - if indicator.icon.texture ~= icon then - indicator.icon.texture = icon - indicator.icon:SetTexture(icon) - end - indicator.icon:Show() - else - indicator.icon:Hide() - end + indicator:ShowAction(target, pos) else indicator:Hide() end end +local function IsParentRecursive(needle, parent) + if needle.id == parent.id then + return true + end + if needle.parent then + local needleParent = WeakAuras.GetData(needle.parent) + return IsParentRecursive(needleParent, parent) + end +end + --[[----------------------------------------------------------------------------- Methods -------------------------------------------------------------------------------]] @@ -502,17 +433,18 @@ local methods = { self:SetWidth(1000); self:SetHeight(32); self.hasThumbnail = false + self.first = false + self.last = false end, ["Initialize"] = function(self) - local data = self.data; self.callbacks = {}; function self.callbacks.OnClickNormal(_, mouseButton) - if(IsControlKeyDown() and not data.controlledChildren) then - if (OptionsPrivate.IsDisplayPicked(data.id)) then - OptionsPrivate.ClearPick(data.id); + if(IsControlKeyDown() and not self.data.controlledChildren) then + if (OptionsPrivate.IsDisplayPicked(self.data.id)) then + OptionsPrivate.ClearPick(self.data.id); else - OptionsPrivate.PickDisplayMultiple(data.id); + OptionsPrivate.PickDisplayMultiple(self.data.id); end self:ReloadTooltip(); elseif(IsShiftKeyDown()) then @@ -526,30 +458,34 @@ local methods = { fullName = name end end - editbox:Insert("[WeakAuras: "..fullName.." - "..data.id.."]"); + editbox:Insert("[WeakAuras: "..fullName.." - "..self.data.id.."]"); OptionsPrivate.Private.linked = OptionsPrivate.Private.linked or {} - OptionsPrivate.Private.linked[data.id] = true - elseif not data.controlledChildren then + OptionsPrivate.Private.linked[self.data.id] = GetTime() + elseif not self.data.controlledChildren then -- select all buttons between 1st select and current - OptionsPrivate.PickDisplayMultipleShift(data.id) + OptionsPrivate.PickDisplayMultipleShift(self.data.id) end else if(mouseButton == "RightButton") then Hide_Tooltip(); - if(OptionsPrivate.IsDisplayPicked(data.id) and OptionsPrivate.IsPickedMultiple()) then + if(OptionsPrivate.IsDisplayPicked(self.data.id) and OptionsPrivate.IsPickedMultiple()) then EasyMenu(OptionsPrivate.MultipleDisplayTooltipMenu(), WeakAuras_DropDownMenu, self.frame, 0, 0, "MENU"); else - UpdateClipboardMenuEntry(data); + UpdateClipboardMenuEntry(self.data); EasyMenu(self.menu, WeakAuras_DropDownMenu, self.frame, 0, 0, "MENU"); - if not(OptionsPrivate.IsDisplayPicked(data.id)) then - WeakAuras.PickDisplay(data.id); + if not(OptionsPrivate.IsDisplayPicked(self.data.id)) then + WeakAuras.PickDisplay(self.data.id); end end else - if (OptionsPrivate.IsDisplayPicked(data.id)) then + if (OptionsPrivate.IsDisplayPicked(self.data.id)) then OptionsPrivate.ClearPicks(); else - WeakAuras.PickDisplay(data.id); + if self.data.controlledChildren then + WeakAuras.PickDisplay(self.data.id, "group") + else + WeakAuras.PickDisplay(self.data.id); + end end self:ReloadTooltip(); end @@ -557,124 +493,160 @@ local methods = { end function self.callbacks.UpdateExpandButton() - if(#self.data.controlledChildren == 0) then + if(not self.data.controlledChildren or #self.data.controlledChildren == 0) then self:DisableExpand(); else self:EnableExpand(); end end + function self.callbacks.OnClickGrouping() if (WeakAuras.IsImporting()) then return end; - if #self.grouping > 0 then - for index, childId in ipairs(self.grouping) do - tinsert(data.controlledChildren, childId); - local childButton = WeakAuras.GetDisplayButton(childId); - local childData = WeakAuras.GetData(childId); - if childData.parent then - childButton:Ungroup(); - end - childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(#data.controlledChildren, #data.controlledChildren); - childData.parent = data.id; - WeakAuras.Add(childData); - WeakAuras.ClearAndUpdateOptions(childData.id) + for index, selectedId in ipairs(self.grouping) do + local selectedData = WeakAuras.GetData(selectedId); + tinsert(self.data.controlledChildren, selectedId); + local selectedButton = WeakAuras.GetDisplayButton(selectedId); + while selectedData.parent do + selectedButton:Ungroup(); + end + selectedButton:SetGroup(self.data.id, self.data.regionType == "dynamicgroup"); + selectedButton:SetGroupOrder(#self.data.controlledChildren, #self.data.controlledChildren); + selectedData.parent = self.data.id; + if (self.data.regionType == "dynamicgroup") then + selectedData.xOffset = 0 + selectedData.yOffset = 0 + end + WeakAuras.Add(selectedData); + OptionsPrivate.ClearOptions(selectedId) + + if (selectedData.controlledChildren) then + for child in OptionsPrivate.Private.TraverseAllChildren(selectedData) do + local childButton = WeakAuras.GetDisplayButton(child.id) + childButton:UpdateOffset() + end end - else - tinsert(data.controlledChildren, self.grouping.id); - local childButton = WeakAuras.GetDisplayButton(self.grouping.id); - childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(#data.controlledChildren, #data.controlledChildren); - self.grouping.parent = data.id; - WeakAuras.Add(self.grouping); - WeakAuras.ClearAndUpdateOptions(self.grouping.id); end - if (data.regionType == "dynamicgroup") then - self.grouping.xOffset = 0; - self.grouping.yOffset = 0; - end - WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id) + + WeakAuras.Add(self.data); + OptionsPrivate.Private.AddParents(self.data) self.callbacks.UpdateExpandButton(); - OptionsPrivate.SetGrouping(); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.ClearAndUpdateOptions(data.id); + OptionsPrivate.StopGrouping(); + OptionsPrivate.ClearOptions(self.data.id); WeakAuras.FillOptions(); - WeakAuras.UpdateGroupOrders(data); - WeakAuras.SortDisplayButtons(); + WeakAuras.UpdateGroupOrders(self.data); + OptionsPrivate.SortDisplayButtons(); self:ReloadTooltip(); + self:Expand() OptionsPrivate.ResetMoverSizer(); end function self.callbacks.OnClickGroupingSelf() - OptionsPrivate.SetGrouping(); + OptionsPrivate.StopGrouping(); self:ReloadTooltip(); end function self.callbacks.OnGroupClick() - OptionsPrivate.SetGrouping(data); + OptionsPrivate.StartGrouping(self.data); + 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 function self.callbacks.OnDeleteClick() if (WeakAuras.IsImporting()) then return end; - local toDelete = {data} - local parents = data.parent and {[data.parent] = true} + local toDelete = {self.data} + local parents = {} + addParents(parents, self.data) OptionsPrivate.ConfirmDelete(toDelete, parents) end + local function DuplicateGroups(sourceParent, targetParent, mapping) + for index, childId in pairs(sourceParent.controlledChildren) do + local childData = WeakAuras.GetData(childId) + if childData.controlledChildren then + local newChildGroup = OptionsPrivate.DuplicateAura(childData, targetParent.id) + mapping[childData] = newChildGroup + DuplicateGroups(childData, newChildGroup, mapping) + end + end + end + + local function DuplicateAuras(sourceParent, targetParent, mapping) + for index, childId in pairs(sourceParent.controlledChildren) do + local childData = WeakAuras.GetData(childId) + if childData.controlledChildren then + DuplicateAuras(childData, mapping[childData], mapping) + else + OptionsPrivate.DuplicateAura(childData, targetParent.id, true, index) + end + end + end + function self.callbacks.OnDuplicateClick() if (WeakAuras.IsImporting()) then return end; - if data.controlledChildren then - local new_idGroup = OptionsPrivate.DuplicateAura(data) - -- Do this after duplicating the parent! + if self.data.controlledChildren then + local newGroup = OptionsPrivate.DuplicateAura(self.data) + + local mapping = {} + -- This builds the group skeleton + DuplicateGroups(self.data, newGroup, mapping) + -- Do this after duplicating all groups OptionsPrivate.Private.PauseAllDynamicGroups() - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - OptionsPrivate.DuplicateAura(childData, new_idGroup, true) + -- And this fills in the leafs + DuplicateAuras(self.data, newGroup, mapping) + + local button = WeakAuras.GetDisplayButton(newGroup.id) + button.callbacks.UpdateExpandButton() + + for old, new in pairs(mapping) do + local button = WeakAuras.GetDisplayButton(new.id) + button.callbacks.UpdateExpandButton() end - local button = WeakAuras.GetDisplayButton(new_idGroup) - button.callbacks.UpdateExpandButton() - WeakAuras.UpdateDisplayButton(WeakAuras.GetData(new_idGroup)) - - WeakAuras.SortDisplayButtons() - OptionsPrivate.PickAndEditDisplay(new_idGroup) + OptionsPrivate.SortDisplayButtons(nil, true) + OptionsPrivate.PickAndEditDisplay(newGroup.id) OptionsPrivate.Private.ResumeAllDynamicGroups() else - local new_id = OptionsPrivate.DuplicateAura(data) - WeakAuras.SortDisplayButtons() - OptionsPrivate.PickAndEditDisplay(new_id) + local new = OptionsPrivate.DuplicateAura(self.data) + OptionsPrivate.SortDisplayButtons(nil, true) + OptionsPrivate.PickAndEditDisplay(new.id) end end function self.callbacks.OnDeleteAllClick() if (WeakAuras.IsImporting()) then return end; local toDelete = {} - if(data.controlledChildren) then - - local region = WeakAuras.regions[data.id]; - if (region.Suspend) then - region:Suspend(); - end - - for _, id in pairs(data.controlledChildren) do - tinsert(toDelete, WeakAuras.GetData(id)); + if(self.data.controlledChildren) then + local region = WeakAuras.regions[self.data.id]; + for child in OptionsPrivate.Private.TraverseAllChildren(self.data) do + tinsert(toDelete, child); end end - tinsert(toDelete, data) - OptionsPrivate.ConfirmDelete(toDelete); + tinsert(toDelete, self.data) + local parents = {} + addParents(parents, self.data) + OptionsPrivate.ConfirmDelete(toDelete, parents); end function self.callbacks.OnUngroupClick() - OptionsPrivate.Ungroup(data); + OptionsPrivate.Ungroup(self.data); end function self.callbacks.OnUpGroupClick() if (WeakAuras.IsImporting()) then return end; - if(data.parent) then - local id = data.id; - local parentData = WeakAuras.GetData(data.parent); + if(self.data.parent) then + local id = self.data.id; + local parentData = WeakAuras.GetData(self.data.parent); local index; for childIndex, childId in pairs(parentData.controlledChildren) do if(childId == id) then @@ -689,16 +661,16 @@ local methods = { tremove(parentData.controlledChildren, index); tinsert(parentData.controlledChildren, index - 1, id); WeakAuras.Add(parentData); + OptionsPrivate.Private.AddParents(parentData) WeakAuras.ClearAndUpdateOptions(parentData.id) self:SetGroupOrder(index - 1, #parentData.controlledChildren); local otherbutton = WeakAuras.GetDisplayButton(parentData.controlledChildren[index]); otherbutton:SetGroupOrder(index, #parentData.controlledChildren); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(); local updata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = -32}; local downdata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = 32}; - OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index-1]).uid, "main", updata, self.frame, true, function() WeakAuras.SortDisplayButtons() end); - OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index]).uid, "main", downdata, otherbutton.frame, true, function() WeakAuras.SortDisplayButtons() end); - WeakAuras.UpdateDisplayButton(parentData); + OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index-1]).uid, "main", updata, self.frame, true, function() OptionsPrivate.SortDisplayButtons() end); + OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index]).uid, "main", downdata, otherbutton.frame, true, function() OptionsPrivate.SortDisplayButtons() end); WeakAuras.FillOptions() end else @@ -711,9 +683,9 @@ local methods = { function self.callbacks.OnDownGroupClick() if (WeakAuras.IsImporting()) then return end; - if(data.parent) then - local id = data.id; - local parentData = WeakAuras.GetData(data.parent); + if(self.data.parent) then + local id = self.data.id; + local parentData = WeakAuras.GetData(self.data.parent); local index; for childIndex, childId in pairs(parentData.controlledChildren) do if(childId == id) then @@ -728,16 +700,16 @@ local methods = { tremove(parentData.controlledChildren, index); tinsert(parentData.controlledChildren, index + 1, id); WeakAuras.Add(parentData); + OptionsPrivate.Private.AddParents(parentData) WeakAuras.ClearAndUpdateOptions(parentData.id) self:SetGroupOrder(index + 1, #parentData.controlledChildren); local otherbutton = WeakAuras.GetDisplayButton(parentData.controlledChildren[index]); otherbutton:SetGroupOrder(index, #parentData.controlledChildren); - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons() local updata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = -32}; local downdata = {duration = 0.15, type = "custom", use_translate = true, x = 0, y = 32}; - OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index+1]).uid, "main", downdata, self.frame, true, function() WeakAuras.SortDisplayButtons() end); - OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index]).uid, "main", updata, otherbutton.frame, true, function() WeakAuras.SortDisplayButtons() end); - WeakAuras.UpdateDisplayButton(parentData); + OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index+1]).uid, "main", downdata, self.frame, true, function() OptionsPrivate.SortDisplayButtons() end); + OptionsPrivate.Private.Animate("button", WeakAuras.GetData(parentData.controlledChildren[index]).uid, "main", updata, otherbutton.frame, true, function() OptionsPrivate.SortDisplayButtons() end); WeakAuras.FillOptions() end else @@ -750,42 +722,21 @@ local methods = { function self.callbacks.OnViewClick() OptionsPrivate.Private.PauseAllDynamicGroups(); - - if(self.view.func() == 2) then - for index, childId in ipairs(data.controlledChildren) do - WeakAuras.GetDisplayButton(childId):PriorityHide(2); + if(self.view.visibility == 2) then + for child in OptionsPrivate.Private.TraverseAllChildren(self.data) do + WeakAuras.GetDisplayButton(child.id):PriorityHide(2); end + self:PriorityHide(2) else - for index, childId in ipairs(data.controlledChildren) do - WeakAuras.GetDisplayButton(childId):PriorityShow(2); + for child in OptionsPrivate.Private.TraverseAllChildren(self.data) do + WeakAuras.GetDisplayButton(child.id):PriorityShow(2); end + self:PriorityShow(2) end - + self:RecheckParentVisibility() OptionsPrivate.Private.ResumeAllDynamicGroups(); end - function self.callbacks.ViewTest() - local none, all = true, true; - for index, childId in ipairs(data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(childId); - if(childButton) then - if(childButton:GetVisibility() ~= 2) then - all = false; - end - if(childButton:GetVisibility() ~= 0) then - none = false; - end - end - end - if(all) then - return 2; - elseif(none) then - return 0; - else - return 1; - end - end - function self.callbacks.OnRenameClick() if (WeakAuras.IsImporting()) then return end; if(self.title:IsVisible()) then @@ -798,78 +749,46 @@ local methods = { end end - function self.callbacks.OnUpdateClick() - local _,_,updateData = self:HasUpdate() - if updateData then - WeakAuras.Import(updateData.encoded, self.data) - end - end - function self.callbacks.OnRenameAction(newid) if (WeakAuras.IsImporting()) then return end; - local oldid = data.id; + local oldid = self.data.id; if not(newid == oldid) then - WeakAuras.Rename(data, newid); + WeakAuras.Rename(self.data, newid); end end function self.callbacks.OnDragStart() - if WeakAuras.IsImporting() or self:IsGroup() then return end; - if #OptionsPrivate.tempGroup.controlledChildren == 0 then - WeakAuras.PickDisplay(data.id); + if WeakAuras.IsImporting() then return end; + if not OptionsPrivate.IsDisplayPicked(self.data.id) then + WeakAuras.PickDisplay(self.data.id) end - OptionsPrivate.SetDragging(data); + OptionsPrivate.StartDrag(self.data); end function self.callbacks.OnDragStop() if not self.dragging then return end - OptionsPrivate.SetDragging(data, true) + local target, area = select(2, GetDropTarget()) + local action = GetAction(target, area) + OptionsPrivate.Drop(self.data, target, action, area) end function self.callbacks.OnKeyDown(self, key) if (key == "ESCAPE") then - OptionsPrivate.SetDragging(); + OptionsPrivate.DragReset() end end - function self.callbacks.wagoStopIgnoreAll(_, skipUpdateIcon) - self.data.ignoreWagoUpdate = nil - self.data.skipWagoUpdate = nil - if not skipUpdateIcon then - self:RefreshUpdate("wagoStopIgnoreAll") + self.frame:SetScript("OnEnter", function() + if(OptionsPrivate.IsPickedMultiple() and OptionsPrivate.IsDisplayPicked(self.frame.id)) then + Show_Long_Tooltip(self.frame, OptionsPrivate.MultipleDisplayTooltipDesc()); + else + if not self.grouping then + self:SetNormalTooltip(); + end + Show_Long_Tooltip(self.frame, self.frame.description); end - end - - function self.callbacks.wagoIgnoreAll(_, skipUpdateIcon) - self.data.ignoreWagoUpdate = true - if not skipUpdateIcon then - self:RefreshUpdate("wagoIgnoreAll") - end - end - - function self.callbacks.wagoStopIgnoreNext(_, skipUpdateIcon) - self.data.skipWagoUpdate = nil - if not skipUpdateIcon then - self:RefreshUpdate("wagoStopIgnoreNext") - end - end - - function self.callbacks.wagoIgnoreNext(_, skipUpdateIcon) - self.data.skipWagoUpdate = self.update.version - if not skipUpdateIcon then - self:RefreshUpdate("wagoIgnoreNext") - end - end - - self.frame.terribleCodeOrganizationHackTable = {}; - - function self.frame.terribleCodeOrganizationHackTable.IsGroupingOrCopying() - return self.grouping; - end - - function self.frame.terribleCodeOrganizationHackTable.SetNormalTooltip() - self:SetNormalTooltip(); - end + end); + self.frame:SetScript("OnLeave", Hide_Tooltip); local copyEntries = {}; tinsert(copyEntries, clipboard.copyEverythingEntry); @@ -883,7 +802,7 @@ local methods = { tinsert(copyEntries, clipboard.copyAuthorOptionsEntry); tinsert(copyEntries, clipboard.copyUserConfigEntry); - self:SetTitle(data.id); + self:SetTitle(self.data.id); self.menu = { { text = L["Rename"], @@ -900,15 +819,15 @@ local methods = { tinsert(self.menu, clipboard.pasteMenuEntry); - if (not data.controlledChildren) then + if (not self.data.controlledChildren) then local convertMenu = {}; for regionType, regionData in pairs(WeakAuras.regionOptions) do - if(regionType ~= "group" and regionType ~= "dynamicgroup" and regionType ~= "timer" and regionType ~= data.regionType) then + if(regionType ~= "group" and regionType ~= "dynamicgroup" and regionType ~= self.data.regionType) then tinsert(convertMenu, { text = regionData.displayName, notCheckable = true, func = function() - OptionsPrivate.ConvertDisplay(data, regionType); + OptionsPrivate.ConvertDisplay(self.data, regionType); WeakAuras_DropDownMenu:Hide(); end }); @@ -929,31 +848,22 @@ local methods = { }); tinsert(self.menu, { - text = L["Export to string..."], + text = L["Export..."], notCheckable = true, - func = function() OptionsPrivate.ExportToString(data.id) end + func = function() OptionsPrivate.ExportToString(self.data.id) end }); tinsert(self.menu, { - text = L["Export to Lua table..."], + text = L["Export debug table..."], notCheckable = true, - func = function() OptionsPrivate.ExportToTable(data.id) end + func = function() OptionsPrivate.ExportToTable(self.data.id) end }); - if WeakAurasCompanion then - tinsert(self.menu, { - text = '|TInterface\\OptionsFrame\\UI-OptionsFrame-NewFeatureIcon:0|t' .. L["Wago Update"], - notCheckable = true, - hasArrow = true, - menuList = { } - }); - end - tinsert(self.menu, { text = " ", notClickable = true, notCheckable = true, }); - if not data.controlledChildren then + if not self.data.controlledChildren then tinsert(self.menu, { text = L["Delete"], notCheckable = true, @@ -961,7 +871,7 @@ local methods = { }); end - if (data.controlledChildren) then + if (self.data.controlledChildren) then tinsert(self.menu, { text = L["Delete children and group"], notCheckable = true, @@ -978,16 +888,18 @@ local methods = { notCheckable = true, func = function() WeakAuras_DropDownMenu:Hide() end }); - if(data.controlledChildren) then - self:SetViewClick(self.callbacks.OnViewClick); - self:SetViewTest(self.callbacks.ViewTest); - self:DisableGroup(); + if(self.data.controlledChildren) then + self.loaded:Hide(); + self.expand:Show(); self.callbacks.UpdateExpandButton(); - self:SetOnExpandCollapse(function() WeakAuras.SortDisplayButtons(nil, true) end); + self:SetOnExpandCollapse(function() OptionsPrivate.SortDisplayButtons(nil, true) end); else - self:SetViewRegion(WeakAuras.regions[data.id].region); - self:EnableGroup(); + self:SetViewRegion(WeakAuras.regions[self.data.id].region); + self.loaded:Show(); + self.expand:Hide(); end + self.group:Show(); + self:SetNormalTooltip(); self.frame:SetScript("OnClick", self.callbacks.OnClickNormal); self.frame:SetScript("OnKeyDown", self.callbacks.OnKeyDown); @@ -1003,37 +915,22 @@ local methods = { self.ungroup:SetScript("OnClick", self.callbacks.OnUngroupClick); self.upgroup:SetScript("OnClick", self.callbacks.OnUpGroupClick); self.downgroup:SetScript("OnClick", self.callbacks.OnDownGroupClick); + self.view:SetScript("OnClick", self.callbacks.OnViewClick); - if WeakAurasCompanion then - local hasUpdate, _, updateData = self:HasUpdate() - if hasUpdate then - self.update.hasUpdate = hasUpdate - self.update.version = updateData.wagoVersion - local showVersion = self.data.semver or self.data.version or 0 - local showCompanionVersion = updateData.wagoSemver or updateData.wagoVersion - self.update.title = L["Update %s by %s"]:format(updateData.name, updateData.author) - self.update.desc = L["From version %s to version %s"]:format(showVersion, showCompanionVersion) - if updateData.versionNote then - self.update.desc = ("%s\n\n%s"):format(self.update.desc, updateData.versionNote) - end - self.update:SetScript("OnClick", self.callbacks.OnUpdateClick); - end - end - - if data.parent then - local parentData = WeakAuras.GetData(data.parent); + if self.data.parent then + local parentData = WeakAuras.GetData(self.data.parent); local index; for childIndex, childId in pairs(parentData.controlledChildren) do - if(childId == data.id) then + if(childId == self.data.id) then index = childIndex; break; end end if(index) then - self:SetGroup(data.parent); + self:SetGroup(self.data.parent); self:SetGroupOrder(index, #parentData.controlledChildren); else - error("Display \""..data.id.."\" thinks it is a member of group \""..data.parent.."\" which does not control it"); + error("Display \""..self.data.id.."\" thinks it is a member of group \""..self.data.parent.."\" which does not control it"); end end @@ -1043,21 +940,29 @@ local methods = { local data = self.data; local namestable = {}; if(data.controlledChildren) then - for index, childId in pairs(data.controlledChildren) do - tinsert(namestable, {" ", childId}); + namestable[1] = ""; + local function addChildrenNames(data, indent) + for index, childId in pairs(data.controlledChildren) do + tinsert(namestable, indent .. childId); + local childData = WeakAuras.GetData(childId) + if (childData.controlledChildren) then + addChildrenNames(childData, indent .. " ") + end + end end + addChildrenNames(data, " ") if (#namestable > 30) then local size = #namestable; namestable[26] = {" ", "[...]"}; - namestable[27] = {L[string.format(L["%s total auras"], #data.controlledChildren)], " " } + namestable[27] = {L[string.format(L["%s total auras"], #namestable)], " " } for i = 28, size do namestable[i] = nil; end end - if(#namestable > 0) then - namestable[1][1] = L["Children:"]; + if(#namestable > 1) then + namestable[1] = L["Children:"]; else namestable[1] = L["No Children"]; end @@ -1067,10 +972,6 @@ local methods = { if(OptionsPrivate.Private.CanHaveClones(data)) then tinsert(namestable, {" ", "|cFF00FF00"..L["Auto-cloning enabled"]}) end - if(OptionsPrivate.Private.IsDefinedByAddon(data.id)) then - tinsert(namestable, " "); - tinsert(namestable, {" ", "|cFF00FFFF"..L["Addon"]..": "..OptionsPrivate.Private.IsDefinedByAddon(data.id)}); - end local hasDescription = data.desc and data.desc ~= ""; local hasUrl = data.url and data.url ~= ""; @@ -1094,8 +995,8 @@ local methods = { tinsert(namestable, " "); tinsert(namestable, {" ", "|cFF00FFFF"..L["Right-click for more options"]}); + tinsert(namestable, {" ", "|cFF00FFFF"..L["Drag to move"]}); if not(data.controlledChildren) then - tinsert(namestable, {" ", "|cFF00FFFF"..L["Drag to move"]}); tinsert(namestable, {" ", "|cFF00FFFF"..L["Control-click to select multiple displays"]}); end tinsert(namestable, {" ", "|cFF00FFFF"..L["Shift-click to create chat link"]}); @@ -1103,193 +1004,243 @@ local methods = { local displayName = regionData and regionData.displayName or ""; self:SetDescription({data.id, displayName}, unpack(namestable)); end, - ["ReloadTooltip"] = function(self)if( - OptionsPrivate.IsPickedMultiple() and OptionsPrivate.IsDisplayPicked(self.data.id)) then - Show_Long_Tooltip(self.frame, OptionsPrivate.MultipleDisplayTooltipDesc()); - else - Show_Long_Tooltip(self.frame, self.frame.description); - end - end, - ["SetGrouping"] = function(self, groupingData, multi) - self.grouping = groupingData; - if(self.grouping) then - if(self.data.id == self.grouping.id or multi) then - self.frame:SetScript("OnClick", self.callbacks.OnClickGroupingSelf); - self:SetDescription(L["Cancel"], L["Do not group this display"]); - else - if(self.data.regionType == "group" or self.data.regionType == "dynamicgroup") then - self.frame:SetScript("OnClick", self.callbacks.OnClickGrouping); - self:SetDescription(self.data.id, L["Add to group %s"]:format(self.data.id)); - else - self:Disable(); - end - end + ["ReloadTooltip"] = function(self) + if(OptionsPrivate.IsPickedMultiple() and OptionsPrivate.IsDisplayPicked(self.data.id)) then + Show_Long_Tooltip(self.frame, OptionsPrivate.MultipleDisplayTooltipDesc()); else - self:SetNormalTooltip(); - self.frame:SetScript("OnClick", self.callbacks.OnClickNormal); - self:Enable(); + Show_Long_Tooltip(self.frame, self.frame.description); end end, + ["StartGrouping"] = function(self, groupingData, selected, groupingGroup, childOfGrouing) + self.grouping = groupingData; + self:UpdateIconsVisible() + if(selected) then + self.frame:SetScript("OnClick", self.callbacks.OnClickGroupingSelf); + self:SetDescription(L["Cancel"], L["Do not group this display"]); + elseif (childOfGrouing) then + self:Disable(); + else + if(self.data.regionType == "dynamicgroup" and groupingGroup) then + self:Disable(); + elseif (self.data.regionType == "group" or self.data.regionType == "dynamicgroup") then + self.frame:SetScript("OnClick", self.callbacks.OnClickGrouping); + self:SetDescription(self.data.id, L["Add to group %s"]:format(self.data.id)); + else + self:Disable(); + end + end + end, + ["StopGrouping"] = function(self) + self.grouping = nil; + self:UpdateIconsVisible() + self:SetNormalTooltip(); + self.frame:SetScript("OnClick", self.callbacks.OnClickNormal); + self:Enable(); + end, ["Ungroup"] = function(self) if (WeakAuras.IsImporting()) then return end; local parentData = WeakAuras.GetData(self.data.parent); if not parentData then return end; - local index; - for childIndex, childId in pairs(parentData.controlledChildren) do - if(childId == self.data.id) then - index = childIndex; - break; - end - end + local index = tIndexOf(parentData.controlledChildren, self.data.id); if(index) then tremove(parentData.controlledChildren, index); WeakAuras.Add(parentData); + OptionsPrivate.Private.AddParents(parentData) WeakAuras.ClearAndUpdateOptions(parentData.id); else error("Display thinks it is a member of a group which does not control it"); end - self:SetGroup(); - self.data.parent = nil; + + local newParent = parentData.parent and WeakAuras.GetData(parentData.parent) + if newParent then + local insertIndex = tIndexOf(newParent.controlledChildren, parentData.id) + if not insertIndex then + error("Parent Display thinks it is a member of a group which does not control it"); + end + insertIndex = insertIndex + 1 + tinsert(newParent.controlledChildren, insertIndex, self.data.id) + end + + self:SetGroup(newParent and newParent.id); + self.data.parent = newParent and newParent.id; WeakAuras.Add(self.data); + self:UpdateIconsVisible() + if newParent then + WeakAuras.Add(newParent) + OptionsPrivate.Private.AddParents(newParent) + WeakAuras.ClearAndUpdateOptions(newParent.id) + WeakAuras.UpdateGroupOrders(newParent) + end WeakAuras.ClearAndUpdateOptions(self.data.id); WeakAuras.UpdateGroupOrders(parentData); - WeakAuras.UpdateDisplayButton(parentData); - WeakAuras.SortDisplayButtons(); - end, - ["SetDragging"] = function(self, data, drop, size) - if (size) then - self.multi = { - size = size, - selected = data and (data.id == self.data.id) - } + if(#parentData.controlledChildren == 0) then + local parentButton = WeakAuras.GetDisplayButton(parentData.id) + parentButton:DisableExpand() end - if data then - -- self - if self.data.id == data.id or self.multi then - if drop then - self:Drop() - self.frame:SetScript("OnClick", self.callbacks.OnClickNormal) - self.frame:EnableKeyboard(false); -- disables self.callbacks.OnKeyDown - else - Hide_Tooltip() - self.frame:SetScript("OnClick", nil) - self.frame:EnableKeyboard(true); -- enables self.callbacks.OnKeyDown - self:Drag() - end - -- invalid targets - elseif not self.data.parent and not self:IsGroup() - then - if drop then - self:Enable() - else - self:Disable() - end - -- valid target - else - if drop then - self.frame:SetScript("OnClick", self.callbacks.OnClickNormal) - else - self.frame:SetScript("OnClick", nil) - end - end + + for child in OptionsPrivate.Private.TraverseAllChildren(self.data) do + local button = WeakAuras.GetDisplayButton(child.id) + button:UpdateOffset() + end + + OptionsPrivate.SortDisplayButtons(); + end, + ["UpdateIconsVisible"] = function(self) + if self.dragging or self.grouping then + self.downgroup:Hide() + self.group:Hide() + self.ungroup:Hide() + self.upgroup:Hide() else - -- restore events and layout - self.frame:SetScript("OnClick", self.callbacks.OnClickNormal) - self.frame:EnableKeyboard(false); - self:Enable() - if (self.dragging) then - self:Drop(true) - end - end - end, - ["ShowTooltip"] = function(self) - end, - ["Drag"] = function(self) - local uiscale, scale = UIParent:GetScale(), self.frame:GetEffectiveScale() - local x, w = self.frame:GetLeft(), self.frame:GetWidth() - local _, y = GetCursorPosition() - -- hide "visual clutter" - self.downgroup:Hide() - self.group:Hide() - self.loaded:Hide() - self.ungroup:Hide() - self.upgroup:Hide() - self.view:Hide() - -- mark as being dragged, attach to mouse and raise frame strata - self.dragging = true - --self.frame:ClearAllPoints() - self.frame.temp = { - parent = self.frame:GetParent(), - strata = self.frame:GetFrameStrata(), - } - self.frame:StartMoving() - --self.frame:SetParent(UIParent) - self.frame:SetFrameStrata("FULLSCREEN_DIALOG") - if not self.multi then - --self.frame:SetPoint("Center", UIParent, "BOTTOMLEFT", (x+w/2)*scale/uiscale, y/uiscale) - else - if self.multi.selected then - -- change label & icon - --self.frame:SetPoint("Center", UIParent, "BOTTOMLEFT", (x+w/2)*scale/uiscale, y/uiscale) - self.frame.temp.title = self.title:GetText() - self.title:SetText((L["%i auras selected"]):format(self.multi.size)) - self:OverrideIcon(); - else - -- Hide frames - self.frame:StopMovingOrSizing() - self.frame:Hide() - end - end - -- attach OnUpdate event to update drop indicator - if not self.multi or (self.multi and self.multi.selected) then - local id = self.data.id - self.frame:SetScript("OnUpdate", function(self,elapsed) - self.elapsed = (self.elapsed or 0) + elapsed - if self.elapsed > 0.1 then - Show_DropIndicator(id) - self.elapsed = 0 - end - end) - Show_DropIndicator(id) - end - OptionsPrivate.UpdateButtonsScroll() - end, - ["Drop"] = function(self, reset) - Show_DropIndicator() - local target, area = select(2, GetDropTarget()) - -- get action and execute it - self.frame:StopMovingOrSizing() - self.frame:SetScript("OnUpdate", nil) - if self.multi and self.multi.selected then - -- restore title and icon - self.title:SetText(self.frame.temp.title) - self:RestoreIcon(); - end - if self.dragging then - self.frame:SetParent(self.frame.temp.parent) - self.frame:SetFrameStrata(self.frame.temp.strata) - self.frame.temp = nil + self.group:Show() if self.data.parent then self.downgroup:Show() self.ungroup:Show() self.upgroup:Show() else - self.group:Show() + self.downgroup:Hide() + self.ungroup:Hide() + self.upgroup:Hide() end + end + end, + ["DragStart"] = function(self, mode, picked, mainAura, size) + self.frame:SetScript("OnClick", nil) + self.view:Hide() + self.expand:Hide() + self.loaded:Hide() + Hide_Tooltip() + if picked then + self.frame:EnableKeyboard(true) + local uiscale, scale = UIParent:GetScale(), self.frame:GetEffectiveScale() + local x, w = self.frame:GetLeft(), self.frame:GetWidth() + local _, y = GetCursorPosition() + -- mark as being dragged, attach to mouse and raise frame strata + self.dragging = true + self.frame:StartMoving() + --self.frame:ClearAllPoints() + self.frame.temp = { + parent = self.frame:GetParent(), + strata = self.frame:GetFrameStrata(), + } + --self.frame:SetParent(UIParent) + self.frame:SetFrameStrata("FULLSCREEN_DIALOG") + if self.data.id == mainAura.id then + self.frame:SetPoint("Center", UIParent, "BOTTOMLEFT", (x+w/2)*scale/uiscale, y/uiscale) + if mode == "MULTI" then + -- change label & icon + self.frame:SetPoint("Center", UIParent, "BOTTOMLEFT", (x+w/2)*scale/uiscale, y/uiscale) + self.frame.temp.title = self.title:GetText() + self.title:SetText((L["%i auras selected"]):format(size)) + self:OverrideIcon(); + end + else + -- Hide frames + self.frame:StopMovingOrSizing() + self.frame:Hide() + end + -- attach OnUpdate event to update drop indicator + if self.data.id == mainAura.id then + local id = self.data.id + self.frame:SetScript("OnUpdate", function(self,elapsed) + self.elapsed = (self.elapsed or 0) + elapsed + if self.elapsed > 0.1 then + Show_DropIndicator(id) + self.elapsed = 0 + end + end) + Show_DropIndicator(id) + end + self:UpdateIconsVisible() + OptionsPrivate.UpdateButtonsScroll() + else + -- Are we a valid target? + -- Top level auras that aren't groups aren't + if not self.data.parent and not self:IsGroup() then + self:Disable() + end + + -- If we are dragging a group, dynamic groups aren't valid targets + if mode == "GROUP" then + if self.data.regionType == "dynamicgroup" then + self:Disable() + else + local parentData = self.data.parent and WeakAuras.GetData(self.data.parent) + if (parentData and parentData.regionType == "dynamicgroup") then + self:Disable() + end + end + end + end + end, + ["Drop"] = function(self, mode, mainAura, target, func) + if mode == "MULTI" or mode == "SINGLE" then + if self.dragging then + if func and target then + func(self, target) + end + end + elseif mode == "GROUP" then + if mainAura.id == self.data.id then + if func and target then + func(self, target) + end + end + end + self:DropEnd() + end, + ["IsDragging"] = function(self) + return self.dragging + end, + ["DragReset"] = function(self) + self:DropEnd() + end, + ["DropEnd"] = function(self) + Show_DropIndicator() + + self.frame:SetScript("OnClick", self.callbacks.OnClickNormal) + self.frame:EnableKeyboard(false); -- disables self.callbacks.OnKeyDown + self.view:Show() + if self.data.controlledChildren then + self.expand:Show() + else self.loaded:Show() - self.view:Show() + end + self:Enable() + + -- get action and execute it + self.frame:StopMovingOrSizing() + self.frame:SetScript("OnUpdate", nil) + if self.dragging then + if self.frame.temp.title then + -- restore title and icon + self.title:SetText(self.frame.temp.title) + self:RestoreIcon(); + end + self.frame:SetParent(self.frame.temp.parent) + self.frame:SetFrameStrata(self.frame.temp.strata) + self.frame.temp = nil end self.dragging = false - -- exit if we have no target or only want to reset - self.multi = nil - if reset or not target then - return OptionsPrivate.UpdateButtonsScroll() + self:UpdateIconsVisible() + end, + ["ShowTooltip"] = function(self) + end, + ["UpdateOffset"] = function(self) + local group = self.frame.dgroup + if group then + local depth = 0 + while(group) do + depth = depth + 1 + group = WeakAuras.GetData(group).parent + end + self.offset:SetWidth(depth * 8 + 1) + else + self.offset:SetWidth(1) end - local action = GetAction(target, area, self) - if action then - action(self, target) - end - WeakAuras.SortDisplayButtons() + end, + ["GetOffset"] = function(self) + return self.offset:GetWidth() end, ["GetGroupOrCopying"] = function(self) return self.group; @@ -1306,35 +1257,14 @@ local methods = { end, ["SetViewRegion"] = function(self, region) self.view.region = region; - self.view.func = function() return self.view.visibility end; - self.view:SetScript("OnClick", function() - if(self.view.visibility < 2) then - self.view:PriorityShow(2); - else - self.view:PriorityHide(2); - end - end); - end, - ["SetViewClick"] = function(self, func) - self.view:SetScript("OnClick", func); - end, - ["SetViewTest"] = function(self, func) - self.view.func = func; end, ["SetRenameAction"] = function(self, func) self.renamebox.func = function() func(self.renamebox:GetText()); end end, - ["DisableGroup"] = function(self) - self.group:Hide(); - self.loaded:Hide(); - self.expand:Show(); - end, ["EnableGroup"] = function(self) - self.group:Show(); - self.loaded:Show(); - self.expand:Hide(); + end, ["SetIds"] = function(self, ids) self.renamebox.ids = ids; @@ -1343,19 +1273,13 @@ local methods = { self.frame.dgroup = group; if(group) then self.icon:SetPoint("LEFT", self.ungroup, "RIGHT"); - self.background:SetPoint("LEFT", self.ungroup, "RIGHT"); - self.ungroup:Show(); - self.group:Hide(); - self.upgroup:Show(); - self.downgroup:Show(); + self.background:SetPoint("LEFT", self.offset, "RIGHT"); else self.icon:SetPoint("LEFT", self.frame, "LEFT"); self.background:SetPoint("LEFT", self.frame, "LEFT"); - self.ungroup:Hide(); - self.group:Show(); - self.upgroup:Hide(); - self.downgroup:Hide(); end + self:UpdateIconsVisible() + self:UpdateOffset() end, ["GetGroup"] = function(self) return self.frame.dgroup; @@ -1418,98 +1342,6 @@ local methods = { self:Collapse(); end end, - ["ShowGroupUpdate"] = function(self) - if self.groupUpdate and self.groupUpdate.disabled then - self.groupUpdate:Show() - self.groupUpdate.disabled = false - end - end, - ["HideGroupUpdate"] = function(self) - if self.groupUpdate and not self.groupUpdate.disabled then - self.groupUpdate:Hide() - self.groupUpdate.disabled = true - end - end, - ["RefreshUpdateMenu"] = function(self) - local pos - for k, menu in pairs(self.menu) do - if menu.text and menu.text:find(L["Wago Update"]) then - pos = k - break - end - end - if pos then - local wagoMenu = self.menu[pos].menuList - for i=1,#wagoMenu do tremove(wagoMenu, 1) end - tinsert(wagoMenu, { - text = self.data.ignoreWagoUpdate and L["Stop ignoring Updates"] or L["Ignore all Updates"], - notCheckable = true, - func = self.data.ignoreWagoUpdate and self.callbacks.wagoStopIgnoreAll or self.callbacks.wagoIgnoreAll - }) - if not self.data.ignoreWagoUpdate and self.update.hasUpdate then - if self.data.skipWagoUpdate and self.update.version == self.data.skipWagoUpdate then - tinsert(wagoMenu, { - text = L["Don't skip this Version"], - notCheckable = true, - func = self.callbacks.wagoStopIgnoreNext - }); - else - tinsert(wagoMenu, { - text = L["Skip this Version"], - notCheckable = true, - func = self.callbacks.wagoIgnoreNext - }); - tinsert(wagoMenu, { - text = " ", - notClickable = true, - notCheckable = true, - }); - tinsert(wagoMenu, { - text = L["Update this Aura"], - notCheckable = true, - func = self.callbacks.OnUpdateClick - }); - end - end - end - end, - ["ShowUpdateIcon"] = function(self) - if self.update and self.update.disabled then - self.update:Show() - self.update:Enable() - self.updateLogo:Show() - self.update.disabled = false - end - end, - ["HideUpdateIcon"] = function(self) - if self.update and not self.update.disabled then - self.update:Hide() - self.update:Disable() - self.updateLogo:Hide() - self.update.disabled = true - end - end, - ["HasUpdate"] = function(self) - -- return hasUpdate, skipVersion, updateData, key - if not WeakAurasCompanion or self.data.ignoreWagoUpdate then return end - local slug = self.data.uid and WeakAurasCompanion.uids[self.data.uid] or WeakAurasCompanion.ids[self.data.id] - if slug then - local updateData = WeakAurasCompanion.slugs[slug] - if updateData then - if not (self.data.skipWagoUpdate and self.data.skipWagoUpdate == updateData.wagoVersion) then - if not self.data.version or tonumber(updateData.wagoVersion) > tonumber(self.data.version) then - -- got update - return true, false, updateData, slug - end - else - -- version skip flag - return true, true, updateData, slug - end - end - end - -- no addon, or no data, or ignore flag - return false, false, nil, nil - end, ["UpdateWarning"] = function(self) local icon, title, warningText = OptionsPrivate.Private.AuraWarnings.FormatWarnings(self.data.uid) if warningText then @@ -1529,82 +1361,32 @@ local methods = { self.warning:Hide() end end, - ["RefreshUpdate"] = function(self, actionFunc) - if self.data.parent then - -- is in a group - local parentButton = WeakAuras.GetDisplayButton(self.data.parent) - if parentButton then - parentButton:RefreshUpdate(actionFunc) - end - else - -- is top level - local hasUpdate, skipVersion, _, slug = self:HasUpdate() - self:RefreshUpdateMenu() - if hasUpdate and not skipVersion then - self:ShowUpdateIcon() - else - self:HideUpdateIcon() - end - if self.data.controlledChildren then - -- is a group - local hasUpdate, skipVersion, _, slug = self:HasUpdate() - local showGroupUpdateIcon = false - for childIndex, childId in pairs(self.data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(childId); - if childButton then - if actionFunc then - childButton.callbacks[actionFunc](nil, true) - end - childButton:RefreshUpdateMenu() - local childHasUpdate, childSkipVersion, _, childSlug = childButton:HasUpdate() - if childHasUpdate and slug ~= childSlug and not childSkipVersion then - showGroupUpdateIcon = true - childButton:ShowUpdateIcon() - else - childButton:HideUpdateIcon() - end - end - end - if showGroupUpdateIcon then - self:ShowGroupUpdate() - else - self:HideGroupUpdate() - end - end - end - end, ["SetGroupOrder"] = function(self, order, max) - if(order == 1) then - self:DisableUpGroup(); - else - self:EnableUpGroup(); - end - if(order == max) then - self:DisableDownGroup(); - else - self:EnableDownGroup(); - end + self.first = (order == 1) + self.last = (order == max) self.frame.dgrouporder = order; + self:UpdateUpDownButtons() + end, + ["UpdateUpDownButtons"] = function(self) + if self.first or not self:IsEnabled() then + self.upgroup:Disable(); + self.upgroup.texture:SetVertexColor(0.3, 0.3, 0.3); + else + self.upgroup:Enable(); + self.upgroup.texture:SetVertexColor(1, 1, 1); + end + + if self.last or not self:IsEnabled() then + self.downgroup:Disable(); + self.downgroup.texture:SetVertexColor(0.3, 0.3, 0.3); + else + self.downgroup:Enable(); + self.downgroup.texture:SetVertexColor(1, 1, 1); + end end, ["GetGroupOrder"] = function(self) return self.frame.dgrouporder; end, - ["DisableUpGroup"] = function(self) - self.upgroup:Disable(); - self.upgroup.texture:SetVertexColor(0.3, 0.3, 0.3); - end, - ["EnableUpGroup"] = function(self) - self.upgroup:Enable(); - self.upgroup.texture:SetVertexColor(1, 1, 1); - end, - ["DisableDownGroup"] = function(self) - self.downgroup:Disable(); - self.downgroup.texture:SetVertexColor(0.3, 0.3, 0.3); - end, - ["EnableDownGroup"] = function(self) - self.downgroup:Enable(); - self.downgroup.texture:SetVertexColor(1, 1, 1); - end, ["DisableLoaded"] = function(self) self.loaded.title = L["Not Loaded"]; self.loaded.desc = L["This display is not currently loaded"]; @@ -1617,19 +1399,117 @@ local methods = { end, ["Pick"] = function(self) self.frame:LockHighlight(); - self.view:PriorityShow(1); + self:PriorityShow(1); + self:RecheckParentVisibility() end, ["ClearPick"] = function(self, noHide) self.frame:UnlockHighlight(); if not noHide then - self.view:PriorityHide(1); + self:PriorityHide(1); + self:RecheckParentVisibility() + end + end, + ["SyncVisibility"] = function(self) + if (not WeakAuras.IsOptionsOpen()) then + return; + end + if self.view.visibility >= 1 then + if(self.view.region and self.view.region.Expand) then + OptionsPrivate.Private.FakeStatesFor(self.view.region.id, true) + if (OptionsPrivate.Private.personalRessourceDisplayFrame) then + OptionsPrivate.Private.personalRessourceDisplayFrame:expand(self.view.region.id); + end + if (OptionsPrivate.Private.mouseFrame) then + OptionsPrivate.Private.mouseFrame:expand(self.view.region.id); + end + end + else + if(self.view.region and self.view.region.Collapse) then + OptionsPrivate.Private.FakeStatesFor(self.view.region.id, false) + if (OptionsPrivate.Private.personalRessourceDisplayFrame) then + OptionsPrivate.Private.personalRessourceDisplayFrame:collapse(self.view.region.id); + end + if (OptionsPrivate.Private.mouseFrame) then + OptionsPrivate.Private.mouseFrame:collapse(self.view.region.id); + end + end end end, ["PriorityShow"] = function(self, priority) - self.view:PriorityShow(priority); + if (not WeakAuras.IsOptionsOpen()) then + return; + end + if(priority >= self.view.visibility and self.view.visibility ~= priority) then + self.view.visibility = priority; + self:SyncVisibility() + self:UpdateViewTexture() + end + if self.view.region and self.view.region.ClickToPick then + self.view.region:ClickToPick(); + end end, ["PriorityHide"] = function(self, priority) - self.view:PriorityHide(priority); + if (not WeakAuras.IsOptionsOpen()) then + return; + end + if(priority >= self.view.visibility and self.view.visibility ~= 0) then + self.view.visibility = 0; + self:SyncVisibility() + self:UpdateViewTexture() + end + end, + ["RecheckParentVisibility"] = function(self) + if self.data.parent then + local parentButton = WeakAuras.GetDisplayButton(self.data.parent) + parentButton:RecheckVisibility() + else + WeakAuras.OptionsFrame().loadedButton:RecheckVisibility() + WeakAuras.OptionsFrame().unloadedButton:RecheckVisibility() + end + end, + ["RecheckVisibility"] = function(self) + local none, all = true, true; + for child in OptionsPrivate.Private.TraverseAllChildren(self.data) do + local childButton = WeakAuras.GetDisplayButton(child.id); + if(childButton) then + if(childButton:GetVisibility() ~= 2) then + all = false; + end + if(childButton:GetVisibility() ~= 0) then + none = false; + end + end + end + local newVisibility + if(all) then + newVisibility = 2; + elseif(none) then + newVisibility = 0; + else + newVisibility = 1; + end + if newVisibility ~= self.view.visibility then + self.view.visibility = newVisibility + self:UpdateViewTexture() + + self:RecheckParentVisibility() + end + end, + ["SetVisibilityDirectly"] = function(self, visibility) + if self.data.parent and visibility ~= self.view.visibility then + self.view.visibility = visibility + self:UpdateViewTexture() + end + end, + ["UpdateViewTexture"] = function(self) + local visibility = self.view.visibility + if(visibility == 2) then + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking0.blp"); + elseif(visibility == 1) then + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking2.blp"); + else + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp"); + end end, ["GetVisibility"] = function(self) return self.view.visibility; @@ -1640,10 +1520,9 @@ local methods = { self.view:Disable(); self.group:Disable(); self.ungroup:Disable(); - self.upgroup:Disable(); - self.downgroup:Disable(); self.loaded:Disable(); self.expand:Disable(); + self:UpdateUpDownButtons() end, ["Enable"] = function(self) self.background:Show(); @@ -1651,9 +1530,8 @@ local methods = { self.view:Enable(); self.group:Enable(); self.ungroup:Enable(); - self.upgroup:Enable(); - self.downgroup:Enable(); self.loaded:Enable(); + self:UpdateUpDownButtons() if not(self.expand.disabled) then self.expand:Enable(); end @@ -1666,7 +1544,6 @@ local methods = { self:SetViewRegion(); self:Enable(); self:SetGroup(); - self:EnableGroup(); self.renamebox:Hide(); self.title:Show(); local id = self.data.id; @@ -1773,6 +1650,13 @@ local function Constructor() button.dgroup = nil; button.data = {}; + local offset = CreateFrame("Frame", nil, button) + button.offset = offset + offset:SetPoint("TOP", button, "TOP"); + offset:SetPoint("BOTTOM", button, "BOTTOM"); + offset:SetPoint("LEFT", button, "LEFT"); + offset:SetWidth(1) + local background = button:CreateTexture(nil, "BACKGROUND"); button.background = background; background:SetTexture("Interface\\BUTTONS\\UI-Listbox-Highlight2.blp"); @@ -1780,14 +1664,14 @@ local function Constructor() background:SetVertexColor(0.5, 0.5, 0.5, 0.25); background:SetPoint("TOP", button, "TOP"); background:SetPoint("BOTTOM", button, "BOTTOM"); - background:SetPoint("LEFT", button, "LEFT"); + background:SetPoint("LEFT", button, "LEFT") background:SetPoint("RIGHT", button, "RIGHT"); local icon = button:CreateTexture(nil, "OVERLAY"); button.icon = icon; icon:SetWidth(32); icon:SetHeight(32); - icon:SetPoint("LEFT", button, "LEFT"); + icon:SetPoint("LEFT", offset, "RIGHT"); local title = button:CreateFontString(nil, "OVERLAY", "GameFontNormal"); button.title = title; @@ -1799,18 +1683,6 @@ local function Constructor() button.description = {}; - button:SetScript("OnEnter", function() - if(OptionsPrivate.IsPickedMultiple() and OptionsPrivate.IsDisplayPicked(button.id)) then - Show_Long_Tooltip(button, OptionsPrivate.MultipleDisplayTooltipDesc()); - else - if not(button.terribleCodeOrganizationHackTable.IsGroupingOrCopying()) then - button.terribleCodeOrganizationHackTable.SetNormalTooltip(); - end - Show_Long_Tooltip(button, button.description); - end - end); - button:SetScript("OnLeave", Hide_Tooltip); - local view = CreateFrame("BUTTON", nil, button); button.view = view; view:SetWidth(16); @@ -1818,55 +1690,15 @@ local function Constructor() view:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT", -2, 0); local viewTexture = view:CreateTexture() view.texture = viewTexture; - viewTexture:SetTexture("Interface\\LFGFrame\\BattlenetWorking1.blp"); + viewTexture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp"); viewTexture:SetTexCoord(0.1, 0.9, 0.1, 0.9); viewTexture:SetAllPoints(view); view:SetNormalTexture(viewTexture); view:SetHighlightTexture("Interface\\BUTTONS\\UI-Panel-MinimizeButton-Highlight.blp"); view:SetScript("OnEnter", function() Show_Tooltip(button, L["View"], L["Toggle the visibility of this display"]) end); view:SetScript("OnLeave", Hide_Tooltip); + view.visibility = 0; - view.PriorityShow = function(self, priority) - if (not WeakAuras.IsOptionsOpen()) then - return; - end - if(priority >= self.visibility) then - self.visibility = priority; - if(self.region and self.region.Expand) then - OptionsPrivate.Private.FakeStatesFor(self.region.id, true) - if (OptionsPrivate.Private.mouseFrame) then - OptionsPrivate.Private.mouseFrame:expand(self.region.id); - end - end - end - if self.region and self.region.ClickToPick then - self.region:ClickToPick(); - end - end - view.PriorityHide = function(self, priority) - if (not WeakAuras.IsOptionsOpen()) then - return; - end - if(priority >= self.visibility) then - self.visibility = 0; - if(self.region and self.region.Collapse) then - OptionsPrivate.Private.FakeStatesFor(self.region.id, false) - if (OptionsPrivate.Private.mouseFrame) then - OptionsPrivate.Private.mouseFrame:collapse(self.region.id); - end - end - end - end - view.func = function() return view.visibility end; - view:SetScript("OnUpdate", function() - if(view.func() == 2) then - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking0.blp"); - elseif(view.func() == 1) then - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking2.blp"); - else - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp"); - end - end); local loaded = CreateFrame("BUTTON", nil, button); button.loaded = loaded; @@ -1928,7 +1760,7 @@ local function Constructor() button.ungroup = ungroup; ungroup:SetWidth(11); ungroup:SetHeight(11); - ungroup:SetPoint("LEFT", button, "LEFT", 0, 0); + ungroup:SetPoint("LEFT", offset, "RIGHT", 0, 0); local ungrouptexture = group:CreateTexture(nil, "OVERLAY"); ungrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp"); ungrouptexture:SetTexCoord(0.5, 0, 0.5, 1, 1, 0, 1, 1); @@ -1943,7 +1775,7 @@ local function Constructor() button.upgroup = upgroup; upgroup:SetWidth(11); upgroup:SetHeight(11); - upgroup:SetPoint("TOPLEFT", button, "TOPLEFT", 0, 0); + upgroup:SetPoint("TOPLEFT", offset, "TOPRIGHT", 0, 0); local upgrouptexture = group:CreateTexture(nil, "OVERLAY"); upgroup.texture = upgrouptexture; upgrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp"); @@ -1960,7 +1792,7 @@ local function Constructor() button.downgroup = downgroup; downgroup:SetWidth(11); downgroup:SetHeight(11); - downgroup:SetPoint("BOTTOMLEFT", button, "BOTTOMLEFT", 0, 0); + downgroup:SetPoint("BOTTOMLEFT", offset, "BOTTOMRIGHT", 0, 0); local downgrouptexture = group:CreateTexture(nil, "OVERLAY"); downgroup.texture = downgrouptexture; downgrouptexture:SetTexture("Interface\\MoneyFrame\\Arrow-Left-Down.blp"); @@ -1989,67 +1821,6 @@ local function Constructor() expand:SetScript("OnEnter", function() Show_Tooltip(button, expand.title, expand.desc) end); expand:SetScript("OnLeave", Hide_Tooltip); - local update, updateLogo - local groupUpdate - if WeakAurasCompanion then - update = CreateFrame("BUTTON", nil, button); - button.update = update - update.disabled = true - update.func = function() end - update:SetNormalTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_refresh.tga]]) - update:Disable() - update:SetWidth(24) - update:SetHeight(24) - update:SetPoint("RIGHT", button, "RIGHT", -35, 0) - update.title = "" - update.desc = "" - update.hasUpdate = false - update.version = nil - update.menuDisabled = true - - -- Add logo - updateLogo = CreateFrame("Frame", nil, button) - button.updateLogo = updateLogo - local tex = updateLogo:CreateTexture(nil, "OVERLAY") - tex:SetTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]]) - tex:SetAllPoints() - updateLogo:SetSize(24,24) - updateLogo:SetPoint("CENTER",update) - - -- Animation On Hover - local animGroup = update:CreateAnimationGroup() - update.animGroup = animGroup - local animRotate = animGroup:CreateAnimation("rotation") - animRotate:SetDegrees(-360) - animRotate:SetDuration(1) - animRotate:SetSmoothing("OUT") - - animGroup:SetScript("OnFinished",function() if (MouseIsOver(update)) then animGroup:Play() end end) - update:SetScript("OnEnter", function() - animGroup:Play() - Show_Tooltip(button, update.title, update.desc) - end); - update:SetScript("OnLeave", Hide_Tooltip) - update:Hide() - updateLogo:Hide() - - -- Update in group icon - groupUpdate = CreateFrame("BUTTON", nil, button) - button.groupUpdate = groupUpdate - local gTex = groupUpdate:CreateTexture(nil, "OVERLAY") - gTex:SetTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]]) - gTex:SetAllPoints() - groupUpdate:SetSize(16, 16) - groupUpdate:SetPoint("BOTTOM", button, "BOTTOM") - groupUpdate:SetPoint("LEFT", icon, "RIGHT", 20, 0) - groupUpdate.disabled = true - groupUpdate.title = L["Update in Group"] - groupUpdate.desc = L["Group contains updates from Wago"] - groupUpdate:SetScript("OnEnter", function() Show_Tooltip(button, groupUpdate.title, groupUpdate.desc) end) - groupUpdate:SetScript("OnLeave", Hide_Tooltip) - groupUpdate:Hide() - end - local warning = CreateFrame("BUTTON", nil, button); button.warning = warning warning:SetWidth(16) @@ -2071,11 +1842,9 @@ local function Constructor() loaded = loaded, background = background, expand = expand, - update = update, warning = warning, - groupUpdate = groupUpdate, - updateLogo = updateLogo, - type = Type + type = Type, + offset = offset } for method, func in pairs(methods) do widget[method] = func diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasExpand.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasExpand.lua index ac9ee6e..e1132d9 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasExpand.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasExpand.lua @@ -3,7 +3,7 @@ Button Widget for our Expand button -------------------------------------------------------------------------------]] if not WeakAuras.IsCorrectVersion() then return end -local Type, Version = "WeakAurasExpand", 2 +local Type, Version = "WeakAurasExpand", 3 local AceGUI = LibStub and LibStub("AceGUI-3.0", true) if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end @@ -92,7 +92,11 @@ local methods = { self.label:SetTextColor(1, 1, 1) self.image:SetVertexColor(1, 1, 1, 1) end - end + end, + + ["SetFontObject"] = function(self, fontObject) + self.label:SetFontObject(fontObject) + end } --[[----------------------------------------------------------------------------- diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasLoadedHeaderButton.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasLoadedHeaderButton.lua index 2438f38..9584878 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasLoadedHeaderButton.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasLoadedHeaderButton.lua @@ -96,8 +96,33 @@ local methods = { ["SetViewClick"] = function(self, func) self.view:SetScript("OnClick", func); end, - ["SetViewTest"] = function(self, func) - self.view.func = func; + ["PriorityShow"] = function(self, priority) + if (not WeakAuras.IsOptionsOpen()) then + return; + end + if(priority >= self.view.visibility and self.view.visibility ~= priority) then + self.view.visibility = priority; + self:UpdateViewTexture() + end + end, + ["PriorityHide"] = function(self, priority) + if (not WeakAuras.IsOptionsOpen()) then + return; + end + if(priority >= self.view.visibility and self.view.visibility ~= 0) then + self.view.visibility = 0; + self:UpdateViewTexture() + end + end, + ["UpdateViewTexture"] = function(self) + local visibility = self.view.visibility + if(visibility == 2) then + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking0.blp"); + elseif(visibility == 1) then + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking2.blp"); + else + self.view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp"); + end end, ["SetViewDescription"] = function(self, desc) self.view.desc = desc; @@ -163,16 +188,6 @@ local function Constructor() view:SetScript("OnEnter", function() Show_Tooltip(button, L["View"], view.desc) end); view:SetScript("OnLeave", Hide_Tooltip); view.visibility = 0; - view.func = function() return view.visibility end; - view:SetScript("OnUpdate", function() - if(view.func() == 2) then - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking0.blp"); - elseif(view.func() == 1) then - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking2.blp"); - else - view.texture:SetTexture("Interface\\LFGFrame\\BattlenetWorking4.blp"); - end - end); local widget = { frame = button, diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingInstallButton.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingInstallButton.lua new file mode 100644 index 0000000..72a800c --- /dev/null +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingInstallButton.lua @@ -0,0 +1,253 @@ +if not WeakAuras.IsCorrectVersion() then + return +end + +local AddonName, OptionsPrivate = ... +local L = WeakAuras.L + +local pairs, next, type, unpack = pairs, next, type, unpack + +local Type, Version = "WeakAurasPendingInstallButton", 2 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) + +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then + return +end + +--[[----------------------------------------------------------------------------- +Methods +-------------------------------------------------------------------------------]] +local methods = { + ["OnAcquire"] = function(self) + self:SetWidth(1000) + self:SetHeight(32) + self.hasThumbnail = false + end, + ["Initialize"] = function(self, id, companionData) + self.callbacks = {} + self.id = id + self.companionData = companionData + + function self.callbacks.OnUpdateClick() + WeakAuras.Import(self.companionData.encoded) + end + + self:SetTitle(self.companionData.name) + self.update:SetScript("OnClick", self.callbacks.OnUpdateClick) + local data = OptionsPrivate.Private.StringToTable(self.companionData.encoded, true) + WeakAuras.PreAdd(data.d) + self.data = data.d + self.frame:EnableKeyboard(false) + self:Enable() + self.frame:Hide() + end, + ["SetLogo"] = function(self, path) + self.frame.updateLogo.tex:SetTexture(path) + end, + ["SetRefreshLogo"] = function(self, path) + self.frame.update:SetNormalTexture(path) + end, + ["Disable"] = function(self) + self.background:Hide() + self.frame:Disable() + end, + ["Enable"] = function(self) + self.background:Show() + self.frame:Enable() + self.update:Show() + self.update:Enable() + self.updateLogo:Show() + self:UpdateThumbnail() + end, + ["OnRelease"] = function(self) + self:ReleaseThumbnail() + self:Enable() + self.title:Show() + self.frame:SetScript("OnEnter", nil) + self.frame:SetScript("OnLeave", nil) + self.frame:SetScript("OnClick", nil) + self.frame:ClearAllPoints() + self.frame:Hide() + self.frame = nil + self.data = nil + end, + ["SetTitle"] = function(self, title) + self.titletext = title + self.title:SetText(title) + end, + ["SetClick"] = function(self, func) + self.frame:SetScript("OnClick", func) + end, + ["UpdateThumbnail"] = function(self) + if not self.hasThumbnail then + return + end + + if self.data.regionType ~= self.thumbnailType then + self:ReleaseThumbnail() + self:AcquireThumbnail() + else + local option = WeakAuras.regionOptions[self.thumbnailType] + if option and option.modifyThumbnail then + option.modifyThumbnail(self.frame, self.thumbnail, self.data) + end + end + end, + ["ReleaseThumbnail"] = function(self) + if not self.hasThumbnail then + return + end + self.hasThumbnail = false + + if self.thumbnail then + local regionType = self.thumbnailType + local option = WeakAuras.regionOptions[regionType] + if self.thumbnail.icon then + self.thumbnail.icon:SetDesaturated(false) + end + option.releaseThumbnail(self.thumbnail) + self.thumbnail = nil + end + end, + ["AcquireThumbnail"] = function(self) + if self.hasThumbnail then + return + end + + if not self.data then + return + end + + self.hasThumbnail = true + + local button = self.frame + local regionType = self.data.regionType + self.thumbnailType = regionType + + local option = WeakAuras.regionOptions[regionType] + if option and option.acquireThumbnail then + self.thumbnail = option.acquireThumbnail(button, self.data) + if self.thumbnail.icon then + self.thumbnail.icon:SetDesaturated(true) + end + self:SetIcon(self.thumbnail) + else + self:SetIcon("Interface\\Icons\\INV_Misc_QuestionMark") + end + end, + ["SetIcon"] = function(self, icon) + self.orgIcon = icon + if (type(icon) == "string" or type(icon) == "number") then + self.icon:SetTexture(icon) + self.icon:Show() + if (self.iconRegion and self.iconRegion.Hide) then + self.iconRegion:Hide() + end + else + self.iconRegion = icon + icon:SetAllPoints(self.icon) + icon:SetParent(self.frame) + icon:Show() + self.iconRegion:Show() + self.icon:Hide() + end + end, +} + +--[[----------------------------------------------------------------------------- +Constructor +-------------------------------------------------------------------------------]] + +local function Constructor() + local name = "WeakAurasPendingInstallButton" .. AceGUI:GetNextWidgetNum(Type) + local button = CreateFrame("BUTTON", name, UIParent) + button:SetHeight(32) + button:SetWidth(1000) + button.data = {} + + local background = button:CreateTexture(nil, "BACKGROUND") + button.background = background + background:SetTexture("Interface\\BUTTONS\\UI-Listbox-Highlight2.blp") + background:SetBlendMode("ADD") + background:SetVertexColor(0.5, 1, 0.5, 0.3) + background:SetPoint("TOP", button, "TOP") + background:SetPoint("BOTTOM", button, "BOTTOM") + background:SetPoint("LEFT", button, "LEFT") + background:SetPoint("RIGHT", button, "RIGHT") + + local icon = button:CreateTexture(nil, "OVERLAY") + button.icon = icon + icon:SetWidth(32) + icon:SetHeight(32) + icon:SetPoint("LEFT", button, "LEFT") + + local title = button:CreateFontString(nil, "OVERLAY", "GameFontNormal") + button.title = title + title:SetHeight(14) + title:SetJustifyH("LEFT") + title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 2, 0) + title:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT") + title:SetVertexColor(0.6, 0.6, 0.6) + + button.description = {} + + local update = CreateFrame("BUTTON", nil, button) + button.update = update + update.disabled = true + update.func = function() + end + update:SetNormalTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_refresh.tga]]) + update:Disable() + update:SetWidth(24) + update:SetHeight(24) + update:SetPoint("RIGHT", button, "RIGHT", -2, 0) + + -- Add logo + local updateLogo = CreateFrame("Frame", nil, button) + button.updateLogo = updateLogo + local tex = updateLogo:CreateTexture() + tex:SetTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]]) + tex:SetAllPoints() + updateLogo.tex = tex + updateLogo:SetSize(24, 24) + updateLogo:SetPoint("CENTER", update) + updateLogo:SetFrameStrata(update:GetFrameStrata()) + updateLogo:SetFrameLevel(update:GetFrameLevel()-1) + + -- Animation On Hover + local animGroup = update:CreateAnimationGroup() + update.animGroup = animGroup + + local animRotate = animGroup:CreateAnimation("rotation") + animRotate:SetDegrees(-360) + animRotate:SetDuration(1) + animRotate:SetSmoothing("OUT") + animGroup:SetScript("OnFinished", function() + if (MouseIsOver(update)) then + animGroup:Play() + end + end) + update:SetScript("OnEnter", function() + animGroup:Play() + end) + update:Hide() + updateLogo:Hide() + + local widget = { + frame = button, + title = title, + icon = icon, + background = background, + update = update, + updateLogo = updateLogo, + type = Type, + } + + for method, func in pairs(methods) do + widget[method] = func + end + + return AceGUI:RegisterAsWidget(widget) +end + +AceGUI:RegisterWidgetType(Type, Constructor, Version) diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingUpdateButton.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingUpdateButton.lua new file mode 100644 index 0000000..6e76883 --- /dev/null +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasPendingUpdateButton.lua @@ -0,0 +1,382 @@ +if not WeakAuras.IsCorrectVersion() then + return +end + +local AddonName, OptionsPrivate = ... +local L = WeakAuras.L + +local pairs, next, type, unpack = pairs, next, type, unpack + +local Type, Version = "WeakAurasPendingUpdateButton", 2 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) + +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then + return +end + +local function Hide_Tooltip() + GameTooltip:Hide() +end + +local function Show_Long_Tooltip(owner, description) + GameTooltip:SetOwner(owner, "ANCHOR_NONE"); + GameTooltip:SetPoint("LEFT", owner, "RIGHT"); + GameTooltip:ClearLines(); + local line = 1; + for i,v in pairs(description) do + if(type(v) == "string") then + if(line > 1) then + GameTooltip:AddLine(v, 1, 1, 1, 1); + else + GameTooltip:AddLine(v); + end + elseif(type(v) == "table") then + if(i == 1) then + GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or "")); + else + GameTooltip:AddDoubleLine(v[1], v[2]..(v[3] and (" |T"..v[3]..":12:12:0:0:64:64:4:60:4:60|t") or ""), 1, 1, 1, 1, 1, 1, 1, 1); + end + end + line = line + 1; + end + GameTooltip:Show(); +end + +--[[----------------------------------------------------------------------------- +Methods +-------------------------------------------------------------------------------]] +local methods = { + ["OnAcquire"] = function(self) + self:SetWidth(1000) + self:SetHeight(32) + self.hasThumbnail = false + end, + ["Initialize"] = function(self, id, companionData) + self.callbacks = {} + self.id = id + self.companionData = companionData + self.linkedAuras = {} + self.linkedChildren = {} + + function self.callbacks.OnUpdateClick() + WeakAuras.Import(self.companionData.encoded) + end + + self:SetTitle(self.companionData.name) + self.update:SetScript("OnClick", self.callbacks.OnUpdateClick) + local data = OptionsPrivate.Private.StringToTable(self.companionData.encoded, true) + WeakAuras.PreAdd(data.d) + self.data = data.d + self.frame:EnableKeyboard(false) + self:Enable() + self.frame:Hide() + + self.menu = {} + + self.frame:SetScript("OnMouseUp", function() + Hide_Tooltip() + self:SetMenu() + EasyMenu(self.menu, WeakAuras_DropDownMenu, self.frame, 0, 0, "MENU") + end) + + self.frame:SetScript("OnEnter", function() + self:SetNormalTooltip() + Show_Long_Tooltip(self.frame, self.frame.description) + end) + self.frame:SetScript("OnLeave", Hide_Tooltip) + end, + ["SetMenu"] = function(self) + wipe(self.menu) + for auraId in pairs(self.linkedAuras) do + if not self.linkedChildren[auraId] then + tinsert(self.menu, + { + text = auraId, + notCheckable = true, + hasArrow = true, + menuList = { + { + text = L["Update"], + notCheckable = true, + func = function() + local auraData = WeakAuras.GetData(auraId) + if auraData then + WeakAuras.Import(self.companionData.encoded, auraData) + end + end + }, + { + text = L["Ignore updates"], + notCheckable = true, + func = function() + StaticPopup_Show("WEAKAURAS_CONFIRM_IGNORE_UPDATES", "", "", auraId) + end + } + } + } + ) + end + end + end, + ["SetLogo"] = function(self, path) + self.frame.updateLogo.tex:SetTexture(path) + end, + ["SetRefreshLogo"] = function(self, path) + self.frame.update:SetNormalTexture(path) + end, + ["Disable"] = function(self) + self.background:Hide() + self.frame:Disable() + end, + ["Enable"] = function(self) + self.background:Show() + self.frame:Enable() + self.update:Show() + self.update:Enable() + self.updateLogo:Show() + self:UpdateThumbnail() + end, + ["OnRelease"] = function(self) + self:ReleaseThumbnail() + self:Enable() + self.title:Show() + self.frame:SetScript("OnEnter", nil) + self.frame:SetScript("OnLeave", nil) + self.frame:SetScript("OnClick", nil) + self.frame:ClearAllPoints() + self.frame:Hide() + self.frame = nil + self.data = nil + end, + ["SetNormalTooltip"] = function(self) + local data = self.data; + local namestable = {}; + + local hasDescription = data.desc and data.desc ~= ""; + local hasUrl = data.url and data.url ~= ""; + local hasVersion = (data.semver and data.semver ~= "") or (data.version and data.version ~= ""); + local hasVersionNote = self.companionData.versionNote and self.companionData.versionNote ~= "" + + if(hasDescription or hasUrl or hasVersion or hasVersionNote) then + tinsert(namestable, " ") + end + + if hasVersionNote then + tinsert(namestable, "|cFFFFD100"..self.companionData.versionNote) + tinsert(namestable, " ") + end + + for auraId in pairs(self.linkedAuras) do + if not self.linkedChildren[auraId] then + tinsert(namestable, "|cFFFFD100" .. L["Linked aura: "] .. auraId .. "|r") + end + end + tinsert(namestable, " ") + + if(hasDescription) then + tinsert(namestable, "|cFFFFD100"..data.desc) + end + + if (hasUrl) then + tinsert(namestable, "|cFFFFD100" .. data.url .. "|r") + end + + if (hasVersion) then + tinsert(namestable, "|cFFFFD100" .. L["Version: "] .. (data.semver or data.version) .. "|r") + end + + self:SetDescription({self.companionData.name or self.data.id, self.companionData.author or ""}, unpack(namestable)) + end, + ["SetDescription"] = function(self, ...) + self.frame.description = {...}; + end, + ["SetTitle"] = function(self, title) + self.titletext = title + self.title:SetText(title) + end, + ["SetClick"] = function(self, func) + self.frame:SetScript("OnClick", func) + end, + ["ResetLinkedAuras"] = function(self) + wipe(self.linkedAuras) + wipe(self.linkedChildren) + end, + ["MarkLinkedAura"] = function(self, auraId) + self.linkedAuras[auraId] = true + end, + ["MarkLinkedChildren"] = function(self, auraId) + self.linkedChildren[auraId] = true + end, + ["UpdateThumbnail"] = function(self) + if not self.hasThumbnail then + return + end + + if self.data.regionType ~= self.thumbnailType then + self:ReleaseThumbnail() + self:AcquireThumbnail() + else + local option = WeakAuras.regionOptions[self.thumbnailType] + if option and option.modifyThumbnail then + option.modifyThumbnail(self.frame, self.thumbnail, self.data) + end + end + end, + ["ReleaseThumbnail"] = function(self) + if not self.hasThumbnail then + return + end + self.hasThumbnail = false + + if self.thumbnail then + local regionType = self.thumbnailType + local option = WeakAuras.regionOptions[regionType] + if self.thumbnail.icon then + self.thumbnail.icon:SetDesaturated(false) + end + option.releaseThumbnail(self.thumbnail) + self.thumbnail = nil + end + end, + ["AcquireThumbnail"] = function(self) + if self.hasThumbnail then + return + end + + if not self.data then + return + end + + self.hasThumbnail = true + + local button = self.frame + local regionType = self.data.regionType + self.thumbnailType = regionType + + local option = WeakAuras.regionOptions[regionType] + if option and option.acquireThumbnail then + self.thumbnail = option.acquireThumbnail(button, self.data) + if self.thumbnail.icon then + self.thumbnail.icon:SetDesaturated(true) + end + self:SetIcon(self.thumbnail) + else + self:SetIcon("Interface\\Icons\\INV_Misc_QuestionMark") + end + end, + ["SetIcon"] = function(self, icon) + self.orgIcon = icon + if (type(icon) == "string" or type(icon) == "number") then + self.icon:SetTexture(icon) + self.icon:Show() + if (self.iconRegion and self.iconRegion.Hide) then + self.iconRegion:Hide() + end + else + self.iconRegion = icon + icon:SetAllPoints(self.icon) + icon:SetParent(self.frame) + icon:Show() + self.iconRegion:Show() + self.icon:Hide() + end + end, +} + +--[[----------------------------------------------------------------------------- +Constructor +-------------------------------------------------------------------------------]] + +local function Constructor() + local name = "WeakAurasPendingUpdateButton" .. AceGUI:GetNextWidgetNum(Type) + local button = CreateFrame("BUTTON", name, UIParent) + button:SetHeight(32) + button:SetWidth(1000) + button.data = {} + + local background = button:CreateTexture(nil, "BACKGROUND") + button.background = background + background:SetTexture("Interface\\BUTTONS\\UI-Listbox-Highlight2.blp") + background:SetBlendMode("ADD") + background:SetVertexColor(0.88, 0.88, 0, 0.3) + background:SetPoint("TOP", button, "TOP") + background:SetPoint("BOTTOM", button, "BOTTOM") + background:SetPoint("LEFT", button, "LEFT") + background:SetPoint("RIGHT", button, "RIGHT") + + local icon = button:CreateTexture(nil, "OVERLAY") + button.icon = icon + icon:SetWidth(32) + icon:SetHeight(32) + icon:SetPoint("LEFT", button, "LEFT") + + local title = button:CreateFontString(nil, "OVERLAY", "GameFontNormal") + button.title = title + title:SetHeight(14) + title:SetJustifyH("LEFT") + title:SetPoint("TOPLEFT", icon, "TOPRIGHT", 2, 0) + title:SetPoint("BOTTOMRIGHT", button, "BOTTOMRIGHT") + title:SetVertexColor(0.6, 0.6, 0.6) + + button.description = {} + + local update = CreateFrame("BUTTON", nil, button) + button.update = update + update.disabled = true + update.func = function() + end + update:SetNormalTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_refresh.tga]]) + update:Disable() + update:SetWidth(24) + update:SetHeight(24) + update:SetPoint("RIGHT", button, "RIGHT", -2, 0) + + -- Add logo + local updateLogo = CreateFrame("Frame", nil, button) + button.updateLogo = updateLogo + local tex = updateLogo:CreateTexture() + tex:SetTexture([[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]]) + tex:SetAllPoints() + updateLogo.tex = tex + updateLogo:SetSize(24, 24) + updateLogo:SetPoint("CENTER", update) + updateLogo:SetFrameStrata(update:GetFrameStrata()) + updateLogo:SetFrameLevel(update:GetFrameLevel()-1) + + -- Animation On Hover + local animGroup = update:CreateAnimationGroup() + update.animGroup = animGroup + + local animRotate = animGroup:CreateAnimation("rotation") + animRotate:SetDegrees(-360) + animRotate:SetDuration(1) + animRotate:SetSmoothing("OUT") + animGroup:SetScript("OnFinished", function() + if (MouseIsOver(update)) then + animGroup:Play() + end + end) + update:SetScript("OnEnter", function() + animGroup:Play() + end) + update:Hide() + updateLogo:Hide() + + local widget = { + frame = button, + title = title, + icon = icon, + background = background, + update = update, + updateLogo = updateLogo, + type = Type, + } + + for method, func in pairs(methods) do + widget[method] = func + end + + return AceGUI:RegisterAsWidget(widget) +end + +AceGUI:RegisterWidgetType(Type, Constructor, Version) diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasSpacer.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasSpacer.lua new file mode 100644 index 0000000..f05d179 --- /dev/null +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasSpacer.lua @@ -0,0 +1,31 @@ +--[[----------------------------------------------------------------------------- +Spacer Widget +Just uses up a bit of horizontal space +-------------------------------------------------------------------------------]] +local Type, Version = "WeakAurasSpacer", 1 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end + +local methods = { + ["OnAcquire"] = function(self) + self:SetFullWidth(true) + self:SetHeight(4) + end, +} + +local function Constructor() + local frame = CreateFrame("Frame", nil, UIParent) + frame:Hide() + + local widget = { + frame = frame, + type = Type + } + for method, func in pairs(methods) do + widget[method] = func + end + + return AceGUI:RegisterAsWidget(widget) +end + +AceGUI:RegisterWidgetType(Type, Constructor, Version) diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasTwoColumnDropDown.lua b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasTwoColumnDropDown.lua index 6473aa2..5f5c42f 100644 --- a/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasTwoColumnDropDown.lua +++ b/WeakAurasOptions/AceGUI-Widgets/AceGUIWidget-WeakAurasTwoColumnDropDown.lua @@ -167,6 +167,19 @@ local methods = { firstDropdown:SetCallback("OnValueChanged", OnFirstDropdownValueChanged) secondDropDown:SetCallback("OnValueChanged", OnSecondDropdownValueChanged) + + local function FireOnEnter(self, event) + widget:Fire("OnEnter") + end + + local function FireOnLeave(self, event) + widget:Fire("OnLeave") + end + + firstDropdown:SetCallback("OnEnter", FireOnEnter) + firstDropdown:SetCallback("OnLeave", FireOnLeave) + secondDropDown:SetCallback("OnEnter", FireOnEnter) + secondDropDown:SetCallback("OnLeave", FireOnLeave) end, ["OnRelease"] = function(self) end, diff --git a/WeakAurasOptions/AceGUI-Widgets/AceGuiWidget-WeakAurasProgressBar.lua b/WeakAurasOptions/AceGUI-Widgets/AceGuiWidget-WeakAurasProgressBar.lua new file mode 100644 index 0000000..809e95c --- /dev/null +++ b/WeakAurasOptions/AceGUI-Widgets/AceGuiWidget-WeakAurasProgressBar.lua @@ -0,0 +1,58 @@ +--[[----------------------------------------------------------------------------- +Progress Bar Widget +A simple progress bar +-------------------------------------------------------------------------------]] +local Type, Version = "WeakAurasProgressBar", 1 +local AceGUI = LibStub and LibStub("AceGUI-3.0", true) +if not AceGUI or (AceGUI:GetWidgetVersion(Type) or 0) >= Version then return end + +local methods = { + ["OnAcquire"] = function(self) + self:SetFullWidth(true) + self:SetHeight(10) + self.value = 0 + self.total = 1 + end, + ["SetProgress"] = function(self, value, total) + self.value = value + self.total = total + local p = value / total + if p > 1 then + p = 1 + end + self.foreground:SetPoint("RIGHT", self.background, "LEFT", p * self.frame:GetWidth(), 0) + end, + ["OnWidthSet"] = function(self) + self:SetProgress(self.value, self.total) + end, +} + +local function Constructor() + local frame = CreateFrame("Frame", nil, UIParent) + local foreground = frame:CreateTexture(nil, "BORDER") + local background = frame:CreateTexture(nil, "BACKGROUND") + foreground:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_White") + background:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_White") + background:SetVertexColor(0.5, 0.5, 0.5) + + background:SetAllPoints() + foreground:SetPoint("TOPLEFT") + foreground:SetPoint("BOTTOMLEFT") + foreground:SetPoint("RIGHT", background, "LEFT", 0, 0) + + frame:Hide() + + local widget = { + frame = frame, + foreground = foreground, + background = background, + type = Type + } + for method, func in pairs(methods) do + widget[method] = func + end + + return AceGUI:RegisterAsWidget(widget) +end + +AceGUI:RegisterWidgetType(Type, Constructor, Version) diff --git a/WeakAurasOptions/ActionOptions.lua b/WeakAurasOptions/ActionOptions.lua index a07bc58..778a99e 100644 --- a/WeakAurasOptions/ActionOptions.lua +++ b/WeakAurasOptions/ActionOptions.lua @@ -12,6 +12,17 @@ local hiddenAll = OptionsPrivate.commonOptions.CreateHiddenAll("action") local getAll = OptionsPrivate.commonOptions.CreateGetAll("action") local setAll = OptionsPrivate.commonOptions.CreateSetAll("action", getAll) +local RestrictedChannelCheck +if WeakAuras.IsClassic() then + RestrictedChannelCheck = function() + return false + end +else + RestrictedChannelCheck = function(data) + return data.message_type == "SAY" or data.message_type == "YELL" or data.message_type == "SMARTRAID" + end +end + function OptionsPrivate.GetActionOptions(data) local action = { type = "group", @@ -94,6 +105,13 @@ function OptionsPrivate.GetActionOptions(data) disabled = function() return not data.actions.start.do_message end, control = "WeakAurasSortedDropdown" }, + start_message_warning = { + type = "description", + width = WeakAuras.doubleWidth, + name = L["Note: Automated Messages to SAY and YELL are blocked outside of Instances."], + order = 2.5, + hidden = function() return not RestrictedChannelCheck(data.actions.start) end + }, start_message_space = { type = "execute", width = WeakAuras.normalWidth, @@ -101,7 +119,7 @@ function OptionsPrivate.GetActionOptions(data) order = 3, image = function() return "", 0, 0 end, hidden = function() - return not(data.actions.start.message_type == "WHISPER" or data.actions.start.message_type == "COMBAT" + return not(data.actions.start.message_type == "COMBAT" or data.actions.start.message_type == "PRINT" or data.actions.start.message_type == "ERROR") end }, @@ -130,7 +148,19 @@ function OptionsPrivate.GetActionOptions(data) name = L["Send To"], order = 3.1, disabled = function() return not data.actions.start.do_message end, - hidden = function() return data.actions.start.message_type ~= "WHISPER" end + hidden = function() return data.actions.start.message_type ~= "WHISPER" end, + desc = function() + return L["Dynamic text tooltip"] .. OptionsPrivate.Private.GetAdditionalProperties(data) + end, + }, + start_message_dest_isunit = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Is Unit"], + order = 3.15, + hidden = function() + return data.actions.start.message_type ~= "WHISPER" + end }, start_message = { type = "input", @@ -300,6 +330,7 @@ function OptionsPrivate.GetActionOptions(data) }, start_glow_color = { type = "color", + hasAlpha = true, width = WeakAuras.normalWidth, name = L["Glow Color"], order = 10.8, @@ -475,13 +506,23 @@ function OptionsPrivate.GetActionOptions(data) disabled = function() return not data.actions.finish.do_message end, control = "WeakAurasSortedDropdown" }, + finish_message_warning = { + type = "description", + width = WeakAuras.doubleWidth, + name = L["Note: Automated Messages to SAY and YELL are blocked outside of Instances."], + order = 22.5, + hidden = function() return not RestrictedChannelCheck(data.actions.finish) end + }, finish_message_space = { type = "execute", width = WeakAuras.normalWidth, name = "", order = 23, image = function() return "", 0, 0 end, - hidden = function() return data.actions.finish.message_type ~= "WHISPER" end + hidden = function() + return not(data.actions.finish.message_type == "COMBAT" + or data.actions.finish.message_type == "PRINT" or data.actions.finish.message_type == "ERROR") + end }, finish_message_color = { type = "color", @@ -510,6 +551,15 @@ function OptionsPrivate.GetActionOptions(data) disabled = function() return not data.actions.finish.do_message end, hidden = function() return data.actions.finish.message_type ~= "WHISPER" end }, + finish_message_dest_isunit = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Is Unit"], + order = 23.15, + hidden = function() + return data.actions.finish.message_type ~= "WHISPER" + end + }, finish_message = { type = "input", width = WeakAuras.doubleWidth, @@ -654,6 +704,7 @@ function OptionsPrivate.GetActionOptions(data) }, finish_glow_color = { type = "color", + hasAlpha = true, width = WeakAuras.normalWidth, name = L["Glow Color"], order = 30.8, @@ -824,7 +875,7 @@ function OptionsPrivate.GetActionOptions(data) 0.011, function() return not data.actions.init.do_custom end, {"actions", "init", "custom"}, true); OptionsPrivate.commonOptions.AddCodeOption(action.args, data, L["Custom Code"], "start_message", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code", - 5, function() return not (data.actions.start.do_message and OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.start.message)) end, {"actions", "start", "message_custom"}, false); + 5, function() return not (data.actions.start.do_message and (OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.start.message) or (data.actions.start.message_type == "WHISPER" and OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.start.message_dest)))) end, {"actions", "start", "message_custom"}, false); local startHidden = function() return OptionsPrivate.IsCollapsed("format_option", "actions", "start_message", true) @@ -868,15 +919,19 @@ function OptionsPrivate.GetActionOptions(data) end if data.controlledChildren then - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) + local list = {} + for child in OptionsPrivate.Private.TraverseLeafs(data) do + tinsert(list, child) + end + + for index, child in ipairs(list) do local startGet = function(key) - return childData.actions.start["message_format_" .. key] + return child.actions.start["message_format_" .. key] end - OptionsPrivate.AddTextFormatOption(childData.actions and childData.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden, index, #data.controlledChildren) + OptionsPrivate.AddTextFormatOption(child.actions and child.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden, true, index, #list) end else - OptionsPrivate.AddTextFormatOption(data.actions and data.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden) + OptionsPrivate.AddTextFormatOption(data.actions and data.actions.start.message, true, startGet, startAddOption, startHidden, startSetHidden, true) end @@ -884,7 +939,7 @@ function OptionsPrivate.GetActionOptions(data) 13, function() return not data.actions.start.do_custom end, {"actions", "start", "custom"}, true); OptionsPrivate.commonOptions.AddCodeOption(action.args, data, L["Custom Code"], "finish_message", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code", - 25, function() return not (data.actions.finish.do_message and OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.finish.message)) end, {"actions", "finish", "message_custom"}, false); + 25, function() return not (data.actions.finish.do_message and (OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.finish.message) or (data.actions.finish.message_type == "WHISPER" and OptionsPrivate.Private.ContainsCustomPlaceHolder(data.actions.finish.message_dest)))) end, {"actions", "finish", "message_custom"}, false); local finishHidden = function() return OptionsPrivate.IsCollapsed("format_option", "actions", "finish_message", true) @@ -898,7 +953,7 @@ function OptionsPrivate.GetActionOptions(data) return data.actions.finish["message_format_" .. key] end - order = 25 + order = 26 usedKeys = {} local function finishAddOption(key, option) if usedKeys[key] then @@ -927,15 +982,18 @@ function OptionsPrivate.GetActionOptions(data) end if data.controlledChildren then - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) + local list = {} + for child in OptionsPrivate.Private.TraverseLeafs(data) do + tinsert(list, child) + end + for index, child in ipairs(list) do local finishGet = function(key) - return childData.actions.finish["message_format_" .. key] + return child.actions.finish["message_format_" .. key] end - OptionsPrivate.AddTextFormatOption(childData.actions and childData.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden, index, #data.controlledChildren) + OptionsPrivate.AddTextFormatOption(child.actions and child.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden, true, index, #list) end else - OptionsPrivate.AddTextFormatOption(data.actions and data.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden) + OptionsPrivate.AddTextFormatOption(data.actions and data.actions.finish.message, true, finishGet, finishAddOption, finishHidden, finishSetHidden, true) end OptionsPrivate.commonOptions.AddCodeOption(action.args, data, L["Custom Code"], "finish", "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#on-hide", diff --git a/WeakAurasOptions/AnimationOptions.lua b/WeakAurasOptions/AnimationOptions.lua index a6e5ef2..0bf8867 100644 --- a/WeakAurasOptions/AnimationOptions.lua +++ b/WeakAurasOptions/AnimationOptions.lua @@ -12,30 +12,24 @@ local hiddenAll = OptionsPrivate.commonOptions.CreateHiddenAll("animation") local getAll = OptionsPrivate.commonOptions.CreateGetAll("animation") local setAll = OptionsPrivate.commonOptions.CreateSetAll("animation", getAll) + + local function filterAnimPresetTypes(intable, id) local ret = {}; local region = WeakAuras.regions[id] and WeakAuras.regions[id].region; local regionType = WeakAuras.regions[id] and WeakAuras.regions[id].regionType; local data = WeakAuras.GetData(id); + + if data.controlledChildren then + return ret + end + if(region and regionType and data) then for key, value in pairs(intable) do local preset = OptionsPrivate.Private.anim_presets[key]; if(preset) then - if(regionType == "group" or regionType == "dynamicgroup") then - local valid = true; - for index, childId in pairs(data.controlledChildren) do - local childRegion = WeakAuras.regions[childId] and WeakAuras.regions[childId].region - if(childRegion and ((preset.use_scale and not childRegion.Scale) or (preset.use_rotate and not childRegion.Rotate))) then - valid = false; - end - end - if(valid) then - ret[key] = value; - end - else - if not((preset.use_scale and not region.Scale) or (preset.use_rotate and not region.Rotate)) then - ret[key] = value; - end + if not((preset.use_scale and not region.Scale) or (preset.use_rotate and not region.Rotate)) then + ret[key] = value; end end end diff --git a/WeakAurasOptions/AuthorOptions.lua b/WeakAurasOptions/AuthorOptions.lua index 722de9b..29efae0 100644 --- a/WeakAurasOptions/AuthorOptions.lua +++ b/WeakAurasOptions/AuthorOptions.lua @@ -109,8 +109,8 @@ local function nameHead(data, option, phrase) if not data.controlledChildren then return phrase else - for _, id in ipairs(data.controlledChildren) do - if not option.references[id] then + for child in OptionsPrivate.Private.TraverseLeafs(data) do + if not option.references[child.id] then return conflictBlue .. phrase end end @@ -527,6 +527,34 @@ local function setArrayStr(data, option, array, index) end end +local function ensureUniqueKey(candidate, suffix, options, index) + index = index or 1 + local goodKey = true + local key = candidate + local existingKeys = {} + for index, option in ipairs(options) do + if option.key then + if option.key == key then + goodKey = false + end + existingKeys[option.key] = true + end + end + if not goodKey then + local prefix = candidate .. suffix + while not goodKey do + key = prefix .. index + goodKey = not existingKeys[key] + index = index + 1 + end + end + return key +end + +local function generateKey(prefix, options, index) + return ensureUniqueKey(prefix, "", options, index) +end + local typeControlAdders, addAuthorModeOption typeControlAdders = { toggle = function(options, args, data, order, prefix, i) @@ -948,7 +976,7 @@ typeControlAdders = { end args[prefix .. "default"] = { type = "multiselect", - width = WeakAuras.normalWidth, + width = WeakAuras.normalWidth * 0.9, name = L["Default"], order = order(), values = defaultValues, @@ -1312,7 +1340,7 @@ typeControlAdders = { path[#path + 1] = j childOption.subOptions[j] = { type = "toggle", - key = "subOption" .. j, + key = generateKey("subOption", childOption.subOptions, j), name = L["Sub Option %i"]:format(j), default = false, width = 1, @@ -1411,7 +1439,7 @@ local function duplicate(data, options, index) end end while existingKeys[newOption.key] do - newOption.key = newOption.key .. "copy" + newOption.key = generateKey(newOption.key .. "copy", childOptions, 1) end end if newOption.name then @@ -1424,7 +1452,7 @@ local function duplicate(data, options, index) end end -local function ensureNonDuplicateKey(option) +local function validateNonDuplicateKey(option) -- note: this has some unintuitive behavior -- e.g. if aura A has option keys "foo", "bar" -- and aura B has option keys "foo", "baz", @@ -1511,6 +1539,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) tinsert(newPath, #childGroup.subOptions + 1) OptionsPrivate.InsertCollapsed(id, "author", newPath, childCollapsed) local childOption = tremove(optionData.options, optionData.index) + childOption.key = ensureUniqueKey(childOption.key, "In", childGroup.subOptions) local childData = optionData.data tinsert(childGroup.subOptions, childOption) WeakAuras.Add(childData) @@ -1540,6 +1569,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) tinsert(newPath, 1) OptionsPrivate.InsertCollapsed(id, "author", newPath, childCollapsed) local childOption = tremove(optionData.options, optionData.index) + childOption.key = ensureUniqueKey(childOption.key, "In", childGroup.subOptions) local childData = optionData.data tinsert(childGroup.subOptions, 1, childOption) WeakAuras.Add(childData) @@ -1575,6 +1605,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) end end OptionsPrivate.RemoveCollapsed(id, "author", optionData.path) + childOption.key = ensureUniqueKey(childOption.key, "Out", parentOptions) tinsert(parentOptions, path[#path - 1], childOption) path[#path] = nil OptionsPrivate.InsertCollapsed(id, "author", path) @@ -1609,6 +1640,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) end end OptionsPrivate.RemoveCollapsed(id, "author", optionData.path) + childOption.key = ensureUniqueKey(childOption.key, "Out", parentOptions) tinsert(parentOptions, path[#path - 1] + 1, childOption) path[#path] = nil path[#path] = path[#path] + 1 @@ -1736,7 +1768,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) -- mostly because it would have a very non-intuitive effect -- the names and keys would likely not match anymore, and so -- the merged display would basically explode into a bunch of separate options - childOption.name = childOption.name or ("Option %i"):format(i) + childOption.name = childOption.name or (L["Option %i"]):format(i) if not childOption.key then local newKey = "option" .. i local existingKeys = {} @@ -1779,7 +1811,7 @@ function addAuthorModeOption(options, args, data, order, prefix, i) width = WeakAuras.normalWidth, name = name(option, "key", optionClass == "group" and L["Group key"] or L["Option key"]), order = order(), - validate = ensureNonDuplicateKey(option), + validate = validateNonDuplicateKey(option), get = get(option, "key"), set = set(data, option, "key") } @@ -2489,18 +2521,12 @@ function OptionsPrivate.GetAuthorOptions(data) local isAuthorMode = true local options = {} local order = createorder(1) - if data.controlledChildren then - -- merge options together - for i = 1, #data.controlledChildren do - local childData = WeakAuras.GetData(data.controlledChildren[i]) - mergeOptions(options, childData, childData.authorOptions, childData.config, {}) - isAuthorMode = isAuthorMode and childData.authorMode - end - else - -- pretend that this is a group with one child - isAuthorMode = data.authorMode - mergeOptions(options, data, data.authorOptions, data.config, {}) + + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + mergeOptions(options, child, child.authorOptions, child.config, {}) + isAuthorMode = isAuthorMode and child.authorMode end + if isAuthorMode then args["enterUserMode"] = { type = "execute", @@ -2509,14 +2535,9 @@ function OptionsPrivate.GetAuthorOptions(data) desc = L["Enter user mode."], order = order(), func = function() - if data.controlledChildren then - for _, id in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(id) - childData.authorMode = nil - -- no need to add, author mode is picked up by ClearAndUpdateOptions - end - else - data.authorMode = nil + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + child.authorMode = nil + -- no need to add, author mode is picked up by ClearAndUpdateOptions end WeakAuras.ClearAndUpdateOptions(data.id, true) end @@ -2535,33 +2556,18 @@ function OptionsPrivate.GetAuthorOptions(data) name = L["Add Option"], order = order(), func = function() - if data.controlledChildren then - for _, id in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(id) - local i = #childData.authorOptions + 1 - childData.authorOptions[i] = { - type = "toggle", - key = "option" .. i, - name = L["Option %i"]:format(i), - default = false, - width = 1, - useDesc = false, - } - OptionsPrivate.SetCollapsed(childData.id, "author", i, false) - WeakAuras.Add(childData) - end - else - local i = #data.authorOptions + 1 - data.authorOptions[i] = { + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + local i = #child.authorOptions + 1 + child.authorOptions[i] = { type = "toggle", - key = "option" .. i, + key = generateKey("option", child.authorOptions, i), name = L["Option %i"]:format(i), default = false, width = 1, useDesc = false, } - OptionsPrivate.SetCollapsed(data.id, "author", i, false) - WeakAuras.Add(data) + OptionsPrivate.SetCollapsed(child.id, "author", i, false) + WeakAuras.Add(child) end WeakAuras.ClearAndUpdateOptions(data.id, true) end @@ -2582,43 +2588,24 @@ function OptionsPrivate.GetAuthorOptions(data) desc = L["Reset all options to their default values."], order = order(), func = function() - if data.controlledChildren then - for _, id in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(id) - OptionsPrivate.ResetCollapsed(id, "config") - childData.config = {} -- config validation in Add() will set all the needed keys to their defaults - WeakAuras.Add(childData) - end - else - data.config = {} - OptionsPrivate.ResetCollapsed(data.id, "config") - WeakAuras.Add(data) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + child.config = {} -- config validation in Add() will set all the needed keys to their defaults + OptionsPrivate.ResetCollapsed(child.id, "config") + WeakAuras.Add(child) end WeakAuras.ClearAndUpdateOptions(data.id, true) end, disabled = function() local path = {} - if data.controlledChildren then - for _, id in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(id) - local childConfig = childData.config - for i, childOption in ipairs(childData.authorOptions) do + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + local config = child.config + for i, option in ipairs(child.authorOptions) do path[1] = i - local result = allChoicesAreDefault(childOption, childConfig, id, path) + local result = allChoicesAreDefault(option, config, child.id, path) if result == false then return false end end - end - else - local config = data.config - for i, option in ipairs(data.authorOptions) do - path[1] = i - local result = allChoicesAreDefault(option, config, data.id, path) - if result == false then - return false - end - end end return true end @@ -2630,13 +2617,8 @@ function OptionsPrivate.GetAuthorOptions(data) desc = L["Configure what options appear on this panel."], order = order(), func = function() - if data.controlledChildren then - for _, id in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(id) - childData.authorMode = true - -- no need to add, author mode is picked up by ClearAndUpdateOptions - end - else + for data in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + -- no need to add, author mode is picked up by ClearAndUpdateOptions data.authorMode = true end WeakAuras.ClearAndUpdateOptions(data.id, true) diff --git a/WeakAurasOptions/BuffTrigger.lua b/WeakAurasOptions/BuffTrigger.lua index 0148c16..422e074 100644 --- a/WeakAurasOptions/BuffTrigger.lua +++ b/WeakAurasOptions/BuffTrigger.lua @@ -130,7 +130,7 @@ local function GetBuffTriggerOptions(data, triggernum) func = function() OptionsPrivate.Private.ConvertBuffTrigger2(trigger); WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data) + WeakAuras.UpdateThumbnail(data) WeakAuras.ClearAndUpdateOptions(data.id); end }, diff --git a/WeakAurasOptions/BuffTrigger2.lua b/WeakAurasOptions/BuffTrigger2.lua index aba0bf0..9f28b61 100644 --- a/WeakAurasOptions/BuffTrigger2.lua +++ b/WeakAurasOptions/BuffTrigger2.lua @@ -76,7 +76,7 @@ local function CanHaveMatchCheck(trigger) return trigger.showClones end -local function CreateNameOptions(aura_options, data, trigger, size, isExactSpellId, isIgnoreList, prefix, baseOrder, useKey, optionKey, name, desc) +local function CreateNameOptions(aura_options, data, trigger, size, isExactSpellId, isIgnoreList, prefix, baseOrder, useKey, optionKey, name, desc, inverse) local spellCache = WeakAuras.spellCache for i = 1, size do @@ -94,7 +94,7 @@ local function CreateNameOptions(aura_options, data, trigger, size, isExactSpell if i ~= 1 then aura_options[prefix .. "space" .. i] = { type = "execute", - name = L["or"], + name = inverse and L["and"] or L["or"], width = WeakAuras.normalWidth - 0.2, image = function() return "", 0, 0 end, order = baseOrder + i / 100 + 0.0001, @@ -107,13 +107,13 @@ local function CreateNameOptions(aura_options, data, trigger, size, isExactSpell type = "execute", width = 0.2, order = baseOrder + i / 100 + 0.0002, - hidden = hiddenFunction + hidden = hiddenFunction, + control = "WeakAurasIcon" } if isExactSpellId then - aura_options[iconOption].name = "" - aura_options[iconOption].desc = function() - local name = GetSpellInfo(trigger[optionKey] and trigger[optionKey][i] or 0) + aura_options[iconOption].name = function() + local name = GetSpellInfo(WeakAuras.SafeToNumber(trigger[optionKey] and trigger[optionKey][i] or 0)) return name end aura_options[iconOption].image = function() @@ -191,7 +191,6 @@ local function CreateNameOptions(aura_options, data, trigger, size, isExactSpell WeakAuras.Add(data) WeakAuras.UpdateThumbnail(data) - WeakAuras.UpdateDisplayButton(data) WeakAuras.ClearAndUpdateOptions(data.id) end, validate = isExactSpellId and WeakAuras.ValidateNumeric or nil @@ -211,6 +210,14 @@ local function GetBuffTriggerOptions(data, triggernum) end end + local function HasMatchPerUnitCount(trigger) + if trigger.type == "aura2" and IsGroupTrigger(trigger) + and trigger.showClones and trigger.combinePerUnit and trigger.perUnitMode ~= "unaffected" + then + return trigger.useMatchPerUnit_count + end + end + local ValidateNumeric = WeakAuras.ValidateNumeric local aura_options = { useUnit = { @@ -230,7 +237,8 @@ local function GetBuffTriggerOptions(data, triggernum) values = function() return OptionsPrivate.Private.unit_types_bufftrigger_2 end, - hidden = function() return not trigger.type == "aura2" end + hidden = function() return not trigger.type == "aura2" end, + desc = L[" |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs.\n |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch.\n|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger.\n |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs.\n |cffffff00Smart Group|r adjusts to your current group type, matching just the \"player\" when solo, \"party\" units (including \"player\") in a party or \"raid\" units in a raid.\n |cffffff00Multi-target|r attempts to use the Combat Log events, rather than unitID, to track affected units.\n|cffff0000Note|r: Without a direct relationship to actual unitIDs, results may vary.\n\n|cffffff00*|r Yellow Unit settings can match multiple units and will default to being active even while no affected units are found without a Unit Count or Match Count setting."], }, useSpecificUnit = { type = "toggle", @@ -275,11 +283,17 @@ local function GetBuffTriggerOptions(data, triggernum) values = OptionsPrivate.Private.debuff_types, hidden = function() return not trigger.type == "aura2" end }, + spell_filters_header = { + type = "header", + name = L["Spell Selection Filters"], + order = 11.15, + }, use_debuffClass = { type = "toggle", width = WeakAuras.normalWidth, name = L["Debuff Type"], order = 11.2, + desc = L["Filter to only dispellable de/buffs of the given type(s)"], hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger)) end }, debuffClass = { @@ -366,6 +380,7 @@ local function GetBuffTriggerOptions(data, triggernum) type = "toggle", width = WeakAuras.normalWidth, name = L["Name Pattern Match"], + desc = L["Filter based on the spell Name string."], order = 55, hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi") end }, @@ -391,6 +406,11 @@ local function GetBuffTriggerOptions(data, triggernum) order = 55.2, hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and trigger.useNamePattern) end }, + aura_filters_header = { + type = "header", + name = L["Active Aura Filters and Info"], + order = 59.9, + }, useStacks = { type = "toggle", width = WeakAuras.normalWidth, @@ -490,90 +510,13 @@ local function GetBuffTriggerOptions(data, triggernum) order = 61.7, hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and not trigger.useTotal) end }, - fetchTooltip = { - type = "toggle", - name = L["Use Tooltip Information"], - desc = L["This adds %tooltip, %tooltip1, %tooltip2, %tooltip3 as text replacements."], - order = 62, - width = WeakAuras.doubleWidth, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and not IsSingleMissing(trigger)) end - }, - use_tooltip = { - type = "toggle", - width = WeakAuras.normalWidth, - name = L["Tooltip Pattern Match"], - order = 62.1, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.fetchTooltip) end - }, - use_tooltipSpace = { - type = "description", - name = "", - order = 62.2, - width = WeakAuras.normalWidth, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and not trigger.use_tooltip and trigger.fetchTooltip) end - }, - tooltip_operator = { - type = "select", - width = WeakAuras.normalWidth, - name = L["Operator"], - order = 62.3, - disabled = function() return not trigger.use_tooltip end, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltip and trigger.fetchTooltip) end, - values = OptionsPrivate.Private.string_operator_types - }, - tooltip = { - type = "input", - name = L["Tooltip Content"], - width = WeakAuras.doubleWidth, - order = 62.4, - disabled = function() return not trigger.use_tooltip end, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltip and trigger.fetchTooltip) end - }, - use_tooltipValue = { - type = "toggle", - width = WeakAuras.normalWidth, - name = L["Tooltip Value"], - order = 63.1, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.fetchTooltip) end - }, - tooltipValueNumber = { - type = "select", - width = WeakAuras.normalWidth, - name = L["Tooltip Value #"], - order = 63.2, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end, - values = OptionsPrivate.Private.tooltip_count - }, - use_tooltipValueSpace = { - type = "description", - name = "", - order = 63.2, - width = WeakAuras.normalWidth, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and not trigger.use_tooltipValue and trigger.fetchTooltip) end - }, - tooltipValue_operator = { - type = "select", - width = WeakAuras.normalWidth, - name = L["Operator"], - order = 63.3, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end, - values = OptionsPrivate.Private.operator_types - }, - tooltipValue = { - type = "input", - name = L["Tooltip"], - width = WeakAuras.normalWidth, - validate = ValidateNumeric, - order = 63.4, - hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end - }, use_stealable = { type = "toggle", name = function(input) local value = trigger.use_stealable if value == nil then return L["Is Stealable"] - elseif value == false then return "|cFFFF0000 "..L["Negator"].." "..L["Is Stealable"] - else return "|cFF00FF00"..L["Is Stealable"] end + elseif value == false then return "|cFFFF0000 " .. L["Negator"] .. " " .. L["Is Stealable"] .. "|r" + else return "|cFF00FF00" .. L["Is Stealable"] .. "|r" end end, width = WeakAuras.doubleWidth, order = 64, @@ -595,21 +538,14 @@ local function GetBuffTriggerOptions(data, triggernum) WeakAuras.Add(data) end }, - useAffected = { - type = "toggle", - name = L["Fetch Affected/Unaffected Names"], - width = WeakAuras.doubleWidth, - order = 65, - hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end - }, ownOnly = { type = "toggle", width = WeakAuras.doubleWidth, name = function() local value = trigger.ownOnly if value == nil then return L["Own Only"] - elseif value == false then return "|cFFFF0000 "..L["Negator"].." "..L["Own Only"] - else return "|cFF00FF00"..L["Own Only"] end + elseif value == false then return "|cFFFF0000 " .. L["Negator"] .. " " .. L["Own Only"] .. "|r" + else return "|cFF00FF00" .. L["Own Only"] .. "|r" end end, desc = function() local value = trigger.ownOnly @@ -633,16 +569,137 @@ local function GetBuffTriggerOptions(data, triggernum) end WeakAuras.Add(data) end, - order = 66, + order = 64.3, hidden = function() return not trigger.type == "aura2" end }, - ignoreSelf = { + + fetchTooltip = { type = "toggle", - name = L["Ignore Self"], - order = 67.3, + name = L["Fetch Tooltip Information"], + desc = L["This adds %tooltip, %tooltip1, %tooltip2, %tooltip3 as text replacements and also allows filtering based on the tooltip content/values."], + order = 64.5, width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and not IsSingleMissing(trigger)) end + }, + use_tooltip = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Tooltip Pattern Match"], + order = 64.51, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.fetchTooltip) end + }, + use_tooltipSpace = { + type = "description", + name = "", + order = 64.52, + width = WeakAuras.normalWidth, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and not trigger.use_tooltip and trigger.fetchTooltip) end + }, + tooltip_operator = { + type = "select", + width = WeakAuras.normalWidth, + name = L["Operator"], + order = 64.53, + disabled = function() return not trigger.use_tooltip end, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltip and trigger.fetchTooltip) end, + values = OptionsPrivate.Private.string_operator_types + }, + tooltip = { + type = "input", + name = L["Tooltip Content"], + width = WeakAuras.doubleWidth, + order = 64.54, + disabled = function() return not trigger.use_tooltip end, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltip and trigger.fetchTooltip) end + }, + use_tooltipValue = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Tooltip Value"], + order = 64.55, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.fetchTooltip) end + }, + tooltipValueNumber = { + type = "select", + width = WeakAuras.normalWidth, + name = L["Tooltip Value #"], + order = 64.56, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end, + values = OptionsPrivate.Private.tooltip_count + }, + use_tooltipValueSpace = { + type = "description", + name = "", + order = 64.57, + width = WeakAuras.normalWidth, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and not trigger.use_tooltipValue and trigger.fetchTooltip) end + }, + tooltipValue_operator = { + type = "select", + width = WeakAuras.normalWidth, + name = L["Operator"], + order = 64.58, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end, + values = OptionsPrivate.Private.operator_types + }, + tooltipValue = { + type = "input", + name = L["Tooltip"], + width = WeakAuras.normalWidth, + validate = ValidateNumeric, + order = 64.59, + hidden = function() return not (trigger.type == "aura2" and trigger.unit ~= "multi" and CanHaveMatchCheck(trigger) and trigger.use_tooltipValue and trigger.fetchTooltip) end + }, + unit_filters_header = { + type = "header", + name = L["Affected Unit Filters and Info"], + order = 65, + hidden = function() return trigger.unit == "multi" end, + }, + useAffected = { + type = "toggle", + name = L["Fetch Affected/Unaffected Names"], + width = WeakAuras.doubleWidth, + order = 65.1, hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end }, + fetchRaidMark = { + type = "toggle", + name = L["Fetch Raid Mark Information"], + desc = L["This adds %raidMark as text replacements."], + order = 65.3, + width = WeakAuras.doubleWidth, + hidden = function() + return not (trigger.type == "aura2" and trigger.unit ~= "multi") + end + }, + use_includePets = { + type = "toggle", + width = WeakAuras.normalWidth, + name = WeakAuras.newFeatureString .. L["Include Pets"], + order = 66.1, + hidden = function() return + not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) + end + }, + includePets = { + type = "select", + values = OptionsPrivate.Private.include_pets_types, + width = WeakAuras.normalWidth, + name = WeakAuras.newFeatureString .. L["Include Pets"], + order = 66.15, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and trigger.use_includePets) end, + }, + includePetsSpace = { + type = "description", + name = "", + order = 66.16, + width = WeakAuras.normalWidth, + hidden = function() + return not (trigger.type == "aura2" + and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and not trigger.use_includePets) + end + }, useClass = { type = "toggle", @@ -669,35 +726,11 @@ local function GetBuffTriggerOptions(data, triggernum) hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and not trigger.useClass) end }, - ignoreDead = { - type = "toggle", - name = L["Ignore Dead"], - order = 68.7, - width = WeakAuras.doubleWidth, - hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end - }, - - ignoreDisconnected = { - type = "toggle", - name = L["Ignore Disconnected"], - order = 68.8, - width = WeakAuras.doubleWidth, - hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end - }, - ignoreInvisible = { - type = "toggle", - name = L["Ignore out of checking range"], - desc = L["Uses UnitIsVisible() to check if in range. This is polled every second."], - order = 68.9, - width = WeakAuras.doubleWidth, - hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end - }, - useUnitName = { type = "toggle", width = WeakAuras.normalWidth, - name = L["UnitName Filter"], - order = 69.1, + name = L["Filter by Unit Name"], + order = 68.4, hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end @@ -705,9 +738,9 @@ local function GetBuffTriggerOptions(data, triggernum) unitName = { type = "input", width = WeakAuras.normalWidth, - name = L["Unit Name Filter"], - desc = L["Filter formats: 'Name', 'Name-Realm', '-Realm'.\n\nSupports multiple entries, separated by commas\n"], - order = 69.2, + name = L["Filter by Unit Name"], + desc = L["Filter formats: 'Name', 'Name-Realm', '-Realm'.\n\nSupports multiple entries, separated by commas\nCan use \\ to escape -."], + order = 68.5, hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party") and trigger.useUnitName) @@ -716,7 +749,7 @@ local function GetBuffTriggerOptions(data, triggernum) unitNameSpace = { type = "description", name = "", - order = 69.3, + order = 68.5, width = WeakAuras.normalWidth, hidden = function() return not (trigger.type == "aura2" @@ -724,7 +757,50 @@ local function GetBuffTriggerOptions(data, triggernum) end }, + ignoreSelf = { + type = "toggle", + name = L["Ignore Self"], + order = 69.35, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end + }, + ignoreDead = { + type = "toggle", + name = L["Ignore Dead"], + order = 69.4, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end + }, + + ignoreDisconnected = { + type = "toggle", + name = L["Ignore Disconnected"], + order = 69.8, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end + }, + ignoreInvisible = { + type = "toggle", + name = L["Ignore out of checking range"], + desc = L["Uses UnitIsVisible() to check if in range. This is polled every second."], + order = 69.9, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and (trigger.unit == "group" or trigger.unit == "raid" or trigger.unit == "party")) end + }, + + show_settings_header = { + type = "header", + name = L["Show and Clone Settings"], + order = 69.91, + }, + multi_unit_hint = { + type = "description", + order = 69.92, + width = WeakAuras.doubleWidth, + hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger)) end, + name = L["|cff999999Triggers tracking multiple units will default to being active even while no affected units are found without a Unit Count or Match Count setting applied.|r"], + }, useGroup_count = { type = "toggle", width = WeakAuras.normalWidth, @@ -833,6 +909,45 @@ local function GetBuffTriggerOptions(data, triggernum) validate = ValidateNumeric, desc = L["Counts the number of matches over all units."] }, + useMatchPerUnit_count = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Match Count per Unit"], + hidden = function() return not (trigger.type == "aura2" and IsGroupTrigger(trigger) + and trigger.showClones and trigger.combinePerUnit and trigger.perUnitMode ~= "unaffected") end, + order = 71.6 + }, + useMatchPerUnit_countSpace = { + type = "description", + name = "", + order = 71.7, + width = WeakAuras.normalWidth, + hidden = function() + if trigger.type == "aura2" and IsGroupTrigger(trigger) + and trigger.showClones and trigger.combinePerUnit and trigger.perUnitMode ~= "unaffected" then + return trigger.useMatchPerUnit_count + end + return true + end + }, + matchPerUnit_countOperator = { + type = "select", + name = L["Operator"], + order = 71.8, + width = WeakAuras.halfWidth, + values = OptionsPrivate.Private.operator_types, + hidden = function() return not (HasMatchPerUnitCount(trigger)) end, + desc = L["Counts the number of matches per unit."] + }, + matchPerUnit_count = { + type = "input", + name = L["Count"], + order = 71.9, + width = WeakAuras.halfWidth, + hidden = function() return not (HasMatchPerUnitCount(trigger)) end, + validate = ValidateNumeric, + desc = L["Counts the number of matches per unit."] + }, showClones = { type = "toggle", name = L["Auto-Clone (Show All Matches)"], @@ -943,21 +1058,25 @@ local function GetBuffTriggerOptions(data, triggernum) CreateNameOptions(aura_options, data, trigger, nameOptionSize, false, false, "name", 12, "useName", "auranames", L["Aura Name"], - L["Enter an Aura Name, partial Aura Name, or Spell ID. A Spell ID will match any spells with the same name."]) + L["Enter an Aura Name, partial Aura Name, or Spell ID. A Spell ID will match any spells with the same name."], + IsSingleMissing(trigger)) CreateNameOptions(aura_options, data, trigger, spellOptionsSize, true, false, "spellid", 22, "useExactSpellId", "auraspellids", - L["Spell ID"], L["Enter a Spell ID"]) + L["Spell ID"], L["Enter a Spell ID"], + IsSingleMissing(trigger)) CreateNameOptions(aura_options, data, trigger, ignoreNameOptionSize, false, true, "ignorename", 32, "useIgnoreName", "ignoreAuraNames", L["Ignored Aura Name"], - L["Enter an Aura Name, partial Aura Name, or Spell ID. A Spell ID will match any spells with the same name."]) + L["Enter an Aura Name, partial Aura Name, or Spell ID. A Spell ID will match any spells with the same name."], + IsSingleMissing(trigger)) CreateNameOptions(aura_options, data, trigger, ignoreSpellOptionsSize, true, true, "ignorespellid", 42, "useIgnoreExactSpellId", "ignoreAuraSpellids", - L["Ignored Spell ID"], L["Enter a Spell ID"]) + L["Ignored Spell ID"], L["Enter a Spell ID"], + IsSingleMissing(trigger)) OptionsPrivate.commonOptions.AddCommonTriggerOptions(aura_options, data, triggernum, true) OptionsPrivate.commonOptions.AddTriggerGetterSetter(aura_options, data, triggernum) diff --git a/WeakAurasOptions/CommonOptions.lua b/WeakAurasOptions/CommonOptions.lua index 42e18fc..b02f86d 100644 --- a/WeakAurasOptions/CommonOptions.lua +++ b/WeakAurasOptions/CommonOptions.lua @@ -41,10 +41,23 @@ commonOptionsCache.GetSameAll = function(self, info) end end +commonOptionsCache.SetNameAll = function(self, info, value) + local base = self:GetOrCreateData(info) + base.nameAll = value +end + +commonOptionsCache.GetNameAll = function(self, info) + local base = self:GetData(info) + if base then + return base.nameAll + end +end + commonOptionsCache.Clear = function(self) self.data = {} end + local parsePrefix = function(input, data, create) local subRegionIndex, property = string.match(input, "^sub%.(%d+)%..-%.(.+)") subRegionIndex = tonumber(subRegionIndex) @@ -341,7 +354,6 @@ local function getChildOption(options, info) end end end - end return options end @@ -365,20 +377,17 @@ local function CreateHiddenAll(subOption) return true; end - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end - if (childOption) then - if (not hiddenChild(childOptionTable, info)) then - return false; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end + if (childOption) then + if (not hiddenChild(childOptionTable, info)) then + return false; end end end @@ -402,20 +411,17 @@ end local function CreateDisabledAll(subOption) return function(data, info) - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption); - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end - if (childOption) then - if (not disabledChild(childOptionTable, info)) then - return false; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOptions = OptionsPrivate.EnsureOptions(child, subOption); + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end + if (childOption) then + if (not disabledChild(childOptionTable, info)) then + return false; end end end @@ -428,6 +434,7 @@ local function disabledOrHiddenChild(childOptionTable, info) return hiddenChild(childOptionTable, info) or disabledChild(childOptionTable, info); end + local function replaceNameDescFuncs(intable, data, subOption) local function compareTables(tableA, tableB) if(#tableA == #tableB) then @@ -472,19 +479,16 @@ local function replaceNameDescFuncs(intable, data, subOption) local function combineKeys(info) local combinedKeys = nil; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local values = getValueFor(OptionsPrivate.EnsureOptions(childData, subOption), info, "values"); - if (values) then - if (type(values) == "function") then - values = values(info); - end - if (type(values) == "table") then - combinedKeys = combinedKeys or {}; - for k, v in pairs(values) do - combinedKeys[k] = v; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local values = getValueFor(OptionsPrivate.EnsureOptions(child, subOption), info, "values"); + if (values) then + if (type(values) == "function") then + values = values(info); + end + if (type(values) == "table") then + combinedKeys = combinedKeys or {}; + for k, v in pairs(values) do + combinedKeys[k] = v; end end end @@ -513,18 +517,15 @@ local function replaceNameDescFuncs(intable, data, subOption) local isToggle = nil - for index, childId in ipairs(data.controlledChildren) do + for child in OptionsPrivate.Private.TraverseLeafs(data) do if isToggle == nil then - local childData = WeakAuras.GetData(childId) - local childOption = getChildOption(OptionsPrivate.EnsureOptions(childData, subOption), info) + local childOption = getChildOption(OptionsPrivate.EnsureOptions(child, subOption), info) isToggle = childOption and childOption.type == "toggle" end - local childData = WeakAuras.GetData(childId); - local regionType = regionPrefix(info[#info]); - if(childData and (not regionType or childData.regionType == regionType or regionType == "sub")) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) + if(child and (not regionType or child.regionType == regionType or regionType == "sub")) then + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) local get = getValueFor(childOptions, info, "get"); if (combinedKeys) then for key, _ in pairs(combinedKeys) do @@ -568,64 +569,69 @@ local function replaceNameDescFuncs(intable, data, subOption) end local function nameAll(info) + local cached = commonOptionsCache:GetNameAll(info) + if (cached ~= nil) then + return cached + end + local combinedName; local first = true; local foundNames = {}; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOption = getChildOption(OptionsPrivate.EnsureOptions(childData, subOption), info); - if (childOption) then - local name; - if(type(childOption.name) == "function") then - name = childOption.name(info); - else - name = childOption.name; + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOption = getChildOption(OptionsPrivate.EnsureOptions(child, subOption), info); + if (childOption) then + local name; + if(type(childOption.name) == "function") then + name = childOption.name(info); + else + name = childOption.name; + commonOptionsCache:SetNameAll(info, name) + return name + end + if (not name) then + -- Do nothing + elseif(first) then + if (name ~= "") then + combinedName = name; + first = false; end - if (not name) then - -- Do nothing - elseif(first) then - if (combinedName ~= "") then - combinedName = name; - first = false; + foundNames[name] = true; + elseif not(foundNames[name]) then + if (name ~= "") then + if (childOption.type == "description") then + combinedName = combinedName .. "\n\n" .. name; + else + combinedName = combinedName .. " / " .. name; end - foundNames[name] = true; - elseif not(foundNames[name]) then - if (name ~= "") then - if (childOption.type == "description") then - combinedName = combinedName .. "\n\n" .. name; - else - combinedName = combinedName .. " / " .. name; - end - end - foundNames[name] = true; end + foundNames[name] = true; end end end - return combinedName; + if combinedName then + commonOptionsCache:SetNameAll(info, combinedName) + end + + return combinedName or "" end local function descAll(info) local combinedDesc; local first = true; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOption = getChildOption(OptionsPrivate.EnsureOptions(childData, subOption), info); - if (childOption) then - local desc; - if(type(childOption.desc) == "function") then - desc = childOption.desc(info); - else - desc = childOption.desc; - end - if(first) then - combinedDesc = desc; - first = false; - elseif not(combinedDesc == desc) then - return L["Not all children have the same value for this option"]; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOption = getChildOption(OptionsPrivate.EnsureOptions(child, subOption), info); + if (childOption) then + local desc; + if(type(childOption.desc) == "function") then + desc = childOption.desc(info); + else + desc = childOption.desc; + end + if(first) then + combinedDesc = desc; + first = false; + elseif not(combinedDesc == desc) then + return L["Not all children have the same value for this option"]; end end end @@ -643,7 +649,7 @@ local function replaceNameDescFuncs(intable, data, subOption) if(name == "") then return name; else - return "|cFF4080FF"..(name or "error"); + return "|cFF4080FF"..(name or "error").."|r"; end end end @@ -657,70 +663,71 @@ local function replaceNameDescFuncs(intable, data, subOption) end local values = {}; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end - if (childOption and not hiddenChild(childOptionTable, info)) then - for i=#childOptionTable,0,-1 do - if(childOptionTable[i].get) then - if(intable.type == "toggle") then - local name, tri; - if(type(childOption.name) == "function") then - name = childOption.name(info); - tri = true; - else - name = childOption.name; - end - if(tri and childOptionTable[i].get(info)) then - tinsert(values, "|cFFE0E000"..childId..": |r"..name); - elseif(tri) then - tinsert(values, "|cFFE0E000"..childId..": |r"..L["Ignored"]); - elseif(childOptionTable[i].get(info)) then - tinsert(values, "|cFFE0E000"..childId..": |r|cFF00FF00"..L["Enabled"]); - else - tinsert(values, "|cFFE0E000"..childId..": |r|cFFFF0000"..L["Disabled"]); - end - elseif(intable.type == "color") then - local r, g, b = childOptionTable[i].get(info); - r, g, b = r or 1, g or 1, b or 1; - tinsert(values, ("|cFF%2x%2x%2x%s"):format(r * 220 + 35, g * 220 + 35, b * 220 + 35, childId)); - elseif(intable.type == "select") then - local selectValues = type(intable.values) == "table" and intable.values or intable.values(info); - local key = childOptionTable[i].get(info); - local display = key and selectValues[key] or L["None"]; - if intable.dialogControl == "LSM30_Font" then - tinsert(values, "|cFFE0E000"..childId..": |r" .. key); - else - tinsert(values, "|cFFE0E000"..childId..": |r"..display); - end - elseif(intable.type == "multiselect") then - local selectedValues = {}; - for k, v in pairs(combinedKeys) do - if (childOptionTable[i].get(info, k)) then - tinsert(selectedValues, tostring(v)) - end - end - tinsert(values, "|cFFE0E000"..childId..": |r"..table.concat(selectedValues, ",")); + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end + if (childOption and not hiddenChild(childOptionTable, info)) then + for i=#childOptionTable,0,-1 do + if(childOptionTable[i].get) then + if(intable.type == "toggle") then + local name, tri; + if(type(childOption.name) == "function") then + name = childOption.name(info); + tri = true; else - local display = childOptionTable[i].get(info) or L["None"]; - if(type(display) == "number") then - display = math.floor(display * 100) / 100; - else - if #display > 50 then - display = display:sub(1, 50) .. "..." - end - end - tinsert(values, "|cFFE0E000"..childId..": |r"..display); + name = childOption.name; end - break; + if(tri and childOptionTable[i].get(info)) then + tinsert(values, "|cFFE0E000"..child.id..": |r"..name); + elseif(tri) then + tinsert(values, "|cFFE0E000"..child.id..": |r"..L["Ignored"]); + elseif(childOptionTable[i].get(info)) then + tinsert(values, "|cFFE0E000"..child.id..": |r|cFF00FF00"..L["Enabled"].."|r"); + else + tinsert(values, "|cFFE0E000"..child.id..": |r|cFFFF0000"..L["Disabled"].."|r"); + end + elseif(intable.type == "color") then + local r, g, b = childOptionTable[i].get(info); + r, g, b = r or 1, g or 1, b or 1; + tinsert(values, ("|cFF%2x%2x%2x%s|r"):format(r * 220 + 35, g * 220 + 35, b * 220 + 35, child.id)); + elseif(intable.type == "select") then + local selectValues = type(intable.values) == "table" and intable.values or intable.values(info); + local key = childOptionTable[i].get(info); + local display = key and selectValues[key] or L["None"]; + if intable.dialogControl == "LSM30_Font" then + tinsert(values, "|cFFE0E000"..child.id..": |r" .. key); + else + if type(display) == "string" then + tinsert(values, "|cFFE0E000"..child.id..": |r"..display); + elseif type(display) == "table" then + tinsert(values, "|cFFE0E000"..child.id..": |r"..display[1].."/"..display[2] ); + end + end + elseif(intable.type == "multiselect") then + local selectedValues = {}; + for k, v in pairs(combinedKeys) do + if (childOptionTable[i].get(info, k)) then + tinsert(selectedValues, tostring(v)) + end + end + tinsert(values, "|cFFE0E000"..child.id..": |r"..table.concat(selectedValues, ",")); + else + local display = childOptionTable[i].get(info) or L["None"]; + if(type(display) == "number") then + display = math.floor(display * 100) / 100; + else + if #display > 50 then + display = display:sub(1, 50) .. "..." + end + end + tinsert(values, "|cFFE0E000"..child.id..": |r"..display); end + break; end end end @@ -740,23 +747,20 @@ local function replaceImageFuncs(intable, data, subOption) local function imageAll(info) local combinedImage = {}; local first = true; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOption = OptionsPrivate.EnsureOptions(childData, subOption) - if not(childOption) then - return "error" - end - childOption = getChildOption(childOption, info); - if childOption and childOption.image then - local image = {childOption.image(info)}; - if(first) then - combinedImage = image; - first = false; - else - if not(combinedImage[1] == image[1]) then - return "", 0, 0; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOption = OptionsPrivate.EnsureOptions(child, subOption) + if not(childOption) then + return "error" + end + childOption = getChildOption(childOption, info); + if childOption and childOption.image then + local image = {childOption.image(info)}; + if(first) then + combinedImage = image; + first = false; + else + if not(combinedImage[1] == image[1]) then + return "", 0, 0; end end end @@ -782,40 +786,37 @@ local function replaceValuesFuncs(intable, data, subOption) local combinedValues = {}; local handledValues = {}; local first = true; - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOption = OptionsPrivate.EnsureOptions(childData, subOption) - if not(childOption) then - return "error" - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOption = OptionsPrivate.EnsureOptions(child, subOption) + if not(childOption) then + return "error" + end - childOption = getChildOption(childOption, info); - if (childOption) then - local values = childOption.values; - if (type(values) == "function") then - values = values(info); + childOption = getChildOption(childOption, info); + if (childOption) then + local values = childOption.values; + if (type(values) == "function") then + values = values(info); + end + if(first) then + for k, v in pairs(values) do + handledValues[k] = handledValues[k] or {}; + handledValues[k][v] = true; + combinedValues[k] = v; end - if(first) then - for k, v in pairs(values) do + first = false; + else + for k, v in pairs(values) do + if (handledValues[k] and handledValues[k][v]) then + -- Already known key/value pair + else + if (combinedValues[k]) then + combinedValues[k] = combinedValues[k] .. "/" .. v; + else + combinedValues[k] = v; + end handledValues[k] = handledValues[k] or {}; handledValues[k][v] = true; - combinedValues[k] = v; - end - first = false; - else - for k, v in pairs(values) do - if (handledValues[k] and handledValues[k][v]) then - -- Already known key/value pair - else - if (combinedValues[k]) then - combinedValues[k] = combinedValues[k] .. "/" .. v; - else - combinedValues[k] = v; - end - handledValues[k] = handledValues[k] or {}; - handledValues[k][v] = true; - end end end end @@ -881,49 +882,43 @@ local getHelper = { end } + local function CreateGetAll(subOption) return function(data, info, ...) local isToggle = nil local allChildren = CopyTable(getHelper) local enabledChildren = CopyTable(getHelper) - - for index, childId in ipairs(data.controlledChildren) do + for child in OptionsPrivate.Private.TraverseLeafs(data) do if isToggle == nil then - local childData = WeakAuras.GetData(childId) - local childOptions = getChildOption(OptionsPrivate.EnsureOptions(childData, subOption), info) + local childOptions = getChildOption(OptionsPrivate.EnsureOptions(child, subOption), info) isToggle = childOptions and childOptions.type == "toggle" end - local childData = WeakAuras.GetData(childId); - - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end - - if (childOption) then - for i=#childOptionTable,0,-1 do - if(childOptionTable[i].get) then - local values = {childOptionTable[i].get(info, ...)}; - if isToggle and values[1] == nil then - values[1] = false - end - - allChildren:Set(values) - if not disabledOrHiddenChild(childOptionTable, info) then - enabledChildren:Set(values) - end - - if not allChildren:GetSame() and not enabledChildren:GetSame() then - return nil; - end - break; + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end + if (childOption) then + for i=#childOptionTable,0,-1 do + if(childOptionTable[i].get) then + local values = {childOptionTable[i].get(info, ...)}; + if isToggle and values[1] == nil then + values[1] = false end + + allChildren:Set(values) + if not disabledOrHiddenChild(childOptionTable, info) then + enabledChildren:Set(values) + end + + if not allChildren:GetSame() and not enabledChildren:GetSame() then + return nil; + end + break; end end end @@ -943,27 +938,24 @@ local function CreateSetAll(subOption, getAll) OptionsPrivate.Private.pauseOptionsProcessing(true); OptionsPrivate.Private.PauseAllDynamicGroups() local before = getAll(data, info, ...) - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end - if (childOption and not disabledOrHiddenChild(childOptionTable, info)) then - for i=#childOptionTable,0,-1 do - if(childOptionTable[i].set) then - if (childOptionTable[i].type == "multiselect") then - childOptionTable[i].set(info, ..., not before); - else - childOptionTable[i].set(info, ...); - end - break; + if (childOption and not disabledOrHiddenChild(childOptionTable, info)) then + for i=#childOptionTable,0,-1 do + if(childOptionTable[i].set) then + if (childOptionTable[i].type == "multiselect") then + childOptionTable[i].set(info, ..., not before); + else + childOptionTable[i].set(info, ...); end + break; end end end @@ -972,7 +964,7 @@ local function CreateSetAll(subOption, getAll) OptionsPrivate.Private.ResumeAllDynamicGroups() OptionsPrivate.Private.pauseOptionsProcessing(false); OptionsPrivate.Private.ScanForLoads(); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); OptionsPrivate.UpdateOptions() end end @@ -980,24 +972,21 @@ end local function CreateExecuteAll(subOption) return function(data, info, button) local secondCall = nil - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childOptions = OptionsPrivate.EnsureOptions(childData, subOption) - local childOption = childOptions; - local childOptionTable = {[0] = childOption}; - for i=1,#info do - childOption = childOption.args[info[i]]; - childOptionTable[i] = childOption; - end + for child in OptionsPrivate.Private.TraverseLeafs(data) do + local childOptions = OptionsPrivate.EnsureOptions(child, subOption) + local childOption = childOptions; + local childOptionTable = {[0] = childOption}; + for i=1,#info do + childOption = childOption.args[info[i]]; + childOptionTable[i] = childOption; + end - if (childOption and not disabledOrHiddenChild(childOptionTable, info)) then - -- Some functions, that is the expand/collapse functions need to be - -- effectively called only once. Passing in the secondCall parameter allows - -- them to distinguish between the first and every other call - childOption.func(info, button, secondCall) - secondCall = true - end + if (childOption and not disabledOrHiddenChild(childOptionTable, info)) then + -- Some functions, that is the expand/collapse functions need to be + -- effectively called only once. Passing in the secondCall parameter allows + -- them to distinguish between the first and every other call + childOption.func(info, button, secondCall) + secondCall = true end end WeakAuras.ClearAndUpdateOptions(data.id) @@ -1013,6 +1002,10 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g end end + local function IsGroupByFrame() + return data.regionType == "dynamicgroup" and data.useAnchorPerUnit + end + local screenWidth, screenHeight = math.ceil(GetScreenWidth() / 20) * 20, math.ceil(GetScreenHeight() / 20) * 20; local positionOptions = { __title = L["Position Settings"], @@ -1055,12 +1048,7 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); OptionsPrivate.ResetMoverSizer(); - if(data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData) then - WeakAuras.Add(parentData); - end - end + OptionsPrivate.Private.AddParents(data) end }, yOffset = { @@ -1079,12 +1067,7 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); OptionsPrivate.ResetMoverSizer(); - if(data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData) then - WeakAuras.Add(parentData); - end - end + OptionsPrivate.Private.AddParents(data) end }, selfPoint = { @@ -1101,7 +1084,9 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g width = WeakAuras.normalWidth, name = L["Anchored To"], order = 72, - hidden = IsParentDynamicGroup, + hidden = function() + return IsParentDynamicGroup() or IsGroupByFrame() + end, values = (data.regionType == "group" or data.regionType == "dynamicgroup") and OptionsPrivate.Private.anchor_frame_types_group or OptionsPrivate.Private.anchor_frame_types, }, -- Input field to select frame to anchor on @@ -1148,9 +1133,9 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g order = 75, hidden = function() if (data.parent) then - --if (IsParentDynamicGroup()) then - -- return true; - --end + if IsGroupByFrame() then + return false + end return data.anchorFrameType == "SCREEN" or data.anchorFrameType == "MOUSE"; else return data.anchorFrameType == "MOUSE"; @@ -1164,6 +1149,9 @@ local function PositionOptions(id, data, _, hideWidthHeight, disableSelfPoint, g name = L["To Group's"], order = 76, hidden = function() + if IsGroupByFrame() then + return true + end if (data.anchorFrameType ~= "SCREEN") then return true; end @@ -1468,7 +1456,6 @@ local function AddCommonTriggerOptions(options, data, triggernum, doubleWidth) end WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); WeakAuras.ClearAndUpdateOptions(data.id); end, control = "WeakAurasSortedDropdown" diff --git a/WeakAurasOptions/ConditionOptions.lua b/WeakAurasOptions/ConditionOptions.lua index 4ae59aa..2165186 100644 --- a/WeakAurasOptions/ConditionOptions.lua +++ b/WeakAurasOptions/ConditionOptions.lua @@ -108,18 +108,17 @@ local function valueToString(a, propertytype) return tostring(a); end -local function isSubset(data, reference) +local function isSubset(data, reference, totalAuraCount) if (data.controlledChildren) then - local auraCount = #data.controlledChildren; - if (auraCount > reference.referenceCount) then + if (totalAuraCount > reference.referenceCount) then return true; end end return false; end -local function blueIfSubset(data, reference) - if (isSubset(data, reference)) then +local function blueIfSubset(data, reference, totalAuraCount) + if (isSubset(data, reference, totalAuraCount)) then return "|cFF4080FF"; end return ""; @@ -143,8 +142,8 @@ local function blueIfNoValue2(data, object, variable, subvariable, blueString, n return normalString or ""; end -local function descIfSubset(data, reference) - if (isSubset(data, reference)) then +local function descIfSubset(data, reference, totalAuraCount) + if (isSubset(data, reference, totalAuraCount)) then local desc = L["Used in auras:"]; for id in pairs(reference.references) do desc = desc .. "\n" .. id; @@ -156,7 +155,6 @@ end local function descIfNoValue(data, object, variable, type, values) if (data.controlledChildren) then - local auraCount = #data.controlledChildren; if (object["same" .. variable] == false) then local desc = ""; for id, reference in pairs(object.references) do @@ -174,7 +172,6 @@ end local function descIfNoValue2(data, object, variable, subvariable, type, values) if (data.controlledChildren) then - local auraCount = #data.controlledChildren; if (object["same" .. variable] and object["same" .. variable][subvariable] == false) then local desc = ""; for id, reference in pairs(object.references) do @@ -215,15 +212,15 @@ local function wrapWithPlaySound(func, kit) end end -local function addControlsForChange(args, order, data, conditionVariable, conditions, i, j, allProperties, usedProperties) +local function addControlsForChange(args, order, data, conditionVariable, totalAuraCount, conditions, i, j, allProperties, usedProperties) local thenText = (j == 1) and L["Then "] or L["And "]; - local display = isSubset(data, conditions[i].changes[j]) and allProperties.displayWithCopy or allProperties.display; + local display = isSubset(data, conditions[i].changes[j], totalAuraCount) and allProperties.displayWithCopy or allProperties.display; local valuesForProperty = filterUsedProperties(allProperties.indexToProperty, display, usedProperties, conditions[i].changes[j].property); args["condition" .. i .. "property" .. j] = { type = "select", width = WeakAuras.normalWidth, - name = blueIfSubset(data, conditions[i].changes[j]) .. thenText, - desc = descIfSubset(data, conditions[i].changes[j]), + name = blueIfSubset(data, conditions[i].changes[j], totalAuraCount) .. thenText, + desc = descIfSubset(data, conditions[i].changes[j], totalAuraCount), order = order, values = valuesForProperty, control = "WeakAurasTwoColumnDropdown", @@ -234,13 +231,13 @@ local function addControlsForChange(args, order, data, conditionVariable, condit set = function(info, index) local property = allProperties.indexToProperty[index]; if (property == "COPY") then - for _, id in ipairs(data.controlledChildren) do - if (conditions[i].changes[j].references[id]) then + for child in OptionsPrivate.Private.TraverseLeafs(data) do + if (conditions[i].changes[j].references[child.id]) then -- Already exist else local insertPoint = 1; for index = j, 1, -1 do - if (conditions[i].changes[index].references[id]) then + if (conditions[i].changes[index].references[child.id]) then insertPoint = index + 1; break; end @@ -254,13 +251,16 @@ local function addControlsForChange(args, order, data, conditionVariable, condit change.value = conditions[i].changes[j].value; end - local conditionIndex = conditions[i].check.references[id].conditionIndex; - local auraData = WeakAuras.GetData(id); - tinsert(auraData[conditionVariable][conditionIndex].changes, insertPoint, change); - WeakAuras.Add(auraData); + local reference = conditions[i].check.references[child.id] + if reference then + local conditionIndex = reference.conditionIndex; + tinsert(child[conditionVariable][conditionIndex].changes, insertPoint, change); + WeakAuras.Add(child); + OptionsPrivate.ClearOptions(child.id) + end end end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; elseif (property == "DELETE") then if (data.controlledChildren) then @@ -269,12 +269,13 @@ local function addControlsForChange(args, order, data, conditionVariable, condit local conditionIndex = conditions[i].check.references[id].conditionIndex; tremove(auraData[conditionVariable][conditionIndex].changes, reference.changeIndex); WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else tremove(conditions[i].changes, j); WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end return; end @@ -287,14 +288,15 @@ local function addControlsForChange(args, order, data, conditionVariable, condit auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].property = property; auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value = default; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end conditions[i].changes[j].property = property; - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else conditions[i].changes[j].property = property; conditions[i].changes[j].value = default; WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end } @@ -311,9 +313,10 @@ local function addControlsForChange(args, order, data, conditionVariable, condit local conditionIndex = conditions[i].check.references[id].conditionIndex; auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value = v; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end conditions[i].changes[j].value = v; - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end setValueColor = function(info, r, g, b, a) for id, reference in pairs(conditions[i].changes[j].references) do @@ -325,16 +328,17 @@ local function addControlsForChange(args, order, data, conditionVariable, condit auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value[3] = b; auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value[4] = a; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end conditions[i].changes[j].value = conditions[i].changes[j].value or {}; conditions[i].changes[j].value[1] = r; conditions[i].changes[j].value[2] = g; conditions[i].changes[j].value[3] = b; conditions[i].changes[j].value[4] = a; - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end - setValueComplex = function(property, reloadOptions) + setValueComplex = function(property) return function(info, v) for id, reference in pairs(conditions[i].changes[j].references) do local auraData = WeakAuras.GetData(id); @@ -344,15 +348,15 @@ local function addControlsForChange(args, order, data, conditionVariable, condit end auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value[property] = v; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end if (type(conditions[i].changes[j].value) ~= "table") then conditions[i].changes[j].value = {}; end conditions[i].changes[j].value[property] = v; - if reloadOptions then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.Add(data) + WeakAuras.ClearAndUpdateOptions(data.id) end end @@ -372,6 +376,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value[property][3] = b; auraData[conditionVariable][conditionIndex].changes[reference.changeIndex].value[property][4] = a; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end if (type(conditions[i].changes[j].value) ~= "table") then conditions[i].changes[j].value = {}; @@ -383,13 +388,14 @@ local function addControlsForChange(args, order, data, conditionVariable, condit conditions[i].changes[j].value[property][2] = g; conditions[i].changes[j].value[property][3] = b; conditions[i].changes[j].value[property][4] = a; - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end else setValue = function(info, v) conditions[i].changes[j].value = v; WeakAuras.Add(data); + WeakAuras.ClearAndUpdateOptions(data.id) end setValueColor = function(info, r, g, b, a) conditions[i].changes[j].value = conditions[i].changes[j].value or {}; @@ -398,18 +404,17 @@ local function addControlsForChange(args, order, data, conditionVariable, condit conditions[i].changes[j].value[3] = b; conditions[i].changes[j].value[4] = a; WeakAuras.Add(data); + WeakAuras.ClearAndUpdateOptions(data.id) end - setValueComplex = function(property, reloadOptions) + setValueComplex = function(property) return function(info, v) if (type (conditions[i].changes[j].value) ~= "table") then conditions[i].changes[j].value = {}; end conditions[i].changes[j].value[property] = v; WeakAuras.Add(data); - if reloadOptions then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.ClearAndUpdateOptions(data.id) end end @@ -426,6 +431,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit conditions[i].changes[j].value[property][3] = b; conditions[i].changes[j].value[property][4] = a; WeakAuras.Add(data); + WeakAuras.ClearAndUpdateOptions(data.id) end end end @@ -694,11 +700,60 @@ local function addControlsForChange(args, order, data, conditionVariable, condit return false; end + if WeakAuras.IsRetail() then + args["condition" .. i .. "value" .. j .. "message type warning"] = { + type = "description", + width = WeakAuras.doubleWidth, + name = L["Note: Automated Messages to SAY and YELL are blocked outside of Instances."], + order = order, + hidden = function() + return not (anyMessageType("SAY") or anyMessageType("YELL") or anyMessageType("SMARTRAID")); + end + } + order = order + 1; + end + + args["condition" .. i .. "value" .. j .. "_indent"] = { + type = "description", + width = WeakAuras.normalWidth, + name = "", + order = order, + hidden = function() + return anyMessageType("WHISPER"); + end + } + order = order + 1; + + args["condition" .. i .. "value" .. j .. "message color"] = { + type = "color", + width = WeakAuras.normalWidth, + hasAlpha = false, + name = blueIfNoValue2(data, conditions[i].changes[j], "value", "message_color", L["Color"], L["Color"]), + desc = descIfNoValue2(data, conditions[i].changes[j], "value", "message_color", propertyType), + order = order, + get = function() + if (conditions[i].changes[j].value and type(conditions[i].changes[j].value) == "table") and type(conditions[i].changes[j].value.message_color) == "table" then + return conditions[i].changes[j].value.message_color[1], conditions[i].changes[j].value.message_color[2], conditions[i].changes[j].value.message_color[3]; + end + return 1, 1, 1, 1; + end, + set = setValueColorComplex("message_color"), + hidden = function() + return not (anyMessageType("COMBAT") or anyMessageType("PRINT") or anyMessageType("ERROR")); + end + } + order = order + 1; + + local descMessage = descIfNoValue2(data, conditions[i].changes[j], "value", "message", propertyType); + if (not descMessage and data ~= OptionsPrivate.tempGroup) then + descMessage = L["Dynamic text tooltip"] .. OptionsPrivate.Private.GetAdditionalProperties(data) + end + args["condition" .. i .. "value" .. j .. "message dest"] = { type = "input", width = WeakAuras.normalWidth, name = blueIfNoValue2(data, conditions[i].changes[j], "value", "message_dest", L["Send To"], L["Send To"]), - desc = descIfNoValue2(data, conditions[i].changes[j], "value", "message_dest", propertyType), + desc = descMessage, order = order, get = function() return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message_dest; @@ -710,10 +765,21 @@ local function addControlsForChange(args, order, data, conditionVariable, condit } order = order + 1; - local descMessage = descIfNoValue2(data, conditions[i].changes[j], "value", "message", propertyType); - if (not descMessage and data ~= OptionsPrivate.tempGroup) then - descMessage = L["Dynamic text tooltip"] .. OptionsPrivate.Private.GetAdditionalProperties(data) - end + args["condition" .. i .. "value" .. j] = { + type = "toggle", + width = WeakAuras.normalWidth, + name = blueIfNoValue(data, conditions[i].changes[j], "value", "message_dest_isunit", L["Is Unit"], L["Is Unit"]), + desc = descIfNoValue(data, conditions[i].changes[j], "value", "message_dest_isunit", propertyType), + order = order, + get = function() + return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message_dest_isunit; + end, + set = setValueComplex("message_dest_isunit"), + hidden = function() + return not anyMessageType("WHISPER"); + end + } + order = order + 1; local message_getter = function() return type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message; @@ -753,8 +819,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit option.desc = descIfNoValue2(data, conditions[i].changes[j], "value", "message_format_" .. key, nil, option.values) end - option.set = setValueComplex("message_format_" .. key, option.reloadOptions) - option.reloadOptions = nil + option.set = setValueComplex("message_format_" .. key) args[fullKey] = option end @@ -776,11 +841,11 @@ local function addControlsForChange(args, order, data, conditionVariable, condit end for index, reference in ipairs(ordered) do local input = reference.value and reference.value.message - hasTextFormatOption = OptionsPrivate.AddTextFormatOption(input, true, formatGet, addOption, hidden, setHidden, index, #ordered) + hasTextFormatOption = OptionsPrivate.AddTextFormatOption(input, true, formatGet, addOption, hidden, setHidden, true, index, #ordered) end else local input = type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value["message"] - hasTextFormatOption = OptionsPrivate.AddTextFormatOption(input, true, formatGet, addOption, hidden, setHidden) + hasTextFormatOption = OptionsPrivate.AddTextFormatOption(input, true, formatGet, addOption, hidden, setHidden, true) end if hasTextFormatOption then @@ -794,8 +859,9 @@ local function addControlsForChange(args, order, data, conditionVariable, condit local function customHidden() local message = type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message; - if (not message) then return true; end - return not OptionsPrivate.Private.ContainsCustomPlaceHolder(message); + local message_dest = type(conditions[i].changes[j].value) == "table" and conditions[i].changes[j].value.message_type == "WHISPER" and conditions[i].changes[j].value.message_dest + if (not message and not message_dest) then return true; end + return not OptionsPrivate.Private.ContainsCustomPlaceHolder(message) and not OptionsPrivate.Private.ContainsCustomPlaceHolder(message_dest); end args["condition" .. i .. "value" .. j .. "custom"] = { @@ -824,9 +890,9 @@ local function addControlsForChange(args, order, data, conditionVariable, condit local changeIndex = reference.changeIndex; multipath[id] = {"conditions", conditionIndex, "changes", changeIndex, "value", "custom"}; end - OptionsPrivate.OpenTextEditor(data, multipath, nil, true, nil, nil, "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#custom-check"); + OptionsPrivate.OpenTextEditor(data, multipath, nil, true, nil, nil, "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code-1"); else - OptionsPrivate.OpenTextEditor(data, {"conditions", i, "changes", j, "value", "custom"}, nil, nil, nil, nil, "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#custom-check"); + OptionsPrivate.OpenTextEditor(data, {"conditions", i, "changes", j, "value", "custom"}, nil, nil, nil, nil, "https://github.com/WeakAuras/WeakAuras2/wiki/Custom-Code-Blocks#chat-message---custom-code-1"); end end } @@ -993,6 +1059,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit name = blueIfNoValue2(data, conditions[i].changes[j], "value", "glow_frame_type", L["Glow Frame Type"], L["Glow Frame Type"]), desc = descIfNoValue2(data, conditions[i].changes[j], "value", "glow_frame_type", propertyType, { UNITFRAME = L["Unit Frame"], + NAMEPLATE = L["Nameplate"], FRAMESELECTOR = L["Frame Selector"] }), order = order, @@ -1065,6 +1132,7 @@ local function addControlsForChange(args, order, data, conditionVariable, condit order = order + 1 args["condition" .. i .. "value" .. j .. "glow_color"] = { type = "color", + hasAlpha = true, width = WeakAuras.normalWidth, name = blueIfNoValue2(data, conditions[i].changes[j], "value", "glow_color", L["Glow Color"], L["Glow Color"]), desc = descIfNoValue2(data, conditions[i].changes[j], "value", "glow_color", "color"), @@ -1284,7 +1352,7 @@ local function removeSubCheck(base, path) tremove(parent.checks, path[#path]); end -local function addControlsForIfLine(args, order, data, conditionVariable, conditions, i, path, conditionTemplates, conditionTemplateWithoutCombinations, allProperties, parentType) +local function addControlsForIfLine(args, order, data, conditionVariable, totalAuraCount, conditions, i, path, conditionTemplates, conditionTemplateWithoutCombinations, allProperties, parentType) local check = getSubCheck(conditions[i].check, path); local indentDepth = min(#path, 3); -- Be reasonable @@ -1293,7 +1361,7 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit local conditionTemplatesToUse = indentDepth < 3 and conditionTemplates or conditionTemplateWithoutCombinations; - local optionsName = blueIfSubset(data, conditions[i].check); + local optionsName = blueIfSubset(data, conditions[i].check, totalAuraCount); local needsTriggerName = check and check.trigger and check.trigger ~= -1 and check.trigger ~= -2; if (parentType) then local isFirst = path[#path] == 1; @@ -1363,27 +1431,27 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit if (indentDepth > 0) then valuesForIf = conditionTemplatesToUse.displayWithRemove; else - valuesForIf = isSubset(data, conditions[i].check) and conditionTemplatesToUse.displayWithCopy or conditionTemplatesToUse.display; + valuesForIf = isSubset(data, conditions[i].check, totalAuraCount) and conditionTemplatesToUse.displayWithCopy or conditionTemplatesToUse.display; end args["condition" .. i .. tostring(path) .. "if"] = { type = "select", name = optionsName, - desc = descIfSubset(data, conditions[i].check), + desc = descIfSubset(data, conditions[i].check, totalAuraCount), order = order, values = valuesForIf, width = normalWidth; set = function(info, v) if (conditionTemplatesToUse.indexToTrigger[v] == "COPY") then - for _, id in ipairs(data.controlledChildren) do - if (conditions[i].check.references[id]) then + for child in OptionsPrivate.Private.TraverseLeafs(data) do + if (conditions[i].check.references[child.id]) then -- Already exists else -- find a good insertion point, if any other condition has a reference to this -- insert directly after that local insertPoint = 1; for index = i, 1, -1 do - if (conditions[index].check.references[id]) then + if (conditions[index].check.references[child.id]) then insertPoint = index + 1; break; end @@ -1414,12 +1482,12 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit end end - local auraData = WeakAuras.GetData(id); - tinsert(auraData[conditionVariable], insertPoint, condition); - WeakAuras.Add(auraData); + tinsert(child[conditionVariable], insertPoint, condition); + WeakAuras.Add(child); + OptionsPrivate.ClearOptions(child.id) end end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; end @@ -1453,8 +1521,9 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit childCheck.trigger = trigger; childCheck.value = nil; WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else local oldType; check = getOrCreateSubCheck(conditions[i].check, path); @@ -1470,7 +1539,7 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit check.value = nil; end WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end, get = function() @@ -1495,7 +1564,7 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit for subCheck = 1, subCheckCount do local subPath = CopyTable(path); tinsert(subPath, subCheck); - order = addControlsForIfLine(args, order, data, conditionVariable, conditions, i, subPath, conditionTemplates, conditionTemplateWithoutCombinations, allProperties, check.variable); + order = addControlsForIfLine(args, order, data, conditionVariable, totalAuraCount, conditions, i, subPath, conditionTemplates, conditionTemplateWithoutCombinations, allProperties, check.variable); end end @@ -1509,46 +1578,33 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit end if (currentConditionTemplate and currentConditionTemplate.type and type(currentConditionTemplate.type) == "string") then - local setOp; - local setValue; - if (data.controlledChildren) then - setOp = function(info, v) - check = getOrCreateSubCheck(conditions[i].check, path); - for id, reference in pairs(conditions[i].check.references) do - local auraData = WeakAuras.GetData(id); - local childCheck = getOrCreateSubCheck(auraData[conditionVariable][reference.conditionIndex].check, path); - childCheck.op = v; - WeakAuras.Add(auraData); + local function makeSetter(field) + if (data.controlledChildren) then + return function(info, v) + check = getOrCreateSubCheck(conditions[i].check, path); + for id, reference in pairs(conditions[i].check.references) do + local auraData = WeakAuras.GetData(id); + local childCheck = getOrCreateSubCheck(auraData[conditionVariable][reference.conditionIndex].check, path); + childCheck[field] = v; + WeakAuras.Add(auraData); + OptionsPrivate.ClearOptions(auraData.id) + end + check[field] = v; + WeakAuras.ClearAndUpdateOptions(data.id) end - check.op = v; - WeakAuras.ClearAndUpdateOptions(data.id, true) - end - setValue = function(info, v) - check = getOrCreateSubCheck(conditions[i].check, path); - for id, reference in pairs(conditions[i].check.references) do - local auraData = WeakAuras.GetData(id); - local childCheck = getOrCreateSubCheck(auraData[conditionVariable][reference.conditionIndex].check, path); - childCheck.value = v; - WeakAuras.Add(auraData); + else + return function(info, v) + check = getOrCreateSubCheck(conditions[i].check, path); + check[field] = v; + WeakAuras.Add(data); + WeakAuras.ClearAndUpdateOptions(data.id) end - check.value = v; - WeakAuras.ClearAndUpdateOptions(data.id, true) - end - else - setOp = function(info, v) - check = getOrCreateSubCheck(conditions[i].check, path); - check.op = v; - WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id) - end - setValue = function(info, v) - check = getOrCreateSubCheck(conditions[i].check, path); - check.value = v; - WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) end end + local setOp = makeSetter("op") + local setValue = makeSetter("value") + if (currentConditionTemplate.type == "number" or currentConditionTemplate.type == "timer" or currentConditionTemplate.type == "elapsedTimer") then local opTypes = OptionsPrivate.Private.operator_types if currentConditionTemplate.operator_types == "without_equal" then @@ -1694,6 +1750,95 @@ local function addControlsForIfLine(args, order, data, conditionVariable, condit order = order + 1; elseif currentConditionTemplate.type == "alwaystrue" then order = addSpace(args, order) + elseif (currentConditionTemplate.type == "range") then + args["condition" .. i .. tostring(path) .. "_op_range"] = { + name = blueIfNoValue(data, conditions[i].check, "op_range", L["Differences"]), + desc = descIfNoValue(data, conditions[i].check, "op_range", currentConditionTemplate.type), + type = "select", + order = order, + values = OptionsPrivate.Private.operator_types_without_equal, + width = WeakAuras.halfWidth, + get = function() + return check.op_range; + end, + set = makeSetter("op_range"), + } + order = order + 1; + + args["condition" .. i .. tostring(path) .. "_range"] = { + type = "input", + name = L["Range in yards"], + desc = descIfNoValue(data, conditions[i].check, "range", currentConditionTemplate.type), + width = WeakAuras.halfWidth, + order = order, + validate = WeakAuras.ValidateNumeric, + get = function() + return check.range; + end, + set = makeSetter("range") + } + order = order + 1; + + if (indentWidth > 0) then + args["condition" .. i .. tostring(path) .. "_space"] = { + type = "description", + name = "", + order = order, + width = WeakAuras.doubleWidth * 1.5, + } + order = order + 1; + args["condition" .. i .. tostring(path) .. "_indent"] = { + type = "description", + width = indentWidth, + name = "", + order = order + } + order = order + 1; + end + + args["condition" .. i .. tostring(path) .. "_type"] = { + type = "select", + width = normalWidth, + name = blueIfNoValue(data, conditions[i].check, "type", L["Differences"]), + desc = descIfNoValue(data, conditions[i].check, "type", currentConditionTemplate.type), + order = order, + values = { + group = L["Group player(s) found"], + }, + get = function() + return check.type + end, + set = makeSetter("type"), + } + order = order + 1; + + args["condition" .. i .. tostring(path) .. "_op"] = { + name = blueIfNoValue(data, conditions[i].check, "op", L["Differences"]), + desc = descIfNoValue(data, conditions[i].check, "op", currentConditionTemplate.type), + type = "select", + order = order, + values = OptionsPrivate.Private.operator_types, + width = WeakAuras.halfWidth, + get = function() + return check.op; + end, + set = setOp, + } + order = order + 1; + + args["condition" .. i .. tostring(path) .. "_value"] = { + type = "input", + name = blueIfNoValue(data, conditions[i].check, "value", L["Differences"]), + desc = descIfNoValue(data, conditions[i].check, "value", currentConditionTemplate.type), + width = WeakAuras.halfWidth, + order = order, + validate = WeakAuras.ValidateNumeric, + get = function() + return check.value; + end, + set = setValue + } + order = order + 1; elseif currentConditionTemplate.type == "customcheck" then args["condition" .. i .. tostring(path) .. "_op"] = { name = blueIfNoValue(data, conditions[i].check, "op", L["Additional Events"], L["Additional Events"]), @@ -1799,7 +1944,7 @@ local function fixUpLinkedInFirstCondition(conditions) end end -local function addControlsForCondition(args, order, data, conditionVariable, conditions, i, conditionTemplates, conditionTemplateWithoutCombinations, allProperties) +local function addControlsForCondition(args, order, data, conditionVariable, totalAuraCount, conditions, i, conditionTemplates, conditionTemplateWithoutCombinations, allProperties) if (not conditions[i].check) then return order; end @@ -1828,11 +1973,12 @@ local function addControlsForCondition(args, order, data, conditionVariable, con for id, reference in pairs(conditions[i].check.references) do local index = reference.conditionIndex OptionsPrivate.SetCollapsed(id, "condition", index, not collapsed); + OptionsPrivate.ClearOptions(id) end else OptionsPrivate.SetCollapsed(data.id, "condition", i, not collapsed); end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end, image = collapsed and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\expand" or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\collapse" , imageWidth = 18, @@ -1870,9 +2016,10 @@ local function addControlsForCondition(args, order, data, conditionVariable, con fixUpLinkedInFirstCondition(auraData[conditionVariable]) WeakAuras.Add(auraData); OptionsPrivate.MoveCollapseDataUp(auraData.id, "condition", {reference.conditionIndex}) + OptionsPrivate.ClearOptions(auraData.id) end end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else if (i > 1) then local tmp = conditions[i]; @@ -1881,7 +2028,7 @@ local function addControlsForCondition(args, order, data, conditionVariable, con fixUpLinkedInFirstCondition(conditions) WeakAuras.Add(data); OptionsPrivate.MoveCollapseDataUp(data.id, "condition", {i}) - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end end, @@ -1923,9 +2070,10 @@ local function addControlsForCondition(args, order, data, conditionVariable, con fixUpLinkedInFirstCondition(auraData[conditionVariable]) WeakAuras.Add(auraData); OptionsPrivate.MoveCollapseDataDown(auraData.id, "condition", {reference.conditionIndex}) + OptionsPrivate.ClearOptions(auraData.id) end end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; else if (i < #conditions) then @@ -1935,7 +2083,7 @@ local function addControlsForCondition(args, order, data, conditionVariable, con fixUpLinkedInFirstCondition(conditions) WeakAuras.Add(data); OptionsPrivate.MoveCollapseDataDown(data.id, "condition", {i}) - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; end end @@ -1960,15 +2108,16 @@ local function addControlsForCondition(args, order, data, conditionVariable, con tinsert(auraData[conditionVariable], reference.conditionIndex + 1, clone); WeakAuras.Add(auraData); OptionsPrivate.DuplicateCollapseData(auraData.id, "condition", {reference.conditionIndex}) + OptionsPrivate.ClearOptions(auraData.id) end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; else local clone = CopyTable(conditions[i]) tinsert(conditions, i + 1, clone); WeakAuras.Add(data); OptionsPrivate.DuplicateCollapseData(data.id, "condition", {i}) - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; end end, @@ -1992,15 +2141,16 @@ local function addControlsForCondition(args, order, data, conditionVariable, con fixUpLinkedInFirstCondition(auraData[conditionVariable]) WeakAuras.Add(auraData); OptionsPrivate.RemoveCollapsed(auraData.id, "condition", {reference.conditionIndex}) + OptionsPrivate.ClearOptions(auraData.id) end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; else tremove(conditions, i); fixUpLinkedInFirstCondition(conditions) WeakAuras.Add(data); OptionsPrivate.RemoveCollapsed(data.id, "condition", {i}) - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) return; end end, @@ -2016,7 +2166,7 @@ local function addControlsForCondition(args, order, data, conditionVariable, con return order; end - order = addControlsForIfLine(args, order, data, conditionVariable, conditions, i, {}, conditionTemplates, conditionTemplateWithoutCombinations, allProperties); + order = addControlsForIfLine(args, order, data, conditionVariable, totalAuraCount, conditions, i, {}, conditionTemplates, conditionTemplateWithoutCombinations, allProperties); -- Add Property changes @@ -2029,7 +2179,7 @@ local function addControlsForCondition(args, order, data, conditionVariable, con end for j = 1, conditions[i].changes and #conditions[i].changes or 0 do - order = addControlsForChange(args, order, data, conditionVariable, conditions, i, j, allProperties, usedProperties); + order = addControlsForChange(args, order, data, conditionVariable, totalAuraCount, conditions, i, j, allProperties, usedProperties); end args["condition" .. i .. "_addChange"] = { @@ -2044,13 +2194,14 @@ local function addControlsForCondition(args, order, data, conditionVariable, con auradata[conditionVariable][reference.conditionIndex].changes = auradata[conditionVariable][reference.conditionIndex].changes or {} tinsert(auradata[conditionVariable][reference.conditionIndex].changes, {}) WeakAuras.Add(auradata); + OptionsPrivate.ClearOptions(auradata.id) end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else conditions[i].changes = conditions[i].changes or {}; conditions[i].changes[#conditions[i].changes + 1] = {}; WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end } @@ -2091,13 +2242,14 @@ local function addControlsForCondition(args, order, data, conditionVariable, con if reference.conditionIndex > 1 then auradata[conditionVariable][reference.conditionIndex].linked = not isLinked WeakAuras.Add(auradata); + OptionsPrivate.ClearOptions(auradata.id) end end - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) else conditions[i].linked = not isLinked WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + WeakAuras.ClearAndUpdateOptions(data.id) end end } @@ -2196,11 +2348,10 @@ local function createConditionTemplates(data) local numTriggers = 0; if (data.controlledChildren) then allConditionTemplates = {}; - for _, id in ipairs(data.controlledChildren) do - local data = WeakAuras.GetData(id); - numTriggers = max(numTriggers, #data.triggers); + for child in OptionsPrivate.Private.TraverseLeafs(data) do + numTriggers = max(numTriggers, #child.triggers); - local auraConditionsTemplate = OptionsPrivate.Private.GetTriggerConditions(data); + local auraConditionsTemplate = OptionsPrivate.Private.GetTriggerConditions(child); mergeConditionTemplates(allConditionTemplates, auraConditionsTemplate, numTriggers) end else @@ -2238,39 +2389,28 @@ end local function buildAllPotentialProperties(data, category) local allProperties = {}; allProperties.propertyMap = {}; - if (data.controlledChildren) then - for _, id in ipairs(data.controlledChildren) do - local auradata = WeakAuras.GetData(id); - local regionProperties = OptionsPrivate.Private.GetProperties(auradata); - if (regionProperties) then - for k, v in pairs(regionProperties) do - if (v.category == category) then - if (allProperties.propertyMap[k]) then - if (allProperties.propertyMap[k].type ~= v.type) then - allProperties.propertyMap[k].type = "incompatible"; - end + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + local regionProperties = OptionsPrivate.Private.GetProperties(child); - if (allProperties.propertyMap[k].type == "list") then - -- Merge value lists - for key, value in pairs(v.values) do - if (allProperties.propertyMap[k].values[key] == nil) then - allProperties.propertyMap[k].values[key] = value; - end - end - end - else - allProperties.propertyMap[k] = CopyTable(v) - end - end - end - end - end - else - local regionProperties = OptionsPrivate.Private.GetProperties(data); if (regionProperties) then for k, v in pairs(regionProperties) do if (v.category == category) then - allProperties.propertyMap[k] = v; + if (allProperties.propertyMap[k]) then + if (allProperties.propertyMap[k].type ~= v.type) then + allProperties.propertyMap[k].type = "incompatible"; + end + + if (allProperties.propertyMap[k].type == "list") then + -- Merge value lists + for key, value in pairs(v.values) do + if (allProperties.propertyMap[k].values[key] == nil) then + allProperties.propertyMap[k].values[key] = value; + end + end + end + else + allProperties.propertyMap[k] = CopyTable(v) + end end end end @@ -2411,7 +2551,7 @@ local function SubPropertiesForChange(change) "glow_scale", "glow_border" } elseif change.property == "chat" then - local result = { "message_type", "message_dest", "message_channel", "message", "custom" } + local result = { "message_type", "message_dest", "message_channel", "message_color", "message", "custom" } local input = change.value and change.value.message if input then local getter = function(key) @@ -2419,14 +2559,15 @@ local function SubPropertiesForChange(change) end OptionsPrivate.AddTextFormatOption(input, false, getter, function(key) tinsert(result, "message_format_" .. key) - end) + end, nil, nil, true) end return result end end local subPropertyToType = { - glow_color = "color" + glow_color = "color", + message_color = "color" } local function mergeConditionChange(all, change, id, changeIndex, allProperties) @@ -2601,16 +2742,23 @@ function OptionsPrivate.GetConditionOptions(data) -- Build currently selected conditions local conditions; + local totalAuraCount + if (data.controlledChildren) then + local allChildren = {} + for child in OptionsPrivate.Private.TraverseLeafs(data) do + tinsert(allChildren, child) + end + totalAuraCount = #allChildren + conditions = {}; - local last = #data.controlledChildren; - for index = last, 1, -1 do - local id = data.controlledChildren[index]; - local data = WeakAuras.GetData(id); - fixupConditions(data[conditionVariable]) - mergeConditions(conditions, data[conditionVariable], data.id, conditionTemplates.all, allProperties); + for index = totalAuraCount, 1, -1 do + local child = allChildren[index] + fixupConditions(child[conditionVariable]) + mergeConditions(conditions, child[conditionVariable], child.id, conditionTemplates.all, allProperties); end else + totalAuraCount = 1 data[conditionVariable] = data[conditionVariable] or {}; conditions = data[conditionVariable]; fixupConditions(data[conditionVariable]) @@ -2618,7 +2766,7 @@ function OptionsPrivate.GetConditionOptions(data) local order = startorder; for i = 1, #conditions do - order = addControlsForCondition(args, order, data, conditionVariable, conditions, i, conditionTemplates, conditionTemplateWithoutCombinations, allProperties); + order = addControlsForCondition(args, order, data, conditionVariable, totalAuraCount, conditions, i, conditionTemplates, conditionTemplateWithoutCombinations, allProperties); end args["addConditionHeader"] = { @@ -2635,28 +2783,17 @@ function OptionsPrivate.GetConditionOptions(data) name = L["Add Condition"], order = order, func = function() - if (data.controlledChildren) then - for _, id in ipairs(data.controlledChildren) do - local aura = WeakAuras.GetData(id); - aura[conditionVariable][#aura[conditionVariable] + 1] = {}; - aura[conditionVariable][#aura[conditionVariable]].check = {}; - aura[conditionVariable][#aura[conditionVariable]].changes = {}; - aura[conditionVariable][#aura[conditionVariable]].changes[1] = {} - aura[conditionVariable][#aura[conditionVariable]].category = category; - OptionsPrivate.SetCollapsed(id, "condition", #aura[conditionVariable], false); - WeakAuras.Add(aura); - end - WeakAuras.ClearAndUpdateOptions(data.id, true) - else - conditions[#conditions + 1] = {}; - conditions[#conditions].check = {}; - conditions[#conditions].changes = {}; - conditions[#conditions].changes[1] = {} - conditions[#conditions].category = category; - OptionsPrivate.SetCollapsed(data.id, "condition", #conditions, false); - WeakAuras.Add(data); - WeakAuras.ClearAndUpdateOptions(data.id, true) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + child[conditionVariable][#child[conditionVariable] + 1] = {}; + child[conditionVariable][#child[conditionVariable]].check = {}; + child[conditionVariable][#child[conditionVariable]].changes = {}; + child[conditionVariable][#child[conditionVariable]].changes[1] = {} + child[conditionVariable][#child[conditionVariable]].category = category; + OptionsPrivate.SetCollapsed(child.id, "condition", #child[conditionVariable], false); + WeakAuras.Add(child); + OptionsPrivate.ClearOptions(child.id) end + WeakAuras.ClearAndUpdateOptions(data.id) end } order = order + 1; diff --git a/WeakAurasOptions/DisplayOptions.lua b/WeakAurasOptions/DisplayOptions.lua index 18bdf00..74a5e1f 100644 --- a/WeakAurasOptions/DisplayOptions.lua +++ b/WeakAurasOptions/DisplayOptions.lua @@ -15,24 +15,21 @@ local hiddenAll = OptionsPrivate.commonOptions.CreateHiddenAll("region") local getAll = OptionsPrivate.commonOptions.CreateGetAll("region") local setAll = OptionsPrivate.commonOptions.CreateSetAll("region", getAll) -local function AddSubRegionImpl(data, subRegionName) - data.subRegions = data.subRegions or {} - if OptionsPrivate.Private.subRegionTypes[subRegionName] and OptionsPrivate.Private.subRegionTypes[subRegionName] then - if OptionsPrivate.Private.subRegionTypes[subRegionName].supports(data.regionType) then - local default = OptionsPrivate.Private.subRegionTypes[subRegionName].default - local subRegionData = type(default) == "function" and default(data.regionType) or CopyTable(default) - subRegionData.type = subRegionName - tinsert(data.subRegions, subRegionData) - WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) +local function AddSubRegion(data, subRegionName) + for data in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + data.subRegions = data.subRegions or {} + if OptionsPrivate.Private.subRegionTypes[subRegionName] and OptionsPrivate.Private.subRegionTypes[subRegionName] then + if OptionsPrivate.Private.subRegionTypes[subRegionName].supports(data.regionType) then + local default = OptionsPrivate.Private.subRegionTypes[subRegionName].default + local subRegionData = type(default) == "function" and default(data.regionType) or CopyTable(default) + subRegionData.type = subRegionName + tinsert(data.subRegions, subRegionData) + WeakAuras.Add(data) + OptionsPrivate.ClearOptions(data.id) + end end end -end - -local function AddSubRegion(data, subRegionName) - if (OptionsPrivate.Private.ApplyToDataOrChildData(data, AddSubRegionImpl, subRegionName)) then - WeakAuras.ClearAndUpdateOptions(data.id) - end + WeakAuras.ClearAndUpdateOptions(data.id) end local function AddOptionsForSupportedSubRegion(regionOption, data, supported) @@ -183,12 +180,7 @@ function OptionsPrivate.GetDisplayOptions(data) end WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); - if(data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData) then - WeakAuras.Add(parentData); - end - end + OptionsPrivate.Private.AddParents(data) OptionsPrivate.ResetMoverSizer(); end, args = options @@ -209,33 +201,32 @@ function OptionsPrivate.GetDisplayOptions(data) local supportedSubRegions = {} local hasSubElements = false - for index, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if childData and not handledRegionTypes[childData.regionType] then - handledRegionTypes[childData.regionType] = true; - if regionOptions[childData.regionType] then - allOptions = union(allOptions, regionOptions[childData.regionType].create(id, data)); + for child in OptionsPrivate.Private.TraverseLeafs(data) do + if child and not handledRegionTypes[child.regionType] then + handledRegionTypes[child.regionType] = true; + if regionOptions[child.regionType] then + allOptions = union(allOptions, regionOptions[child.regionType].create(id, data)); else unsupportedCount = unsupportedCount + 1 allOptions["__unsupported" .. unsupportedCount] = { - __title = "|cFFFFFF00" .. childData.regionType, + __title = "|cFFFFFF00" .. child.regionType, __order = 1, warning = { type = "description", - name = L["Regions of type \"%s\" are not supported."]:format(childData.regionType), + name = L["Regions of type \"%s\" are not supported."]:format(child.regionType), order = 1 }, } end for subRegionName, subRegionType in pairs(OptionsPrivate.Private.subRegionTypes) do - if subRegionType.supports(childData.regionType) then + if subRegionType.supports(child.regionType) then supportedSubRegions[subRegionName] = true end end end - if childData.subRegions then + if child.subRegions then local subIndex = {} - for index, subRegionData in ipairs(childData.subRegions) do + for index, subRegionData in ipairs(child.subRegions) do local subRegionType = subRegionData.type local alreadyHandled = handledSubRegionTypes[index] and handledSubRegionTypes[index][subRegionType] if OptionsPrivate.Private.subRegionOptions[subRegionType] and not alreadyHandled then diff --git a/WeakAurasOptions/ExternalAddons.lua b/WeakAurasOptions/ExternalAddons.lua deleted file mode 100644 index 8224f72..0000000 --- a/WeakAurasOptions/ExternalAddons.lua +++ /dev/null @@ -1,336 +0,0 @@ -if not WeakAuras.IsCorrectVersion() then return end -local AddonName, OptionsPrivate = ... - --- Lua APIs -local tinsert, wipe = table.insert, wipe -local pairs = pairs - -local AceGUI = LibStub("AceGUI-3.0") - -local displayButtons = WeakAuras.displayButtons - -local importAddonButtons = {} -local importDisplayButtons = {} -WeakAuras.importDisplayButtons = importDisplayButtons - -function OptionsPrivate.CreateImportButtons() - wipe(importAddonButtons); - wipe(importDisplayButtons); - for addonName, addonData in pairs(OptionsPrivate.Private) do - local addonButton = AceGUI:Create("WeakAurasImportButton"); - importAddonButtons[addonName] = addonButton; - addonButton:SetTitle(addonData.displayName); - addonButton:SetIcon(addonData.icon); - addonButton:SetDescription(addonData.description); - addonButton:SetClick(function() - if(addonButton.checkbox:GetChecked()) then - for id, data in pairs(addonData.displays) do - if not(data.parent) then - local childButton = importDisplayButtons[id]; - childButton.checkbox:SetChecked(true); - WeakAuras.EnableAddonDisplay(id); - end - end - for id, data in pairs(addonData.displays) do - if(data.parent) then - local childButton = importDisplayButtons[id]; - childButton.checkbox:SetChecked(true); - WeakAuras.EnableAddonDisplay(id); - end - end - else - for id, data in pairs(addonData.displays) do - if not(data.parent) then - local childButton = importDisplayButtons[id]; - childButton.checkbox:SetChecked(false); - WeakAuras.DisableAddonDisplay(id); - end - end - for id, data in pairs(addonData.displays) do - if(data.parent) then - local childButton = importDisplayButtons[id]; - childButton.checkbox:SetChecked(false); - WeakAuras.DisableAddonDisplay(id); - end - end - end - OptionsPrivate.Private.ResolveCollisions(function() - for groupId, dataFromAddon in pairs(addonData.displays) do - if(dataFromAddon.controlledChildren) then - local data = WeakAuras.GetData(groupId); - if(data) then - for index, childId in pairs(data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(childId); - childButton:SetGroup(groupId, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(index, #data.controlledChildren); - end - - local button = WeakAuras.GetDisplayButton(groupId); - button.callbacks.UpdateExpandButton(); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.ClearAndUpdateOptions(data.id); - end - end - end - - OptionsPrivate.Private.ScanForLoads(); - WeakAuras.SortDisplayButtons(); - end); - end); - - local function UpdateAddonChecked() - local shouldBeChecked = true; - for id, data in pairs(addonData.displays) do - if not(OptionsPrivate.Private.IsDefinedByAddon(id)) then - shouldBeChecked = false; - break; - end - end - addonButton.checkbox:SetChecked(shouldBeChecked); - end - - local numAddonDisplays = 0; - for id, data in pairs(addonData.displays) do - if(data.controlledChildren) then - numAddonDisplays = numAddonDisplays + 1; - local groupButton = AceGUI:Create("WeakAurasImportButton"); - importDisplayButtons[id] = groupButton; - - groupButton:SetTitle(id); - groupButton:SetDescription(data.desc); - - local numGroupDisplays = 0; - - local function UpdateGroupChecked() - local shouldBeChecked = true; - for index, childId in pairs(data.controlledChildren) do - if not(OptionsPrivate.Private.IsDefinedByAddon(childId)) then - shouldBeChecked = false; - break; - end - end - groupButton.checkbox:SetChecked(shouldBeChecked); - UpdateAddonChecked(); - end - - for index, childId in pairs(data.controlledChildren) do - numGroupDisplays = numGroupDisplays + 1; - numAddonDisplays = numAddonDisplays + 1; - local childButton = AceGUI:Create("WeakAurasImportButton"); - importDisplayButtons[childId] = childButton; - - local data = OptionsPrivate.Private[addonName].displays[childId]; - - childButton:SetTitle(childId); - childButton:SetDescription(data.desc); - childButton:SetExpandVisible(false); - childButton:SetLevel(3); - - childButton:SetClick(function() - if(childButton.checkbox:GetChecked()) then - WeakAuras.EnableAddonDisplay(childId); - else - WeakAuras.DisableAddonDisplay(childId); - end - OptionsPrivate.Private.ResolveCollisions(function() - OptionsPrivate.Private.ScanForLoads(); - WeakAuras.SortDisplayButtons(); - UpdateGroupChecked(); - end); - end); - childButton.updateChecked = UpdateGroupChecked; - childButton.checkbox:SetChecked(OptionsPrivate.Private.IsDefinedByAddon(childId)); - end - - groupButton:SetClick(function() - if(groupButton.checkbox:GetChecked()) then - WeakAuras.EnableAddonDisplay(id); - for index, childId in pairs(data.controlledChildren) do - local childButton = importDisplayButtons[childId]; - childButton.checkbox:SetChecked(true); - WeakAuras.EnableAddonDisplay(childId); - end - else - WeakAuras.DisableAddonDisplay(id); - for index, childId in pairs(data.controlledChildren) do - local childButton = importDisplayButtons[childId]; - childButton.checkbox:SetChecked(false); - WeakAuras.DisableAddonDisplay(childId); - end - end - OptionsPrivate.Private.ResolveCollisions(function() - local data = WeakAuras.GetData(id); - if(data) then - for index, childId in pairs(data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(childId); - childButton:SetGroup(id, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(index, #data.controlledChildren); - end - - local button = WeakAuras.GetDisplayButton(id); - button.callbacks.UpdateExpandButton(); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.ClearAndUpdateOptions(data.id); - end - - OptionsPrivate.Private.ScanForLoads(); - WeakAuras.SortDisplayButtons(); - UpdateAddonChecked(); - end); - end); - groupButton.updateChecked = UpdateAddonChecked; - groupButton:SetExpandVisible(true); - if(numGroupDisplays > 0) then - groupButton:EnableExpand(); - groupButton:SetOnExpandCollapse(WeakAuras.SortImportButtons); - end - groupButton:SetLevel(2); - UpdateGroupChecked(); - elseif not(importDisplayButtons[id]) then - numAddonDisplays = numAddonDisplays + 1; - local displayButton = AceGUI:Create("WeakAurasImportButton"); - importDisplayButtons[id] = displayButton; - - displayButton:SetTitle(id); - displayButton:SetDescription(data.desc); - displayButton:SetExpandVisible(false); - displayButton:SetLevel(2); - - displayButton:SetClick(function() - if(displayButton.checkbox:GetChecked()) then - WeakAuras.EnableAddonDisplay(id); - else - WeakAuras.DisableAddonDisplay(id); - end - OptionsPrivate.Private.ResolveCollisions(function() - WeakAuras.SortDisplayButtons() - UpdateAddonChecked(); - end); - end); - displayButton.updateChecked = UpdateAddonChecked; - displayButton.checkbox:SetChecked(OptionsPrivate.Private.IsDefinedByAddon(id)); - end - end - - addonButton:SetExpandVisible(true); - if(numAddonDisplays > 0) then - addonButton:EnableExpand(); - addonButton:SetOnExpandCollapse(WeakAuras.SortImportButtons); - end - addonButton:SetLevel(1); - UpdateAddonChecked(); - end -end - -local container = nil; -function WeakAuras.SortImportButtons(newContainer) - container = newContainer or container; - wipe(container.children); - local toSort = {}; - for addon, addonData in pairs(OptionsPrivate.Private) do - container:AddChild(importAddonButtons[addon]); - wipe(toSort); - for id, data in pairs(addonData.displays) do - if not(data.parent) then - tinsert(toSort, id); - end - end - table.sort(toSort, function(a, b) return a < b end); - for index, id in ipairs(toSort) do - if(importAddonButtons[addon]:GetExpanded()) then - importDisplayButtons[id].frame:Show(); - container:AddChild(importDisplayButtons[id]); - else - importDisplayButtons[id].frame:Hide(); - end - if(addonData.displays[id].controlledChildren) then - for childIndex, childId in pairs(addonData.displays[id].controlledChildren) do - if(importAddonButtons[addon]:GetExpanded() and importDisplayButtons[id]:GetExpanded()) then - importDisplayButtons[childId].frame:Show(); - container:AddChild(importDisplayButtons[childId]); - else - importDisplayButtons[childId].frame:Hide(); - end - end - end - end - end - - container:DoLayout(); -end - -function WeakAuras.EnableAddonDisplay(id) - local db = OptionsPrivate.savedVars.db - if not(db.registered[id]) then - local addon, data; - for addonName, addonData in pairs(OptionsPrivate.Private) do - if(addonData.displays[id]) then - addon = addonName; - data = CopyTable(addonData.displays[id]); - break; - end - end - - if(db.displays[id]) then - -- ID collision - OptionsPrivate.Private.collisions[id] = {addon, data}; - else - db.registered[id] = addon; - if(data.controlledChildren) then - wipe(data.controlledChildren); - end - WeakAuras.Add(data); - OptionsPrivate.Private.SyncParentChildRelationships(true); - OptionsPrivate.AddDisplayButton(data); - end - end -end - --- This function overrides the WeakAuras.CollisionResolved that is defined in WeakAuras.lua, --- ensuring that sidebar buttons are created properly after collision resolution -function WeakAuras.CollisionResolved(addon, data, force) - WeakAuras.EnableAddonDisplay(data.id); -end - -function WeakAuras.DisableAddonDisplay(id) - local frame = WeakAuras.OptionsFrame() - local db = OptionsPrivate.savedVars.db - db.registered[id] = false; - local data = WeakAuras.GetData(id); - if(data) then - local parentData; - if(data.parent) then - parentData = db.displays[data.parent]; - end - - 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 - - WeakAuras.Delete(data); - OptionsPrivate.Private.SyncParentChildRelationships(true); - frame.buttonsScroll:DeleteChild(displayButtons[id]); - displayButtons[id] = nil; - - if(parentData and parentData.controlledChildren) then - for index, childId in pairs(parentData.controlledChildren) do - local childButton = displayButtons[childId]; - if(childButton) then - childButton:SetGroupOrder(index, #parentData.controlledChildren); - end - end - WeakAuras.Add(parentData); - WeakAuras.ClearAndUpdateOptions(parentData.id); - WeakAuras.UpdateDisplayButton(parentData); - end - end -end diff --git a/WeakAurasOptions/GenericTrigger.lua b/WeakAurasOptions/GenericTrigger.lua index 294d5f2..af41845 100644 --- a/WeakAurasOptions/GenericTrigger.lua +++ b/WeakAurasOptions/GenericTrigger.lua @@ -37,7 +37,7 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.custom_type = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); WeakAuras.ClearAndUpdateOptions(data.id); end }, @@ -55,7 +55,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.check = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, check2 = { @@ -72,7 +71,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.check = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, events = { @@ -88,7 +86,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.events = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, events2 = { @@ -102,7 +99,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.events = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, event_customError = { @@ -173,7 +169,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.custom_hide = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, custom_hide2 = { @@ -187,7 +182,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.custom_hide = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); end }, dynamicDuration = { @@ -202,7 +196,6 @@ local function GetCustomTriggerOptions(data, triggernum) set = function(info, v) trigger.dynamicDuration = v; WeakAuras.Add(data); - WeakAuras.UpdateDisplayButton(data); WeakAuras.ClearAndUpdateOptions(data.id); end }, @@ -255,7 +248,7 @@ local function GetCustomTriggerOptions(data, triggernum) }; local function extraSetFunction() - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); end local function extraSetFunctionReload() @@ -287,7 +280,8 @@ local function GetCustomTriggerOptions(data, triggernum) type = "string", test = "function", events = "table", - values = "table" + values = "table", + display = "string" } local function validateCustomVariables(variables) diff --git a/WeakAurasOptions/InformationOptions.lua b/WeakAurasOptions/InformationOptions.lua index 4ea0084..a3f7656 100644 --- a/WeakAurasOptions/InformationOptions.lua +++ b/WeakAurasOptions/InformationOptions.lua @@ -47,23 +47,16 @@ function OptionsPrivate.GetInformationOptions(data) local sameURL = true local commonURL local desc = "" - if not isTmpGroup then - commonURL = data.url - if data.url then - desc = "|cFFE0E000"..data.id..": |r".. data.url .. "\n" + + local traverseForUrl = isTmpGroup and OptionsPrivate.Private.TraverseAllChildren or OptionsPrivate.Private.TraverseAll + for child in traverseForUrl(data) do + if child.url then + desc = desc .. "|cFFE0E000"..child.id..": |r"..child.url .. "\n" end - end - if data.controlledChildren then - for _, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - if childData.url then - desc = desc .. "|cFFE0E000"..childData.id..": |r"..childData.url .. "\n" - end - if not commonURL then - commonURL = childData.url or "" - elseif childData.url ~= commonURL then - sameURL = false - end + if not commonURL then + commonURL = child.url or "" + elseif child.url ~= commonURL then + sameURL = false end end @@ -72,26 +65,15 @@ function OptionsPrivate.GetInformationOptions(data) name = sameURL and L["URL"] or "|cFF4080FF" .. L["URL"], width = WeakAuras.doubleWidth, get = function() - if data.controlledChildren then - return sameURL and commonURL or "" - else - return data.url - end + return sameURL and commonURL or "" end, set = function(info, v) - if data.controlledChildren then - for _, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - childData.url = v - WeakAuras.Add(childData) - OptionsPrivate.ClearOptions(childData.id) - end + for child in traverseForUrl(data) do + child.url = v + WeakAuras.Add(child) + OptionsPrivate.ClearOptions(child.id) end - if not isTmpGroup then - data.url = v - WeakAuras.Add(data) - end WeakAuras.ClearAndUpdateOptions(data.id) end, desc = sameURL and "" or desc, @@ -177,7 +159,7 @@ function OptionsPrivate.GetInformationOptions(data) local properties = { ignoreOptionsEventErrors = { - name = L["Ignore Lua Errors on OPTIONS event"] + name = L["Ignore Lua Errors on OPTIONS event"], }, groupOffset = { name = L["Offset by 1px"], @@ -200,23 +182,7 @@ function OptionsPrivate.GetInformationOptions(data) } for property, propertyData in pairs(properties) do - if not propertyData.onParent and data.controlledChildren then - for _, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - if not propertyData.regionType or propertyData.regionType == childData.regionType then - mergedDesc[property] = (mergedDesc[property] or "") .. "|cFFE0E000"..childData.id..": |r".. (childData.information[property] and "true" or "false") .. "\n" - if common[property] == nil then - if childData.information[property] ~= nil then - common[property] = childData.information[property] - else - common[property] = false - end - elseif childData.information[property] ~= common[property] then - same[property] = false - end - end - end - else + if propertyData.onParent then if not isTmpGroup and (not propertyData.regionType or propertyData.regionType == data.regionType) then if data.information[property] ~= nil then common[property] = data.information[property] @@ -224,6 +190,24 @@ function OptionsPrivate.GetInformationOptions(data) common[property] = false end end + else + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + if not propertyData.regionType or propertyData.regionType == child.regionType then + local effectiveProperty = child.information[property] + if effectiveProperty == nil then + effectiveProperty = false + end + + mergedDesc[property] = (mergedDesc[property] or "") .. "|cFFE0E000" .. child.id .. ": |r" + .. (effectiveProperty and "true" or "false") .. "\n" + + if common[property] == nil then + common[property] = effectiveProperty + elseif effectiveProperty ~= common[property] then + same[property] = false + end + end + end end if common[property] ~= nil then @@ -232,26 +216,25 @@ function OptionsPrivate.GetInformationOptions(data) name = same[property] and propertyData.name or "|cFF4080FF" .. propertyData.name, width = WeakAuras.doubleWidth, get = function() - if not propertyData.onParent and data.controlledChildren then - return same[property] and common[property] or false - else + if propertyData.onParent then return data.information[property] + else + return same[property] and common[property] or false end end, set = function(info, v) - if not propertyData.onParent and data.controlledChildren then - for _, childId in ipairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - if not propertyData.regionType or propertyData.regionType == childData.regionType then - childData.information[property] = v - WeakAuras.Add(childData) - OptionsPrivate.ClearOptions(childData.id) - end - end - else + if propertyData.onParent then data.information[property] = v WeakAuras.Add(data) OptionsPrivate.ClearOptions(data.id) + else + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + if not propertyData.regionType or propertyData.regionType == child.regionType then + child.information[property] = v + WeakAuras.Add(child) + OptionsPrivate.ClearOptions(child.id) + end + end end WeakAuras.ClearAndUpdateOptions(data.id) end, diff --git a/WeakAurasOptions/LoadOptions.lua b/WeakAurasOptions/LoadOptions.lua index c70398d..8c79819 100644 --- a/WeakAurasOptions/LoadOptions.lua +++ b/WeakAurasOptions/LoadOptions.lua @@ -147,8 +147,8 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum name = function(input) local value = trigger["use_"..realname]; if(value == nil) then return arg.display; - elseif(value == false) then return "|cFFFF0000 "..L["Negator"].." "..arg.display; - else return "|cFF00FF00"..arg.display; end + elseif(value == false) then return "|cFFFF0000 "..L["Negator"].." "..arg.display.."|r"; + else return "|cFF00FF00"..arg.display.."|r"; end end, desc = arg.desc, get = function() @@ -174,8 +174,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end, hidden = hidden, order = order @@ -223,8 +222,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end, hidden = hidden, order = order @@ -271,8 +269,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; end @@ -317,8 +314,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -329,7 +325,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.ClearAndUpdateOptions(data.id) end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end order = order + 1; @@ -352,8 +348,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -364,7 +359,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.ClearAndUpdateOptions(data.id) end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end order = order + 1; @@ -385,8 +380,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; @@ -406,7 +400,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.ClearAndUpdateOptions(data.id) end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end order = order + 1; @@ -428,8 +422,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -440,7 +433,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.ClearAndUpdateOptions(data.id) end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end order = order + 1; @@ -461,8 +454,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -473,7 +465,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.ClearAndUpdateOptions(data.id) end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end order = order + 1; @@ -493,8 +485,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum WeakAuras.Add(data); OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end, }; order = order + 1; @@ -549,7 +540,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum return nil; end elseif(arg.type == "spell") then - local useExactSpellId = (arg.showExactOption and trigger["use_exact_"..realname]) or arg.forceExactOption + local useExactSpellId = (arg.showExactOption and trigger["use_exact_"..realname]) if(trigger["use_"..realname]) then if (trigger[realname] and trigger[realname] ~= "") then if useExactSpellId then @@ -588,8 +579,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; order = order + 1; @@ -611,6 +601,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum order = order, hidden = hidden, values = values, + desc = arg.desc, disabled = function() return not trigger["use_"..realname]; end, get = function() if(arg.type == "unit" and trigger["use_specific_"..realname]) then @@ -642,8 +633,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -660,8 +650,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end if (arg.control) then @@ -733,8 +722,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -746,8 +734,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end @@ -767,7 +754,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum trigger[realname .. "_extraOption"] = v WeakAuras.Add(data) OptionsPrivate.Private.ScanForLoads({[data.id] = true}) - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons(nil, true) end } order = order + 1 @@ -800,8 +787,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end }; if(arg.required and not triggertype) then @@ -817,8 +803,7 @@ function OptionsPrivate.ConstructOptions(prototype, data, startorder, triggernum end OptionsPrivate.Private.ScanForLoads({[data.id] = true}); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end end @@ -886,7 +871,7 @@ function OptionsPrivate.GetLoadOptions(data) WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); OptionsPrivate.Private.ScanForLoads({[data.id] = true}); - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(nil, true); end, args = {} } diff --git a/WeakAurasOptions/Locales/ruRU.lua b/WeakAurasOptions/Locales/ruRU.lua index 7e6001e..562dced 100644 --- a/WeakAurasOptions/Locales/ruRU.lua +++ b/WeakAurasOptions/Locales/ruRU.lua @@ -7,29 +7,100 @@ end local L = WeakAuras.L -- WeakAuras/Options + L[" • %d |4aura:auras; added"] = " • %d |4индикация добавлена:индикации добавлены:индикаций добавлено;" + L[" • %d |4aura:auras; deleted"] = " • %d |4индикация удалена:индикации удалены:индикаций удалено;" + L[" • %d |4aura:auras; modified"] = " • %d |4индикация изменена:индикации изменены:индикаций изменено;" + --[[Translation missing --]] + L[" and "] = " and " L[" and |cFFFF0000mirrored|r"] = "; Отражение" - L["-- Do not remove this comment, it is part of this trigger: "] = "-- Не удаляйте этот комментарий, он является частью этого триггера: " + L["-- Do not remove this comment, it is part of this aura: "] = "-- Не удаляйте этот комментарий! Он является частью кода индикации " L[" rotated |cFFFF0000%s|r degrees"] = "; Поворот %.4g" L["% of Progress"] = "% прогресса" L["%i auras selected"] = "%i |4индикация выбрана:индикации выбраны:индикаций выбрано;" L["%i Matches"] = "%i |4совпадение:совпадения:совпадений;" + --[[Translation missing --]] + L["%s - %i. Trigger"] = "%s - %i. Trigger" + L["%s - Alpha Animation"] = "%s анимация прозрачности" + L["%s - Color Animation"] = "%s анимация цвета" + --[[Translation missing --]] + L["%s - Condition Custom Chat %s"] = "%s - Condition Custom Chat %s" + --[[Translation missing --]] + L["%s - Condition Custom Check %s"] = "%s - Condition Custom Check %s" + --[[Translation missing --]] + L["%s - Condition Custom Code %s"] = "%s - Condition Custom Code %s" + --[[Translation missing --]] + L["%s - Custom Anchor"] = "%s - Custom Anchor" + --[[Translation missing --]] + L["%s - Custom Grow"] = "%s - Custom Grow" + --[[Translation missing --]] + L["%s - Custom Sort"] = "%s - Custom Sort" + --[[Translation missing --]] + L["%s - Custom Text"] = "%s - Custom Text" + L["%s - Finish"] = "%s - Конечная" + --[[Translation missing --]] + L["%s - Finish Action"] = "%s - Finish Action" + --[[Translation missing --]] + L["%s - Finish Custom Text"] = "%s - Finish Custom Text" + --[[Translation missing --]] + L["%s - Init Action"] = "%s - Init Action" + L["%s - Main"] = "%s - Основная" L["%s - Option #%i has the key %s. Please choose a different option key."] = "Ключ |cFFE6CC80%3$s|r уже используется в Параметре %2$i индикации %1$s. Пожалуйста, выберите другой ключ." + L["%s - Rotate Animation"] = "%s анимация вращения" + L["%s - Scale Animation"] = "%s анимация масштаба" + L["%s - Start"] = "%s - Начальная" + --[[Translation missing --]] + L["%s - Start Action"] = "%s - Start Action" + --[[Translation missing --]] + L["%s - Start Custom Text"] = "%s - Start Custom Text" + L["%s - Translate Animation"] = "%s анимация перемещения" + L["%s - Trigger Logic"] = "%s - Комбинация триггеров" L["%s %s, Lines: %d, Frequency: %0.2f, Length: %d, Thickness: %d"] = "%s %s; Линии %d; Частота %.3g; Длина %0.3g; Толщина %0.3g" L["%s %s, Particles: %d, Frequency: %0.2f, Scale: %0.2f"] = "%s %s; Частицы %d; Частота %.3g; Масштаб %.3g" + --[[Translation missing --]] + L["%s %u. Overlay Function"] = "%s %u. Overlay Function" L["%s Alpha: %d%%"] = "%s Прозрачность %d%%" + --[[Translation missing --]] + L[ [=[%s auras will be added. +]=] ] = [=[%s auras will be added. +]=] + --[[Translation missing --]] + L[ [=[%s auras will be removed. +]=] ] = [=[%s auras will be removed. +]=] L["%s Color"] = "%s " + --[[Translation missing --]] + L["%s Custom Variables"] = "%s Custom Variables" L["%s Default Alpha, Zoom, Icon Inset, Aspect Ratio"] = "%s Значения по умолчанию" + --[[Translation missing --]] + L["%s Duration Function"] = "%s Duration Function" + --[[Translation missing --]] + L["%s Icon Function"] = "%s Icon Function" L["%s Inset: %d%%"] = "%s Вставка %d%%" L["%s is not a valid SubEvent for COMBAT_LOG_EVENT_UNFILTERED"] = "%s не является допустимым внутренним событием COMBAT_LOG_EVENT_UNFILTERED" L["%s Keep Aspect Ratio"] = "%s Сохранение пропорций изображения" + --[[Translation missing --]] + L["%s Name Function"] = "%s Name Function" + --[[Translation missing --]] + L["%s Stacks Function"] = "%s Stacks Function" + L["%s Texture"] = "%s" + --[[Translation missing --]] + L["%s Texture Function"] = "%s Texture Function" L["%s total auras"] = "Всего %s |4индикация:индикации:индикаций;" + --[[Translation missing --]] + L["%s Trigger Function"] = "%s Trigger Function" + --[[Translation missing --]] + L["%s Untrigger Function"] = "%s Untrigger Function" L["%s Zoom: %d%%"] = "%s Увеличение %d%%" L["%s, Border"] = "%s; Граница" L["%s, Offset: %0.2f;%0.2f"] = "%s; Смещение (%.4g, %.4g)" L["%s, offset: %0.2f;%0.2f"] = "%s; Смещение (%.4g, %.4g)" L["%s|cFFFF0000custom|r texture with |cFFFF0000%s|r blend mode%s%s"] = "Своя %sтекстура; Режим наложения |cFFE6CC80%s|r%s%s" L["(Right click to rename)"] = "(Правый клик для смены названия)" + --[[Translation missing --]] + L[", "] = ", " L["|c%02x%02x%02x%02xCustom Color|r"] = "Свечение |c%02x%02x%02x%02xO|r цвета" + --[[Translation missing --]] + L["|cff999999Triggers tracking multiple units will default to being active even while no affected units are found without a Unit Count or Match Count setting applied.|r"] = "|cff999999Triggers tracking multiple units will default to being active even while no affected units are found without a Unit Count or Match Count setting applied.|r" L["|cFFE0E000Note:|r This sets the description only on '%s'"] = "|cFFFFCC00Примечание.|r Задает описание только для индикации %s" L["|cFFE0E000Note:|r This sets the URL on all selected auras"] = "|cFFFFCC00Примечание.|r Устанавливает URL-адрес для выбранных индикаций" L["|cFFE0E000Note:|r This sets the URL on this group and all its members."] = "|cFFFFCC00Примечание.|r Устанавливает URL-адрес для этой группы и всех ее индикаций" @@ -46,6 +117,24 @@ local L = WeakAuras.L L["|cFFffcc00Font Flags:|r |cFFFF0000%s|r and shadow |c%sColor|r with offset |cFFFF0000%s/%s|r%s%s"] = "|cFFFFCC00Атрибуты текста:|r |cFFE6CC80%s|r; Тень |c%sO|r цвета со смещением (%s, %s);%s%s" L["|cFFffcc00Font Flags:|r |cFFFF0000%s|r and shadow |c%sColor|r with offset |cFFFF0000%s/%s|r%s%s%s"] = "|cFFFFCC00Атрибуты текста:|r |cFFE6CC80%s|r; Тень |c%sO|r цвета со смещением (%s, %s);%s%s%s" L["|cFFffcc00Format Options|r"] = "|cFFFFCC00Параметры форматирования|r" + --[[Translation missing --]] + L[ [=[• |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs. +• |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch. +|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger. +• |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs. +• |cffffff00Smart Group|r adjusts to your current group type, matching just the "player" when solo, "party" units (including "player") in a party or "raid" units in a raid. +• |cffffff00Multi-target|r attempts to use the Combat Log events, rather than unitID, to track affected units. +|cffff0000Note|r: Without a direct relationship to actual unitIDs, results may vary. + +|cffffff00*|r Yellow Unit settings can match multiple units and will default to being active even while no affected units are found without a Unit Count or Match Count setting.]=] ] = [=[• |cff00ff00Player|r, |cff00ff00Target|r, |cff00ff00Focus|r, and |cff00ff00Pet|r correspond directly to those individual unitIDs. +• |cff00ff00Specific Unit|r lets you provide a specific valid unitID to watch. +|cffff0000Note|r: The game will not fire events for all valid unitIDs, making some untrackable by this trigger. +• |cffffff00Party|r, |cffffff00Raid|r, |cffffff00Boss|r, |cffffff00Arena|r, and |cffffff00Nameplate|r can match multiple corresponding unitIDs. +• |cffffff00Smart Group|r adjusts to your current group type, matching just the "player" when solo, "party" units (including "player") in a party or "raid" units in a raid. +• |cffffff00Multi-target|r attempts to use the Combat Log events, rather than unitID, to track affected units. +|cffff0000Note|r: Without a direct relationship to actual unitIDs, results may vary. + +|cffffff00*|r Yellow Unit settings can match multiple units and will default to being active even while no affected units are found without a Unit Count or Match Count setting.]=] L["1 Match"] = "1 cовпадение" L["A 20x20 pixels icon"] = "Иконка 20х20 пикселей" L["A 32x32 pixels icon"] = "Иконка 32х32 пикселей" @@ -53,9 +142,15 @@ local L = WeakAuras.L L["A 48x48 pixels icon"] = "Иконка 48х48 пикселей" L["A 64x64 pixels icon"] = "Иконка 64х64 пикселей" L["A group that dynamically controls the positioning of its children"] = "Группа, динамически изменяющая позиции своих индикаций" + L[ [=[A timer will automatically be displayed according to default Interface Settings (overridden by some addons). +Enable this setting if you want this timer to be hidden, or when using a WeakAuras text to display the timer]=] ] = [=[Отсчет времени будет отображаться в соответствии с настройками интерфейса (переопределено некоторыми аддонами). + +Включите этот параметр, если вы хотите скрыть этот отсчет или использовать текст WeakAuras для его отображения.]=] L["A Unit ID (e.g., party1)."] = [=[Введите идентификатор единицы (UID, Unit ID). Например: party4, raid7, arena3, boss2, nameplate6, target, focus, pet и др.]=] L["Actions"] = "Действия" + --[[Translation missing --]] + L["Active Aura Filters and Info"] = "Active Aura Filters and Info" L["Add"] = "Добавить" L["Add %s"] = "%s" L["Add a new display"] = "Добавить новую индикацию" @@ -84,6 +179,7 @@ local L = WeakAuras.L L["Anchor Point"] = "Точка крепления" L["Anchored To"] = "Прикрепить к" L["And "] = "И " + L["and"] = "и" L["and aligned left"] = "Выравнивание по левому краю;" L["and aligned right"] = "Выравнивание по правому краю;" L["and rotated left"] = "Текст повернут вверх;" @@ -111,12 +207,13 @@ local L = WeakAuras.L L["Arcane Orb"] = "Чародейский шар" L["At a position a bit left of Left HUD position."] = "Немного левее позиции левого HUD" L["At a position a bit left of Right HUD position"] = "Немного правее позиции правого HUD" - L["At the same position as Blizzard's spell alert"] = "В таком же положении, как предупреждение заклинаний Blizzard" + L["At the same position as Blizzard's spell alert"] = "В таком же положении, что и предупреждение о заклинаниях Blizzard" L[ [=[Aura is Off Screen]=] ] = [=[Индикация за пределами экрана]=] L["Aura Name"] = "Название эффекта" L["Aura Name Pattern"] = "Образец названия эффекта" + L["Aura received from: %s"] = "Индикация получена от: %s" L["Aura Type"] = "Тип эффекта" L["Aura(s)"] = "Эффекты" L["Author Options"] = "Параметры автора" @@ -158,9 +255,12 @@ Off Screen]=] ] = [=[Индикация за L["Can be a Name or a Unit ID (e.g. party1). A name only works on friendly players in your group."] = "Введите имя или идентификатор единицы (Unit ID). Имена работают только для игроков, находящихся в вашей группе." L["Can be a UID (e.g., party1)."] = [=[Введите идентификатор единицы (UID, Unit ID). Например: party4, raid7, arena3, boss2, nameplate6, target, focus, pet и др.]=] + L["Can set to 0 if Columns * Width equal File Width"] = "Можно указать 0 в качестве значения, если последовательность изображений занимает всю ширину текстуры (т. е. произведение количества столбцов и ширины кадра равно ширине текстуры)" + L["Can set to 0 if Rows * Height equal File Height"] = "Можно указать 0 в качестве значения, если последовательность изображений занимает всю высоту текстуры (т. е. произведение количества строк и высоты кадра равно высоте текстуры)" L["Cancel"] = "Отмена" - L["Cast by Player Character"] = "Применён игроком" - L["Cast by Players"] = "Применён игроком" + --[[Translation missing --]] + L["Cast by a Player Character"] = "Cast by a Player Character" + L["Categories to Update"] = "Категории для обновления" L["Center"] = "Центр" L["Chat Message"] = "Сообщение в чат" L["Chat with WeakAuras experts on our Discord server."] = "Общайтесь со знатоками WeakAuras на нашем сервере Discord." @@ -172,16 +272,18 @@ Off Screen]=] ] = [=[Индикация за L["Clip Overlays"] = "Обрезать наложения" L["Clipped by Progress"] = "Ограничить прогрессом" L["Close"] = "Закрыть" + L["Code Editor"] = "Редактор кода" L["Collapse"] = "Свернуть" L["Collapse all loaded displays"] = "Свернуть все загруженные индикации" L["Collapse all non-loaded displays"] = "Свернуть все не загруженные индикации" + L["Collapse all pending Import"] = "Свернуть все индикации, ожидающие импорта" L["Collapsible Group"] = "Свёртываемая группа" L["color"] = "цвет" L["Color"] = "Цвет" L["Column Height"] = "Высота столбца" L["Column Space"] = "Отступ между столбцами" L["Columns"] = "Столбцы" - L["Combinations"] = "Логические операции" + L["Combinations"] = "Комбинации" L["Combine Matches Per Unit"] = "Объединить совпадения для каждой единицы" L["Common Text"] = "Общие параметры текста" L["Compare against the number of units affected."] = "Сравнение с количеством единиц, находящихся под действием эффекта." @@ -195,6 +297,8 @@ Off Screen]=] ] = [=[Индикация за L["Controls the positioning and configuration of multiple displays at the same time"] = "Управляет позиционированием и настройкой нескольких индикаций одновременно" L["Convert to New Aura Trigger"] = "Преобразовать в новую версию триггера" L["Convert to..."] = "Преобразовать в ..." + --[[Translation missing --]] + L["Cooldown Reduction changes the duration of seconds instead of showing the real time seconds."] = "Cooldown Reduction changes the duration of seconds instead of showing the real time seconds." L["Cooldown Edge"] = "Эффект Edge (кромка)" L["Cooldown Settings"] = "Настройки восстановления" L["Cooldown Swipe"] = "Эффект Swipe (затемнение)" @@ -204,8 +308,10 @@ Off Screen]=] ] = [=[Индикация за L["Could not parse '%s'. Expected a table."] = "Не удалось разобрать переменную %s. Требуется таблица." L["Count"] = "Количество" L["Counts the number of matches over all units."] = "Сравнение с количеством совпадений для всех единиц." - L["Creating buttons: "] = "Создание кнопок:" - L["Creating options: "] = "Создание настроек:" + --[[Translation missing --]] + L["Create a Copy"] = "Create a Copy" + L["Creating buttons: "] = "Создание кнопок: " + L["Creating options: "] = "Создание параметров: " L["Crop X"] = "Обрезать по X" L["Crop Y"] = "Обрезать по Y" L["Custom"] = "Самостоятельно" diff --git a/WeakAurasOptions/OptionsFrames/CodeReview.lua b/WeakAurasOptions/OptionsFrames/CodeReview.lua index 4e44c0b..3deedcd 100644 --- a/WeakAurasOptions/OptionsFrames/CodeReview.lua +++ b/WeakAurasOptions/OptionsFrames/CodeReview.lua @@ -56,25 +56,37 @@ local colorScheme = { } local function ConstructCodeReview(frame) - local group = AceGUI:Create("InlineGroup"); + local group = AceGUI:Create("WeakAurasInlineGroup"); group.frame:SetParent(frame); - group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 30); - group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -10); + group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -16); + group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 46); group.frame:Hide(); group:SetLayout("flow"); + local title = AceGUI:Create("Label") + title:SetFontObject(GameFontNormalHuge) + title:SetFullWidth(true) + title:SetText(L["Custom Code Viewer"]) + group:AddChild(title) + local codeTree = AceGUI:Create("TreeGroup"); + codeTree:SetTreeWidth(300, false) + codeTree:SetFullWidth(true) + codeTree:SetFullHeight(true) + codeTree:SetLayout("flow") + codeTree.dragger:Hide() + codeTree.border:SetBackdrop(nil) + codeTree.content:SetAllPoints() group.codeTree = codeTree; - group:SetLayout("fill"); group:AddChild(codeTree); local codebox = AceGUI:Create("MultiLineEditBox"); - codebox.frame:SetAllPoints(codeTree.content); - codebox.frame:SetFrameStrata("FULLSCREEN"); codebox:SetLabel(""); - group:AddChild(codebox); + codebox:DisableButton(true) + codebox:SetFullWidth(true) + codebox:SetFullHeight(true) + codeTree:AddChild(codebox) - codebox.button:Hide(); IndentationLib.enable(codebox.editBox, colorScheme, 4); local fontPath = SharedMedia:Fetch("font", "Fira Mono Medium"); if(fontPath) then @@ -92,7 +104,7 @@ local function ConstructCodeReview(frame) local cancel = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); cancel:SetScript("OnClick", function() group:Close() end); - cancel:SetPoint("bottomright", frame, "bottomright", -27, 11); + cancel:SetPoint("BOTTOMRIGHT", -20, -24); cancel:SetHeight(20); cancel:SetWidth(100); cancel:SetText(L["Okay"]); @@ -102,16 +114,17 @@ local function ConstructCodeReview(frame) return end + local _, firstEntry = next(data) self.data = data; self.codeTree:SetTree(data); + self.codeTree:SelectByValue(firstEntry.value) - WeakAuras.ShowOptions(); frame.window = "codereview"; frame:UpdateFrameVisible() end function group.Close() - frame.window = "default"; + frame.window = "update"; frame:UpdateFrameVisible() end diff --git a/WeakAurasOptions/OptionsFrames/IconPicker.lua b/WeakAurasOptions/OptionsFrames/IconPicker.lua index be0b480..02f9f48 100644 --- a/WeakAurasOptions/OptionsFrames/IconPicker.lua +++ b/WeakAurasOptions/OptionsFrames/IconPicker.lua @@ -121,21 +121,18 @@ local function ConstructIconPicker(frame) function group.Pick(self, texturePath) local valueToPath = OptionsPrivate.Private.ValueToPath - if(not self.groupIcon and self.baseObject.controlledChildren) then - for index, childId in pairs(self.baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - valueToPath(childData, self.paths[childId], texturePath) - WeakAuras.Add(childData) - WeakAuras.ClearAndUpdateOptions(childData.id) - WeakAuras.UpdateThumbnail(childData); - end - end - else + if self.groupIcon then valueToPath(self.baseObject, self.paths[self.baseObject.id], texturePath) WeakAuras.Add(self.baseObject) WeakAuras.ClearAndUpdateOptions(self.baseObject.id) WeakAuras.UpdateThumbnail(self.baseObject) + else + for child in OptionsPrivate.Private.TraverseLeafsOrAura(self.baseObject) do + valueToPath(child, self.paths[child.id], texturePath) + WeakAuras.Add(child) + WeakAuras.ClearAndUpdateOptions(child.id) + WeakAuras.UpdateThumbnail(child); + end end local success = icon:SetTexture(texturePath) and texturePath; if(success) then @@ -150,18 +147,17 @@ local function ConstructIconPicker(frame) self.baseObject = baseObject self.paths = paths self.groupIcon = groupIcon - if(not groupIcon and baseObject.controlledChildren) then - self.givenPath = {}; - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local value = valueFromPath(childData, paths[childId]) - self.givenPath[childId] = value; - end - end - else + if groupIcon then local value = valueFromPath(self.baseObject, paths[self.baseObject.id]) self.givenPath = value + else + self.givenPath = {}; + for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do + if(child) then + local value = valueFromPath(child, paths[child.id]) + self.givenPath[child.id] = value or ""; + end + end end -- group:Pick(self.givenPath); frame.window = "icon"; @@ -177,24 +173,22 @@ local function ConstructIconPicker(frame) function group.CancelClose() local valueToPath = OptionsPrivate.Private.ValueToPath - if(not group.groupIcon and group.baseObject.controlledChildren) then - for index, childId in pairs(group.baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - if (group.givenPath[childId]) then - valueToPath(childData, group.paths[childId], group.givenPath[childId]) - WeakAuras.Add(childData); - WeakAuras.ClearAndUpdateOptions(childData.id) - WeakAuras.UpdateThumbnail(childData); - end - end - end - else + if group.groupIcon then valueToPath(group.baseObject, group.paths[group.baseObject.id], group.givenPath) WeakAuras.Add(group.baseObject) WeakAuras.ClearAndUpdateOptions(group.baseObject.id) WeakAuras.UpdateThumbnail(group.baseObject) + else + for child in OptionsPrivate.Private.TraverseLeafsOrAura(group.baseObject) do + if (group.givenPath[child.id]) then + valueToPath(child, group.paths[child.id], group.givenPath[child.id]) + WeakAuras.Add(child); + WeakAuras.ClearAndUpdateOptions(child.id) + WeakAuras.UpdateThumbnail(child); + end + end end + group.Close(); end diff --git a/WeakAurasOptions/OptionsFrames/ImportExport.lua b/WeakAurasOptions/OptionsFrames/ImportExport.lua index aad2afe..f04d74c 100644 --- a/WeakAurasOptions/OptionsFrames/ImportExport.lua +++ b/WeakAurasOptions/OptionsFrames/ImportExport.lua @@ -5,7 +5,7 @@ local AddonName, OptionsPrivate = ... local strtrim, strsub = strtrim, strsub -- WoW APIs -local CreateFrame = CreateFrame +local GetTime, CreateFrame = GetTime, CreateFrame local AceGUI = LibStub("AceGUI-3.0") @@ -15,26 +15,31 @@ local L = WeakAuras.L local importexport local function ConstructImportExport(frame) - local group = AceGUI:Create("InlineGroup"); + local group = AceGUI:Create("WeakAurasInlineGroup"); group.frame:SetParent(frame); - group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 12); - group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -10); + group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -16); + group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 46); group.frame:Hide(); - group:SetLayout("fill"); + group:SetLayout("flow"); + + local title = AceGUI:Create("Label") + title:SetFontObject(GameFontNormalHuge) + title:SetFullWidth(true) + group:AddChild(title) local input = AceGUI:Create("MultiLineEditBox"); - input:SetWidth(400); - input.button:Hide(); - --input.frame:SetClipsChildren(true); + input:DisableButton(true) + input:SetFullWidth(true) + input:SetFullHeight(true) group:AddChild(input); local close = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); close:SetScript("OnClick", function() group:Close() end); - close:SetPoint("BOTTOMRIGHT", -27, 13); + close:SetPoint("BOTTOMRIGHT", -20, -24); close:SetFrameLevel(close:GetFrameLevel() + 1) close:SetHeight(20); close:SetWidth(100); - close:SetText(L["Done"]) + close:SetText(L["Close"]) function group.Open(self, mode, id) if(frame.window == "texture") then @@ -47,16 +52,17 @@ local function ConstructImportExport(frame) frame.window = "importexport"; frame:UpdateFrameVisible() if(mode == "export" or mode == "table") then + title:SetText(L["Exporting"]) if(id) then local displayStr; if(mode == "export") then - displayStr = WeakAuras.DisplayToString(id, true); + displayStr = OptionsPrivate.Private.DisplayToString(id, true); elseif(mode == "table") then displayStr = OptionsPrivate.Private.DataToString(id); end input.editBox:SetMaxBytes(nil); input.editBox:SetScript("OnEscapePressed", function() group:Close(); end); - input.editBox:SetScript("OnChar", function() input:SetText(displayStr); input.editBox:HighlightText(); end); + input.editBox:SetScript("OnTextChanged", function() input:SetText(displayStr); input.editBox:HighlightText(); end); input.editBox:SetScript("OnMouseUp", function() input.editBox:HighlightText(); end); input:SetLabel(id.." - "..#displayStr); input.button:Hide(); @@ -65,37 +71,21 @@ local function ConstructImportExport(frame) input:SetFocus(); end elseif(mode == "import") then - local textBuffer, i, lastPaste = {}, 0 - local function clearBuffer(self) - lastPaste = nil - self:SetScript('OnUpdate', nil) - local pasted = strtrim(table.concat(textBuffer)) - input.editBox:ClearFocus(); - pasted = pasted:match( "^%s*(.-)%s*$" ); - if (#pasted > 20) then - WeakAuras.Import(pasted); - input:SetLabel(L["Processed %i chars"]:format(i)); - input.editBox:SetMaxBytes(2500); - input.editBox:SetText(strsub(pasted, 1, 2500)); + title:SetText(L["Importing"]) + input.editBox:SetScript("OnTextChanged", function(self) + local pasted = self:GetText() + pasted = pasted:match("^%s*(.-)%s*$") + if #pasted > 20 then + WeakAuras.Import(pasted) end - end - - input.editBox:SetScript('OnChar', function(self, c) - if not lastPaste then - textBuffer, i, lastPaste = {}, 0, 1 - self:SetScript('OnUpdate', clearBuffer) - end - i = i + 1 - textBuffer[i] = c end) - input.editBox:SetText(""); - input.editBox:SetMaxBytes(2500); input.editBox:SetScript("OnEscapePressed", function() group:Close(); end); input.editBox:SetScript("OnMouseUp", nil); input:SetLabel(L["Paste text below"]); input:SetFocus(); end + group:DoLayout() end function group.Close(self) diff --git a/WeakAurasOptions/OptionsFrames/ModelPicker.lua b/WeakAurasOptions/OptionsFrames/ModelPicker.lua index 08a037e..5f56eb9 100644 --- a/WeakAurasOptions/OptionsFrames/ModelPicker.lua +++ b/WeakAurasOptions/OptionsFrames/ModelPicker.lua @@ -20,31 +20,23 @@ local function GetAll(baseObject, path, property, default) if not property then return default end - if baseObject.controlledChildren then - local result - local first = true - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local childObject = valueFromPath(childData, path) - if childObject and childObject[property] then - if first then - result = childObject[property] - first = false - else - if result ~= childObject[property] then - return default - end + + local result = default + local first = true + for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do + local childObject = valueFromPath(child, path) + if childObject and childObject[property] then + if first then + result = childObject[property] + first = false + else + if result ~= childObject[property] then + return default end end end - return result - else - local object = valueFromPath(baseObject, path) - if object and object[property] then - return object[property] - end - return default end + return result end local function ConstructModelPicker(frame) @@ -161,11 +153,19 @@ local function ConstructModelPicker(frame) group:Pick(nil, nil, nil, modelPickerY:GetValue()); end); + local modelPickerRotation = AceGUI:Create("Slider"); + modelPickerRotation:SetSliderValues(0, 360, 0.05); + modelPickerRotation:SetLabel(L["Rotation"]); + modelPickerRotation.frame:SetParent(group.frame); + modelPickerRotation:SetCallback("OnValueChanged", function() + group:Pick(nil, nil, nil, nil, modelPickerRotation:GetValue()); + end); + local modelTree = AceGUI:Create("WeakAurasTreeGroup"); group.modelTree = modelTree; - group.frame:SetScript("OnUpdate", function() + group.frame:SetScript("OnSizeChanged", function() local frameWidth = frame:GetWidth(); - local sliderWidth = (frameWidth - 50) / 3; + local sliderWidth = (frameWidth - 50) / 4; local narrowSliderWidth = (frameWidth - 50) / 7; modelTree:SetTreeWidth(frameWidth - 370); @@ -178,6 +178,9 @@ local function ConstructModelPicker(frame) modelPickerY.frame:SetPoint("bottomleft", frame, "bottomleft", 35 + (2 * sliderWidth), 43); modelPickerY.frame:SetPoint("bottomright", frame, "bottomleft", 35 + (3 * sliderWidth), 43); + + modelPickerRotation.frame:SetPoint("bottomleft", frame, "bottomleft", 45 + (3 * sliderWidth), 43); + modelPickerRotation.frame:SetPoint("bottomright", frame, "bottomleft", 45 + (4 * sliderWidth), 43); end); group:SetLayout("fill"); modelTree:SetTree(WeakAuras.ModelPaths); @@ -194,7 +197,26 @@ local function ConstructModelPicker(frame) model:SetFrameStrata("FULLSCREEN"); group.model = model; - local function SetOnObject(object, model_path, model_z, model_x, model_y) + local startX, rotation + local function OnUpdateScript() + local uiScale, x = UIParent:GetEffectiveScale(), GetCursorPosition() + local screenW, screenH = GetScreenWidth(), GetScreenHeight() + local diffX = startX/uiScale - x/uiScale + rotation = (rotation + 180 / screenW * diffX) % 360 + model:SetFacing(rad(rotation)) + end + model:EnableMouse() + model:SetScript("OnMouseDown", function(self) + startX = GetCursorPosition() + rotation = group.selectedValues.rotation or 0 + self:SetScript("OnUpdate", OnUpdateScript) + end) + model:SetScript("OnMouseUp", function(self) + self:SetScript("OnUpdate", nil) + group:Pick(nil, nil, nil, nil, rotation) + end) + + local function SetOnObject(object, model_path, model_z, model_x, model_y, rotation) if model_path then object.model_path = model_path end @@ -207,37 +229,31 @@ local function ConstructModelPicker(frame) if model_y then object.model_y = model_y end + if rotation then + object.rotation = rotation + end end - function group.Pick(self, model_path, model_z, model_x, model_y) + function group.Pick(self, model_path, model_z, model_x, model_y, rotation) local valueFromPath = OptionsPrivate.Private.ValueFromPath self.selectedValues.model_path = model_path or self.selectedValues.model_path self.selectedValues.model_x = model_x or self.selectedValues.model_x self.selectedValues.model_y = model_y or self.selectedValues.model_y self.selectedValues.model_z = model_z or self.selectedValues.model_z + self.selectedValues.rotation = rotation or self.selectedValues.rotation WeakAuras.SetModel(self.model, self.selectedValues.model_path) self.model:SetPosition(self.selectedValues.model_z, self.selectedValues.model_x, self.selectedValues.model_y); self.model:SetFacing(rad(self.selectedValues.rotation)); - if(self.baseObject.controlledChildren) then - for index, childId in pairs(self.baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local object = valueFromPath(childData, self.path) - if(object) then - SetOnObject(object, model_path, model_z, model_x, model_y) - WeakAuras.Add(childData) - WeakAuras.UpdateThumbnail(childData) - end - end - else - local object = valueFromPath(self.baseObject, self.path) - if object then - SetOnObject(object, model_path, model_z, model_x, model_y) - WeakAuras.Add(self.baseObject) - WeakAuras.UpdateThumbnail(self.baseObject) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(self.baseObject) do + local object = valueFromPath(child, self.path) + if(object) then + SetOnObject(object, model_path, model_z, model_x, model_y, rotation) + WeakAuras.Add(child) + WeakAuras.UpdateThumbnail(child) end end end @@ -266,20 +282,24 @@ local function ConstructModelPicker(frame) modelPickerX.editbox:SetText(("%.2f"):format(self.selectedValues.model_x)); modelPickerY:SetValue(self.selectedValues.model_y); modelPickerY.editbox:SetText(("%.2f"):format(self.selectedValues.model_y)); + modelPickerRotation:SetValue(self.selectedValues.rotation); + modelPickerRotation.editbox:SetText(("%.2f"):format(self.selectedValues.rotation)); if(baseObject.controlledChildren) then self.givenModel = {}; self.givenZ = {}; self.givenX = {}; self.givenY = {}; - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local object = valueFromPath(childData, path) + self.givenRotation = {}; + for child in OptionsPrivate.Private.TraverseLeafs(baseObject) do + local childId = child.id + local object = valueFromPath(child, path) if(object) then self.givenModel[childId] = object.model_path; self.givenZ[childId] = object.model_z; self.givenX[childId] = object.model_x; self.givenY[childId] = object.model_y; + self.givenRotation[childId] = object.rotation; end end else @@ -289,6 +309,7 @@ local function ConstructModelPicker(frame) self.givenZ = object.model_z; self.givenX = object.model_x; self.givenY = object.model_y; + self.givenRotation = object.rotation; end frame.window = "model"; frame:UpdateFrameVisible() @@ -300,31 +321,33 @@ local function ConstructModelPicker(frame) WeakAuras.FillOptions() end - function group.CancelClose(self) + function group.CancelClose() local valueFromPath = OptionsPrivate.Private.ValueFromPath if(group.baseObject.controlledChildren) then - for index, childId in pairs(group.baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - local object = valueFromPath(childData, self.path) + for child in OptionsPrivate.Private.TraverseLeafs(group.baseObject) do + local childId = child.id + local object = valueFromPath(child, group.path) if(object) then object.model_path = group.givenModel[childId]; object.model_z = group.givenZ[childId]; object.model_x = group.givenX[childId]; object.model_y = group.givenY[childId]; - WeakAuras.Add(childData); - WeakAuras.UpdateThumbnail(childData); + object.rotation = group.givenRotation[childId]; + WeakAuras.Add(child); + WeakAuras.UpdateThumbnail(child); end end else - local object = valueFromPath(self.baseObject, self.path) + local object = valueFromPath(group.baseObject, group.path) if(object) then object.model_path = group.givenModel object.model_z = group.givenZ object.model_x = group.givenX object.model_y = group.givenY - WeakAuras.Add(self.baseObject); - WeakAuras.UpdateThumbnail(self.baseObject); + object.rotation = group.givenRotation + WeakAuras.Add(group.baseObject); + WeakAuras.UpdateThumbnail(group.baseObject); end end group.Close(); diff --git a/WeakAurasOptions/OptionsFrames/MoverSizer.lua b/WeakAurasOptions/OptionsFrames/MoverSizer.lua index d6e2612..a550770 100644 --- a/WeakAurasOptions/OptionsFrames/MoverSizer.lua +++ b/WeakAurasOptions/OptionsFrames/MoverSizer.lua @@ -45,12 +45,7 @@ local function moveOnePxl(direction) WeakAuras.Add(data, nil, true) WeakAuras.UpdateThumbnail(data) OptionsPrivate.ResetMoverSizer() - if data.parent then - local parentData = WeakAuras.GetData(data.parent) - if parentData then - WeakAuras.Add(parentData) - end - end + OptionsPrivate.Private.AddParents(data) WeakAuras.FillOptions() end end @@ -382,11 +377,9 @@ local function BuildAlignLines(mover) y = {} } local x, y = {}, {} - local skipIds = { [data.id] = true } - if data.controlledChildren then - for _, id in pairs(data.controlledChildren) do - skipIds[id] = true - end + local skipIds = {} + for child in OptionsPrivate.Private.TraverseAll(data) do + skipIds[child.id] = true end for k, v in pairs(WeakAuras.displayButtons) do @@ -400,8 +393,8 @@ local function BuildAlignLines(mover) tinsert(y, (region:GetBottom() or 0) * scale) else local centerX, centerY = region:GetCenter() - tinsert(x, centerX or 0 * scale) - tinsert(y, centerY or 0 * scale) + tinsert(x, (centerX or 0) * scale) + tinsert(y, (centerY or 0) * scale) end end end @@ -657,12 +650,7 @@ local function ConstructMoverSizer(parent) mover:SetHeight(region:GetHeight() * scale) mover:SetPoint(mover.selfPoint, mover.anchor, mover.anchorPoint, xOff * scale, yOff * scale) end - if data.parent then - local parentData = db.displays[data.parent] - if parentData then - WeakAuras.Add(parentData) - end - end + OptionsPrivate.Private.AddParents(data) WeakAuras.FillOptions() OptionsPrivate.Private.Animate("display", data.uid, "main", data.animation.main, WeakAuras.regions[data.id].region, false, nil, true) -- hide alignment lines @@ -767,6 +755,7 @@ local function ConstructMoverSizer(parent) region:ResetPosition() WeakAuras.Add(data, nil, true) + OptionsPrivate.Private.AddParents(data) WeakAuras.UpdateThumbnail(data) frame:ScaleCorners(region:GetWidth(), region:GetHeight()) diff --git a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua index 9ec543d..c7b4e55 100644 --- a/WeakAurasOptions/OptionsFrames/OptionsFrame.lua +++ b/WeakAurasOptions/OptionsFrames/OptionsFrame.lua @@ -20,7 +20,6 @@ local L = WeakAuras.L local displayButtons = WeakAuras.displayButtons local regionOptions = WeakAuras.regionOptions local tempGroup = OptionsPrivate.tempGroup -local prettyPrint = WeakAuras.prettyPrint local aceOptions = {} local function CreateDecoration(frame) @@ -140,16 +139,17 @@ function OptionsPrivate.CreateFrame() local odb = OptionsPrivate.savedVars.odb -------- Mostly Copied from AceGUIContainer-Frame-------- frame = CreateFrame("FRAME", "WeakAurasOptions", UIParent) + tinsert(UISpecialFrames, frame:GetName()) frame:SetBackdrop({ - bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background", + bgFile = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_FullWhite", edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", tile = true, tileSize = 32, edgeSize = 32, insets = { left = 8, right = 8, top = 8, bottom = 8 } }) - frame:SetBackdropColor(0, 0, 0, 1) + frame:SetBackdropColor(0.1, 0.1, 0.1, 0.85) frame:EnableMouse(true) frame:SetMovable(true) frame:SetResizable(true) @@ -274,6 +274,7 @@ function OptionsPrivate.CreateFrame() self.iconPicker.frame:Hide() self.modelPicker.frame:Hide() self.importexport.frame:Hide() + self.update.frame:Hide() self.texteditor.frame:Hide() self.codereview.frame:Hide() if self.newView then @@ -346,7 +347,11 @@ function OptionsPrivate.CreateFrame() self.newView.frame:Hide() end end - + if self.window == "update" then + self.update.frame:Show() + else + self.update.frame:Hide() + end if self.window == "default" then if self.loadProgessVisible then self.loadProgress:Show() @@ -482,7 +487,7 @@ function OptionsPrivate.CreateFrame() addFooter(L["Find Auras"], [[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_logo.tga]], "https://wago.io", L["Browse Wago, the largest collection of auras."]) - if not WeakAurasCompanion then + if not OptionsPrivate.Private.CompanionData.slugs then addFooter(L["Update Auras"], [[Interface\AddOns\WeakAuras\Media\Textures\wagoupdate_refresh.tga]], "https://weakauras.wtf", L["Keep your Wago imports up to date with the Companion App."]) end @@ -518,7 +523,6 @@ function OptionsPrivate.CreateFrame() container.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 10) container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -63 - WeakAuras.normalWidth * 340, -14) container.frame:Show() - --container.frame:SetClipsChildren(true) container.titletext:Hide() -- Hide the border container.content:GetParent():SetBackdrop(nil) @@ -532,6 +536,7 @@ function OptionsPrivate.CreateFrame() frame.importexport = OptionsPrivate.ImportExport(frame) frame.texteditor = OptionsPrivate.TextEditor(frame) frame.codereview = OptionsPrivate.CodeReview(frame) + frame.update = OptionsPrivate.UpdateFrame(frame) frame.moversizer, frame.mover = OptionsPrivate.MoverSizer(frame) @@ -596,7 +601,7 @@ function OptionsPrivate.CreateFrame() else self.Instructions:Hide() end - WeakAuras.SortDisplayButtons(filterInput:GetText()) + OptionsPrivate.SortDisplayButtons(filterInput:GetText()) end) filterInput:SetHeight(15) filterInput:SetPoint("TOP", frame, "TOP", 0, -44) @@ -715,10 +720,8 @@ function OptionsPrivate.CreateFrame() -- override SetScroll to make children visible as needed local oldSetScroll = buttonsScroll.SetScroll buttonsScroll.SetScroll = function(self, value) - if self:GetScrollPos() ~= value then - oldSetScroll(self, value) - self.LayoutFunc(self.content, self.children, true) - end + oldSetScroll(self, value) + self.LayoutFunc(self.content, self.children, true) end function buttonsScroll:SetScrollPos(top, bottom) @@ -746,20 +749,53 @@ function OptionsPrivate.CreateFrame() status.scrollvalue = status.offset / ((height - viewheight) / 1000.0) end - local numAddons = 0 - - for addon, addonData in pairs(OptionsPrivate.Private) do - numAddons = numAddons + 1 + -- Ready to Install section + local pendingInstallButton = AceGUI:Create("WeakAurasLoadedHeaderButton") + pendingInstallButton:SetText(L["Ready for Install"]) + pendingInstallButton:Disable() + pendingInstallButton:EnableExpand() + pendingInstallButton.frame.view:Hide() + if odb.pendingImportCollapse then + pendingInstallButton:Collapse() + else + pendingInstallButton:Expand() end + pendingInstallButton:SetOnExpandCollapse(function() + if pendingInstallButton:GetExpanded() then + odb.pendingImportCollapse = nil + else + odb.pendingImportCollapse = true + end + OptionsPrivate.SortDisplayButtons() + end) + pendingInstallButton:SetExpandDescription(L["Expand all pending Import"]) + pendingInstallButton:SetCollapseDescription(L["Collapse all pending Import"]) + frame.pendingInstallButton = pendingInstallButton - if numAddons > 0 then - local addonsButton = AceGUI:Create("WeakAurasNewHeaderButton") - addonsButton:SetText(L["Addons"]) - addonsButton:SetDescription(L["Manage displays defined by Addons"]) - addonsButton:SetClick(function() frame:PickOption("Addons") end) - frame.addonsButton = addonsButton + -- Ready for update section + local pendingUpdateButton = AceGUI:Create("WeakAurasLoadedHeaderButton") + pendingUpdateButton:SetText(L["Ready for Update"]) + pendingUpdateButton:Disable() + pendingUpdateButton:EnableExpand() + pendingUpdateButton.frame.view:Hide() + if odb.pendingUpdateCollapse then + pendingUpdateButton:Collapse() + else + pendingUpdateButton:Expand() end + pendingUpdateButton:SetOnExpandCollapse(function() + if pendingUpdateButton:GetExpanded() then + odb.pendingUpdateCollapse = nil + else + odb.pendingUpdateCollapse = true + end + OptionsPrivate.SortDisplayButtons() + end) + pendingUpdateButton:SetExpandDescription(L["Expand all pending Import"]) + pendingUpdateButton:SetCollapseDescription(L["Collapse all pending Import"]) + frame.pendingUpdateButton = pendingUpdateButton + -- Loaded section local loadedButton = AceGUI:Create("WeakAurasLoadedHeaderButton") loadedButton:SetText(L["Loaded"]) loadedButton:Disable() @@ -775,28 +811,30 @@ function OptionsPrivate.CreateFrame() else odb.loadedCollapse = true end - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons() end) loadedButton:SetExpandDescription(L["Expand all loaded displays"]) loadedButton:SetCollapseDescription(L["Collapse all loaded displays"]) loadedButton:SetViewClick(function() OptionsPrivate.Private.PauseAllDynamicGroups() - if loadedButton.view.func() == 2 then + if loadedButton.view.visibility == 2 then for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] ~= nil then child:PriorityHide(2) end end + loadedButton:PriorityHide(2) else for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] ~= nil then child:PriorityShow(2) end end + loadedButton:PriorityShow(2) end OptionsPrivate.Private.ResumeAllDynamicGroups() end) - loadedButton:SetViewTest(function() + loadedButton.RecheckVisibility = function(self) local none, all = true, true for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] ~= nil then @@ -808,17 +846,23 @@ function OptionsPrivate.CreateFrame() end end end + local newVisibility if all then - return 2 + newVisibility = 2 elseif none then - return 0 + newVisibility = 0 else - return 1 + newVisibility = 1 end - end) + if newVisibility ~= self.view.visibility then + self.view.visibility = newVisibility + self:UpdateViewTexture() + end + end loadedButton:SetViewDescription(L["Toggle the visibility of all loaded displays"]) frame.loadedButton = loadedButton + -- Not Loaded section local unloadedButton = AceGUI:Create("WeakAurasLoadedHeaderButton") unloadedButton:SetText(L["Not Loaded"]) unloadedButton:Disable() @@ -834,28 +878,30 @@ function OptionsPrivate.CreateFrame() else odb.unloadedCollapse = true end - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons() end) unloadedButton:SetExpandDescription(L["Expand all non-loaded displays"]) unloadedButton:SetCollapseDescription(L["Collapse all non-loaded displays"]) unloadedButton:SetViewClick(function() OptionsPrivate.Private.PauseAllDynamicGroups() - if unloadedButton.view.func() == 2 then + if unloadedButton.view.visibility == 2 then for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] == nil then child:PriorityHide(2) end end + unloadedButton:PriorityHide(2) else for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] == nil then child:PriorityShow(2) end end + unloadedButton:PriorityShow(2) end OptionsPrivate.Private.ResumeAllDynamicGroups() end) - unloadedButton:SetViewTest(function() + unloadedButton.RecheckVisibility = function(self) local none, all = true, true for id, child in pairs(displayButtons) do if OptionsPrivate.Private.loaded[id] == nil then @@ -867,14 +913,19 @@ function OptionsPrivate.CreateFrame() end end end + local newVisibility if all then - return 2 + newVisibility = 2 elseif none then - return 0 + newVisibility = 0 else - return 1 + newVisibility = 1 end - end) + if newVisibility ~= self.view.visibility then + self.view.visibility = newVisibility + self:UpdateViewTexture() + end + end unloadedButton:SetViewDescription(L["Toggle the visibility of all non-loaded displays"]) frame.unloadedButton = unloadedButton @@ -887,8 +938,8 @@ function OptionsPrivate.CreateFrame() if data and data.parent then frame:ClearOptions(data.parent) end - for _, tmpId in ipairs(tempGroup.controlledChildren) do - if (id == tmpId) then + for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do + if (id == child.id) then frame:ClearOptions(tempGroup.id) end end @@ -906,10 +957,8 @@ function OptionsPrivate.CreateFrame() data = tempGroup end - if data.controlledChildren then - for _, id in ipairs(data.controlledChildren) do - frame:ClearOptions(id) - end + for child in OptionsPrivate.Private.TraverseAllChildren(data) do + frame:ClearOptions(child.id) end end if (type(self.pickedDisplay) == "string" and self.pickedDisplay == id) @@ -1026,6 +1075,10 @@ function OptionsPrivate.CreateFrame() AceConfigDialog:Open("WeakAuras", group) tabsWidget:SetTitle("") + + if data.controlledChildren and #data.controlledChildren == 0 then + WeakAurasOptions:NewAura() + end end frame.ClearPick = function(self, id) @@ -1047,19 +1100,31 @@ function OptionsPrivate.CreateFrame() self:FillOptions() end + frame.OnRename = function(self, uid, oldid, newid) + if type(frame.pickedDisplay) == "string" and frame.pickedDisplay == oldid then + frame.pickedDisplay = newid + else + for i, childId in pairs(tempGroup.controlledChildren) do + if (childId == newid) then + tempGroup.controlledChildren[i] = newid + end + end + end + end + frame.ClearPicks = function(self, noHide) OptionsPrivate.Private.PauseAllDynamicGroups() + for id, button in pairs(displayButtons) do + button:ClearPick(true) + if not noHide then + button:PriorityHide(1) + button:SetVisibilityDirectly(0) + end + end frame.pickedDisplay = nil frame.pickedOption = nil wipe(tempGroup.controlledChildren) - for id, button in pairs(displayButtons) do - button:ClearPick(noHide) - end - --newButton:ClearPick(noHide) - if frame.addonsButton then - frame.addonsButton:ClearPick(noHide) - end loadedButton:ClearPick(noHide) unloadedButton:ClearPick(noHide) container:ReleaseChildren() @@ -1071,30 +1136,46 @@ function OptionsPrivate.CreateFrame() OptionsPrivate.ClearTriggerExpandState() end - local function GetTarget(pickedDisplay) - local targetId - if pickedDisplay then - if type(pickedDisplay) == "table" and tempGroup.controlledChildren and tempGroup.controlledChildren[1] then - targetId = tempGroup.controlledChildren[1] - elseif type(pickedDisplay) == "string" then - targetId = pickedDisplay + frame.GetTargetAura = function(self) + if self.pickedDisplay then + if type(self.pickedDisplay) == "table" and tempGroup.controlledChildren and tempGroup.controlledChildren[1] then + return tempGroup.controlledChildren[1] + elseif type(self.pickedDisplay) == "string" then + return self.pickedDisplay end end - return targetId + return nil end - frame.NewAura = function(self, fromGroup) - local targetId = GetTarget(self.pickedDisplay) - self:ClearPicks() + frame.NewAura = function(self) + local targetId + local targetIsDynamicGroup + + if self.pickedDisplay then + if type(self.pickedDisplay) == "table" and tempGroup.controlledChildren and tempGroup.controlledChildren[1] then + targetId = tempGroup.controlledChildren[1] + WeakAuras.PickDisplay(targetId) + elseif type(self.pickedDisplay) == "string" then + targetId = self.pickedDisplay + else + self:ClearPicks() + end + end + if targetId then local pickedButton = WeakAuras.GetDisplayButton(targetId) - if pickedButton then - pickedButton:Pick() + if pickedButton.data.controlledChildren then + targetIsDynamicGroup = pickedButton.data.regionType == "dynamicgroup" + else + local parent = pickedButton.data.parent + local parentData = parent and WeakAuras.GetData(parent) + targetIsDynamicGroup = parentData and parentData.regionType == "dynamicgroup" end end self.moversizer:Hide() self.pickedOption = "New" + container:ReleaseChildren() container.frame:SetPoint("TOPLEFT", frame, "TOPRIGHT", -63 - WeakAuras.normalWidth * 340, -8) container:SetLayout("fill") local border = AceGUI:Create("InlineGroup") @@ -1119,7 +1200,7 @@ function OptionsPrivate.CreateFrame() button:SetDescription(L["Offer a guided way to create auras for your character"]) button:SetIcon("Interface\\Icons\\INV_Misc_Book_06") button:SetClick(function() - WeakAuras.OpenTriggerTemplate(nil, targetId) + OptionsPrivate.OpenTriggerTemplate(nil, self:GetTargetAura()) end) containerScroll:AddChild(button) @@ -1140,13 +1221,31 @@ function OptionsPrivate.CreateFrame() tinsert(regionTypesSorted, regionType) end + -- Sort group + dynamic group first, then the others alphabeticaly table.sort(regionTypesSorted, function(a, b) + if (a == "group") then + return true + end + + if (b == "group") then + return false + end + + if (a == "dynamicgroup") then + return true + end + if (b == "dynamicgroup") then + return false + end + return regionOptions[a].displayName < regionOptions[b].displayName end) for index, regionType in ipairs(regionTypesSorted) do - local regionData = regionOptions[regionType] - if (not (fromGroup and (regionType == "group" or regionType == "dynamicgroup"))) then + if (targetIsDynamicGroup and (regionType == "group" or regionType == "dynamicgroup")) then + -- Dynamic groups can't contain group/dynamic groups + else + local regionData = regionOptions[regionType] local button = AceGUI:Create("WeakAurasNewButton") button:SetTitle(regionData.displayName) if(type(regionData.icon) == "string" or type(regionData.icon) == "table") then @@ -1154,7 +1253,7 @@ function OptionsPrivate.CreateFrame() end button:SetDescription(regionData.description) button:SetClick(function() - WeakAuras.NewAura(nil, regionType, targetId) + WeakAuras.NewAura(nil, regionType, self:GetTargetAura()) end) containerScroll:AddChild(button) end @@ -1213,50 +1312,22 @@ function OptionsPrivate.CreateFrame() containerScroll:AddChild(importButton) end - frame.PickOption = function(self, option, fromGroup) - local targetId = GetTarget(self.pickedDisplay) - self:ClearPicks() - if targetId then - local pickedButton = WeakAuras.GetDisplayButton(targetId) - if pickedButton then - pickedButton:Pick() - end - end - self.moversizer:Hide() - self.pickedOption = option - if option == "Addons" then - frame.addonsButton:Pick() - - local containerScroll = AceGUI:Create("ScrollFrame") - containerScroll:SetLayout("AbsoluteList") - container:SetLayout("fill") - container:AddChild(containerScroll) - - OptionsPrivate.CreateImportButtons() - WeakAuras.SortImportButtons(containerScroll) - else - error("An options button other than New or Addons was selected... but there are no other options buttons!") - end - end - - frame.PickDisplay = function(self, id, tab, noHide) - if self.pickedDisplay == id then - return - end - - OptionsPrivate.Private.PauseAllDynamicGroups() - - self:ClearPicks(noHide) - local data = WeakAuras.GetData(id) - - displayButtons[id]:Pick() - self.pickedDisplay = id - + local function ExpandParents(data) if data.parent then if not displayButtons[data.parent]:GetExpanded() then displayButtons[data.parent]:Expand() end + local parentData = WeakAuras.GetData(data.parent) + ExpandParents(parentData) end + end + + frame.PickDisplay = function(self, id, tab, noHide) + local data = WeakAuras.GetData(id) + + -- Always expand even if already picked + ExpandParents(data) + if OptionsPrivate.Private.loaded[id] ~= nil then -- Under loaded if not loadedButton:GetExpanded() then @@ -1269,6 +1340,18 @@ function OptionsPrivate.CreateFrame() end end + if self.pickedDisplay == id then + return + end + + OptionsPrivate.Private.PauseAllDynamicGroups() + + self:ClearPicks(noHide) + + displayButtons[id]:Pick() + self.pickedDisplay = id + + if tab then self.selectedTab = tab end @@ -1283,15 +1366,10 @@ function OptionsPrivate.CreateFrame() self.buttonsScroll:SetScrollPos(yOffset, yOffset - 32) end - if data.controlledChildren then - for index, childId in pairs(data.controlledChildren) do - displayButtons[childId]:PriorityShow(1) - end - end - - if data.controlledChildren and #data.controlledChildren == 0 then - WeakAurasOptions:NewAura(true) + for child in OptionsPrivate.Private.TraverseAllChildren(data) do + displayButtons[child.id]:PriorityShow(1) end + displayButtons[data.id]:RecheckParentVisibility() OptionsPrivate.Private.ResumeAllDynamicGroups() end @@ -1318,7 +1396,7 @@ function OptionsPrivate.CreateFrame() else local wasGroup = false if type(self.pickedDisplay) == "string" then - if WeakAuras.GetData(self.pickedDisplay).controlledChildren then + if WeakAuras.GetData(self.pickedDisplay).controlledChildren or WeakAuras.GetData(id).controlledChildren then wasGroup = true elseif not OptionsPrivate.IsDisplayPicked(id) then tinsert(tempGroup.controlledChildren, self.pickedDisplay) @@ -1337,15 +1415,13 @@ function OptionsPrivate.CreateFrame() end frame.PickDisplayBatch = function(self, batchSelection) + local alreadySelected = {} + for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do + alreadySelected[child.id] = true + end + for index, id in ipairs(batchSelection) do - local alreadySelected = false - for _, v in pairs(tempGroup.controlledChildren) do - if v == id then - alreadySelected = true - break - end - end - if not alreadySelected then + if not alreadySelected[id] then displayButtons[id]:Pick() tinsert(tempGroup.controlledChildren, id) end diff --git a/WeakAurasOptions/OptionsFrames/TextEditor.lua b/WeakAurasOptions/OptionsFrames/TextEditor.lua index 87aa991..e43717b 100644 --- a/WeakAurasOptions/OptionsFrames/TextEditor.lua +++ b/WeakAurasOptions/OptionsFrames/TextEditor.lua @@ -4,6 +4,7 @@ local AddonName, OptionsPrivate = ... -- Lua APIs local pairs, type, ipairs = pairs, type, ipairs local loadstring = loadstring +local gsub = gsub -- WoW APIs local CreateFrame = CreateFrame @@ -148,22 +149,40 @@ end]=] } local function ConstructTextEditor(frame) - local group = AceGUI:Create("InlineGroup") + local group = AceGUI:Create("WeakAurasInlineGroup") group.frame:SetParent(frame) - group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -17, 12) - group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 17, -10) + group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -16); + group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 46); group.frame:Hide() - group:SetLayout("fill") + group:SetLayout("flow") + + local title = AceGUI:Create("Label") + title:SetFontObject(GameFontNormalHuge) + title:SetFullWidth(true) + title:SetText(L["Code Editor"]) + group:AddChild(title) local editor = AceGUI:Create("MultiLineEditBox") - editor:SetWidth(400) - editor.button:Hide() + editor:SetFullWidth(true) + editor:SetFullHeight(true) + editor:DisableButton(true) local fontPath = SharedMedia:Fetch("font", "Fira Mono Medium") if (fontPath) then editor.editBox:SetFont(fontPath, 12) end group:AddChild(editor) - --editor.frame:SetClipsChildren(true) + + local originalOnCursorChanged = editor.editBox:GetScript("OnCursorChanged") + editor.editBox:SetScript("OnCursorChanged", function(self, ...) + -- WORKAROUND the editbox sends spurious OnCursorChanged events if its resized + -- That makes AceGUI scroll the editbox to make the cursor visible, leading to unintended + -- movements. Prevent all of that by checking if the edit box has focus, as otherwise the cursor + -- is invisible, and we don't care about making it visible + if not self:HasFocus() then + return + end + originalOnCursorChanged(self, ...) + end) -- The indention lib overrides GetText, but for the line number -- display we ned the original, so save it here. @@ -178,7 +197,7 @@ local function ConstructTextEditor(frame) group:CancelClose() end ) - cancel:SetPoint("BOTTOMRIGHT", -27, 13) + cancel:SetPoint("BOTTOMRIGHT", -20, -24) cancel:SetFrameLevel(cancel:GetFrameLevel() + 1) cancel:SetHeight(20) cancel:SetWidth(100) @@ -205,7 +224,7 @@ local function ConstructTextEditor(frame) settings_frame:RegisterForClicks("LeftButtonUp") local helpButton = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate") - helpButton:SetPoint("BOTTOMLEFT", 12, 13) + helpButton:SetPoint("BOTTOMLEFT", 12, -24) helpButton:SetFrameLevel(cancel:GetFrameLevel() + 1) helpButton:SetHeight(20) helpButton:SetWidth(100) @@ -220,11 +239,11 @@ local function ConstructTextEditor(frame) urlText:Hide() local urlCopyLabel = urlText:CreateFontString(nil, "BACKGROUND", "GameFontHighlightSmall") - urlCopyLabel:SetPoint("BOTTOMLEFT", group.frame, "BOTTOMLEFT", 12, 18) + urlCopyLabel:SetPoint("BOTTOMLEFT", group.frame, "BOTTOMLEFT", 12, -20) urlCopyLabel:SetText(L["Press Ctrl+C to copy"]) urlCopyLabel:Hide() - urlText:SetPoint("TOPLEFT", urlCopyLabel, "TOPRIGHT", 12, 13) + urlText:SetPoint("TOPLEFT", urlCopyLabel, "TOPRIGHT", 12, 0) urlText:SetPoint("RIGHT", settings_frame, "LEFT") local dropdown = CreateFrame("Frame", "SettingsMenuFrame", settings_frame, "UIDropDownMenuTemplate") @@ -349,8 +368,9 @@ local function ConstructTextEditor(frame) -- iterate saved snippets and make buttons for order, snippet in ipairs(savedSnippets) do local button = AceGUI:Create("WeakAurasSnippetButton") + local snippetInsert = gsub(snippet.snippet, "|", "||") button:SetTitle(snippet.name) - button:SetDescription(snippet.snippet) + button:SetDescription(snippetInsert) button:SetEditable(true) button:SetRelativeWidth(1) button:SetNew(snippet.new) @@ -358,7 +378,7 @@ local function ConstructTextEditor(frame) button:SetCallback( "OnClick", function() - editor.editBox:Insert(snippet.snippet) + editor.editBox:Insert(snippetInsert) editor:SetFocus() end ) @@ -662,32 +682,34 @@ local function ConstructTextEditor(frame) self.oldOnTextChanged(...) end ) - if (data.controlledChildren and not setOnParent) then + + if setOnParent then + editor:SetText(OptionsPrivate.Private.ValueFromPath(data, path) or "") + else local singleText local sameTexts = true local combinedText = "" - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do local text if multipath then - text = path[childId] and OptionsPrivate.Private.ValueFromPath(childData, path[childId]) + text = path[child.id] and OptionsPrivate.Private.ValueFromPath(child, path[child.id]) else - text = OptionsPrivate.Private.ValueFromPath(childData, path) + text = OptionsPrivate.Private.ValueFromPath(child, path) end if text then if not (singleText) then singleText = text else - if not (singleText == text) then + if singleText ~= text then sameTexts = false end end - if not (combinedText == "") then + if combinedText ~= "" then combinedText = combinedText .. "\n\n" end combinedText = - combinedText .. L["-- Do not remove this comment, it is part of this trigger: "] .. childId .. "\n" + combinedText .. L["-- Do not remove this comment, it is part of this aura: "] .. child.id .. "\n" combinedText = combinedText .. (text or "") end end @@ -698,8 +720,6 @@ local function ConstructTextEditor(frame) editor:SetText(combinedText) editor.combinedText = true end - else - editor:SetText(OptionsPrivate.Private.ValueFromPath(data, path) or "") end editor:SetFocus() end @@ -711,13 +731,13 @@ local function ConstructTextEditor(frame) frame:UpdateFrameVisible() end - local function extractTexts(input, ids) + local function extractTexts(input) local texts = {} local currentPos, id, startIdLine, startId, endId, endIdLine while (true) do startIdLine, startId = - string.find(input, L["-- Do not remove this comment, it is part of this trigger: "], currentPos, true) + string.find(input, L["-- Do not remove this comment, it is part of this aura: "], currentPos, true) if (not startId) then break end @@ -749,31 +769,21 @@ local function ConstructTextEditor(frame) end function group.Close(self) - if (self.data.controlledChildren and not self.setOnParent) then - local textById = editor.combinedText and extractTexts(editor:GetText(), self.data.controlledChildren) - for index, childId in pairs(self.data.controlledChildren) do - local text = editor.combinedText and (textById[childId] or "") or editor:GetText() - local childData = WeakAuras.GetData(childId) - OptionsPrivate.Private.ValueToPath(childData, self.multipath and self.path[childId] or self.path, text) - WeakAuras.Add(childData) - end - else + if self.setOnParent then OptionsPrivate.Private.ValueToPath(self.data, self.path, editor:GetText()) WeakAuras.Add(self.data) - end - if (self.reloadOptions) then - if (self.data.controlledChildren) then - for index, childId in pairs(self.data.controlledChildren) do - WeakAuras.ClearAndUpdateOptions(childId) - end - WeakAuras.ClearAndUpdateOptions(self.data.id) - else - WeakAuras.ClearAndUpdateOptions(self.data.id) - end else - WeakAuras.ClearAndUpdateOptions(self.data.id) + local textById = editor.combinedText and extractTexts(editor:GetText()) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(self.data) do + local text = editor.combinedText and (textById[child.id] or "") or editor:GetText() + OptionsPrivate.Private.ValueToPath(child, self.multipath and self.path[child.id] or self.path, text) + WeakAuras.Add(child) + OptionsPrivate.ClearOptions(child.id) + end end + WeakAuras.ClearAndUpdateOptions(self.data.id) + editor.editBox:SetScript("OnTextChanged", self.oldOnTextChanged) editor:ClearFocus() frame.window = "default" diff --git a/WeakAurasOptions/OptionsFrames/TexturePicker.lua b/WeakAurasOptions/OptionsFrames/TexturePicker.lua index 4cf998b..c549657 100644 --- a/WeakAurasOptions/OptionsFrames/TexturePicker.lua +++ b/WeakAurasOptions/OptionsFrames/TexturePicker.lua @@ -42,52 +42,34 @@ local function GetAll(baseObject, path, property, default) if not property then return default end - if baseObject.controlledChildren then - local result - local first = true - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local childObject = valueFromPath(childData, path) - if childObject and childObject[property] then - if first then - result = childObject[property] - first = false - else - if not CompareValues(result, childObject[property]) then - return default - end + + local result = default + local first = true + for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do + local childObject = valueFromPath(child, path) + if childObject and childObject[property] then + if first then + result = childObject[property] + first = false + else + if not CompareValues(result, childObject[property]) then + return default end end end - return result - else - local object = valueFromPath(baseObject, path) - if object and object[property] then - return object[property] - end - return default end + return result end local function SetAll(baseObject, path, property, value) local valueFromPath = OptionsPrivate.Private.ValueFromPath - if baseObject.controlledChildren then - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local object = valueFromPath(childData, path) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do + local object = valueFromPath(child, path) if object then object[property] = value - WeakAuras.Add(childData) - WeakAuras.UpdateThumbnail(childData) + WeakAuras.Add(child) + WeakAuras.UpdateThumbnail(child) end - end - else - local object = valueFromPath(baseObject, path) - if object then - object[property] = value - WeakAuras.Add(baseObject) - WeakAuras.UpdateThumbnail(baseObject) - end end end @@ -114,7 +96,6 @@ local function ConstructTexturePicker(frame) local scroll = AceGUI:Create("ScrollFrame"); scroll:SetWidth(540); scroll:SetLayout("flow"); - --scroll.frame:SetClipsChildren(true); dropdown:AddChild(scroll); local function texturePickerGroupSelected(widget, event, uniquevalue) @@ -153,6 +134,7 @@ local function ConstructTexturePicker(frame) dropdown:SetCallback("OnGroupSelected", texturePickerGroupSelected) function group.UpdateList(self) + dropdown.dropdown.pullout:Close() wipe(dropdown.list); for categoryName, category in pairs(self.textures) do local match = false; @@ -198,21 +180,11 @@ local function ConstructTexturePicker(frame) self.SetTextureFunc = SetTextureFunc self.givenPath = {}; self.selectedTextures = {} - if(baseObject.controlledChildren) then - for index, childId in pairs(baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childObject = valueFromPath(childData, path) - if childObject and childObject[properties.texture] then - self.givenPath[childId] = childObject[properties.texture] - self.selectedTextures[childObject[properties.texture]] = true - end - end - end - else - local object = valueFromPath(baseObject, path) + + for child in OptionsPrivate.Private.TraverseLeafsOrAura(baseObject) do + local object = valueFromPath(child, path) if object and object[properties.texture] then - self.givenPath[baseObject.id] = object[properties.texture] + self.givenPath[child.id] = object[properties.texture] self.selectedTextures[object[properties.texture]] = true end end @@ -262,24 +234,12 @@ local function ConstructTexturePicker(frame) function group.CancelClose() local valueFromPath = OptionsPrivate.Private.ValueFromPath - if(group.baseObject.controlledChildren) then - for index, childId in pairs(group.baseObject.controlledChildren) do - local childData = WeakAuras.GetData(childId); - if(childData) then - local childObject = valueFromPath(childData, group.path) - if childObject then - childObject[group.properties.texture] = group.givenPath[childId] - WeakAuras.Add(childData); - WeakAuras.UpdateThumbnail(childData); - end - end - end - else - local object = valueFromPath(group.baseObject, group.path) - if object then - object[group.properties.texture] = group.givenPath[group.baseObject.id] - WeakAuras.Add(group.baseObject) - WeakAuras.UpdateThumbnail(group.baseObject) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(group.baseObject) do + local childObject = valueFromPath(child, group.path) + if childObject then + childObject[group.properties.texture] = group.givenPath[child.id] + WeakAuras.Add(child); + WeakAuras.UpdateThumbnail(child); end end group.Close(); diff --git a/WeakAurasOptions/OptionsFrames/Update.lua b/WeakAurasOptions/OptionsFrames/Update.lua new file mode 100644 index 0000000..613b73d --- /dev/null +++ b/WeakAurasOptions/OptionsFrames/Update.lua @@ -0,0 +1,2047 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, OptionsPrivate = ... + +local AceGUI = LibStub("AceGUI-3.0") + +local WeakAuras = WeakAuras +local L = WeakAuras.L + +-- Scam Check +local function notEmptyString(str) + return str and str ~= "" and string.find(str, "%S") +end + +local function addCode(codes, text, code, ...) + -- The 4th paramter is a "check" if the code is active + -- The following line let's distinguish between addCode(a, b, c, nil) and addCode(a, b, c) + -- If the 4th parameter is nil, then we want to return + if (select("#", ...) > 0) then + if not select(1, ...) then + return + end + end + + if code and notEmptyString(code) then + local t = {}; + t.text = text; + t.value = text + t.code = code + tinsert(codes, t); + end +end + +local function checkTrigger(codes, id, trigger, untrigger) + if not trigger or trigger.type ~= "custom" then return end; + + addCode(codes, L["%s Trigger Function"]:format(id), trigger.custom) + + if trigger.custom_type == "stateupdate" then + addCode(codes, L["%s Custom Variables"]:format(id), trigger.customVariables, trigger.custom_type == "stateupdate") + else + addCode(codes, L["%s Untrigger Function"]:format(id), untrigger and untrigger.custom) + addCode(codes, L["%s Duration Function"]:format(id), trigger.customDuration) + addCode(codes, L["%s Name Function"]:format(id), trigger.customName) + addCode(codes, L["%s Icon Function"]:format(id), trigger.customIcon) + addCode(codes, L["%s Texture Function"]:format(id),trigger.customTexture) + addCode(codes, L["%s Stacks Function"]:format(id), trigger.customStacks) + for i = 1, 7 do + local property = "customOverlay" .. i; + addCode(codes, L["%s %u. Overlay Function"]:format(id, i), trigger[property]) + end + end +end + +local function checkAnimation(codes, id, a) + if not a or a.type ~= "custom" then return end + addCode(codes, L["%s - Alpha Animation"]:format(id), a.alphaFunc, a.alphaType == "custom" and a.use_alpha) + addCode(codes, L["%s - Translate Animation"]:format(id), a.translateFunc, a.translateType == "custom" and a.use_translate) + addCode(codes, L["%s - Scale Animation"]:format(id), a.scaleFunc, a.scaleType == "custom" and a.use_scale) + addCode(codes, L["%s - Rotate Animation"]:format(id), a.rotateFunc, a.rotateType == "custom" and a.use_rotate) + addCode(codes, L["%s - Color Animation"]:format(id), a.colorFunc, a.colorType == "custom" and a.use_color) +end + +local function scamCheck(codes, data) + for i, v in ipairs(data.triggers) do + checkTrigger(codes, L["%s - %i. Trigger"]:format(data.id, i), v.trigger, v.untrigger); + end + + addCode(codes, L["%s - Trigger Logic"]:format(data.id), data.triggers.customTriggerLogic, data.triggers.disjunctive == "custom"); + addCode(codes, L["%s - Custom Text"]:format(data.id), data.customText) + addCode(codes, L["%s - Custom Anchor"]:format(data.id), data.customAnchor, data.anchorFrameType == "CUSTOM") + + if (data.actions) then + if data.actions.init then + addCode(codes, L["%s - Init Action"]:format(data.id), data.actions.init.custom, data.actions.init.do_custom) + end + if data.actions.start then + addCode(codes, L["%s - Start Action"]:format(data.id), data.actions.start.custom, data.actions.start.do_custom) + addCode(codes, L["%s - Start Custom Text"]:format(data.id), data.actions.start.message_custom, data.actions.start.do_message) + end + if data.actions.finish then + addCode(codes, L["%s - Finish Action"]:format(data.id), data.actions.finish.custom, data.actions.finish.do_custom) + addCode(codes, L["%s - Finish Custom Text"]:format(data.id), data.actions.finish.message_custom, data.actions.finish.do_message) + end + end + + if (data.animation) then + checkAnimation(codes, L["%s - Start"]:format(data.id), data.animation.start); + checkAnimation(codes, L["%s - Main"]:format(data.id), data.animation.main); + checkAnimation(codes, L["%s - Finish"]:format(data.id), data.animation.finish); + end + + addCode(codes, L["%s - Custom Grow"]:format(data.id), data.customGrow, data.regionType == "dynamicgroup" and data.grow == "CUSTOM") + addCode(codes, L["%s - Custom Sort"]:format(data.id), data.customSort, data.regionType == "dynamicgroup" and data.sort == "custom") + addCode(codes, L["%s - Custom Anchor"]:format(data.id), data.customAnchorPerUnit, + data.regionType == "dynamicgroup" and data.grow ~= "CUSTOM" and data.useAnchorPerUnit and data.anchorPerUnit == "CUSTOM") + + if (data.conditions) then + local customChat = 1 + local customCode = 1 + local customCheck = 1 + for _, condition in ipairs(data.conditions) do + if (condition.changes) then + for _, property in ipairs(condition.changes) do + if type(property.value) == "table" and property.value.custom then + if property.property == "chat" then + addCode(codes, L["%s - Condition Custom Chat %s"]:format(data.id, customChat), property.value.custom); + customChat = customChat + 1 + elseif property.property == "customcode" then + addCode(codes, L["%s - Condition Custom Code %s"]:format(data.id, customCode), property.value.custom); + customCode = customCode + 1 + end + end + end + end + + local function recurseAddCustomCheck(checks) + if not checks then return end + for _, check in pairs(checks) do + if check.trigger == -1 and check.variable == "customcheck" then + addCode(codes, L["%s - Condition Custom Check %s"]:format(data.id, customCheck), check.value); + customCheck = customCheck + 1 + end + recurseAddCustomCheck(check.checks) + end + end + + if condition.check then + if condition.check.trigger == -1 and condition.check.variable == "customcheck" then + addCode(codes, L["%s - Condition Custom Check %s"]:format(data.id, customCheck), condition.check.value); + customCheck = customCheck + 1 + end + recurseAddCustomCheck(condition.check.checks) + end + end + end +end +-- End of scam check + +-- Diff algorithm +local deleted = {} -- magic value +local fieldToCategory +local internalFieldMarker = {} + +local function FieldToCategory(field, isRoot) + if not fieldToCategory then + -- Initialize fieldToCategory + fieldToCategory = {} + for _, cat in ipairs(OptionsPrivate.Private.update_categories) do + for _, property in ipairs(cat.fields) do + fieldToCategory[property] = cat.name + end + end + for _, key in pairs(OptionsPrivate.Private.internal_fields) do + fieldToCategory[key] = internalFieldMarker + end + end + + local category = fieldToCategory[field] + if category == internalFieldMarker then + return nil + end + + if category == nil then + category = "display" + end + -- For child auras, anchor fields are arrangement + if not isRoot and category == "anchor" then + category = "arrangement" + end + return category +end + +local function recurseUpdate(data, chunk) + for k,v in pairs(chunk) do + if v == deleted then + data[k] = nil + elseif type(v) == 'table' and type(data[k]) == 'table' then + recurseUpdate(data[k], v) + else + data[k] = v + end + end +end + +local ignoredForDiffChecking -- Needs to be created lazyly +local function RecurseDiff(ours, theirs) + local diff, seen, same = {}, {}, true + for key, ourVal in pairs(ours) do + if not ignoredForDiffChecking[key] then + seen[key] = true + local theirVal = theirs[key] + if type(ourVal) == "table" and type(theirVal) == "table" then + local diffVal = RecurseDiff(ourVal, theirVal) + if diffVal then + diff[key] = diffVal + same = false + end + elseif ourVal ~= theirVal and -- of course, floating points can be nonequal by less than we could possibly care + not(type(ourVal) == "number" and type(theirVal) == "number" and math.abs(ourVal - theirVal) < 1e-6) then + if (theirVal == nil) then + diff[key] = deleted + else + diff[key] = theirVal; + end + same = false + end + end + end + for key, theirVal in pairs(theirs) do + if not seen[key] and not ignoredForDiffChecking[key] then + diff[key] = theirVal + same = false + end + end + if not same then return diff end +end + +-- for debug purposes +local function RecurseSerial(lines, depth, chunk) + for k, v in pairs(chunk) do + if v == deleted then + tinsert(lines, string.rep(" ", depth) .. "|cFFFF0000" .. k .. " -> deleted|r") + elseif type(v) == "table" then + tinsert(lines, string.rep(" ", depth) .. k .. " -> {") + RecurseSerial(lines, depth + 1, v) + tinsert(lines, string.rep(" ", depth) .. "}") + else + tinsert(lines, string.rep(" ", depth) .. k .. " -> " .. tostring(v)) + end + end +end + +local function DebugPrintDiff(diff) + local lines = { + "==========================", + "Diff detected: ", + "{", + } + RecurseSerial(lines, 1, diff) + tinsert(lines, "}") + tinsert(lines, "==========================") + print(table.concat(lines, "\n")) +end + +local function Diff(ours, theirs) + if not ignoredForDiffChecking then + ignoredForDiffChecking = WeakAuras.Mixin({}, OptionsPrivate.Private.internal_fields, + OptionsPrivate.Private.non_transmissable_fields) + end + + -- generates a diff which WeakAuras.Update can use + local debug = false + if not ours or not theirs then return end + local diff = RecurseDiff(ours, theirs) + if diff then + if debug then + DebugPrintDiff(diff, ours.id, theirs.id) + end + return diff + end +end +-- End of diff + +local function EnsureUniqueUid(data) + if not data.uid then + data.uid = WeakAuras.GenerateUniqueID() + elseif OptionsPrivate.Private.GetDataByUID(data.uid) then + data.uid = WeakAuras.GenerateUniqueID() + end +end + +local function CopyDiff(diff) + local copy = {} + for k, v in pairs(diff) do + if v == deleted then + copy[k] = deleted + elseif type(v) == "table" then + copy[k] = CopyDiff(v) + else + copy[k] = v + end + end + return copy +end + +local function BuildUidMap(data, children, type) + children = children or {} + -- The eventual result + local uidMap = { + map = { -- per uid + -- originalName: The original id of the aura + -- id: The current id of the aura, might have changed due to ids being unique + -- data: The raw data, contains non-authoritative information on e.g. id, controlledChildren, parent, sortHybridTable + -- controlledChildren: A array of child uids + -- parent: The parent uid + -- sortHybrid: optional bool !! the parent's sortHybridTable is split up and recorded per aura: + -- nil, if the parent is not a dynamic group + -- false/true based on the sortHybridTable of the dynamic group + + -- matchedUid: for "update", the matched uid. Is from a different domain! + -- diff, categories: for "update", the diff and the categories of that diff between the aura and its match + + -- index, total, parentIsDynamicGroup: helpers that transport data between phase 1 and 2 + }, + type = type -- Either old or new, only used for error checking + -- root: uid of the root + -- totalCount: count of members + -- idToUid maps from id to uid + } + uidMap.root = data.uid + uidMap.totalCount = #children + 1 + + -- Build helper map from id to uid + local idToUid = {} + idToUid[data.id] = data.uid + for i, child in ipairs(children) do + if idToUid[child.id] then + error("Diplicated id in import data") + end + idToUid[child.id] = child.uid + end + + uidMap.idToUid = idToUid + + local function handle(data) + -- Add names and data to map + uidMap.map[data.uid] = { + originalName = data.id, + id = data.id, + data = data + } + + -- Add controlled children + if data.controlledChildren then + local uidChildren = {} + for i, id in ipairs(data.controlledChildren) do + tinsert(uidChildren, idToUid[id]) + end + uidMap.map[data.uid].controlledChildren = uidChildren + end + + -- Add parent + if data.parent then + uidMap.map[data.uid].parent = idToUid[data.parent] + end + end + + local function handleSortHybridTable(data) + if data.regionType == "dynamicgroup" then + local sortHybridTableByUid = {} + if data.sortHybridTable then + for id, b in pairs(data.sortHybridTable) do + -- The sortHybridTable might contain stale ids, since e.g. ungroup doesn't correctly + -- remove entries + if idToUid[id] then + sortHybridTableByUid[idToUid[id]] = b + end + end + end + + local children = uidMap.map[data.uid].controlledChildren or {} + for _, childUid in ipairs(children) do + local sortHybrid = sortHybridTableByUid[childUid] and true or false + uidMap.map[childUid].sortHybrid = sortHybrid + end + end + end + + handle(data) + for i, child in ipairs(children) do + handle(child) + end + + handleSortHybridTable(data) + for _, child in ipairs(children) do + handleSortHybridTable(child) + end + + + uidMap.InsertData = function(self, data, parentUid, children, sortHybrid, index) + self.idToUid[data.id] = data.uid + self.totalCount = self.totalCount + 1 + + -- clean up children/sortHybird + -- The Update code first inserts children before it inserts us + -- But not every child might be inserted, since empty groups aren't inserted + -- so clean that up here + if children then + for index, childUid in ipairs_reverse(children) do + if not self:Contains(childUid) then + tremove(children, index) + if sortHybrid then + sortHybrid[childUid] = nil + end + end + end + end + + uidMap.map[data.uid] = { + originalName = data.id, + id = data.id, + data = data, + parent = parentUid, + matchedUid = data.uid, + controlledChildren = children, + sortHybrid = sortHybrid + } + + if index then + if uidMap.map[parentUid] and uidMap.map[parentUid].controlledChildren then + tinsert(uidMap.map[parentUid].controlledChildren, index, data.uid) + else + error("Can't insert into parent") + end + end + end + + uidMap.GetRootUID = function(self) + return self.root + end + + uidMap.GetType = function(self) + return self.type + end + + uidMap.Contains = function(self, uid) + return self.map[uid] and true or false + end + + uidMap.GetTotalCount = function(self) + return self.totalCount + end + + uidMap.GetRawData = function(self, uid) + if not self.map[uid] then + error("GetRawData for unknown uid") + return + end + return self.map[uid].data + end + + -- Cleans up id, controlledChildren, sortHybridTable, parent + uidMap.GetPhase1Data = function(self, uid, withAppliedPath, activeCategories) + if not self.map[uid] then + error("GetPhase1Data for unknown uid") + return nil + end + local data = CopyTable(self.map[uid].data) + if withAppliedPath then + if self.type == "new" then + error("Can't apply patch on new side") + end + local diff = self:GetDiff(uid, activeCategories) + if diff then + recurseUpdate(data, diff) + end + end + + data.id = self.map[uid].id + + if (data.controlledChildren) then + data.controlledChildren = {} + end + + if (data.sortHybridTable) then + data.sortHybridTable = {} + end + + data.parent = nil + return data + end + + -- Remaps parent, controlledChildren, sortHybridTable + uidMap.GetPhase2Data = function(self, uid, withAppliedPath, activeCategories) + if not self.map[uid] then + error("GetPhase2Data for unknown uid") + return nil + end + + local data = CopyTable(self.map[uid].data) + if withAppliedPath then + if self.type == "new" then + error("Can't apply patch on new side") + end + local diff = self:GetDiff(uid, activeCategories) + + if diff then + recurseUpdate(data, diff) + end + end + data.id = self.map[uid].id + if uid == self.root then + data.parent = self.rootParent + elseif self.map[uid].parent then + data.parent = self:GetIdFor(self.map[uid].parent) + else + data.parent = nil + end + + if self.map[uid].controlledChildren then + data.controlledChildren = {} + for i, childUid in ipairs(self.map[uid].controlledChildren) do + data.controlledChildren[i] = self:GetIdFor(childUid) + end + else + data.controlledChildren = nil + end + + if data.regionType == "dynamicgroup" then + data.sortHybridTable = {} + for i, childUid in ipairs(self.map[uid].controlledChildren) do + data.sortHybridTable[self:GetIdFor(childUid)] = self:GetSortHybrid(childUid) + end + else + data.sortHybridTable = nil + end + + return data + end + + uidMap.GetChildren = function(self, uid) + return self.map[uid] and self.map[uid].controlledChildren or {} + end + + uidMap.GetRawChildren = function(self, uid) + return self.map[uid] and self.map[uid].controlledChildren + end + + uidMap.GetSortHybrid = function(self, uid) + return self.map[uid] and self.map[uid].sortHybrid + end + + uidMap.ChangeId = function(self, uid, id) + if not self.map[uid] then + error("ChangeId for unknown uid") + return + end + + local oldId = self.map[uid].id + if (oldId == id) then + return + end + uidMap.idToUid[oldId] = nil + uidMap.idToUid[id] = uid + + self.map[uid].id = id + end + + uidMap.ChangeUID = function(self, uid, newUid) + if self.root == uid then + self.root = newUid + end + if not self.map[uid] or self.map[newUid] then + error("Invalid ChangeUID") + end + + if self.map[uid] then + self.map[newUid] = self.map[uid] + self.map[uid] = nil + + self.map[newUid].data.uid = newUid + self.idToUid[self.map[newUid].id] = newUid + if self.map[newUid].parent then + local parentMap = self.map[self.map[newUid].parent] + for i, childUid in ipairs(parentMap.controlledChildren) do + if childUid == uid then + parentMap.controlledChildren[i] = newUid + break; + end + end + end + + if self.map[newUid].controlledChildren then + for index, childUid in ipairs(self.map[newUid].controlledChildren) do + self.map[childUid].parent = newUid + end + end + + end + end + + uidMap.GetIdFor = function(self, uid) + if not uid or not self.map[uid] then + error(string.format("GetIdFor for unknown uid %s", uid)) + return + end + return self.map[uid].id + end + + uidMap.GetOriginalName = function(self, uid) + if not uid or not self.map[uid] then + error(string.format("GetOriginalName for unknown uid %s", uid)) + return + end + return self.map[uid].originalName + end + + uidMap.GetGroupOrder = function(self, uid) + if not self.map[uid] then + error("GetGroupOrder for unknown uid") + return + end + return self.map[uid].index, self.map[uid].total + end + + uidMap.SetGroupOrder = function(self, uid, index, total) + if not self.map[uid] then + error("SetGroupOrder for unknown uid") + return + end + self.map[uid].index = index + self.map[uid].total = total + end + + uidMap.GetParent = function(self, uid) + if not self.map[uid] then + error("GetParent for unknown uid") + return + end + return self.map[uid].parent + end + + uidMap.GetParentIsDynamicGroup = function(self, uid) + if not self.map[uid] then + error("GetParentIsDynamicGroup for unknown uid") + return + end + return self.map[uid].parentIsDynamicGroup + end + + uidMap.SetParentIsDynamicGroup = function(self, uid, parentIsDynamicGroup) + if not self.map[uid] then + error("SetParentIsDynamicGroup for unknown uid") + return + end + self.map[uid].parentIsDynamicGroup = parentIsDynamicGroup + end + + uidMap.SetUIDMatch = function(self, uid, matchedUid) + if not self.map[uid] then + error("SetUIDMatch for unknown uid") + return + end + self.map[uid].matchedUid = matchedUid + end + + uidMap.GetUIDMatch = function(self, uid) + if not self.map[uid] then + error("GetUIDMatch for unknown uid") + return + end + return self.map[uid].matchedUid + end + + uidMap.SetDiff = function(self, uid, diff, categories) + if not self.map[uid] then + error("SetDiff for unknown uid") + return + end + self.map[uid].diff = diff + self.map[uid].categories = categories + end + + uidMap.GetDiff = function(self, uid, categories) + if not self.map[uid] then + error("GetDiff for unknown uid") + return + end + if not self.map[uid].diff then + return + end + local diff = CopyDiff(self.map[uid].diff) + local isRoot = not self.map[uid].parent + for key in pairs(diff) do + local category = FieldToCategory(key, isRoot) + if category == nil or not categories[category] then + diff[key] = nil + end + end + return diff + end + + uidMap.GetGroupRegionType = function(self, uid) + if not self.map[uid] then + error("GetGroupRegionType for unknown uid") + return + end + local data = self.map[uid].data + if data.regionType == "group" or data.regionType == "dynamicgroup" then + return data.regionType + end + return nil + end + + uidMap.EnsureUniqueIdOfUnmatched = function(self, uid, IncProgress) + uid = uid or self.root + if not self.map[uid] then + error(string.format("EnsureUniqueIdOfUnmatched for unknown uid %s", uid)) + return + end + + if self.type == "old" then + error("Call to EnsureUniqueIdOfUnmatched for old") + end + + if not self:GetUIDMatch(uid) then + if OptionsPrivate.Private.GetDataByUID(uid) then + local newUid = WeakAuras.GenerateUniqueID() + self:ChangeUID(uid, newUid) + uid = newUid + end + end + IncProgress() + local children = self:GetChildren(uid) + for _, childUid in ipairs(children) do + self:EnsureUniqueIdOfUnmatched(childUid, IncProgress) + end + end + + uidMap.InsertUnmatchedPhase1 = function(self, otherUidMap, otherUid, IncProgress) + local children = otherUidMap:GetChildren(otherUid) + local lastMatchUid = nil -- our uid + local waitingForMatch = {} -- Auras that we haven't assigned to a match yet + -- Will be added to before on finding a match + -- or the parent will be added + local matchToInsert = { + -- from our uid to + -- before: array of other uids that should be inserted before the uid + -- after: array of other uids that should be inserted after the uid + } + + for index, childUid in ipairs(children) do + local needsToBeInserted = self:InsertUnmatchedPhase1(otherUidMap, childUid, IncProgress) + local matchedUid = otherUidMap:GetUIDMatch(childUid) + if matchedUid then + lastMatchUid = matchedUid + matchToInsert[matchedUid] = matchToInsert[matchedUid] or {} + matchToInsert[matchedUid].before = waitingForMatch + waitingForMatch = {} + else + -- Auras => matchToInsert/waitingForMatch + -- Groups: + -- with Children: => matchToInsert/waitingForMatch + -- without Children => skip groups that are empty and don't match + local toInsert = otherUidMap:GetGroupRegionType(childUid) == nil or needsToBeInserted + if toInsert then + if lastMatchUid then + matchToInsert[lastMatchUid] = matchToInsert[lastMatchUid] or {} + matchToInsert[lastMatchUid].after = matchToInsert[lastMatchUid].after or {} + tinsert(matchToInsert[lastMatchUid].after, childUid) + else + tinsert(waitingForMatch, childUid) + end + else + IncProgress() + coroutine.yield() + end + end + coroutine.yield() + end + + for uid, otherList in pairs(matchToInsert) do + -- First find uid in parent + local parent = self.map[uid].parent + if parent then + local parentChildren = self:GetChildren(parent) + local index = tIndexOf(parentChildren, uid) + + if otherList.before then + for _, otherUid in ipairs(otherList.before) do + local otherData = otherUidMap:GetRawData(otherUid) + local rawChildren = otherUidMap:GetRawChildren(otherUid) + local sortHybrid = otherUidMap:GetSortHybrid(otherUid) + self:InsertData(otherData, parent, rawChildren, sortHybrid, index) + index = index + 1 + otherUidMap:SetUIDMatch(otherUid, otherUid) -- Uids are the same! + self:SetUIDMatch(otherUid, otherUid) + IncProgress() + coroutine.yield() + end + end + + if otherList.after then + index = index + 1 -- We insert after the match + for _, otherUid in ipairs(otherList.after) do + local otherData = otherUidMap:GetRawData(otherUid) + local rawChildren = otherUidMap:GetRawChildren(otherUid) + local sortHybrid = otherUidMap:GetSortHybrid(otherUid) + self:InsertData(otherData, parent, rawChildren, sortHybrid, index) + index = index + 1 + otherUidMap:SetUIDMatch(otherUid, otherUid) -- Uids are the same! + self:SetUIDMatch(otherUid, otherUid) + IncProgress() + coroutine.yield() + end + end + end + coroutine.yield() + end + + for _, otherUid in ipairs(waitingForMatch) do + local otherData = otherUidMap:GetRawData(otherUid) + local parent = otherUidMap:GetParent(otherUid) + local rawChildren = otherUidMap:GetRawChildren(otherUid) + local sortHybrid = otherUidMap:GetSortHybrid(otherUid) + + if otherUidMap:GetUIDMatch(parent) then + -- the parent is matched, we need to insert ourselves into it + local matchedParent = otherUidMap:GetUIDMatch(parent) + self:InsertData(otherData, matchedParent, rawChildren, sortHybrid, #(self:GetChildren(matchedParent)) + 1) + else + -- the parent is unmatched, so we'll end up inserting it + self:InsertData(otherData, parent, rawChildren, sortHybrid) + end + otherUidMap:SetUIDMatch(otherUid, otherUid) -- Uids are the same! + self:SetUIDMatch(otherUid, otherUid) + IncProgress() + coroutine.yield() + end + + return #waitingForMatch > 0 + end + + uidMap.InsertUnmatchedFrom = function(self, otherUidMap, IncProgress) + self:InsertUnmatchedPhase1(otherUidMap, otherUidMap:GetRootUID(), IncProgress) + end + + uidMap.Remove = function(self, uid) + if not self.map[uid] then + error("Can't remove what isn't there") + end + + local id = self:GetIdFor(uid) + local parent = self:GetParent(uid) + self.map[uid] = nil + self.idToUid[id] = nil + self.totalCount = self.totalCount - 1 + if parent then + if not self.map[parent] then + error("Parent not found") + end + tDeleteItem(self.map[parent].controlledChildren, uid) + end + end + + uidMap.SetRootParent = function(self, parentId) + self.rootParent = parentId + end + + return uidMap, uidMap.root +end + + +local function hasChildren(data) + return data.controlledChildren and true or false +end + +local function MatchChild(uid, newUidMap, oldUidMap) + if oldUidMap:Contains(uid) then + newUidMap:SetUIDMatch(uid, uid) + oldUidMap:SetUIDMatch(uid, uid) + end + + local newChildren = newUidMap:GetChildren(uid) + for _, childUid in ipairs(newChildren) do + MatchChild(childUid, newUidMap, oldUidMap) + end + +end + +local function BuildMatches(newUidMap, oldUidMap) + newUidMap:SetUIDMatch(newUidMap:GetRootUID(), oldUidMap:GetRootUID()) + oldUidMap:SetUIDMatch(oldUidMap:GetRootUID(), newUidMap:GetRootUID()) + + local newChildren = newUidMap:GetChildren(newUidMap:GetRootUID()) + for _, childUid in ipairs(newChildren) do + MatchChild(childUid, newUidMap, oldUidMap) + end +end + +local function CheckForChangedRegionTypesHelper(newUidMap, oldUidMap, uid) + local matchedUid = newUidMap:GetUIDMatch(uid) + if matchedUid then + if newUidMap:GetGroupRegionType(uid) ~= oldUidMap:GetGroupRegionType(matchedUid) then + return false + end + end + + local newChildren = newUidMap:GetChildren(uid) + for _, childUID in ipairs(newChildren) do + if not CheckForChangedRegionTypesHelper(newUidMap, oldUidMap, childUID) then + return false + end + end + return true +end + +local function CheckForChangedRegionTypes(newUidMap, oldUidMap) + return CheckForChangedRegionTypesHelper(newUidMap, oldUidMap, newUidMap:GetRootUID()) +end + +-- This checks for this kind of matches: +-- Old: +-- Root +-- |> A +-- |-> B +-- New: +-- Root +-- |> B +-- |-> A +-- Where the structures conflict. +-- We do that with the following check per aura 'A' in new: +-- Consider the parents of A_new, root -> A_new +-- For each (recursive) child of A_old, check that none point to any parent of A_new +local function CheckForIncompatibleStructuresCheckOld(oldUid, oldUidMap, parents) + local oldChildren = oldUidMap:GetChildren(oldUid) + for _, oldChildUid in ipairs(oldChildren) do + if parents[oldUidMap:GetUIDMatch(oldChildUid)] then + return false + end + if not CheckForIncompatibleStructuresCheckOld(oldChildUid, oldUidMap, parents) then + return false + end + end + + return true +end + +local function CheckForIncompatibleStructuresHelper(uid, parents, newUidMap, oldUidMap) + local oldUid = newUidMap:GetUIDMatch(uid) + if not CheckForIncompatibleStructuresCheckOld(oldUid, oldUidMap, parents) then + return false + end + + parents[uid] = true + local newChildren = newUidMap:GetChildren(uid) + for _, newChildUid in ipairs(newChildren) do + if not CheckForIncompatibleStructuresHelper(newChildUid, parents, newUidMap, oldUidMap) then + return false + end + end + parents[uid] = nil + return true +end + +local function CheckForIncompatibleStructures(newUidMap, oldUidMap) + local parents = {} + return CheckForIncompatibleStructuresHelper(newUidMap:GetRootUID(), parents, newUidMap, oldUidMap) +end + +local function SetCategories(globalCategories, categories) + for key, b in pairs(categories) do + if b then + globalCategories[key] = true + end + end +end + + + +local function GetCategories(diff, isRoot) + local categories = {} + for key in pairs(diff) do + local category = FieldToCategory(key, isRoot) + if category then + categories[category] = true + end + end + return categories +end + +local function BuildDiffsHelper(uid, newUidMap, oldUidMap, matchInfo) + local matchedUid = newUidMap:GetUIDMatch(uid) + local isGroup = newUidMap:GetGroupRegionType(uid) + if matchedUid then + local newParent = newUidMap:GetParent(uid) + local oldParent = oldUidMap:GetParent(matchedUid) + + local differentParents = false + if newParent == nil and oldParent == nil then + -- Same + elseif newParent == nil or oldParent == nil then + -- Can't really happen + differentParents = true + else + if newUidMap:GetUIDMatch(newParent) ~= oldParent then + differentParents = true + end + end + + if differentParents then + matchInfo.activeCategories.arrangement = true + end + + if newUidMap:GetSortHybrid(uid) ~= oldUidMap:GetSortHybrid(matchedUid) then + matchInfo.activeCategories.arrangement = true + end + + -- We can use the raw data, because the diff algorithm ignores all the members that + -- aren't directly comparable + local oldRawData = oldUidMap:GetRawData(matchedUid) + local newRawData = newUidMap:GetRawData(uid) + local diff = Diff(oldRawData, newRawData) + if diff then + local categories = GetCategories(diff, uid == newUidMap:GetRootUID()) + newUidMap:SetDiff(uid, diff, categories) + oldUidMap:SetDiff(matchedUid, diff, categories) + SetCategories(matchInfo.activeCategories, categories) + + matchInfo.diffs[uid] = true + if isGroup then + matchInfo.modifiedGroupCount = matchInfo.modifiedGroupCount + 1 + else + matchInfo.modifiedCount = matchInfo.modifiedCount + 1 + end + else + matchInfo.unmodified[uid] = true + if isGroup then + matchInfo.unmodifiedGroupCount = matchInfo.unmodifiedGroupCount + 1 + else + matchInfo.unmodifiedCount = matchInfo.unmodifiedCount + 1 + end + end + else + if isGroup then + matchInfo.addedGroupCount = matchInfo.addedGroupCount + 1 + matchInfo.activeCategories.arrangement = true + else + matchInfo.added[uid] = true + matchInfo.addedCount = matchInfo.addedCount + 1 + matchInfo.activeCategories.newchildren = true + end + end + + local newChildren = newUidMap:GetChildren(uid) + for _, newChildUid in ipairs(newChildren) do + BuildDiffsHelper(newChildUid, newUidMap, oldUidMap, matchInfo) + end + + return matchInfo +end + +local function BuildDiffsRemoved(oldUID, newUidMap, oldUidMap, matchInfo) + local uid = oldUidMap:GetUIDMatch(oldUID) + local isGroup = oldUidMap:GetGroupRegionType(oldUID) + if not uid then + if isGroup then + matchInfo.deletedGroupCount = matchInfo.deletedGroupCount + 1 + matchInfo.activeCategories.arrangement = true + else + matchInfo.deleted[oldUID] = true + matchInfo.deletedCount = matchInfo.deletedCount + 1 + matchInfo.activeCategories.oldchildren = true + end + end + + local oldChildren = oldUidMap:GetChildren(oldUID) + for _, oldChildUid in ipairs(oldChildren) do + BuildDiffsRemoved(oldChildUid, newUidMap, oldUidMap, matchInfo) + end +end + +-- This function compares the order of children in a given parent +-- It detects e.g. +-- Group Group +-- A => B +--- B A +local function CompareControlledChildrenOrder(oldUID, newUidMap, oldUidMap, matchInfo) + local newUid = oldUidMap:GetUIDMatch(oldUID) + if newUid then + -- We first iterate over the old order, and remember the index for all matches + local oldOrder = { + -- maps childUids of newUid to the index the corresponding aura has in oldUid + } + local oldChildren = oldUidMap:GetChildren(oldUID) + for index, oldChildUid in ipairs(oldChildren) do + local newChildUid = oldUidMap:GetUIDMatch(oldChildUid) + if newChildUid then + oldOrder[newChildUid] = index + end + end + + -- We now iterate the new order, and expect the indexes to monotonically increase + local highestIndex = -1 + local newChildren = newUidMap:GetChildren(newUid) + for index, newChildUid in ipairs(newChildren) do + local oldIndex = oldOrder[newChildUid] + if oldIndex then + if oldIndex < highestIndex then + matchInfo.activeCategories.arrangement = true + return -- Don't need to check more + else + highestIndex = oldIndex + end + end + end + end + + local oldChildren = oldUidMap:GetChildren(oldUID) + for _, oldChildUid in ipairs(oldChildren) do + CompareControlledChildrenOrder(oldChildUid, newUidMap, oldUidMap, matchInfo) + end +end + +local function hasChanges(matchInfo) + return matchInfo.modifiedCount > 0 + or matchInfo.modifiedGroupCount > 0 + or matchInfo.addedCount > 0 + or matchInfo.addedGroupCount > 0 + or matchInfo.deletedCount > 0 + or matchInfo.deletedGroupCount > 0 + or matchInfo.activeCategories.arrangement +end + +local function BuildDiffs(newUidMap, oldUidMap) + local matchInfo = { + modifiedCount = 0, + modifiedGroupCount = 0, + unmodifiedCount = 0, + unmodifiedGroupCount = 0, + addedCount = 0, + addedGroupCount = 0, + deletedCount = 0, + deletedGroupCount = 0, + diffs = {}, -- Contains diffs for new uids + unmodified = {}, -- Contains new uids that had a empty diff + added = {}, -- Contains new uids that were added + deleted = {}, -- Contains old uids that were removed + activeCategories = {} -- maps from name of Private.update_categories to true/nil + } + -- Handles addition + modification + BuildDiffsHelper(newUidMap:GetRootUID(), newUidMap, oldUidMap, matchInfo) + -- Handles removals + BuildDiffsRemoved(oldUidMap:GetRootUID(), newUidMap, oldUidMap, matchInfo) + if not matchInfo.activeCategories.arrangement then + CompareControlledChildrenOrder(oldUidMap:GetRootUID(), newUidMap, oldUidMap, matchInfo) + end + + return matchInfo +end + +local function MatchInfo(data, children, target) + -- Check that the import has uids, otherwise we won't even try to match + if not data.uid then + return nil, L["Import has no UID, cannot be matched to existing auras."] + end + if children then + for _, child in ipairs(children) do + if not child.uid then + return nil, L["Import has no UID, cannot be matched to existing auras."] + end + end + end + + if target then + if hasChildren(data) ~= hasChildren(target) then + return nil, L["Invalid target aura"] + end + else + target = OptionsPrivate.Private.GetDataByUID(data.uid) + if target and hasChildren(data) ~= hasChildren(target) then + target = nil + end + end + if not target then + return nil -- No error + end + + -- Build a uid map for the target auras + local oldChildren = {} + for child in OptionsPrivate.Private.TraverseAllChildren(target) do + tinsert(oldChildren, child) + end + + local newUidMap = BuildUidMap(data, children, "new") + local oldUidMap = BuildUidMap(target, oldChildren, "old") + oldUidMap:SetRootParent(target.parent) + newUidMap:SetRootParent(target.parent) + + BuildMatches(newUidMap, oldUidMap) + if not CheckForChangedRegionTypes(newUidMap, oldUidMap) then + return nil, L["Incompatible changes to group region types detected"] + end + + if not CheckForIncompatibleStructures(newUidMap, oldUidMap) then + return nil, L["Incompatible changes to group structure detected"] + end + + local matchInfo = BuildDiffs(newUidMap, oldUidMap) + matchInfo.newUidMap = newUidMap + matchInfo.oldUidMap = oldUidMap + + return matchInfo +end + +local function AddAuraList(container, uidMap, list, expandText) + local expand = AceGUI:Create("WeakAurasExpand") + local collapsed = true + local image = collapsed and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\expand" + or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\collapse" + expand:SetImage(image) + expand:SetImageSize(10, 10) + expand:SetFontObject(GameFontHighlight) + expand:SetFullWidth(true) + expand:SetLabel(expandText) + container:AddChild(expand) + + local auraLabelContainer = AceGUI:Create("WeakAurasInlineGroup") + auraLabelContainer:SetFullWidth(true) + auraLabelContainer:DoLayout() + container:AddChild(auraLabelContainer) + + local sortedNames = {} + for uid in pairs(list) do + tinsert(sortedNames, uidMap:GetIdFor(uid)) + end + table.sort(sortedNames) + + expand:SetCallback("OnClick", function() + collapsed = not collapsed + local image = collapsed and "Interface\\AddOns\\WeakAuras\\Media\\Textures\\expand" + or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\collapse" + expand:SetImage(image) + + if collapsed then + auraLabelContainer:ReleaseChildren() + else + local text + for _, name in ipairs(sortedNames) do + text = (text or "") .. " • " .. name .. "\n" + end + if text then + local auraLabel = AceGUI:Create("Label") + auraLabel:SetText(text) + auraLabel:SetFullWidth(true) + auraLabelContainer:AddChild(auraLabel) + end + end + auraLabelContainer:DoLayout() + container:DoLayout() + end) +end + +local methods = { + Open = function(self, data, children, target, sender) + if(self.optionsWindow.window == "importexport") then + self.optionsWindow.importexport:Close(); + elseif(self.optionsWindow.window == "texture") then + self.optionsWindow.texturePicker:CancelClose(); + elseif(self.optionsWindow.window == "icon") then + self.optionsWindow.iconPicker:CancelClose(); + elseif(self.optionsWindow.window == "model") then + self.optionsWindow.modelPicker:CancelClose(); + end + self.optionsWindow.window = "update" + self.optionsWindow:UpdateFrameVisible() + + self.pendingData = { + data = data, + children = children or {}, + target = target, + sender = sender + } + self.userChoices = { + + } + + self:ReleaseChildren() + self:AddBasicInformationWidgets(data, sender) + + local matchInfoResult = AceGUI:Create("Label") + matchInfoResult:SetFontObject(GameFontHighlight) + matchInfoResult:SetFullWidth(true) + self:AddChild(matchInfoResult) + + local matchInfo, errorMessage = MatchInfo(data, children, target) + self.matchInfo = matchInfo + + -- Cases: + -- No match => Import + -- Match, but no difference => Import as Copy + -- Match with difference => Import as Copy / Update, preference depends on preferToUpdate + if matchInfo ~= nil then + if not hasChanges(matchInfo) then + -- there is no difference whatsoever + self.userChoices.mode = "import" + matchInfoResult:SetText(L["You already have this group/aura. Importing will create a duplicate."]) + self.importButton:SetText(L["Import as Copy"]) + else + local oldRootId = matchInfo.oldUidMap:GetIdFor(matchInfo.oldUidMap:GetRootUID()) + local preferToUpdate = matchInfo.oldUidMap:GetRawData(matchInfo.oldUidMap:GetRootUID()).preferToUpdate + if (data.regionType == "group" or data.regionType == "dynamicgroup") then + local matchInfoText = L["This is a modified version of your group: |cff9900FF%s|r"]:format(oldRootId) + matchInfoResult:SetText(matchInfoText) + if matchInfo.addedCount ~= 0 then + AddAuraList(self, matchInfo.newUidMap, matchInfo.added, L["%d |4aura:auras; added"]:format(matchInfo.addedCount)) + end + if matchInfo.modifiedCount ~= 0 then + AddAuraList(self, matchInfo.oldUidMap, matchInfo.diffs, L["%d |4aura:auras; modified"]:format(matchInfo.modifiedCount)) + end + if matchInfo.deletedCount ~= 0 then + AddAuraList(self, matchInfo.oldUidMap, matchInfo.deleted, L["%d |4aura:auras; deleted"]:format(matchInfo.deletedCount)) + end + else + matchInfoResult:SetText(L["This is a modified version of your aura, |cff9900FF%s.|r"]:format(oldRootId)) + end + + self:AddChild(AceGUI:Create("WeakAurasSpacer")) + local choicesHeader = AceGUI:Create("Label") + choicesHeader:SetText(L["What do you want to do?"]) + choicesHeader:SetFontObject(GameFontNormalHuge) + choicesHeader:SetFullWidth(true) + self:AddChild(choicesHeader) + + local importCopyRadioButton = AceGUI:Create("CheckBox") + importCopyRadioButton:SetLabel(L["Create a Copy"]) + importCopyRadioButton:SetType("radio") + importCopyRadioButton:SetFullWidth(true) + self.importCopyRadioButton = importCopyRadioButton + self:AddChild(importCopyRadioButton) + + local updateRadioButton = AceGUI:Create("CheckBox") + updateRadioButton:SetLabel(L["Update Auras"]) + updateRadioButton:SetType("radio") + updateRadioButton:SetFullWidth(true) + self.updateRadioButton = updateRadioButton + self:AddChild(updateRadioButton) + + local updateUiArea = AceGUI:Create("WeakAurasInlineGroup") + updateUiArea:SetFullWidth(true) + updateUiArea:SetFullHeight(true) + self.updateUiArea = updateUiArea + self:AddChild(updateUiArea) + + importCopyRadioButton:SetCallback("OnValueChanged", function(_, _, v) + self:SelectMode(v and "import" or "update") + self:DoLayout() + end) + + updateRadioButton:SetCallback("OnValueChanged", function(_, _, v) + self:SelectMode(v and "update" or "import") + self:DoLayout() + end) + + self:SelectMode(preferToUpdate and "update" or "import") + end + else + self.userChoices.mode = "import" + local matchInfoText = "" + if (errorMessage) then + matchInfoText = matchInfoText .. "|cFFFF0000" .. errorMessage .. "|r\n" + end + + -- No match, so plain import + if data.controlledChildren then + matchInfoText = matchInfoText .. L["Importing a group with %s child auras."]:format(#children) + else + matchInfoText = matchInfoText .. L["Importing a stand-alone aura."] + end + + matchInfoResult:SetText(matchInfoText) + self.importButton:SetText(L["Import"]) + end + + local scamCheckResult = {} + scamCheck(scamCheckResult, data) + if children then + for _, child in ipairs(children) do + scamCheck(scamCheckResult, child) + end + end + self.scamCheckResult = scamCheckResult + + if (#scamCheckResult > 0) then + self:AddChild(AceGUI:Create("WeakAurasSpacer")) + + local scamCheckText = AceGUI:Create("Label") + scamCheckText:SetFontObject(GameFontHighlight) + scamCheckText:SetFullWidth(true) + scamCheckText:SetText(L["This aura contains custom Lua code.\nMake sure you can trust the person who sent it!"]) + scamCheckText:SetColor(1, 0, 0) + self:AddChild(scamCheckText) + end + + local highestVersion = data.internalVersion or 0 + if children then + for _, child in ipairs(children) do + highestVersion = max(highestVersion, child.internalVersion or 0) + end + end + + if (highestVersion > WeakAuras.InternalVersion()) then + local highestVersionWarning = AceGUI:Create("Label") + highestVersionWarning:SetFontObject(GameFontHighlight) + highestVersionWarning:SetFullWidth(true) + highestVersionWarning:SetText(L["This aura was created with a newer version of WeakAuras.\nIt might not work correctly with your version!"]) + highestVersionWarning:SetColor(1, 0, 0) + self:AddChild(highestVersionWarning) + end + + + local currentBuild = floor(WeakAuras.BuildInfo / 10000) + local importBuild = data.tocversion and floor(data.tocversion / 10000) + + if importBuild and currentBuild ~= importBuild then + local flavorWarning = AceGUI:Create("Label") + flavorWarning:SetFontObject(GameFontHighlight) + flavorWarning:SetFullWidth(true) + flavorWarning:SetText(L["This aura was created with a different version (%s) of World of Warcraft.\nIt might not work correctly!"]:format(OptionsPrivate.Private.TocToExpansion[importBuild])) + flavorWarning:SetColor(1, 0, 0) + self:AddChild(flavorWarning) + end + + if (#scamCheckResult > 0) then + self.viewCodeButton:Show() + else + self.viewCodeButton:Hide() + end + + self:DoLayout() + end, + CreateUpdateArea = function(self, area, matchInfo) + area:AddChild(AceGUI:Create("WeakAurasSpacer")) + local categoryHeader = AceGUI:Create("Label") + categoryHeader:SetText(L["Categories to Update"]) + categoryHeader:SetFontObject(GameFontNormalHuge) + categoryHeader:SetFullWidth(true) + area:AddChild(categoryHeader) + + self.userChoices.activeCategories = {} + for index, category in pairs(OptionsPrivate.Private.update_categories) do + local name = category.name + if matchInfo.activeCategories[name] then + local button = AceGUI:Create("CheckBox") + button:SetLabel(category.label) + button:SetFullWidth(true) + button:SetValue(category.default) + area:AddChild(button) + + self.userChoices.activeCategories[name] = category.default + + button:SetCallback("OnValueChanged", function(_, _, value) + self.userChoices.activeCategories[name] = value + end) + + end + end + + area:DoLayout() + end, + SelectMode = function(self, mode) + if self.userChoices.mode == mode then + return + end + self.userChoices.mode = mode + if mode == "update" then + self.importButton:SetText(L["Update"]) + self.updateRadioButton:SetValue(true) + self.importCopyRadioButton:SetValue(false) + self:CreateUpdateArea(self.updateUiArea, self.matchInfo) + elseif mode == "import" then + self.importButton:SetText(L["Import as Copy"]) + self.updateRadioButton:SetValue(false) + self.importCopyRadioButton:SetValue(true) + self.updateUiArea:ReleaseChildren() + end + end, + Import = function(self) + OptionsPrivate.Private.dynFrame:AddAction("import", coroutine.create(function() + self:ImportImpl() + end)) + end, + ImportImpl = function(self) + local pendingData = self.pendingData + local userChoices = self.userChoices + local matchInfo = self.matchInfo + + self.importButton:Disable() + self.closeButton:Disable() + self.viewCodeButton:Disable() + OptionsPrivate.Private.SetImporting(true) + + -- Adjust UI + self:ReleaseChildren() + self:AddBasicInformationWidgets(pendingData.data, pendingData.sender) + self:AddProgressWidgets() + + local pendingPickData + + if userChoices.mode == "import" then + self:InitializeProgress(2 * (#pendingData.children + 1)) + + EnsureUniqueUid(pendingData.data) + for i, child in ipairs(pendingData.children) do + EnsureUniqueUid(child) + end + + local uidMap = BuildUidMap(pendingData.data, pendingData.children, "new") + + local phase2Order = {} + self:ImportPhase1(uidMap, uidMap:GetRootUID(), phase2Order) + self:ImportPhase2(uidMap, phase2Order) + + pendingPickData = { + id = uidMap:GetIdFor(uidMap:GetRootUID()) + } + if #pendingData.children > 0 then + pendingPickData.tabToShow = "group" + end + + OptionsPrivate.SortDisplayButtons() + elseif userChoices.mode == "update" then + local onePhaseProgress = matchInfo.oldUidMap:GetTotalCount() + matchInfo.newUidMap:GetTotalCount() + local IncProgress = function() self:IncProgress() end + + -- The progress is more for appereance than anything resembling real calculation + -- The estimate for the total work is wonky, as is how the code compensates for that + -- But then again, lying progress bar is a industry standard pratice + self:InitializeProgress(onePhaseProgress * 26) + -- The uids of unmatched auras, might already be in use already, assign unique uids then + -- This can happen if e.g. the user imports a group with a aura "A", but moves the aura out of the group + -- On update, we won't match A_new to A_old, because A_old is outside the matched parent group + -- Thus on import A_new needs to get its own uid + -- On next import, the auras uids won't match either, there's not much we can do about that. + matchInfo.newUidMap:EnsureUniqueIdOfUnmatched(nil, IncProgress) + self:SetMinimumProgress(1 * onePhaseProgress) + coroutine.yield() + + if userChoices.activeCategories.oldchildren then + self:RemoveUnmatchedOld(matchInfo.oldUidMap, matchInfo.oldUidMap:GetRootUID()) + end + + self:SetMinimumProgress(2 * onePhaseProgress) + + if not userChoices.activeCategories.newchildren then + self:RemoveUnmatchedNew(matchInfo.newUidMap, matchInfo.newUidMap:GetRootUID()) + end + self:SetMinimumProgress(3 * onePhaseProgress) + + local targetNames = {} + + local structureUidMap -- We iterate either over new or old, depending on the mode + local GetPhase1Data -- Getting the right data is a bit tricky, and depends on the mode + local GetPhase2Data + if userChoices.activeCategories.arrangement then + -- new arragement + structureUidMap = matchInfo.newUidMap + if not userChoices.activeCategories.oldchildren then + -- Keep old children + matchInfo.newUidMap:InsertUnmatchedFrom(matchInfo.oldUidMap, IncProgress) + end + + self:SetMinimumProgress(4 * onePhaseProgress) + + -- This ensures that we use unique (for new uids) or the same id (for existing uids) for the initial add + -- There's another renaming after everything has been added + self:FixUpNames(matchInfo.newUidMap) + self:SetMinimumProgress(5 * onePhaseProgress) + + local useOldNames = not userChoices.activeCategories.name + self:GatherTargetNames(matchInfo.newUidMap, matchInfo.oldUidMap, useOldNames, targetNames) + self:SetMinimumProgress(6 * onePhaseProgress) + + GetPhase1Data = function(uid) + local matchedUid = matchInfo.newUidMap:GetUIDMatch(uid) + if matchedUid then + local data = matchInfo.oldUidMap:GetPhase1Data(matchedUid, true, userChoices.activeCategories) + data.uid = uid + data.id = matchInfo.newUidMap:GetIdFor(uid) + return data + else + return matchInfo.newUidMap:GetPhase1Data(uid) + end + end + GetPhase2Data = function(uid) + local matchedUid = matchInfo.newUidMap:GetUIDMatch(uid) + if matchedUid then + -- We want a combination of the old data updated via the diff and + -- the new structure. + local oldData = matchInfo.oldUidMap:GetPhase2Data(matchedUid, true, userChoices.activeCategories) + local newData = matchInfo.newUidMap:GetPhase2Data(uid) + oldData.controlledChildren = newData.controlledChildren + oldData.parent = newData.parent + oldData.sortHybridTable = newData.sortHybridTable + oldData.uid = uid + oldData.id = matchInfo.newUidMap:GetIdFor(uid) + return oldData + else + return matchInfo.newUidMap:GetPhase2Data(uid) + end + end + else + -- old arrangement + structureUidMap = matchInfo.oldUidMap + if userChoices.activeCategories.newchildren then + -- Add new children + matchInfo.oldUidMap:InsertUnmatchedFrom(matchInfo.newUidMap, IncProgress) + end + self:SetMinimumProgress(4 * onePhaseProgress) + + self:FixUpNames(matchInfo.oldUidMap) + self:SetMinimumProgress(5 * onePhaseProgress) + + local useNewNames = userChoices.activeCategories.name + self:GatherTargetNames(matchInfo.oldUidMap, matchInfo.newUidMap, useNewNames, targetNames) + self:SetMinimumProgress(6 * onePhaseProgress) + + GetPhase1Data = function(uid) + return matchInfo.oldUidMap:GetPhase1Data(uid, true, userChoices.activeCategories) + end + GetPhase2Data = function(uid) + return matchInfo.oldUidMap:GetPhase2Data(uid, true, userChoices.activeCategories) + end + end + + local phase2Order = {} + self:UpdatePhase1(structureUidMap, structureUidMap:GetRootUID(), GetPhase1Data, phase2Order) + self:SetMinimumProgress(16 * onePhaseProgress) + + self:UpdatePhase2(structureUidMap, GetPhase2Data, phase2Order) + self:SetMinimumProgress(26 * onePhaseProgress) + while(self:RenameAuras(targetNames)) do + -- Try renaming again and again... + end + self:SetMaxProgress() + coroutine.yield() + + pendingPickData = { + id = OptionsPrivate.Private.GetDataByUID(matchInfo.oldUidMap:GetRootUID()).id + } + if matchInfo.oldUidMap:GetGroupRegionType(matchInfo.oldUidMap:GetRootUID()) then + pendingPickData.tabToShow = "group" + end + + OptionsPrivate.SortDisplayButtons() + end + + OptionsPrivate.Private.SetImporting(false) + self.viewCodeButton:Enable() + self.importButton:Enable() + self.closeButton:Enable() + OptionsPrivate.Private.callbacks:Fire("Import") + + self:Close() + + + if pendingPickData then + OptionsPrivate.ClearPicks() + WeakAuras.PickDisplay(pendingPickData.id, pendingPickData.tabToShow) + end + end, + -- This ensures that the id that we are adding is either + -- same for existing uids + -- or unique for non-existing uids + -- Note: There's a final renaming via WeakAuras.Rename at the end of the update process + FixUpNames = function(self, uidMap, uid) + uid = uid or uidMap:GetRootUID() + local existingData = OptionsPrivate.Private.GetDataByUID(uid) + if existingData then + if uidMap:GetIdFor(uid) ~= existingData.id then + end + uidMap:ChangeId(uid, existingData.id) + else + if WeakAuras.GetData(uidMap:GetIdFor(uid)) then + local newId = WeakAuras.FindUnusedId(uidMap:GetIdFor(uid)) + uidMap:ChangeId(uid, newId) + end + end + self:IncProgress() + coroutine.yield() + local children = uidMap:GetChildren(uid) + for _, childUid in ipairs(children) do + self:FixUpNames(uidMap, childUid) + end + end, + GatherTargetNames = function(self, structureUidMap, otherUidMap, useOtherUidMapNames, targetNames, uid) + uid = uid or structureUidMap:GetRootUID() + + if useOtherUidMapNames then + local matchedUid = structureUidMap:GetUIDMatch(uid) + if matchedUid then + targetNames[uid] = otherUidMap:GetOriginalName(matchedUid) + else + targetNames[uid] = structureUidMap:GetOriginalName(uid) + end + else + targetNames[uid] = structureUidMap:GetOriginalName(uid) + end + + self:IncProgress() + coroutine.yield() + local children = structureUidMap:GetChildren(uid) + for _, childUid in ipairs(children) do + self:GatherTargetNames(structureUidMap, otherUidMap, useOtherUidMapNames, targetNames, childUid) + end + end, + RenameAuras = function(self, targetNames) + local changed = false + for uid, targetName in pairs(targetNames) do + local aura = WeakAuras.GetData(targetName) + if not aura then + -- No squatter, so just take the name + local data = OptionsPrivate.Private.GetDataByUID(uid) + WeakAuras.Rename(data, targetName) + targetNames[uid] = nil + changed = true + self:IncProgress() + coroutine.yield() + elseif aura.uid == uid then + -- Already the correct name + targetNames[uid] = nil + else + -- Somebody else is squatting the name, rename us with a suffix, + -- so maybe a different aura can take our name + + local data = OptionsPrivate.Private.GetDataByUID(uid) + if string.sub(data.id, 1, #targetName) == targetName then + -- Our name is already prefixed with targetName, don't try to improve + else + local newId = WeakAuras.FindUnusedId(targetName) + local oldid = data.id + WeakAuras.Rename(data, newId) + if targetName[aura.uid] then -- We can hope that the aura the squatter renames itself, so try again + changed = true + end + self:IncProgress() + coroutine.yield() + end + end + end + coroutine.yield() + return changed + end, + RemoveUnmatchedOld = function(self, uidMap, uid) + if uidMap:GetType() ~= "old" then + error("Wrong map for delete") + end + + local children = uidMap:GetChildren(uid) + local removedAllChildren = true + for index, childUid in ipairs_reverse(children) do + local removed = self:RemoveUnmatchedOld(uidMap, childUid) + if not removed then + removedAllChildren = false + end + end + + local matchedUid = uidMap:GetUIDMatch(uid) + if not matchedUid and removedAllChildren then + if uidMap:GetRootUID() == uid then + error("Can't remove root") + end + + local data = OptionsPrivate.Private.GetDataByUID(uid) + if not data then + error("Can't find data") + end + WeakAuras.Delete(data) + uidMap:Remove(uid) + self:IncProgress() + coroutine.yield() + return true + end + self:IncProgress() + coroutine.yield() + return false + end, + RemoveUnmatchedNew = function(self, uidMap, uid) + if uidMap:GetType() ~= "new" then + error("Wrong map for delete") + end + + local children = uidMap:GetChildren(uid) + local removedAllChildren = true + for index, childUid in ipairs_reverse(children) do + local removed = self:RemoveUnmatchedNew(uidMap, childUid) + if not removed then + removedAllChildren = false + end + end + + local matchedUid = uidMap:GetUIDMatch(uid) + if not matchedUid and removedAllChildren then + if uidMap:GetRootUID() == uid then + error("Can't remove root") + end + + uidMap:Remove(uid) + self:IncProgress() + coroutine.yield() + return true + end + self:IncProgress() + coroutine.yield() + return false + end, + UpdatePhase1 = function(self, structureUidMap, uid, GetPhase1Data, phase2Order) + local matched = structureUidMap:GetUIDMatch(uid) + + tinsert(phase2Order, uid) + local data = GetPhase1Data(uid) + data.preferToUpdate = true + data.authorMode = nil + + WeakAuras.Add(data) + WeakAuras.NewDisplayButton(data, true) + self:IncProgress10() + coroutine.yield() + + local children = structureUidMap:GetChildren(uid) + local parentIsDynamicGroup = data.regionType == "dynamicgroup" + for index, childUid in ipairs(children) do + self:UpdatePhase1(structureUidMap, childUid, GetPhase1Data, phase2Order) + structureUidMap:SetGroupOrder(childUid, index, #children) + structureUidMap:SetParentIsDynamicGroup(childUid, parentIsDynamicGroup) + end + end, + UpdatePhase2 = function(self, structureUidMap, GetPhase2Data, phase2Order) + for i = #phase2Order, 1, -1 do + local uid = phase2Order[i] + local data = GetPhase2Data(uid) + data.preferToUpdate = true + data.authorMode = nil + WeakAuras.Add(data) + OptionsPrivate.Private.SetHistory(data.uid, data, "import") + local button = WeakAuras.GetDisplayButton(data.id) + button:SetData(data) + if (data.parent) then + local parentIsDynamicGroup = structureUidMap:GetParentIsDynamicGroup(uid) + local index, total = structureUidMap:GetGroupOrder(uid) + button:SetGroup(data.parent, parentIsDynamicGroup) + button:SetGroupOrder(index, total) + else + button:SetGroup() + button:SetGroupOrder(nil, nil) + end + button.callbacks.UpdateExpandButton() + WeakAuras.UpdateGroupOrders(data) + WeakAuras.UpdateThumbnail(data) + WeakAuras.ClearAndUpdateOptions(data.id) + self:IncProgress10() + coroutine.yield() + end + + -- Since we add from the leafs to the top, we need to correct the offset last + for i = #phase2Order, 1, -1 do + local uid = phase2Order[i] + local data = OptionsPrivate.Private.GetDataByUID(uid) + local displayButton = WeakAuras.GetDisplayButton(data.id) + displayButton:UpdateOffset() + end + end, + ImportPhase1 = function(self, uidMap, uid, phase2Order) + tinsert(phase2Order, uid) + local data = uidMap:GetPhase1Data(uid) + local newId = WeakAuras.FindUnusedId(data.id) + uidMap:ChangeId(uid, newId) + + data.preferToUpdate = false + data.authorMode = nil + data.id = newId + + WeakAuras.Add(data) + WeakAuras.NewDisplayButton(data, true) + + self:IncProgress() + coroutine.yield() + + local children = uidMap:GetChildren(uid) + local totalChildren = #children + local parentIsDynamicGroup = data.regionType == "dynamicgroup" + for index, childUid in ipairs(children) do + self:ImportPhase1(uidMap, childUid, phase2Order) + uidMap:SetGroupOrder(childUid, index, totalChildren) + uidMap:SetParentIsDynamicGroup(childUid, parentIsDynamicGroup) + end + end, + ImportPhase2 = function(self, uidMap, phase2Order) + for i = #phase2Order, 1, -1 do + local uid = phase2Order[i] + local data = uidMap:GetPhase2Data(uid) + data.preferToUpdate = false + data.authorMode = nil + WeakAuras.Add(data) + OptionsPrivate.Private.SetHistory(data.uid, data, "import") + + local button = WeakAuras.GetDisplayButton(data.id) + button:SetData(data) + if (data.parent) then + local parentIsDynamicGroup = uidMap:GetParentIsDynamicGroup(uid) + local index, total = uidMap:GetGroupOrder(uid) + button:SetGroup(data.parent, parentIsDynamicGroup) + button:SetGroupOrder(index, total) + else + button:SetGroup() + button:SetGroupOrder(nil, nil) + end + button.callbacks.UpdateExpandButton() + WeakAuras.UpdateGroupOrders(data) + WeakAuras.UpdateThumbnail(data) + WeakAuras.ClearAndUpdateOptions(data.id) + self:IncProgress() + coroutine.yield() + end + + for i = #phase2Order, 1, -1 do + local uid = phase2Order[i] + local data = OptionsPrivate.Private.GetDataByUID(uid) + local displayButton = WeakAuras.GetDisplayButton(data.id) + displayButton:UpdateOffset() + end + + end, + InitializeProgress = function(self, total) + self.progress = 0 + self.total = total + self.minProgress = nil + self.progressBar:SetProgress(self.progress, self.total) + end, + IncProgress = function(self) + if self.minProgress and self.progress + 10 < self.minProgress then + self.progress = self.progress + 1 + floor((self.minProgress - self.progress + 1) / 10) + else + self.progress = self.progress + 1 + end + self.progressBar:SetProgress(self.progress, self.total) + end, + IncProgress10 = function(self) + if self.minProgress and self.progress + 10 < self.minProgress then + self.progress = self.progress + 10 + floor((self.minProgress - self.progress + 10) / 10) + else + self.progress = self.progress + 10 + end + self.progressBar:SetProgress(self.progress, self.total) + end, + SetMinimumProgress = function(self, minProgress) + self.minProgress = minProgress + end, + SetMaxProgress = function(self) + self.progress = self.total + self.progressBar:SetProgress(self.progress, self.total) + end, + Close = function(self) + self.optionsWindow.window = "default"; + self.optionsWindow:UpdateFrameVisible() + end, + AddBasicInformationWidgets = function(self, data, sender) + local title = AceGUI:Create("Label") + title:SetFontObject(GameFontNormalHuge) + title:SetFullWidth(true) + title:SetText(L["Importing %s"]:format(data.id)) + self:AddChild(title) + + local description = AceGUI:Create("Label") + description:SetFontObject(GameFontHighlight) + description:SetFullWidth(true) + description:SetText(data.desc or "") + self:AddChild(description) + + if data.url and data.url ~= "" then + local url = AceGUI:Create("Label") + url:SetFontObject(GameFontHighlight) + url:SetFullWidth(true) + url:SetText(L["Url: %s"]:format(data.url)) + self:AddChild(url) + end + + if data.semver or data.version then + local version = AceGUI:Create("Label") + version:SetFontObject(GameFontHighlight) + version:SetFullWidth(true) + version:SetText(L["Version: %s"]:format(data.semver or data.version)) + self:AddChild(version) + end + + if sender then + local senderLabel = AceGUI:Create("Label") + senderLabel:SetFontObject(GameFontHighlight) + senderLabel:SetFullWidth(true) + senderLabel:SetText(L["Aura received from: %s"]:format(sender)) + self:AddChild(senderLabel) + end + end, + AddProgressWidgets = function(self) + local title = AceGUI:Create("Label") + title:SetFontObject(GameFontNormalHuge) + title:SetFullWidth(true) + title:SetText(L["Importing...."]) + self:AddChild(title) + + local progress = AceGUI:Create("WeakAurasProgressBar") + self.progressBar = progress + self:AddChild(progress) + end +} + +local updateFrame +local function ConstructUpdateFrame(frame) + local group = AceGUI:Create("ScrollFrame"); + group.frame:SetParent(frame); + group.frame:SetPoint("TOPLEFT", frame, "TOPLEFT", 16, -16); + group.frame:SetPoint("BOTTOMRIGHT", frame, "BOTTOMRIGHT", -16, 46); + group.frame:Hide(); + group:SetLayout("flow"); + group.optionsWindow = frame + + + -- Action buttons + local viewCodeButton = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); + viewCodeButton:SetScript("OnClick", function() OptionsPrivate.OpenCodeReview(group.scamCheckResult) end); + viewCodeButton:SetPoint("BOTTOMLEFT", 20, -24); + viewCodeButton:SetFrameLevel(viewCodeButton:GetFrameLevel() + 1) + viewCodeButton:SetHeight(20); + viewCodeButton:SetWidth(160); + viewCodeButton:SetText(L["View custom code"]) + + local importButton = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); + importButton:SetScript("OnClick", function() group:Import() end); + importButton:SetPoint("BOTTOMRIGHT", -190, -24); + importButton:SetFrameLevel(importButton:GetFrameLevel() + 1) + importButton:SetHeight(20); + importButton:SetWidth(160); + importButton:SetText(L["Import"]) + + local closeButton = CreateFrame("Button", nil, group.frame, "UIPanelButtonTemplate"); + closeButton:SetScript("OnClick", function() group:Close() end); + closeButton:SetPoint("BOTTOMRIGHT", -20, -24); + closeButton:SetFrameLevel(closeButton:GetFrameLevel() + 1) + closeButton:SetHeight(20); + closeButton:SetWidth(160); + closeButton:SetText(L["Close"]) + + group.viewCodeButton = viewCodeButton + group.importButton = importButton + group.closeButton = closeButton + + for name, method in pairs(methods) do + group[name] = method + end + + return group +end + +function OptionsPrivate.UpdateFrame(frame) + updateFrame = updateFrame or ConstructUpdateFrame(frame) + return updateFrame +end diff --git a/WeakAurasOptions/RegionOptions/AuraBar.lua b/WeakAurasOptions/RegionOptions/AuraBar.lua index bf411d4..e17aa6e 100644 --- a/WeakAurasOptions/RegionOptions/AuraBar.lua +++ b/WeakAurasOptions/RegionOptions/AuraBar.lua @@ -178,12 +178,8 @@ local function createOptions(id, data) func = function() local path = {"displayIcon"} local paths = {} - if data.controlledChildren then - for i, childId in pairs(data.controlledChildren) do - paths[childId] = path - end - else - paths[data.id] = path + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + paths[child.id] = path end OptionsPrivate.OpenIconPicker(data, paths) end, @@ -393,12 +389,32 @@ local function createOptions(id, data) } local index = 0.01 for id, display in ipairs(overlayInfo) do + options["overlaytexture" .. id] = { + type = "select", + dialogControl = "LSM30_Statusbar", + width = WeakAuras.doubleWidth, + name = string.format(L["%s Texture"], display), + values = AceGUIWidgetLSMlists.statusbar, + order = 58.1 + index, + set = function(info, texture) + if (not data.overlaysTexture) then + data.overlaysTexture = {}; + end + data.overlaysTexture[id] = texture; + WeakAuras.Add(data); + end, + get = function() + if data.overlaysTexture and data.overlaysTexture[id] then + return data.overlaysTexture[id] + end + end + } options["overlaycolor" .. id] = { type = "color", width = WeakAuras.normalWidth, name = string.format(L["%s Color"], display), hasAlpha = true, - order = 58 + index, + order = 58.2 + index, get = function() if (data.overlays and data.overlays[id]) then return unpack(data.overlays[id]); @@ -420,7 +436,7 @@ local function createOptions(id, data) type = "toggle", width = WeakAuras.normalWidth, name = L["Clip Overlays"], - order = 58 + index; + order = 58.3 + index; } end @@ -770,27 +786,5 @@ local function GetAnchors(data) return anchorPoints; end -local function subCreateOptions(parentData, data, index, subIndex) - local order = 9 - local options = { - __title = L["Foreground"], - __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "aurabar_bar")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "aurabar_bar")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __notcollapsable = true - } - return options -end - -- Register new region type options with WeakAuras WeakAuras.RegisterRegionOptions("aurabar", createOptions, createIcon, L["Progress Bar"], createThumbnail, modifyThumbnail, L["Shows a progress bar with name, timer, and icon"], templates, GetAnchors); - -WeakAuras.RegisterSubRegionOptions("aurabar_bar", subCreateOptions, L["Foreground"]); diff --git a/WeakAurasOptions/RegionOptions/DynamicGroup.lua b/WeakAurasOptions/RegionOptions/DynamicGroup.lua index 300dba1..9a37cee 100644 --- a/WeakAurasOptions/RegionOptions/DynamicGroup.lua +++ b/WeakAurasOptions/RegionOptions/DynamicGroup.lua @@ -134,12 +134,7 @@ local function createOptions(id, data) order = 1.5, width = WeakAuras.normalWidth, name = L["Group by Frame"], - desc = L[ -[[Group and anchor each auras by frame. - -- Unit Frames: attach to unit frame buttons per unit. -- Custom Frames: choose which frame each region should be anchored to.]] - ], + desc = L["Group and anchor each auras by frame.\n\n- Unit Frames: attach to unit frame buttons per unit.\n- Custom Frames: choose which frame each region should be anchored to."], hidden = function() return data.grow == "CUSTOM" end, }, anchorPerUnit = { diff --git a/WeakAurasOptions/RegionOptions/Group.lua b/WeakAurasOptions/RegionOptions/Group.lua index 6de9b79..0735854 100644 --- a/WeakAurasOptions/RegionOptions/Group.lua +++ b/WeakAurasOptions/RegionOptions/Group.lua @@ -54,40 +54,8 @@ local function getWidth(data, region) end end --- Create region options table -local function createOptions(id, data) - -- Region options - local options = { - __title = L["Group Settings"], - __order = 1, - groupIcon = { - type = "input", - width = WeakAuras.doubleWidth - 0.15, - name = L["Group Icon"], - desc = L["Set Thumbnail Icon"], - order = 0.50, - get = function() - return data.groupIcon and tostring(data.groupIcon) or "" - end, - set = function(info, v) - data.groupIcon = v - WeakAuras.Add(data) - WeakAuras.UpdateThumbnail(data) - end - }, - chooseIcon = { - type = "execute", - width = 0.15, - name = L["Choose"], - order = 0.51, - func = function() - OptionsPrivate.OpenIconPicker(data, { [data.id] = {"groupIcon"} }, true) - end, - imageWidth = 24, - imageHeight = 24, - control = "WeakAurasIcon", - image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", - }, +local function createDistributeAlignOptions(id, data) + return { align_h = { type = "select", width = WeakAuras.normalWidth, @@ -551,7 +519,45 @@ local function createOptions(id, data) WeakAuras.Add(data); OptionsPrivate.ResetMoverSizer(); end + } + } +end + +-- Create region options table +local function createOptions(id, data) + -- Region options + local options = { + __title = L["Group Settings"], + __order = 1, + groupIcon = { + type = "input", + width = WeakAuras.doubleWidth - 0.15, + name = L["Group Icon"], + desc = L["Set Thumbnail Icon"], + order = 0.50, + get = function() + return data.groupIcon and tostring(data.groupIcon) or "" + end, + set = function(info, v) + data.groupIcon = v + WeakAuras.Add(data) + WeakAuras.UpdateThumbnail(data) + end }, + chooseIcon = { + type = "execute", + width = 0.15, + name = L["Choose"], + order = 0.51, + func = function() + OptionsPrivate.OpenIconPicker(data, { [data.id] = {"groupIcon"} }, true) + end, + imageWidth = 24, + imageHeight = 24, + control = "WeakAurasIcon", + image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", + }, + -- Alignment/Distribute options are added below scale = { type = "range", width = WeakAuras.normalWidth, @@ -581,8 +587,33 @@ local function createOptions(id, data) }, }; - for k, v in pairs(OptionsPrivate.commonOptions.BorderOptions(id, data, nil, nil, 70)) do - options[k] = v + local hasSubGroups = false + local hasDynamicSubGroup = false + for index, childId in pairs(data.controlledChildren) do + local childData = WeakAuras.GetData(childId); + if childData.controlledChildren then + hasSubGroups = true + end + if childData.regionType == "dynamicgroup" then + hasDynamicSubGroup = true + end + + if hasSubGroups and hasDynamicSubGroup then + break + end + end + + + if not hasSubGroups then + for k, v in pairs(createDistributeAlignOptions(id, data)) do + options[k] = v + end + end + + if not hasDynamicSubGroup then + for k, v in pairs(OptionsPrivate.commonOptions.BorderOptions(id, data, nil, nil, 70)) do + options[k] = v + end end return { diff --git a/WeakAurasOptions/RegionOptions/Icon.lua b/WeakAurasOptions/RegionOptions/Icon.lua index e62c59b..b05c7a6 100644 --- a/WeakAurasOptions/RegionOptions/Icon.lua +++ b/WeakAurasOptions/RegionOptions/Icon.lua @@ -55,12 +55,8 @@ local function createOptions(id, data) func = function() local path = {"displayIcon"} local paths = {} - if data.controlledChildren then - for i, childId in pairs(data.controlledChildren) do - paths[childId] = path - end - else - paths[data.id] = path + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + paths[child.id] = path end OptionsPrivate.OpenIconPicker(data, paths) end, @@ -188,13 +184,14 @@ local function createOptions(id, data) cooldownHeader = { type = "header", order = 11, - name = L["Cooldown Settings"], + name = L["Swipe Overlay Settings"], }, cooldown = { type = "toggle", width = WeakAuras.normalWidth, - name = L["Show Cooldown"], + name = L["Enable Swipe"], order = 11.1, + desc = L["Enable the \"Swipe\" radial overlay"], disabled = function() return not OptionsPrivate.Private.CanHaveDuration(data); end, get = function() return OptionsPrivate.Private.CanHaveDuration(data) and data.cooldown; end }, @@ -203,6 +200,7 @@ local function createOptions(id, data) width = WeakAuras.normalWidth, name = L["Inverse"], order = 11.2, + desc = L["Invert the direction of progress"], disabled = function() return not (OptionsPrivate.Private.CanHaveDuration(data) and data.cooldown); end, get = function() return data.inverse and OptionsPrivate.Private.CanHaveDuration(data) and data.cooldown; end, hidden = function() return not data.cooldown end @@ -210,8 +208,9 @@ local function createOptions(id, data) cooldownEdge = { type = "toggle", width = WeakAuras.normalWidth, - name = L["Cooldown Edge"], - order = 11.3, + name = L["Show \"Edge\""], + order = 11.4, + desc = "|TInterface\\AddOns\\WeakAuras\\Media\\Textures\\edge-example:30|t\n"..L["Enable \"Edge\" part of the overlay"], disabled = function() return not OptionsPrivate.Private.CanHaveDuration(data) end, hidden = function() return not data.cooldown end, }, diff --git a/WeakAurasOptions/RegionOptions/Model.lua b/WeakAurasOptions/RegionOptions/Model.lua index 487fb19..ac33080 100644 --- a/WeakAurasOptions/RegionOptions/Model.lua +++ b/WeakAurasOptions/RegionOptions/Model.lua @@ -42,7 +42,7 @@ local function createOptions(id, data) width = WeakAuras.normalWidth, name = L["Animation Sequence"], min = 0, - max = 150, + softMax = 150, step = 1, bigStep = 1, order = 6, @@ -106,7 +106,7 @@ local function createOptions(id, data) else options.model_path = { type = "input", - width = WeakAuras.doubleWidth, + width = WeakAuras.doubleWidth - 0.15, name = L["Model"], order = 1 } diff --git a/WeakAurasOptions/RegionOptions/ProgressTexture.lua b/WeakAurasOptions/RegionOptions/ProgressTexture.lua index 6577c7e..58db043 100644 --- a/WeakAurasOptions/RegionOptions/ProgressTexture.lua +++ b/WeakAurasOptions/RegionOptions/ProgressTexture.lua @@ -189,12 +189,6 @@ local function createOptions(id, data) data.crop_x = v; WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); - if(data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData) then - WeakAuras.Add(parentData); - end - end OptionsPrivate.ResetMoverSizer(); end, }, @@ -212,12 +206,6 @@ local function createOptions(id, data) data.crop_y = v; WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); - if(data.parent) then - local parentData = WeakAuras.GetData(data.parent); - if(parentData) then - WeakAuras.Add(parentData); - end - end OptionsPrivate.ResetMoverSizer(); end, }, @@ -240,18 +228,25 @@ local function createOptions(id, data) bigStep = 0.01, isPercent = true }, + smoothProgress = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Smooth Progress"], + desc = L["Animates progress changes"], + order = 55.1 + }, slanted = { type = "toggle", width = WeakAuras.normalWidth, name = L["Slanted"], - order = 55.2, + order = 55.3, hidden = function() return data.orientation == "CLOCKWISE" or data.orientation == "ANTICLOCKWISE"; end }, slant = { type = "range", width = WeakAuras.normalWidth, name = L["Slant Amount"], - order = 55.3, + order = 55.4, min = 0, max = 1, bigStep = 0.1, @@ -261,14 +256,14 @@ local function createOptions(id, data) type = "toggle", width = WeakAuras.normalWidth, name = L["Inverse Slant"], - order = 55.4, + order = 55.5, hidden = function() return not data.slanted or data.orientation == "CLOCKWISE" or data.orientation == "ANTICLOCKWISE" end }, slantMode = { type = "select", width = WeakAuras.normalWidth, name = L["Slant Mode"], - order = 55.5, + order = 55.6, hidden = function() return not data.slanted or data.orientation == "CLOCKWISE" or data.orientation == "ANTICLOCKWISE" end, values = OptionsPrivate.Private.slant_mode }, @@ -413,11 +408,11 @@ local function createThumbnail() local background = region:CreateTexture(nil, "BACKGROUND"); borderframe.background = background; - local foreground = region:CreateTexture(nil, "ART"); + local foreground = region:CreateTexture(nil, "ARTWORK"); borderframe.foreground = foreground; - borderframe.foregroundSpinner = WeakAuras.createSpinner(region, "ARTWORK", region:GetFrameLevel() + 2); borderframe.backgroundSpinner = WeakAuras.createSpinner(region, "BACKGROUND", region:GetFrameLevel() + 1); + borderframe.foregroundSpinner = WeakAuras.createSpinner(region, "ARTWORK", region:GetFrameLevel() + 2); return borderframe; end @@ -792,8 +787,4 @@ local templates = { }, } -local function GetAnchors(data) - return OptionsPrivate.Private.default_types_for_anchor -end - -WeakAuras.RegisterRegionOptions("progresstexture", createOptions, createIcon, L["Progress Texture"], createThumbnail, modifyThumbnail, L["Shows a texture that changes based on duration"], templates, GetAnchors); +WeakAuras.RegisterRegionOptions("progresstexture", createOptions, createIcon, L["Progress Texture"], createThumbnail, modifyThumbnail, L["Shows a texture that changes based on duration"], templates); diff --git a/WeakAurasOptions/RegionOptions/StopMotion.lua b/WeakAurasOptions/RegionOptions/StopMotion.lua index a4dd752..785867c 100644 --- a/WeakAurasOptions/RegionOptions/StopMotion.lua +++ b/WeakAurasOptions/RegionOptions/StopMotion.lua @@ -8,34 +8,74 @@ local setTile = WeakAuras.setTile; local function setTextureFunc(textureWidget, texturePath, textureName) local data = texture_data[texturePath]; + if not(data) then + local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]" + local pattern2 = "%.x(%d+)y(%d+)f(%d+)w(%d+)h(%d+)W(%d+)H(%d+)%.[tb][gl][ap]" + local rows, columns, frames = texturePath:lower():match(pattern) + if rows then + data = { + count = tonumber(frames), + rows = tonumber(rows), + columns = tonumber(columns) + } + else + local rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight = texturePath:match(pattern2) + if rows then + rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight = tonumber(rows), tonumber(columns), tonumber(frames), tonumber(frameWidth), tonumber(frameHeight), tonumber(fileWidth), tonumber(fileHeight) + local frameScaleW = 1 + local frameScaleH = 1 + if fileWidth > 0 and frameWidth > 0 then + frameScaleW = (frameWidth * columns) / fileWidth + end + if fileHeight > 0 and frameHeight > 0 then + frameScaleH = (frameHeight * rows) / fileHeight + end + data = { + count = frames, + rows = rows, + columns = columns, + frameScaleW = frameScaleW, + frameScaleH = frameScaleH + } + end + end + end textureWidget.frameNr = 0; if (data) then if (data.rows and data.columns) then -- Texture Atlas textureWidget:SetTexture(texturePath, textureName); - setTile(textureWidget, data.count, data.rows, data.columns); + setTile(textureWidget, data.count, data.rows, data.columns, data.frameScaleW or 1, data.frameScaleH or 1); - textureWidget:SetOnUpdate(function() - textureWidget.frameNr = textureWidget.frameNr + 1; - if (textureWidget.frameNr == data.count) then - textureWidget.frameNr = 1; + textureWidget:SetOnUpdate(function(self, elapsed) + self.elapsed = (self.elapsed or 0) + elapsed + if(self.elapsed > 0.1) then + self.elapsed = self.elapsed - 0.1; + textureWidget.frameNr = textureWidget.frameNr + 1; + if (textureWidget.frameNr == data.count) then + textureWidget.frameNr = 1; + end + setTile(textureWidget, textureWidget.frameNr, data.rows, data.columns, data.frameScaleW or 1, data.frameScaleH or 1); end - setTile(textureWidget, textureWidget.frameNr, data.rows, data.columns); - end); + end) else -- Numbered Textures local texture = texturePath .. format("%03d", texture_data[texturePath].count) textureWidget:SetTexture(texture, textureName) textureWidget:SetTexCoord(0, 1, 0, 1); - textureWidget:SetOnUpdate(function() - textureWidget.frameNr = textureWidget.frameNr + 1; - if (textureWidget.frameNr == data.count) then - textureWidget.frameNr = 1; + textureWidget:SetOnUpdate(function(self, elapsed) + self.elapsed = (self.elapsed or 0) + elapsed + if(self.elapsed > 0.1) then + self.elapsed = self.elapsed - 0.1; + textureWidget.frameNr = textureWidget.frameNr + 1; + if (textureWidget.frameNr == data.count) then + textureWidget.frameNr = 1; + end + local texture = texturePath .. format("%03d", textureWidget.frameNr) + textureWidget:SetTexture(texture, textureName); end - local texture = texturePath .. format("%03d", textureWidget.frameNr) - textureWidget:SetTexture(texture, textureName); end); end else @@ -45,9 +85,16 @@ local function setTextureFunc(textureWidget, texturePath, textureName) end local function textureNameHasData(textureName) - local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]" - local rows, columns, frames = textureName:lower():match(pattern) - return rows and columns and frames + local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]$" + local pattern2 = "%.x(%d+)y(%d+)f(%d+)w(%d+)h(%d+)W(%d+)H(%d+)%.[tb][gl][ap]$" + local ok = textureName:lower():match(pattern) + if ok then return true end + local ok2 = textureName:match(pattern2) + if ok2 then + return true + else + return false + end end local function createOptions(id, data) @@ -56,7 +103,7 @@ local function createOptions(id, data) __order = 1, foregroundTexture = { type = "input", - width = WeakAuras.normalWidth - 0.15, + width = WeakAuras.doubleWidth - 0.15, name = L["Texture"], order = 1, }, @@ -79,169 +126,157 @@ local function createOptions(id, data) control = "WeakAurasIcon", image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", }, - backgroundTexture = { - type = "input", - width = WeakAuras.normalWidth - 0.15, - name = L["Background Texture"], - order = 5, - disabled = function() return data.sameTexture or data.hideBackground end, - get = function() return data.sameTexture and data.foregroundTexture or data.backgroundTexture; end, - }, - chooseBackgroundTexture = { - type = "execute", - width = 0.15, - name = L["Choose"], - order = 6, - func = function() - OptionsPrivate.OpenTexturePicker(data, {}, { - texture = "backgroundTexture", - color = "backgroundColor", - rotation = "rotation", - mirror = "mirror", - blendMode = "blendMode" - }, texture_types, setTextureFunc); - end, - disabled = function() return data.sameTexture or data.hideBackground; end, - imageWidth = 24, - imageHeight = 24, - control = "WeakAurasIcon", - image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", - }, - sameTextureSpace = { - type = "description", - width = WeakAuras.normalWidth, - name = "", - order = 13, - }, - hideBackground = { - type = "toggle", - name = L["Hide"], - order = 14, - width = WeakAuras.halfWidth, - }, - sameTexture = { - type = "toggle", - width = WeakAuras.halfWidth, - name = L["Same"], - order = 15, - disabled = function() return data.hideBackground; end + foregroundColor = { + type = "color", + width = WeakAuras.normalWidth, + name = L["Color"], + hasAlpha = true, + order = 3 }, desaturateForeground = { - type = "toggle", - width = WeakAuras.normalWidth, - name = L["Desaturate"], - order = 17.5, - }, - desaturateBackground = { - type = "toggle", - name = L["Desaturate"], - order = 17.6, - width = WeakAuras.normalWidth, - disabled = function() return data.hideBackground; end - }, - -- Foreground options for custom textures - customForegroundHeader = { - type = "header", - name = L["Custom Foreground"], - order = 17.70, - hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Desaturate"], + order = 3.5, }, customForegroundRows = { - type = "range", - width = WeakAuras.normalWidth, + type = "input", + width = WeakAuras.doubleWidth / 3, name = L["Rows"], - min = 1, - max = 64, - order = 17.71, + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customForegroundRows and tostring(data.customForegroundRows) or ""; + end, + set = function(info, v) + data.customForegroundRows = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 4, hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, customForegroundColumns = { - type = "range", - width = WeakAuras.normalWidth, + type = "input", + width = WeakAuras.doubleWidth / 3, name = L["Columns"], - min = 1, - max = 64, - order = 17.72, + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customForegroundColumns and tostring(data.customForegroundColumns) or ""; + end, + set = function(info, v) + data.customForegroundColumns = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 5, hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, customForegroundFrames = { - type = "range", - width = WeakAuras.normalWidth, + type = "input", + width = WeakAuras.doubleWidth / 3, name = L["Frame Count"], - min = 0, - max = 4096, - --bigStep = 0.01, - order = 17.73, + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customForegroundFrames and tostring(data.customForegroundFrames) or ""; + end, + set = function(info, v) + data.customForegroundFrames = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 6, hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, - customForegroundSpace = { - type = "execute", - width = WeakAuras.normalWidth, - name = "", - order = 17.74, - image = function() return "", 0, 0 end, - hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end + customForegroundFileWidth = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["File Width"], + desc = L["Must be a power of 2"], + validate = function(info, val) + if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then + return false; + end + return true + end, + get = function() + return data.customForegroundFileWidth and tostring(data.customForegroundFileWidth) or ""; + end, + set = function(info, v) + data.customForegroundFileWidth = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 7, + hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, - -- Background options for custom textures - customBackgroundHeader = { - type = "header", - name = L["Custom Background"], - order = 18.00, - hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) - or data.hideBackground end + customForegroundFileHeight = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["File Height"], + desc = L["Must be a power of 2"], + validate = function(info, val) + if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then + return false; + end + return true + end, + get = function() + return data.customForegroundFileHeight and tostring(data.customForegroundFileHeight) or ""; + end, + set = function(info, v) + data.customForegroundFileHeight = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 8, + hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, - customBackgroundRows = { - type = "range", - width = WeakAuras.normalWidth, - name = L["Rows"], - min = 1, - max = 64, - order = 18.01, - hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) - or data.hideBackground end + customForegroundFrameWidth = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["Frame Width"], + validate = WeakAuras.ValidateNumeric, + desc = L["Can set to 0 if Columns * Width equal File Width"], + get = function() + return data.customForegroundFrameWidth and tostring(data.customForegroundFrameWidth) or ""; + end, + set = function(info, v) + data.customForegroundFrameWidth = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 9, + hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, - customBackgroundColumns = { - type = "range", - width = WeakAuras.normalWidth, - name = L["Columns"], - min = 1, - max = 64, - order = 18.02, - hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) - or data.hideBackground end - }, - customBackgroundFrames = { - type = "range", - width = WeakAuras.normalWidth, - name = L["Frame Count"], - min = 0, - max = 4096, - step = 1, - order = 18.03, - hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) - or data.hideBackground end - }, - customBackgroundSpace = { - type = "execute", - width = WeakAuras.normalWidth, - name = "", - order = 18.04, - image = function() return "", 0, 0 end, - hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) - or data.hideBackground end + customForegroundFrameHeight = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["Frame Height"], + validate = WeakAuras.ValidateNumeric, + desc = L["Can set to 0 if Rows * Height equal File Height"], + get = function() + return data.customForegroundFrameHeight and tostring(data.customForegroundFrameHeight) or ""; + end, + set = function(info, v) + data.customForegroundFrameHeight = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 10, + hidden = function() return texture_data[data.foregroundTexture] or textureNameHasData(data.foregroundTexture) end }, blendMode = { type = "select", width = WeakAuras.normalWidth, name = L["Blend Mode"], - order = 20, + order = 11, values = OptionsPrivate.Private.blend_types }, animationType = { type = "select", width = WeakAuras.normalWidth, name = L["Animation Mode"], - order = 21, + order = 12, values = animation_types }, startPercent = { @@ -251,7 +286,7 @@ local function createOptions(id, data) min = 0, max = 1, --bigStep = 0.01, - order = 22, + order = 13, isPercent = true }, endPercent = { @@ -261,7 +296,7 @@ local function createOptions(id, data) min = 0, max = 1, --bigStep = 0.01, - order = 23, + order = 14, isPercent = true }, frameRate = { @@ -272,47 +307,225 @@ local function createOptions(id, data) max = 120, step = 1, bigStep = 3, - order = 24, + order = 15, disabled = function() return data.animationType == "progress" end; }, - backgroundPercent = { - type = "range", - width = WeakAuras.normalWidth, - name = L["Background"], - min = 0, - max = 1, - order = 25, - isPercent = true, - disabled = function() return data.hideBackground; end + inverse = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Inverse"], + order = 15.5 }, - foregroundColor = { - type = "color", - width = WeakAuras.normalWidth, - name = L["Foreground Color"], - hasAlpha = true, - order = 30 + customBackgroundHeader = { + type = "header", + name = L["Background Texture"], + order = 16, + }, + hideBackground = { + type = "toggle", + name = L["Hide Background"], + order = 17, + width = WeakAuras.normalWidth, + }, + sameTexture = { + type = "toggle", + width = WeakAuras.normalWidth, + name = L["Same texture as Foreground"], + order = 18, + disabled = function() return data.hideBackground; end, + hidden = function() return data.hideBackground; end + }, + backgroundTexture = { + type = "input", + width = WeakAuras.doubleWidth - 0.15, + name = L["Background Texture"], + order = 19, + disabled = function() return data.sameTexture or data.hideBackground end, + hidden = function() return data.hideBackground end, + get = function() return data.sameTexture and data.foregroundTexture or data.backgroundTexture; end, + }, + chooseBackgroundTexture = { + type = "execute", + width = 0.15, + name = L["Choose"], + order = 20, + func = function() + OptionsPrivate.OpenTexturePicker(data, {}, { + texture = "backgroundTexture", + color = "backgroundColor", + rotation = "rotation", + mirror = "mirror", + blendMode = "blendMode" + }, texture_types, setTextureFunc); + end, + disabled = function() return data.sameTexture or data.hideBackground; end, + hidden = function() return data.hideBackground end, + imageWidth = 24, + imageHeight = 24, + control = "WeakAurasIcon", + image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", }, backgroundColor = { type = "color", width = WeakAuras.normalWidth, - name = L["Background Color"], + name = L["Color"], hasAlpha = true, - order = 32, - disabled = function() return data.hideBackground; end + order = 21, + disabled = function() return data.hideBackground; end, + hidden = function() return data.hideBackground; end }, - inverse = { - type = "toggle", - width = WeakAuras.normalWidth, - name = L["Inverse"], - order = 33 - }, - space3 = { - type = "execute", - width = WeakAuras.normalWidth, - name = "", - order = 36, - image = function() return "", 0, 0 end, + desaturateBackground = { + type = "toggle", + name = L["Desaturate"], + order = 22, + width = WeakAuras.normalWidth, + disabled = function() return data.hideBackground; end, + hidden = function() return data.hideBackground; end + }, + backgroundColorHiddenSpacer = { + type = "execute", + width = WeakAuras.normalWidth, + name = "", + order = 23, + image = function() return "", 0, 0 end, + hidden = function() return not data.hideBackground end }, + customBackgroundRows = { + type = "input", + width = WeakAuras.doubleWidth / 3, + name = L["Rows"], + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customBackgroundRows and tostring(data.customBackgroundRows) or ""; + end, + set = function(info, v) + data.customBackgroundRows = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 24, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundColumns = { + type = "input", + width = WeakAuras.doubleWidth / 3, + name = L["Columns"], + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customBackgroundColumns and tostring(data.customBackgroundColumns) or ""; + end, + set = function(info, v) + data.customBackgroundColumns = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 25, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundFrames = { + type = "input", + width = WeakAuras.doubleWidth / 3, + name = L["Frame Count"], + validate = WeakAuras.ValidateNumeric, + get = function() + return data.customBackgroundFrames and tostring(data.customBackgroundFrames) or ""; + end, + set = function(info, v) + data.customBackgroundFrames = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 26, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundFileWidth = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["File Width"], + desc = L["Must be a power of 2"], + validate = function(info, val) + if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then + return false; + end + return true + end, + get = function() + return data.customBackgroundFileWidth and tostring(data.customBackgroundFileWidth) or ""; + end, + set = function(info, v) + data.customBackgroundFileWidth = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 27, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundFileHeight = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["File Height"], + desc = L["Must be a power of 2"], + validate = function(info, val) + if val ~= nil and val ~= "" and (not tonumber(val) or tonumber(val) >= 2^31 or math.frexp(val) ~= 0.5) then + return false; + end + return true + end, + get = function() + return data.customBackgroundFileHeight and tostring(data.customBackgroundFileHeight) or ""; + end, + set = function(info, v) + data.customBackgroundFileHeight = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 28, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundFrameWidth = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["Frame Width"], + validate = WeakAuras.ValidateNumeric, + desc = L["Can set to 0 if Columns * Width equal File Width"], + get = function() + return data.customBackgroundFrameWidth and tostring(data.customBackgroundFrameWidth) or ""; + end, + set = function(info, v) + data.customBackgroundFrameWidth = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 29, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + customBackgroundFrameHeight = { + type = "input", + width = WeakAuras.normalWidth / 2, + name = L["Frame Height"], + validate = WeakAuras.ValidateNumeric, + desc = L["Can set to 0 if Rows * Height equal File Height"], + get = function() + return data.customBackgroundFrameHeight and tostring(data.customBackgroundFrameHeight) or ""; + end, + set = function(info, v) + data.customBackgroundFrameHeight = v and tonumber(v) or 0 + WeakAuras.Add(data); + WeakAuras.UpdateThumbnail(data); + end, + order = 30, + hidden = function() return data.sameTexture or texture_data[data.backgroundTexture] or textureNameHasData(data.backgroundTexture) end + }, + backgroundPercent = { + type = "range", + width = WeakAuras.normalWidth, + name = L["Selected Frame"], + min = 0, + max = 1, + order = 31, + isPercent = true, + hidden = function() return data.hideBackground; end + } }; if OptionsPrivate.commonOptions then @@ -361,29 +574,55 @@ local function modifyThumbnail(parent, region, data, fullModify, size) end local frame = 1; - + region.foreground = region.foreground or {} local tdata = texture_data[data.foregroundTexture]; if (tdata) then local lastFrame = tdata.count - 1; region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foregroundRows = tdata.rows; - region.foregroundColumns = tdata.columns; + region.foreground.rows = tdata.rows; + region.foreground.columns = tdata.columns; + region.foreground.fileWidth = 0 + region.foreground.fileHeight = 0 + region.foreground.frameWidth = 0 + region.foreground.frameHeight = 0 else local pattern = "%.x(%d+)y(%d+)f(%d+)%.[tb][gl][ap]" + local pattern2 = "%.x(%d+)y(%d+)f(%d+)w(%d+)h(%d+)W(%d+)H(%d+)%.[tb][gl][ap]" local rows, columns, frames = data.foregroundTexture:lower():match(pattern) - if rows and columns and frames then + if rows then local lastFrame = frames - 1; region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foregroundRows = rows; - region.foregroundColumns = columns; + region.foreground.rows = tonumber(rows); + region.foreground.columns = tonumber(columns); + region.foreground.fileWidth = 0 + region.foreground.fileHeight = 0 + region.foreground.frameWidth = 0 + region.foreground.frameHeight = 0 else - local lastFrame = data.customForegroundFrames - 1; - region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; - region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; - region.foregroundRows = data.customForegroundRows; - region.foregroundColumns = data.customForegroundColumns; + local rows, columns, frames, frameWidth, frameHeight, fileWidth, fileHeight = data.foregroundTexture:match(pattern2) + if rows then + local lastFrame = frames - 1; + region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; + region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; + region.foreground.rows = tonumber(rows) + region.foreground.columns = tonumber(columns) + region.foreground.fileWidth = tonumber(fileWidth) + region.foreground.fileHeight = tonumber(fileHeight) + region.foreground.frameWidth = tonumber(frameWidth) + region.foreground.frameHeight = tonumber(frameHeight) + else + local lastFrame = data.customForegroundFrames - 1; + region.startFrame = floor( (data.startPercent or 0) * lastFrame) + 1; + region.endFrame = floor( (data.endPercent or 1) * lastFrame) + 1; + region.foreground.rows = data.customForegroundRows; + region.foreground.columns = data.customForegroundColumns; + region.foreground.fileWidth = data.customForegroundFileWidth + region.foreground.fileHeight = data.customForegroundFileHeight + region.foreground.frameWidth = data.customForegroundFrameWidth + region.foreground.frameHeight = data.customForegroundFrameHeight + end end end @@ -393,13 +632,20 @@ local function modifyThumbnail(parent, region, data, fullModify, size) local texture = data.foregroundTexture or "Interface\\AddOns\\WeakAuras\\Media\\Textures\\stopmotion"; - if (region.foregroundRows and region.foregroundColumns) then + if (region.foreground.rows and region.foreground.columns) then region.texture:SetTexture(texture); - setTile(region.texture, frame, region.foregroundRows, region.foregroundColumns); + local frameScaleW, frameScaleH = 1, 1 + if region.foreground.fileWidth and region.foreground.frameWidth and region.foreground.fileWidth > 0 and region.foreground.frameWidth > 0 then + frameScaleW = (region.foreground.frameWidth * region.foreground.columns) / region.foreground.fileWidth + end + if region.foreground.fileHeight and region.foreground.frameHeight and region.foreground.fileHeight > 0 and region.foreground.frameHeight > 0 then + frameScaleH = (region.foreground.frameHeight * region.foreground.rows) / region.foreground.fileHeight + end + setTile(region.texture, frame, region.foreground.rows, region.foreground.columns, frameScaleW, frameScaleH); region.SetValue = function(self, percent) local frame = floor(percent * (region.endFrame - region.startFrame) + region.startFrame); - setTile(self.texture, frame, region.foregroundRows, region.foregroundColumns); + setTile(self.texture, frame, region.foreground.rows, region.foreground.columns, frameScaleW, frameScaleH); end else region.texture:SetTexture(texture .. format("%03d", frame)); diff --git a/WeakAurasOptions/RegionOptions/Text.lua b/WeakAurasOptions/RegionOptions/Text.lua index 8f48d45..79137c8 100644 --- a/WeakAurasOptions/RegionOptions/Text.lua +++ b/WeakAurasOptions/RegionOptions/Text.lua @@ -289,22 +289,20 @@ local function createOptions(id, data) options["displayText_format_" .. key] = option end - if data.controlledChildren then - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - local get = function(key) - return childData["displayText_format_" .. key] - end - local input = childData.displayText - OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden, index, #data.controlledChildren) - end - else - local get = function(key) - return data["displayText_format_" .. key] - end - local input = data.displayText - OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden) + local total, index = 0, 1 + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + total = total + 1 end + + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + local get = function(key) + return child["displayText_format_" .. key] + end + local input = child.displayText + OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden, false, index, total) + index = index + 1 + end + addOption("footer", { type = "description", name = "", @@ -354,6 +352,9 @@ local function modifyThumbnail(parent, borderframe, data, fullModify, size) local fontPath = SharedMedia:Fetch("font", data.font) or data.font; text:SetFont(fontPath, data.fontSize < 33 and data.fontSize or 33, data.outline and "OUTLINE" or nil); + if not text:GetFont() then -- Font invalid, set the font but keep the setting + text:SetFont(STANDARD_TEXT_FONT, data.fontSize < 33 and data.fontSize or 33, data.outline and "OUTLINE" or nil); + end text:SetTextHeight(data.fontSize); text:SetText(data.displayText); text:SetTextColor(data.color[1], data.color[2], data.color[3], data.color[4]); diff --git a/WeakAurasOptions/RegionOptions/Texture.lua b/WeakAurasOptions/RegionOptions/Texture.lua index 6dfdf8f..0d34887 100644 --- a/WeakAurasOptions/RegionOptions/Texture.lua +++ b/WeakAurasOptions/RegionOptions/Texture.lua @@ -241,8 +241,4 @@ local templates = { }, } -local function GetAnchors(data) - return OptionsPrivate.Private.default_types_for_anchor -end - -WeakAuras.RegisterRegionOptions("texture", createOptions, createIcon, L["Texture"], createThumbnail, modifyThumbnail, L["Shows a custom texture"], templates, GetAnchors); +WeakAuras.RegisterRegionOptions("texture", createOptions, createIcon, L["Texture"], createThumbnail, modifyThumbnail, L["Shows a custom texture"], templates); diff --git a/WeakAurasOptions/SubRegionOptions/Background.lua b/WeakAurasOptions/SubRegionOptions/Background.lua new file mode 100644 index 0000000..e248125 --- /dev/null +++ b/WeakAurasOptions/SubRegionOptions/Background.lua @@ -0,0 +1,57 @@ +if not WeakAuras.IsCorrectVersion() then return end +local AddonName, OptionsPrivate = ... +local L = WeakAuras.L; + +do + local function subCreateOptions(parentData, data, index, subIndex) + local order = 9 + local options = { + __title = L["Background"], + __order = 1, + __up = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionUp(child, index, "subbackground") + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end, + __down = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionDown(child, index, "subbackground") + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end, + __notcollapsable = true + } + return options + end + + WeakAuras.RegisterSubRegionOptions("subbackground", subCreateOptions, L["Background"]); +end + +-- Foreground for aurabar + +do + local function subCreateOptions(parentData, data, index, subIndex) + local order = 9 + local options = { + __title = L["Foreground"], + __order = 1, + __up = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionUp(child, index, "subforeground") + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end, + __down = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionDown(child, index, "subforeground") + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end, + __notcollapsable = true + } + return options + end + + WeakAuras.RegisterSubRegionOptions("subforeground", subCreateOptions, L["Foreground"]); +end \ No newline at end of file diff --git a/WeakAurasOptions/SubRegionOptions/Border.lua b/WeakAurasOptions/SubRegionOptions/Border.lua index a4a7fa8..eb7f30a 100644 --- a/WeakAurasOptions/SubRegionOptions/Border.lua +++ b/WeakAurasOptions/SubRegionOptions/Border.lua @@ -10,26 +10,6 @@ local function createOptions(parentData, data, index, subIndex) local options = { __title = L["Border %s"]:format(subIndex), __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "subborder")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "subborder")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __duplicate = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.DuplicateSubRegion, index, "subborder")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __delete = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, WeakAuras.DeleteSubRegion, index, "subborder")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, border_visible = { type = "toggle", width = WeakAuras.doubleWidth, @@ -79,6 +59,8 @@ local function createOptions(parentData, data, index, subIndex) } } + OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "subborder") + return options end diff --git a/WeakAurasOptions/SubRegionOptions/Glow.lua b/WeakAurasOptions/SubRegionOptions/Glow.lua index 7444fe6..aab4deb 100644 --- a/WeakAurasOptions/SubRegionOptions/Glow.lua +++ b/WeakAurasOptions/SubRegionOptions/Glow.lua @@ -19,26 +19,6 @@ local function createOptions(parentData, data, index, subIndex) local options = { __title = L["Glow %s"]:format(subIndex), __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "subglow")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "subglow")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __duplicate = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.DuplicateSubRegion, index, "subglow")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __delete = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, WeakAuras.DeleteSubRegion, index, "subglow")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, glow = { type = "toggle", width = WeakAuras.normalWidth, @@ -138,6 +118,7 @@ local function createOptions(parentData, data, index, subIndex) }, glowColor = { type = "color", + hasAlpha = true, width = WeakAuras.normalWidth, name = L["Custom Color"], order = 7, @@ -251,7 +232,7 @@ local function createOptions(parentData, data, index, subIndex) hidden = function() return hiddenGlowExtra() or data.glowType ~= "Pixel" end, }, - glow_anchor = { + glow_expand_anchor = { type = "description", name = "", order = 20, @@ -262,6 +243,9 @@ local function createOptions(parentData, data, index, subIndex) } } } + + OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "subglow") + return options end diff --git a/WeakAurasOptions/SubRegionOptions/BarModel.lua b/WeakAurasOptions/SubRegionOptions/Model.lua similarity index 62% rename from WeakAurasOptions/SubRegionOptions/BarModel.lua rename to WeakAurasOptions/SubRegionOptions/Model.lua index 868a07a..cd36f2b 100644 --- a/WeakAurasOptions/SubRegionOptions/BarModel.lua +++ b/WeakAurasOptions/SubRegionOptions/Model.lua @@ -8,27 +8,7 @@ local function createOptions(parentData, data, index, subIndex) local options = { __title = L["Model %s"]:format(subIndex), __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "subbarmodel")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "subbarmodel")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __duplicate = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.DuplicateSubRegion, index, "subbarmodel")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __delete = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, WeakAuras.DeleteSubRegion, index, "subbarmodel")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - bar_model_visible = { + model_visible = { type = "toggle", width = WeakAuras.doubleWidth, name = L["Show Model"], @@ -36,27 +16,51 @@ local function createOptions(parentData, data, index, subIndex) }, model_path = { type = "input", - width = WeakAuras.normalWidth, + width = WeakAuras.doubleWidth - 0.15, name = L["Model"], - order = 10 + order = 10.5 }, chooseModel = { type = "execute", - width = WeakAuras.normalWidth, + width = 0.15, name = L["Choose"], order = 11, func = function() - OptionsPrivate.OpenModelPicker(data, parentData); + OptionsPrivate.OpenModelPicker(parentData, {"subRegions", index}); end, + imageWidth = 24, + imageHeight = 24, + control = "WeakAurasIcon", + image = "Interface\\AddOns\\WeakAuras\\Media\\Textures\\browse", }, bar_model_clip = { type = "toggle", - width = WeakAuras.normalWidth, + width = WeakAuras.doubleWidth, name = L["Clipped by Progress"], order = 12, hidden = function() return parentData.regionType ~= "aurabar" end }, - bar_model_alpha = { + extra_width = { + type = "range", + width = WeakAuras.normalWidth, + name = L["Extra Width"], + order = 12.1, + softMin = -100, + softMax = 500, + step = 1, + hidden = function() return data.bar_model_clip and parentData.regionType == "aurabar" end + }, + extra_height = { + type = "range", + width = WeakAuras.normalWidth, + name = L["Extra Height"], + order = 12.2, + softMin = -100, + softMax = 500, + step = 1, + hidden = function() return data.bar_model_clip and parentData.regionType == "aurabar" end + }, + model_alpha = { type = "range", width = WeakAuras.normalWidth, name = L["Alpha"], @@ -106,7 +110,10 @@ local function createOptions(parentData, data, index, subIndex) order = 45, }, } + + OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "submodel") + return options end -WeakAuras.RegisterSubRegionOptions("subbarmodel", createOptions, L["Shows a model"]); +WeakAuras.RegisterSubRegionOptions("submodel", createOptions, L["Shows a model"]); diff --git a/WeakAurasOptions/SubRegionOptions/SubRegionCommon.lua b/WeakAurasOptions/SubRegionOptions/SubRegionCommon.lua index ec113d8..170e41e 100644 --- a/WeakAurasOptions/SubRegionOptions/SubRegionCommon.lua +++ b/WeakAurasOptions/SubRegionOptions/SubRegionCommon.lua @@ -41,7 +41,7 @@ function WeakAuras.DeleteSubRegion(data, index, regionType) AdjustConditions(data, replacements); WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) + OptionsPrivate.ClearOptions(data.id) end end @@ -60,7 +60,7 @@ function OptionsPrivate.MoveSubRegionUp(data, index, regionType) AdjustConditions(data, replacements); WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) + OptionsPrivate.ClearOptions(data.id) end end @@ -79,7 +79,7 @@ function OptionsPrivate.MoveSubRegionDown(data, index, regionType) AdjustConditions(data, replacements); WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) + OptionsPrivate.ClearOptions(data.id) end end @@ -98,6 +98,33 @@ function OptionsPrivate.DuplicateSubRegion(data, index, regionType) AdjustConditions(data, replacements); WeakAuras.Add(data) - WeakAuras.ClearAndUpdateOptions(data.id) + OptionsPrivate.ClearOptions(data.id) + end +end + +function OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, subRegionType) + options.__up = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionUp(child, index, subRegionType) + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end + options.__down = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.MoveSubRegionDown(child, index, subRegionType) + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end + options.__duplicate = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + OptionsPrivate.DuplicateSubRegion(child, index, subRegionType) + end + WeakAuras.ClearAndUpdateOptions(parentData.id) + end + options.__delete = function() + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + WeakAuras.DeleteSubRegion(child, index, subRegionType) + end + WeakAuras.ClearAndUpdateOptions(parentData.id) end end diff --git a/WeakAurasOptions/SubRegionOptions/SubText.lua b/WeakAurasOptions/SubRegionOptions/SubText.lua index 4b67bd1..b112c3b 100644 --- a/WeakAurasOptions/SubRegionOptions/SubText.lua +++ b/WeakAurasOptions/SubRegionOptions/SubText.lua @@ -31,26 +31,6 @@ local function createOptions(parentData, data, index, subIndex) local options = { __title = L["Text %s"]:format(subIndex), __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "subtext")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "subtext")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __duplicate = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.DuplicateSubRegion, index, "subtext")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __delete = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, WeakAuras.DeleteSubRegion, index, "subtext")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, text_visible = { type = "toggle", width = WeakAuras.halfWidth, @@ -278,16 +258,11 @@ local function createOptions(parentData, data, index, subIndex) -- design I had for anchor options proved to be not general enough for -- what SubText needed. So, I removed it, and postponed making it work for unknown future -- sub regions - local anchors - if parentData.controlledChildren then - anchors = {} - for index, childId in ipairs(parentData.controlledChildren) do - local childData = WeakAuras.GetData(childId) - WeakAuras.Mixin(anchors, OptionsPrivate.Private.GetAnchorsForData(childData, "point")) - end - else - anchors = OptionsPrivate.Private.GetAnchorsForData(parentData, "point") + local anchors = {} + for child in OptionsPrivate.Private.TraverseLeafsOrAura(parentData) do + WeakAuras.Mixin(anchors, OptionsPrivate.Private.GetAnchorsForData(child, "point")) end + -- Anchor Options options.text_anchorsDescription = { type = "execute", @@ -469,25 +444,29 @@ local function createOptions(parentData, data, index, subIndex) end if parentData.controlledChildren then - for childIndex, childId in pairs(parentData.controlledChildren) do - local parentChildData = WeakAuras.GetData(childId) - if parentChildData.subRegions then - local childData = parentChildData.subRegions[index] - if childData then - local get = function(key) - return childData["text_text_format_" .. key] - end - local input = childData["text_text"] - OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden, childIndex, #parentData.controlledChildren) + local list = {} + for child in OptionsPrivate.Private.TraverseLeafs(parentData) do + if child.subRegions then + local childSubRegion = child.subRegions[index] + if childSubRegion then + tinsert(list, childSubRegion) end end end + + for listIndex, childSubRegion in ipairs(list) do + local get = function(key) + return childSubRegion["text_text_format_" .. key] + end + local input = childSubRegion["text_text"] + OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden, false, listIndex, #list) + end else local get = function(key) return data["text_text_format_" .. key] end local input = data["text_text"] - OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden) + OptionsPrivate.AddTextFormatOption(input, true, get, addOption, hidden, setHidden, false) end addOption("footer", { @@ -497,6 +476,8 @@ local function createOptions(parentData, data, index, subIndex) hidden = hidden }) + OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "subtext") + return options, commonTextOptions end diff --git a/WeakAurasOptions/SubRegionOptions/Tick.lua b/WeakAurasOptions/SubRegionOptions/Tick.lua index 16a59d9..2b31060 100644 --- a/WeakAurasOptions/SubRegionOptions/Tick.lua +++ b/WeakAurasOptions/SubRegionOptions/Tick.lua @@ -13,26 +13,6 @@ local function createOptions(parentData, data, index, subIndex) local options = { __title = L["Tick %s"]:format(subIndex), __order = 1, - __up = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionUp, index, "subtick")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __down = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.MoveSubRegionDown, index, "subtick")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __duplicate = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, OptionsPrivate.DuplicateSubRegion, index, "subtick")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, - __delete = function() - if (OptionsPrivate.Private.ApplyToDataOrChildData(parentData, WeakAuras.DeleteSubRegion, index, "subtick")) then - WeakAuras.ClearAndUpdateOptions(parentData.id) - end - end, tick_visible = { type = "toggle", width = WeakAuras.normalWidth, @@ -235,6 +215,9 @@ local function createOptions(parentData, data, index, subIndex) } } } + + OptionsPrivate.AddUpDownDeleteDuplicate(options, parentData, index, "subtick") + return options end diff --git a/WeakAurasOptions/TriggerOptions.lua b/WeakAurasOptions/TriggerOptions.lua index 6ac66d8..1c9cccc 100644 --- a/WeakAurasOptions/TriggerOptions.lua +++ b/WeakAurasOptions/TriggerOptions.lua @@ -16,8 +16,6 @@ local executeAll = OptionsPrivate.commonOptions.CreateExecuteAll("trigger") local flattenRegionOptions = OptionsPrivate.commonOptions.flattenRegionOptions local fixMetaOrders = OptionsPrivate.commonOptions.fixMetaOrders -local spellCache = WeakAuras.spellCache - local function union(table1, table2) local meta = {}; for i,v in pairs(table1) do @@ -80,7 +78,6 @@ local function GetGlobalOptions(data) data.triggers.activeTriggerMode = v; WeakAuras.Add(data); WeakAuras.UpdateThumbnail(data); - WeakAuras.UpdateDisplayButton(data); end, hidden = function() return #data.triggers <= 1 end } @@ -153,13 +150,8 @@ end function OptionsPrivate.GetTriggerOptions(data) local allOptions = {} - if data.controlledChildren then - for index, childId in pairs(data.controlledChildren) do - local childData = WeakAuras.GetData(childId) - allOptions = AddOptions(allOptions, childData) - end - else - allOptions = AddOptions(allOptions, data) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(data) do + allOptions = AddOptions(allOptions, child) end fixMetaOrders(allOptions) @@ -323,11 +315,12 @@ function OptionsPrivate.AddTriggerMetaFunctions(options, data, triggernum) local canDelete = false -- Since we want to handle all selected auras in one dialog, we have to iterate over GetPickedDisplay local picked = OptionsPrivate.GetPickedDisplay() - OptionsPrivate.Private.ApplyToDataOrChildData(picked, function(childData) - if #childData.triggers > 1 and #childData.triggers >= triggernum then + for child in OptionsPrivate.Private.TraverseLeafsOrAura(picked) do + if #child.triggers > 1 and #child.triggers >= triggernum then canDelete = true + break; end - end) + end if canDelete then StaticPopupDialogs["WEAKAURAS_CONFIRM_TRIGGER_DELETE"] = { @@ -335,15 +328,16 @@ function OptionsPrivate.AddTriggerMetaFunctions(options, data, triggernum) button1 = L["Delete"], button2 = L["Cancel"], OnAccept = function() - OptionsPrivate.Private.ApplyToDataOrChildData(picked, function(childData) - if #childData.triggers > 1 and #childData.triggers >= triggernum then - tremove(childData.triggers, triggernum) - DeleteConditionsForTrigger(childData, triggernum) - WeakAuras.Add(childData) + for child in OptionsPrivate.Private.TraverseLeafsOrAura(picked) do + if #child.triggers > 1 and #child.triggers >= triggernum then + tremove(child.triggers, triggernum) + DeleteConditionsForTrigger(child, triggernum) + WeakAuras.Add(child) OptionsPrivate.RemoveCollapsed(collapsedId, "trigger", {triggernum}) - WeakAuras.ClearAndUpdateOptions(childData.id) + OptionsPrivate.ClearOptions(child.id) end - end) + end + WeakAuras.FillOptions() triggerDeleteDialogOpen = false end, @@ -363,7 +357,9 @@ function OptionsPrivate.AddTriggerMetaFunctions(options, data, triggernum) local _, _, _, enabled = GetAddOnInfo("WeakAurasTemplates") if enabled then options.__applyTemplate = function() - WeakAuras.OpenTriggerTemplate(data) + -- If we have more than a single aura selected, + -- we want to open the template view with the group/multi selection + OptionsPrivate.OpenTriggerTemplate(OptionsPrivate.GetPickedDisplay()) end end end diff --git a/WeakAurasOptions/WeakAurasOptions.lua b/WeakAurasOptions/WeakAurasOptions.lua index 0e67fb8..6396b80 100644 --- a/WeakAurasOptions/WeakAurasOptions.lua +++ b/WeakAurasOptions/WeakAurasOptions.lua @@ -46,7 +46,8 @@ local tempGroup = { }; OptionsPrivate.tempGroup = tempGroup; -function OptionsPrivate.DuplicateAura(data, newParent, massEdit) +-- Does not duplicate child auras. +function OptionsPrivate.DuplicateAura(data, newParent, massEdit, targetIndex) local base_id = data.id .. " " local num = 2 @@ -77,16 +78,19 @@ function OptionsPrivate.DuplicateAura(data, newParent, massEdit) local parentId = newParent or data.parent local parentData = WeakAuras.GetData(parentId) local index - if newParent then - index = #parentData.controlledChildren + if targetIndex then + index = targetIndex + elseif newParent then + index = #parentData.controlledChildren + 1 else - index = tIndexOf(parentData.controlledChildren, data.id) + index = tIndexOf(parentData.controlledChildren, data.id) + 1 end if(index) then - tinsert(parentData.controlledChildren, index + 1, newData.id) + tinsert(parentData.controlledChildren, index, newData.id) newData.parent = parentId - WeakAuras.Add(parentData) WeakAuras.Add(newData) + WeakAuras.Add(parentData) + OptionsPrivate.Private.AddParents(parentData) for index, id in pairs(parentData.controlledChildren) do local childButton = WeakAuras.GetDisplayButton(id) @@ -97,12 +101,11 @@ function OptionsPrivate.DuplicateAura(data, newParent, massEdit) if not massEdit then local button = WeakAuras.GetDisplayButton(parentData.id) button.callbacks.UpdateExpandButton() - WeakAuras.UpdateDisplayButton(parentData) end OptionsPrivate.ClearOptions(parentData.id) end end - return newData.id + return newData end AceGUI:RegisterLayout("AbsoluteList", function(content, children) @@ -223,6 +226,115 @@ loadedFrame:SetScript("OnEvent", function(self, event, addon) 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 = WeakAuras.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 = WeakAuras.FindUnusedId(tempGroup.controlledChildren[1].." Group"), + regionType = regionType, + }; + + WeakAuras.DeepMixin(data, WeakAuras.data_stub) + data.internalVersion = WeakAuras.InternalVersion() + WeakAuras.validate(data, WeakAuras.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 = WeakAuras.GetDisplayButton(parent) + parentButton.callbacks.UpdateExpandButton(); + parentButton:Expand(); + parentButton:ReloadTooltip(); + else + WeakAuras.Add(data); + WeakAuras.NewDisplayButton(data); + end + + for index, childId in pairs(tempGroup.controlledChildren) do + local childData = WeakAuras.GetData(childId); + local childButton = WeakAuras.GetDisplayButton(childId) + local oldParent = childData.parent + local oldParentData = WeakAuras.GetData(oldParent) + if (oldParent) then + local oldIndex = childButton:GetGroupOrder() + print("CHILD ID", childId, "OLD PARENT", oldParent, "OLD INDEX", oldIndex) + print("###", oldParentData.controlledChildren[oldIndex]) + + tremove(oldParentData.controlledChildren, oldIndex) + WeakAuras.Add(oldParentData) + OptionsPrivate.Private.AddParents(oldParentData) + WeakAuras.UpdateGroupOrders(oldParentData); + WeakAuras.ClearAndUpdateOptions(oldParent); + local oldParentButton = WeakAuras.GetDisplayButton(oldParent) + oldParentButton.callbacks.UpdateExpandButton(); + oldParentButton:ReloadTooltip() + 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 = WeakAuras.GetDisplayButton(data.id); + button.callbacks.UpdateExpandButton(); + OptionsPrivate.SortDisplayButtons(); + button:Expand(); + + if data.parent then + OptionsPrivate.Private.AddParents(data) + end +end + function OptionsPrivate.MultipleDisplayTooltipMenu() local frame = frame; local menu = { @@ -230,90 +342,24 @@ function OptionsPrivate.MultipleDisplayTooltipMenu() text = L["Add to new Group"], notCheckable = 1, func = function() - local data = { - id = WeakAuras.FindUnusedId(tempGroup.controlledChildren[1].." Group"), - regionType = "group", - }; - - WeakAuras.DeepMixin(data, WeakAuras.data_stub) - data.internalVersion = WeakAuras.InternalVersion() - WeakAuras.Add(data); - WeakAuras.NewDisplayButton(data); - - for index, childId in pairs(tempGroup.controlledChildren) do - local childData = WeakAuras.GetData(childId); - tinsert(data.controlledChildren, childId); - childData.parent = data.id; - WeakAuras.Add(data); - WeakAuras.Add(childData); - OptionsPrivate.ClearOptions(childData.id) - end - - for index, id in pairs(data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(id); - childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(index, #data.controlledChildren); - end - - local button = WeakAuras.GetDisplayButton(data.id); - button.callbacks.UpdateExpandButton(); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); - button:Expand(); + CreateNewGroupFromSelection("group") end }, { text = L["Add to new Dynamic Group"], notCheckable = 1, func = function() - local data = { - id = WeakAuras.FindUnusedId(tempGroup.controlledChildren[1].." Group"), - regionType = "dynamicgroup", - }; - - WeakAuras.DeepMixin(data, WeakAuras.data_stub) - data.internalVersion = WeakAuras.InternalVersion() - WeakAuras.Add(data); - WeakAuras.NewDisplayButton(data); - - for index, childId in pairs(tempGroup.controlledChildren) do - local childData = WeakAuras.GetData(childId); - tinsert(data.controlledChildren, childId); - childData.parent = data.id; - childData.xOffset = 0; - childData.yOffset = 0; - WeakAuras.Add(data); - WeakAuras.Add(childData); - end - - for index, id in pairs(data.controlledChildren) do - local childButton = WeakAuras.GetDisplayButton(id); - childButton:SetGroup(data.id, data.regionType == "dynamicgroup"); - childButton:SetGroupOrder(index, #data.controlledChildren); - end - - local button = WeakAuras.GetDisplayButton(data.id); - button.callbacks.UpdateExpandButton(); - WeakAuras.UpdateDisplayButton(data); - WeakAuras.SortDisplayButtons(); - button:Expand(); - WeakAuras.PickDisplay(data.id); + CreateNewGroupFromSelection("dynamicgroup", true) end }, { text = L["Duplicate All"], notCheckable = 1, func = function() - local toDuplicate = {}; - for index, id in pairs(tempGroup.controlledChildren) do - toDuplicate[index] = id; - end - local duplicated = {}; - - for index, id in ipairs(toDuplicate) do - local childData = WeakAuras.GetData(id); - duplicated[index] = OptionsPrivate.DuplicateAura(childData); + for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do + local newData = OptionsPrivate.DuplicateAura(child) + tinsert(duplicated, newData.id); end OptionsPrivate.ClearPicks(); @@ -331,12 +377,9 @@ function OptionsPrivate.MultipleDisplayTooltipMenu() func = function() local toDelete = {}; local parents = {}; - for index, id in pairs(tempGroup.controlledChildren) do - local childData = WeakAuras.GetData(id); - toDelete[index] = childData; - if(childData.parent) then - parents[childData.parent] = true; - end + for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do + tinsert(toDelete, child) + addParents(parents, child) end OptionsPrivate.ConfirmDelete(toDelete, parents) end @@ -352,20 +395,43 @@ function OptionsPrivate.MultipleDisplayTooltipMenu() func = function() WeakAuras_DropDownMenu:Hide() end } }; - local anyGrouped = false; + + local anyGroup = false; + local allSameParent = true + local commonParent = nil + local first = true for index, id in pairs(tempGroup.controlledChildren) do local childData = WeakAuras.GetData(id); - if(childData and childData.parent) then - anyGrouped = true; - break; + 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(anyGrouped) then - menu[1].notClickable = 1; - menu[1].text = "|cFF777777"..menu[1].text; + + 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 @@ -395,22 +461,13 @@ StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"] = { parentButton:SetNormalTooltip() WeakAuras.Add(parentData) WeakAuras.ClearAndUpdateOptions(parentData.id) - WeakAuras.UpdateDisplayButton(parentData) end end OptionsPrivate.Private.ResumeAllDynamicGroups() - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons(nil, true) end end, OnCancel = function(self) - if self.data.parents then - for id in pairs(self.data.parents) do - local parentRegion = WeakAuras.GetRegion(id) - if parentRegion.Resume then - parentRegion:Resume() - end - end - end self.data = nil end, showAlert = 1, @@ -419,6 +476,26 @@ StaticPopupDialogs["WEAKAURAS_CONFIRM_DELETE"] = { preferredindex = STATICPOPUP_NUMDIALOGS, } +StaticPopupDialogs["WEAKAURAS_CONFIRM_IGNORE_UPDATES"] = { + text = L["Do you want to ignore all future updates for this aura"], + button1 = L["Yes"], + button2 = L["Cancel"], + OnAccept = function(self) + if self.data then + local auraData = WeakAuras.GetData(self.data) + if auraData then + for child in OptionsPrivate.Private.TraverseAll(auraData) do + child.ignoreWagoUpdate = true + end + end + OptionsPrivate.SortDisplayButtons(nil, true) + end + end, + OnCancel = function(self) end, + whileDead = true, + preferredindex = STATICPOPUP_NUMDIALOGS, +} + 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?"] @@ -438,7 +515,7 @@ end local function AfterScanForLoads() if(frame) then if (frame:IsVisible()) then - WeakAuras.SortDisplayButtons(nil, true); + OptionsPrivate.SortDisplayButtons(nil, true); else frame.needsSort = true; end @@ -483,7 +560,6 @@ local function OnDelete(event, uid, id, parentUid, parentId) end end WeakAuras.ClearAndUpdateOptions(parentData.id) - WeakAuras.UpdateDisplayButton(parentData) end end @@ -506,9 +582,19 @@ local function OnRename(event, uid, oldid, newid) end end - OptionsPrivate.SetGrouping() - WeakAuras.SortDisplayButtons() + OptionsPrivate.StopGrouping() + OptionsPrivate.SortDisplayButtons() + + 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 function WeakAuras.ToggleOptions(msg, Private) @@ -517,7 +603,7 @@ function WeakAuras.ToggleOptions(msg, Private) end if not OptionsPrivate.Private then OptionsPrivate.Private = Private - OptionsPrivate.Private:RegisterCallback("AuraWarningsUpdated", function(event, uid) + 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 @@ -525,9 +611,10 @@ function WeakAuras.ToggleOptions(msg, Private) end end) - OptionsPrivate.Private:RegisterCallback("ScanForLoads", AfterScanForLoads) - OptionsPrivate.Private:RegisterCallback("AboutToDelete", OnAboutToDelete) - OptionsPrivate.Private:RegisterCallback("Rename", OnRename) + OptionsPrivate.Private.callbacks:RegisterCallback("ScanForLoads", AfterScanForLoads) + OptionsPrivate.Private.callbacks:RegisterCallback("AboutToDelete", OnAboutToDelete) + OptionsPrivate.Private.callbacks:RegisterCallback("Rename", OnRename) + OptionsPrivate.Private.OpenUpdate = OptionsPrivate.OpenUpdate end if(frame and frame:IsVisible()) then @@ -580,13 +667,9 @@ local function GetSortedOptionsLists() end table.sort(to_sort, function(a, b) return a < b end); for _, id in ipairs(to_sort) do - tinsert(loadedSorted, id); local data = WeakAuras.GetData(id); - local controlledChildren = data.controlledChildren; - if(controlledChildren) then - for _, childId in pairs(controlledChildren) do - tinsert(loadedSorted, childId); - end + for child in OptionsPrivate.Private.TraverseAll(data) do + tinsert(loadedSorted, child.id) end end @@ -600,13 +683,9 @@ local function GetSortedOptionsLists() end table.sort(to_sort, function(a, b) return a < b end); for _, id in ipairs(to_sort) do - tinsert(unloadedSorted, id); local data = WeakAuras.GetData(id); - local controlledChildren = data.controlledChildren; - if(controlledChildren) then - for _, childId in pairs(controlledChildren) do - tinsert(unloadedSorted, childId); - end + for child in OptionsPrivate.Private.TraverseAll(data) do + tinsert(unloadedSorted, child.id) end end @@ -622,10 +701,10 @@ local function LayoutDisplayButtons(msg) local loadedSorted, unloadedSorted = GetSortedOptionsLists(); frame:SetLoadProgressVisible(true) - --frame.buttonsScroll:AddChild(frame.newButton); - --if(frame.addonsButton) then - -- frame.buttonsScroll:AddChild(frame.addonsButton); - --end + 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); @@ -635,7 +714,7 @@ local function LayoutDisplayButtons(msg) local data = WeakAuras.GetData(id); if(data) then EnsureDisplayButton(data); - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); frame.buttonsScroll:AddChild(displayButtons[data.id]); @@ -654,7 +733,7 @@ local function LayoutDisplayButtons(msg) frame.buttonsScroll:ResumeLayout() frame.buttonsScroll:PerformLayout() - WeakAuras.SortDisplayButtons(msg); + OptionsPrivate.SortDisplayButtons(msg); OptionsPrivate.Private.PauseAllDynamicGroups(); if (WeakAuras.IsOptionsOpen()) then @@ -662,11 +741,8 @@ local function LayoutDisplayButtons(msg) if(OptionsPrivate.Private.loaded[id] ~= nil) then button:PriorityShow(1); end - if WeakAurasCompanion and not button.data.parent then - -- initialize update icons on top level buttons - button:RefreshUpdate() - end end + WeakAuras.OptionsFrame().loadedButton:RecheckVisibility() end OptionsPrivate.Private.ResumeAllDynamicGroups(); @@ -680,7 +756,7 @@ local function LayoutDisplayButtons(msg) local data = WeakAuras.GetData(id); if(data) then EnsureDisplayButton(data); - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); local button = displayButtons[data.id] frame.buttonsScroll:AddChild(button); @@ -731,7 +807,7 @@ function WeakAuras.ShowOptions(msg) frame.buttonsScroll.frame:Show(); if (frame.needsSort) then - WeakAuras.SortDisplayButtons(); + OptionsPrivate.SortDisplayButtons(); frame.needsSort = nil; end @@ -749,9 +825,7 @@ function WeakAuras.ShowOptions(msg) -- Show what was last shown OptionsPrivate.Private.PauseAllDynamicGroups(); for id, button in pairs(displayButtons) do - if (button:GetVisibility() > 0) then - button:PriorityShow(button:GetVisibility()); - end + button:SyncVisibility() end OptionsPrivate.Private.ResumeAllDynamicGroups(); end @@ -819,15 +893,14 @@ function OptionsPrivate.ImportFromString() frame.importexport:Open("import"); end -function WeakAuras.CloseImportExport() - frame.codereview:Close(); - frame.importexport:Close(); +function OptionsPrivate.OpenUpdate(data, children, target, sender) + return frame.update:Open(data, children, target, sender) end function OptionsPrivate.ConvertDisplay(data, newType) local id = data.id; local visibility = displayButtons[id]:GetVisibility(); - displayButtons[id]:PriorityHide(0); + displayButtons[id]:PriorityHide(2); WeakAuras.regions[id].region:Collapse(); OptionsPrivate.Private.CollapseAllClones(id); @@ -838,20 +911,20 @@ function OptionsPrivate.ConvertDisplay(data, newType) displayButtons[id]:PriorityShow(visibility); frame:ClearOptions(id) frame:FillOptions(); - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); WeakAuras.SetMoverSizer(id) OptionsPrivate.ResetMoverSizer(); - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons() end function WeakAuras.NewDisplayButton(data, massEdit) local id = data.id; OptionsPrivate.Private.ScanForLoads({[id] = true}); EnsureDisplayButton(db.displays[id]); - WeakAuras.UpdateDisplayButton(db.displays[id]); + WeakAuras.UpdateThumbnail(db.displays[id]); frame.buttonsScroll:AddChild(displayButtons[id]); if not massEdit then - WeakAuras.SortDisplayButtons() + OptionsPrivate.SortDisplayButtons() end end @@ -870,11 +943,31 @@ function OptionsPrivate.UpdateButtonsScroll() 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; -function WeakAuras.SortDisplayButtons(filter, overrideReset, id) +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 @@ -887,158 +980,177 @@ function WeakAuras.SortDisplayButtons(filter, overrideReset, id) filter = filter:lower(); wipe(frame.buttonsScroll.children); - --tinsert(frame.buttonsScroll.children, frame.newButton); - --if(frame.addonsButton) then - -- tinsert(frame.buttonsScroll.children, frame.addonsButton); - --end - tinsert(frame.buttonsScroll.children, frame.loadedButton); - local numLoaded = 0; - local to_sort = {}; - local children = {}; - local containsFilter = false; + 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) + end + if not child.frame:IsShown() then + child.frame:Show() + child:AcquireThumbnail() + end + tinsert(frame.buttonsScroll.children, child) + 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) + 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 and filter ~= "" + local topLevelLoadedAuras = {} + local topLevelUnloadedAuras = {} local visible = {} for id, child in pairs(displayButtons) do - containsFilter = not filter or filter == ""; - local data = WeakAuras.GetData(id); - if not(data) then - print("|cFF8800FFWeakAuras|r: No data for", id); + if(OptionsPrivate.Private.loaded[id]) then + child:EnableLoaded(); else - if(not containsFilter and data.controlledChildren) then - for index, childId in pairs(data.controlledChildren) do - if(childId:lower():find(filter, 1, true)) then - containsFilter = true; - break; - end - end - end - if( - frame.loadedButton:GetExpanded() - and (not filter or id:lower():find(filter, 1, true) or containsFilter) - ) then - - local group = child:GetGroup(); - if(group) then - -- In a Group - if(OptionsPrivate.Private.loaded[group]) then - if(OptionsPrivate.Private.loaded[id]) then - child:EnableLoaded(); - else - child:DisableLoaded(); - end - children[group] = children[group] or {}; - visible[id] = true - tinsert(children[group], id); - end - else - -- Top Level - if(OptionsPrivate.Private.loaded[id] ~= nil) then - if(OptionsPrivate.Private.loaded[id]) then - child:EnableLoaded(); - else - child:DisableLoaded(); - end - visible[id] = true - tinsert(to_sort, child); - end + child:DisableLoaded(); + end + + if useTextFilter then + if(id:lower():find(filter, 1, true)) then + aurasMatchingFilter[id] = true + for parent in OptionsPrivate.Private.TraverseParents(child.data) do + aurasMatchingFilter[parent.id] = true end end + else + aurasMatchingFilter[id] = true end - end - table.sort(to_sort, function(a, b) return a:GetTitle() < b:GetTitle() end); - for _, child in ipairs(to_sort) do - child.frame:Show(); - if child.AcquireThumbnail then - child:AcquireThumbnail() - end - tinsert(frame.buttonsScroll.children, child); - local controlledChildren = children[child:GetTitle()]; - if(controlledChildren) then - table.sort(controlledChildren, function(a, b) return displayButtons[a]:GetGroupOrder() < displayButtons[b]:GetGroupOrder(); end); - for _, groupchild in ipairs(controlledChildren) do - if(child:GetExpanded() and visible[groupchild]) then - displayButtons[groupchild].frame:Show(); - if displayButtons[groupchild].AcquireThumbnail then - displayButtons[groupchild]:AcquireThumbnail() - end - tinsert(frame.buttonsScroll.children, displayButtons[groupchild]); - end - end - end - end - - -- Now handle unloaded auras - tinsert(frame.buttonsScroll.children, frame.unloadedButton); - local numUnloaded = 0; - wipe(to_sort); - wipe(children); - - for id, child in pairs(displayButtons) do - containsFilter = not filter or filter == ""; - local data = WeakAuras.GetData(id); - if(not containsFilter and data.controlledChildren) then - for index, childId in pairs(data.controlledChildren) do - if(childId:lower():find(filter, 1, true)) then - containsFilter = true; - break; - end - end - end - if( - frame.unloadedButton:GetExpanded() - and (not filter or id:lower():find(filter, 1, true) or containsFilter) - ) then - local group = child:GetGroup(); - if(group) then - if not(OptionsPrivate.Private.loaded[group]) then - if(OptionsPrivate.Private.loaded[id]) then - child:EnableLoaded(); - else - child:DisableLoaded(); - end - children[group] = children[group] or {}; - visible[id] = true - tinsert(children[group], id); - end + if not child:GetGroup() then + -- Top Level aura + if OptionsPrivate.Private.loaded[child.data.id] ~= nil then + tinsert(topLevelLoadedAuras, id) else - if(OptionsPrivate.Private.loaded[id] == nil) then - child:DisableLoaded(); - visible[id] = true - tinsert(to_sort, child); - end - end - end - end - table.sort(to_sort, function(a, b) return a:GetTitle() < b:GetTitle() end); - - for _, child in ipairs(to_sort) do - child.frame:Show(); - if child.AcquireThumbnail then - child:AcquireThumbnail() - end - tinsert(frame.buttonsScroll.children, child); - local controlledChildren = children[child:GetTitle()]; - if(controlledChildren) then - table.sort(controlledChildren, function(a, b) return displayButtons[a]:GetGroupOrder() < displayButtons[b]:GetGroupOrder(); end); - for _, groupchild in ipairs(controlledChildren) do - if(child:GetExpanded() and visible[groupchild]) then - displayButtons[groupchild].frame:Show(); - if displayButtons[groupchild].AcquireThumbnail then - displayButtons[groupchild]:AcquireThumbnail() - end - tinsert(frame.buttonsScroll.children, displayButtons[groupchild]); - end + tinsert(topLevelUnloadedAuras, id) + end + end + end + + if frame.loadedButton:GetExpanded() then + table.sort(topLevelLoadedAuras) + for _, id in ipairs(topLevelLoadedAuras) do + if aurasMatchingFilter[id] then + addButton(displayButtons[id], aurasMatchingFilter, visible) + end + end + end + + tinsert(frame.buttonsScroll.children, frame.unloadedButton); + + if frame.unloadedButton:GetExpanded() then + table.sort(topLevelUnloadedAuras) + for _, id in ipairs(topLevelUnloadedAuras) do + if aurasMatchingFilter[id] then + addButton(displayButtons[id], aurasMatchingFilter, visible) end end end - -- Hiding the other buttons for id, child in pairs(displayButtons) do - local group = child:GetGroup(); - local groupVisible = not group or visible[group] and displayButtons[group]:GetExpanded() - if(not groupVisible or not visible[id]) then + if(not visible[child]) then child.frame:Hide(); if child.ReleaseThumbnail then child:ReleaseThumbnail() @@ -1052,6 +1164,7 @@ function WeakAuras.SortDisplayButtons(filter, overrideReset, id) end end + function OptionsPrivate.IsPickedMultiple() if(frame.pickedDisplay == tempGroup) then return true; @@ -1062,8 +1175,8 @@ end function OptionsPrivate.IsDisplayPicked(id) if(frame.pickedDisplay == tempGroup) then - for index, childId in pairs(tempGroup.controlledChildren) do - if(id == childId) then + for child in OptionsPrivate.Private.TraverseLeafs(tempGroup) do + if(id == child.id) then return true; end end @@ -1080,8 +1193,8 @@ end function OptionsPrivate.PickAndEditDisplay(id) frame:PickDisplay(id); - displayButtons[id].callbacks.OnRenameClick(); OptionsPrivate.UpdateButtonsScroll() + displayButtons[id].callbacks.OnRenameClick(); end function OptionsPrivate.ClearPick(id) @@ -1120,7 +1233,11 @@ function OptionsPrivate.PickDisplayMultipleShift(target) table.insert(batchSelection, child); for i = index + 1, #group.controlledChildren do local current = group.controlledChildren[i]; - table.insert(batchSelection, current); + 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; @@ -1132,22 +1249,24 @@ function OptionsPrivate.PickDisplayMultipleShift(target) elseif (firstData.parent == nil and targetData.parent == nil) then -- top-level for index, button in ipairs(frame.buttonsScroll.children) do - 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; + 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 - break; end end end @@ -1169,36 +1288,59 @@ end function OptionsPrivate.AddDisplayButton(data) EnsureDisplayButton(data); - WeakAuras.UpdateDisplayButton(data); + WeakAuras.UpdateThumbnail(data); frame.buttonsScroll:AddChild(displayButtons[data.id]); if(WeakAuras.regions[data.id] and WeakAuras.regions[data.id].region.SetStacks) then WeakAuras.regions[data.id].region:SetStacks(1); end end -function OptionsPrivate.SetGrouping(data) - if (frame.pickedDisplay == tempGroup and #tempGroup.controlledChildren > 0 and data) then +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 = {}; - -- set grouping for selected buttons + -- start grouping for selected buttons for index, childId in ipairs(tempGroup.controlledChildren) do local button = WeakAuras.GetDisplayButton(childId); - button:SetGrouping(tempGroup.controlledChildren, true); + button:StartGrouping(tempGroup.controlledChildren, true); children[childId] = true; end -- set grouping for non selected buttons for id, button in pairs(displayButtons) do if not children[button.data.id] then - button:SetGrouping(tempGroup.controlledChildren); + 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:SetGrouping(data); + 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) + 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 = WeakAuras.GetDisplayButton(childId); @@ -1211,27 +1353,157 @@ function OptionsPrivate.Ungroup(data) WeakAuras.FillOptions() end -function OptionsPrivate.SetDragging(data, drop) +function OptionsPrivate.DragReset() + for id, 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 = WeakAuras.GetDisplayButton(parents[parent]) + local bButton = WeakAuras.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 = WeakAuras.GetDisplayButton(lastAParent) + local bButton = WeakAuras.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 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) + else + button:Drop(mode, mainAura, target, action); + end + end + + 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 index, button in ipairs(buttonsToSort) do + button:Drop(mode, mainAura, target, action) + 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 id, button in pairs(displayButtons) do + button:UpdateOffset(); + end + + OptionsPrivate.SortDisplayButtons() + OptionsPrivate.UpdateButtonsScroll() +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 index = size, 1, -1 do - local childId = tempGroup.controlledChildren[index]; - local button = WeakAuras.GetDisplayButton(childId); - button:SetDragging(data, drop, size); - children[childId] = true; + + for child in OptionsPrivate.Private.TraverseAllChildren(tempGroup) do + local button = WeakAuras.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:SetDragging(data, drop); + button:DragStart("MULTI", false, mainAura); end end else - for id, button in pairs(displayButtons) do - button:SetDragging(data, drop); + if mainAura.controlledChildren then + -- Group aura + local mode = "GROUP" + local children = {}; + for child in OptionsPrivate.Private.TraverseAll(mainAura) do + local button = WeakAuras.GetDisplayButton(child.id); + button:DragStart(mode, true, mainAura) + 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(mode, false, mainAura); + end + end + else + for id, button in pairs(displayButtons) do + button:DragStart("SINGLE", id == mainAura.id, mainAura); + end end end end @@ -1243,34 +1515,53 @@ function OptionsPrivate.DropIndicator() indicator:SetHeight(4) indicator:SetFrameStrata("FULLSCREEN") - local texture = indicator:CreateTexture(nil, "ARTWORK") - texture:SetBlendMode("ADD") - texture:SetAllPoints(indicator) - texture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") + local groupTexture = indicator:CreateTexture(nil, "ARTWORK") + groupTexture:SetBlendMode("ADD") + groupTexture:SetTexture("Interface\\AddOns\\WeakAuras\\Media\\Textures\\Square_FullWhite") - local icon = indicator:CreateTexture(nil, "OVERLAY") - icon:SetSize(16,16) - icon:SetPoint("CENTER", indicator) + local lineTexture = indicator:CreateTexture(nil, "ARTWORK") + lineTexture:SetBlendMode("ADD") + lineTexture:SetAllPoints(indicator) + lineTexture:SetTexture("Interface\\PaperDollInfoFrame\\UI-Character-Tab-Highlight") - indicator.icon = icon - indicator.texture = texture + 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.UpdateDisplayButton(data) - local id = data.id; - local button = displayButtons[id]; - if (button) then - button:UpdateThumbnail() - if WeakAurasCompanion and button:IsGroup() then - button:RefreshUpdate() - end - end -end - function WeakAuras.UpdateThumbnail(data) local id = data.id local button = displayButtons[id] @@ -1279,6 +1570,7 @@ function WeakAuras.UpdateThumbnail(data) end button:UpdateThumbnail() end + function OptionsPrivate.OpenTexturePicker(baseObject, path, properties, textures, SetTextureFunc) frame.texturePicker:Open(baseObject, path, properties, textures, SetTextureFunc) end @@ -1292,7 +1584,7 @@ function OptionsPrivate.OpenModelPicker(baseObject, path) local loaded, reason = LoadAddOn("WeakAurasModelPaths"); if not(loaded) then reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") - WeakAuras.prettyPrint("ModelPaths could not be loaded, the addon is " .. reason); + WeakAuras.prettyPrint(string.format(L["ModelPaths could not be loaded, the addon is %s"], reason)); WeakAuras.ModelPaths = {}; end frame.modelPicker.modelTree:SetTree(WeakAuras.ModelPaths); @@ -1300,26 +1592,24 @@ function OptionsPrivate.OpenModelPicker(baseObject, path) frame.modelPicker:Open(baseObject, path); end -function WeakAuras.OpenCodeReview(data) +function OptionsPrivate.OpenCodeReview(data) frame.codereview:Open(data); end -function WeakAuras.CloseCodeReview(data) - frame.codereview:Close(); -end - -function WeakAuras.OpenTriggerTemplate(data, targetId) +function OptionsPrivate.OpenTriggerTemplate(data, targetId) if not(IsAddOnLoaded("WeakAurasTemplates")) then local loaded, reason = LoadAddOn("WeakAurasTemplates"); if not(loaded) then reason = string.lower("|cffff2020" .. _G["ADDON_" .. reason] .. "|r.") - WeakAuras.prettyPrint("Templates could not be loaded, the addon is " .. reason); + WeakAuras.prettyPrint(string.format(L["Templates could not be loaded, the addon is %s"], reason)); return; end - frame.newView = WeakAuras.CreateTemplateView(frame); + 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 - frame.newView.targetId = targetId; - frame.newView:Open(data); end function OptionsPrivate.ResetMoverSizer() @@ -1369,7 +1659,7 @@ function WeakAuras.NewAura(sourceData, regionType, targetId) AddDefaultSubRegions(data) - if (data.regionType ~= "group" and data.regionType ~= "dynamicgroup" and targetId) then + if targetId then local target = WeakAuras.GetDisplayButton(targetId); local group if (target) then @@ -1379,6 +1669,11 @@ function WeakAuras.NewAura(sourceData, regionType, targetId) group = WeakAuras.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 @@ -1392,10 +1687,10 @@ function WeakAuras.NewAura(sourceData, regionType, targetId) 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); - WeakAuras.UpdateDisplayButton(group.data); group.callbacks.UpdateExpandButton(); group:Expand(); group:ReloadTooltip(); @@ -1407,7 +1702,7 @@ function WeakAuras.NewAura(sourceData, regionType, targetId) OptionsPrivate.PickAndEditDisplay(data.id); end else - error("Calling 'WeakAuras.NewAura' with invalid groupId. Reload your UI to fix the display list.") + 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 @@ -1417,6 +1712,7 @@ function WeakAuras.NewAura(sourceData, regionType, targetId) end end + function OptionsPrivate.ResetCollapsed(id, namespace) if id then if namespace and collapsedOptions[id] then @@ -1572,7 +1868,6 @@ function OptionsPrivate.DuplicateCollapseData(id, namespace, path) local tmp = collapsedOptions[id][namespace] local lastKey = tremove(path) for _, key in ipairs(path) do - print(" key: ", key) tmp[key] = tmp[key] or {} tmp = tmp[key] end @@ -1583,7 +1878,7 @@ function OptionsPrivate.DuplicateCollapseData(id, namespace, path) end end -function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, hidden, setHidden, index, total) +function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, hidden, setHidden, withoutColor, index, total) local headerOption if withHeader and (not index or index == 1) then headerOption = { @@ -1615,7 +1910,7 @@ function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, h local triggerNum, sym = string.match(symbol, "(.+)%.(.+)") sym = sym or symbol - if sym == "c" or sym == "i" then + if sym == "i" then -- No special options for these else addOption(symbol .. "desc", { @@ -1635,7 +1930,7 @@ function OptionsPrivate.AddTextFormatOption(input, withHeader, get, addOption, h local selectedFormat = get(symbol .. "_format") if (OptionsPrivate.Private.format_types[selectedFormat]) then - OptionsPrivate.Private.format_types[selectedFormat].AddOptions(symbol, hidden, addOption, get) + OptionsPrivate.Private.format_types[selectedFormat].AddOptions(symbol, hidden, addOption, get, withoutColor) end seenSymbols[symbol] = true end diff --git a/WeakAurasOptions/WeakAurasOptions.toc b/WeakAurasOptions/WeakAurasOptions.toc index 5cda844..eaf880d 100644 --- a/WeakAurasOptions/WeakAurasOptions.toc +++ b/WeakAurasOptions/WeakAurasOptions.toc @@ -1,7 +1,7 @@ ## Interface: 30300 ## Title: WeakAuras Options ## Author: The WeakAuras Team -## Version: 3.2.3 +## Version: 3.7.9 ## Notes: Options for WeakAuras ## Notes-esES: Opciones para WeakAuras ## Notes-deDE: Optionen für WeakAuras @@ -29,11 +29,12 @@ RegionOptions\Model.lua RegionOptions\ProgressTexture.lua SubRegionOptions\SubRegionCommon.lua +SubRegionOptions\Background.lua SubRegionOptions\SubText.lua SubRegionOptions\Border.lua SubRegionOptions\Glow.lua SubRegionOptions\Tick.lua -SubRegionOptions\BarModel.lua +SubRegionOptions\Model.lua Cache.lua @@ -51,19 +52,21 @@ BuffTrigger2.lua GenericTrigger.lua WeakAurasOptions.lua -ExternalAddons.lua ConditionOptions.lua AuthorOptions.lua OptionsFrames\OptionsFrame.lua -# -- Groups + +# Groups OptionsFrames\CodeReview.lua OptionsFrames\IconPicker.lua OptionsFrames\ImportExport.lua OptionsFrames\ModelPicker.lua OptionsFrames\TextEditor.lua OptionsFrames\TexturePicker.lua -# -- misc Frames +OptionsFrames\Update.lua + +# Misc frames OptionsFrames\MoverSizer.lua OptionsFrames\FrameChooser.lua @@ -73,6 +76,8 @@ AceGUI-Widgets\AceGUIWidget-WeakAurasIcon.lua AceGUI-Widgets\AceGUIWidget-WeakAurasNewHeaderButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasLoadedHeaderButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasDisplayButton.lua +AceGUI-Widgets\AceGUIWidget-WeakAurasPendingInstallButton.lua +AceGUI-Widgets\AceGUIWidget-WeakAurasPendingUpdateButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasTextureButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasIconButton.lua AceGUI-Widgets\AceGUIWidget-WeakAurasMultiLineEditBox.lua @@ -86,3 +91,5 @@ AceGUI-Widgets\AceGUIContainer-WeakAurasTreeGroup.lua AceGUI-Widgets\AceGUIWidget-WeakAurasSnippetButton.lua AceGUI-Widgets\AceGUIContainer-WeakAurasInlineGroup.lua AceGUI-Widgets\AceGUIWidget-WeakAurasExpandAnchor.lua +AceGUI-Widgets\AceGUIWidget-WeakAurasSpacer.lua +AceGUI-Widgets\AceGuiWidget-WeakAurasProgressBar.lua