initial commit
This commit is contained in:
634
Core/API.lua
Normal file
634
Core/API.lua
Normal file
@@ -0,0 +1,634 @@
|
||||
------------------------------------------------------------------------
|
||||
-- Collection of functions that can be used in multiple places
|
||||
------------------------------------------------------------------------
|
||||
local E, L, V, P, G = unpack(select(2, ...))
|
||||
|
||||
local _G = _G
|
||||
local wipe, date = wipe, date
|
||||
local format, select, type, ipairs, pairs = format, select, type, ipairs, pairs
|
||||
local strmatch, strfind, tonumber, tostring = strmatch, strfind, tonumber, tostring
|
||||
local strlen, CreateFrame = strlen, CreateFrame
|
||||
local GetAddOnEnableState = GetAddOnEnableState
|
||||
local GetBattlefieldArenaFaction = GetBattlefieldArenaFaction
|
||||
local GetCVar, SetCVar = GetCVar, SetCVar
|
||||
local GetCVarBool = GetCVarBool
|
||||
local GetFunctionCPUUsage = GetFunctionCPUUsage
|
||||
local GetInstanceInfo = GetInstanceInfo
|
||||
local GetSpecialization = GetSpecialization
|
||||
local GetSpecializationRole = GetSpecializationRole
|
||||
local InCombatLockdown = InCombatLockdown
|
||||
local IsAddOnLoaded = IsAddOnLoaded
|
||||
local IsWargame = IsWargame
|
||||
local PLAYER_FACTION_GROUP = PLAYER_FACTION_GROUP
|
||||
local RequestBattlefieldScoreData = RequestBattlefieldScoreData
|
||||
local UIParentLoadAddOn = UIParentLoadAddOn
|
||||
local UnitAttackPower = UnitAttackPower
|
||||
local UnitFactionGroup = UnitFactionGroup
|
||||
local UnitGroupRolesAssigned = UnitGroupRolesAssigned
|
||||
local UnitHasVehicleUI = UnitHasVehicleUI
|
||||
local UnitIsMercenary = UnitIsMercenary
|
||||
local UnitStat = UnitStat
|
||||
local C_PetBattles_IsInBattle = C_PetBattles.IsInBattle
|
||||
local C_PvP_IsRatedBattleground = C_PvP.IsRatedBattleground
|
||||
local FACTION_HORDE = FACTION_HORDE
|
||||
local FACTION_ALLIANCE = FACTION_ALLIANCE
|
||||
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
|
||||
-- GLOBALS: ElvDB
|
||||
|
||||
function E:ClassColor(class, usePriestColor)
|
||||
if not class then return end
|
||||
|
||||
local color = (_G.CUSTOM_CLASS_COLORS and _G.CUSTOM_CLASS_COLORS[class]) or _G.RAID_CLASS_COLORS[class]
|
||||
if type(color) ~= 'table' then return end
|
||||
|
||||
if not color.colorStr then
|
||||
color.colorStr = E:RGBToHex(color.r, color.g, color.b, 'ff')
|
||||
elseif strlen(color.colorStr) == 6 then
|
||||
color.colorStr = 'ff'..color.colorStr
|
||||
end
|
||||
|
||||
if usePriestColor and class == 'PRIEST' and tonumber(color.colorStr, 16) > tonumber(E.PriestColors.colorStr, 16) then
|
||||
return E.PriestColors
|
||||
else
|
||||
return color
|
||||
end
|
||||
end
|
||||
|
||||
do -- other non-english locales require this
|
||||
E.UnlocalizedClasses = {}
|
||||
for k, v in pairs(_G.LOCALIZED_CLASS_NAMES_MALE) do E.UnlocalizedClasses[v] = k end
|
||||
for k, v in pairs(_G.LOCALIZED_CLASS_NAMES_FEMALE) do E.UnlocalizedClasses[v] = k end
|
||||
|
||||
function E:UnlocalizedClassName(className)
|
||||
return (className and className ~= '') and E.UnlocalizedClasses[className]
|
||||
end
|
||||
end
|
||||
|
||||
function E:IsFoolsDay()
|
||||
return strfind(date(), '04/01/') and not E.global.aprilFools
|
||||
end
|
||||
|
||||
do
|
||||
local essenceTextureID = 2975691
|
||||
function E:ScanTooltipTextures()
|
||||
local tt = E.ScanTooltip
|
||||
|
||||
if not tt.gems then
|
||||
tt.gems = {}
|
||||
else
|
||||
wipe(tt.gems)
|
||||
end
|
||||
|
||||
if not tt.essences then
|
||||
tt.essences = {}
|
||||
else
|
||||
for _, essences in pairs(tt.essences) do
|
||||
wipe(essences)
|
||||
end
|
||||
end
|
||||
|
||||
local step = 1
|
||||
for i = 1, 10 do
|
||||
local tex = _G['ElvUI_ScanTooltipTexture'..i]
|
||||
local texture = tex and tex:IsShown() and tex:GetTexture()
|
||||
if texture then
|
||||
if texture == essenceTextureID then
|
||||
local selected = (tt.gems[i-1] ~= essenceTextureID and tt.gems[i-1]) or nil
|
||||
if not tt.essences[step] then tt.essences[step] = {} end
|
||||
|
||||
tt.essences[step][1] = selected --essence texture if selected or nil
|
||||
tt.essences[step][2] = tex:GetAtlas() --atlas place 'tooltip-heartofazerothessence-major' or 'tooltip-heartofazerothessence-minor'
|
||||
tt.essences[step][3] = texture --border texture placed by the atlas
|
||||
--`CollectEssenceInfo` will add 4 (hex quality color) and 5 (essence name)
|
||||
|
||||
step = step + 1
|
||||
|
||||
if selected then
|
||||
tt.gems[i-1] = nil
|
||||
end
|
||||
else
|
||||
tt.gems[i] = texture
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return tt.gems, tt.essences
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetPlayerRole()
|
||||
local assignedRole = UnitGroupRolesAssigned('player')
|
||||
if assignedRole == 'NONE' then
|
||||
return E.myspec and GetSpecializationRole(E.myspec)
|
||||
end
|
||||
|
||||
return assignedRole
|
||||
end
|
||||
|
||||
function E:CheckRole()
|
||||
self.myspec = GetSpecialization()
|
||||
self.myrole = E:GetPlayerRole()
|
||||
|
||||
-- myrole = group role; TANK, HEALER, DAMAGER
|
||||
-- role = class role; Tank, Melee, Caster
|
||||
|
||||
local role
|
||||
if type(self.ClassRole[self.myclass]) == 'string' then
|
||||
role = self.ClassRole[self.myclass]
|
||||
elseif self.myspec then
|
||||
role = self.ClassRole[self.myclass][self.myspec]
|
||||
end
|
||||
|
||||
if not role then
|
||||
local playerint = select(2, UnitStat('player', 4))
|
||||
local playeragi = select(2, UnitStat('player', 2))
|
||||
local base, posBuff, negBuff = UnitAttackPower('player')
|
||||
local playerap = base + posBuff + negBuff
|
||||
|
||||
role = ((playerap > playerint) or (playeragi > playerint)) and 'Melee' or 'Caster'
|
||||
end
|
||||
|
||||
if self.role ~= role then
|
||||
self.role = role
|
||||
self.callbacks:Fire('RoleChanged')
|
||||
end
|
||||
|
||||
local dispel = self.DispelClasses[self.myclass]
|
||||
if self.myrole and (self.myclass ~= 'PRIEST' and dispel ~= nil) then
|
||||
dispel.Magic = (self.myrole == 'HEALER')
|
||||
end
|
||||
end
|
||||
|
||||
function E:IsDispellableByMe(debuffType)
|
||||
local dispel = self.DispelClasses[self.myclass]
|
||||
return dispel and dispel[debuffType]
|
||||
end
|
||||
|
||||
do
|
||||
local function SetOriginalHeight(f)
|
||||
if InCombatLockdown() then
|
||||
E:RegisterEventForObject('PLAYER_REGEN_ENABLED', SetOriginalHeight, SetOriginalHeight)
|
||||
return
|
||||
end
|
||||
|
||||
E.UIParent:SetHeight(E.UIParent.origHeight)
|
||||
|
||||
if f == SetOriginalHeight then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_ENABLED', SetOriginalHeight, SetOriginalHeight)
|
||||
end
|
||||
end
|
||||
|
||||
local function SetModifiedHeight(f)
|
||||
if InCombatLockdown() then
|
||||
E:RegisterEventForObject('PLAYER_REGEN_ENABLED', SetModifiedHeight, SetModifiedHeight)
|
||||
return
|
||||
end
|
||||
|
||||
E.UIParent:SetHeight(E.UIParent.origHeight - (_G.OrderHallCommandBar:GetHeight() + E.Border))
|
||||
|
||||
if f == SetModifiedHeight then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_ENABLED', SetModifiedHeight, SetModifiedHeight)
|
||||
end
|
||||
end
|
||||
|
||||
--This function handles disabling of OrderHall Bar or resizing of ElvUIParent if needed
|
||||
function E:HandleCommandBar()
|
||||
if E.global.general.commandBarSetting == 'DISABLED' then
|
||||
_G.OrderHallCommandBar:UnregisterAllEvents()
|
||||
_G.OrderHallCommandBar:SetScript('OnShow', _G.OrderHallCommandBar.Hide)
|
||||
_G.OrderHallCommandBar:Hide()
|
||||
_G.UIParent:UnregisterEvent('UNIT_AURA') --Only used for OrderHall Bar
|
||||
elseif E.global.general.commandBarSetting == 'ENABLED_RESIZEPARENT' then
|
||||
_G.OrderHallCommandBar:HookScript('OnShow', SetModifiedHeight)
|
||||
_G.OrderHallCommandBar:HookScript('OnHide', SetOriginalHeight)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local Masque = E.Libs.Masque
|
||||
local MasqueGroupState = {}
|
||||
local MasqueGroupToTableElement = {
|
||||
['ActionBars'] = {'actionbar', 'actionbars'},
|
||||
['Pet Bar'] = {'actionbar', 'petBar'},
|
||||
['Stance Bar'] = {'actionbar', 'stanceBar'},
|
||||
['Buffs'] = {'auras', 'buffs'},
|
||||
['Debuffs'] = {'auras', 'debuffs'},
|
||||
}
|
||||
|
||||
function E:MasqueCallback(Group, _, _, _, _, Disabled)
|
||||
if not E.private then return end
|
||||
local element = MasqueGroupToTableElement[Group]
|
||||
if element then
|
||||
if Disabled then
|
||||
if E.private[element[1]].masque[element[2]] and MasqueGroupState[Group] == 'enabled' then
|
||||
E.private[element[1]].masque[element[2]] = false
|
||||
E:StaticPopup_Show('CONFIG_RL')
|
||||
end
|
||||
MasqueGroupState[Group] = 'disabled'
|
||||
else
|
||||
MasqueGroupState[Group] = 'enabled'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if Masque then
|
||||
Masque:Register('ElvUI', E.MasqueCallback)
|
||||
end
|
||||
end
|
||||
|
||||
do
|
||||
local CPU_USAGE = {}
|
||||
local function CompareCPUDiff(showall, minCalls)
|
||||
local greatestUsage, greatestCalls, greatestName, newName, newFunc
|
||||
local greatestDiff, lastModule, mod, usage, calls, diff = 0
|
||||
|
||||
for name, oldUsage in pairs(CPU_USAGE) do
|
||||
newName, newFunc = strmatch(name, '^([^:]+):(.+)$')
|
||||
if not newFunc then
|
||||
E:Print('CPU_USAGE:', name, newFunc)
|
||||
else
|
||||
if newName ~= lastModule then
|
||||
mod = E:GetModule(newName, true) or E
|
||||
lastModule = newName
|
||||
end
|
||||
usage, calls = GetFunctionCPUUsage(mod[newFunc], true)
|
||||
diff = usage - oldUsage
|
||||
if showall and (calls > minCalls) then
|
||||
E:Print('Name('..name..') Calls('..calls..') MS('..(usage or 0)..') Diff('..(diff > 0 and format('%.3f', diff) or 0)..')')
|
||||
end
|
||||
if (diff > greatestDiff) and calls > minCalls then
|
||||
greatestName, greatestUsage, greatestCalls, greatestDiff = name, usage, calls, diff
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if greatestName then
|
||||
E:Print(greatestName.. ' had the CPU usage of: '..(greatestUsage > 0 and format('%.3f', greatestUsage) or 0)..'ms. And has been called '.. greatestCalls..' times.')
|
||||
else
|
||||
E:Print('CPU Usage: No CPU Usage differences found.')
|
||||
end
|
||||
|
||||
wipe(CPU_USAGE)
|
||||
end
|
||||
|
||||
function E:GetTopCPUFunc(msg)
|
||||
if not GetCVarBool('scriptProfile') then
|
||||
E:Print('For `/cpuusage` to work, you need to enable script profiling via: `/console scriptProfile 1` then reload. Disable after testing by setting it back to 0.')
|
||||
return
|
||||
end
|
||||
|
||||
local module, showall, delay, minCalls = strmatch(msg, '^(%S+)%s*(%S*)%s*(%S*)%s*(.*)$')
|
||||
local checkCore, mod = (not module or module == '') and 'E'
|
||||
|
||||
showall = (showall == 'true' and true) or false
|
||||
delay = (delay == 'nil' and nil) or tonumber(delay) or 5
|
||||
minCalls = (minCalls == 'nil' and nil) or tonumber(minCalls) or 15
|
||||
|
||||
wipe(CPU_USAGE)
|
||||
if module == 'all' then
|
||||
for moduName, modu in pairs(self.modules) do
|
||||
for funcName, func in pairs(modu) do
|
||||
if funcName ~= 'GetModule' and type(func) == 'function' then
|
||||
CPU_USAGE[moduName..':'..funcName] = GetFunctionCPUUsage(func, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
if not checkCore then
|
||||
mod = self:GetModule(module, true)
|
||||
if not mod then
|
||||
self:Print(module..' not found, falling back to checking core.')
|
||||
mod, checkCore = self, 'E'
|
||||
end
|
||||
else
|
||||
mod = self
|
||||
end
|
||||
for name, func in pairs(mod) do
|
||||
if (name ~= 'GetModule') and type(func) == 'function' then
|
||||
CPU_USAGE[(checkCore or module)..':'..name] = GetFunctionCPUUsage(func, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
self:Delay(delay, CompareCPUDiff, showall, minCalls)
|
||||
self:Print('Calculating CPU Usage differences (module: '..(checkCore or module)..', showall: '..tostring(showall)..', minCalls: '..tostring(minCalls)..', delay: '..tostring(delay)..')')
|
||||
end
|
||||
end
|
||||
|
||||
function E:Dump(object, inspect)
|
||||
if GetAddOnEnableState(E.myname, 'Blizzard_DebugTools') == 0 then
|
||||
E:Print('Blizzard_DebugTools is disabled.')
|
||||
return
|
||||
end
|
||||
|
||||
local debugTools = IsAddOnLoaded('Blizzard_DebugTools')
|
||||
if not debugTools then UIParentLoadAddOn('Blizzard_DebugTools') end
|
||||
|
||||
if inspect then
|
||||
local tableType = type(object)
|
||||
if tableType == 'table' then
|
||||
_G.DisplayTableInspectorWindow(object)
|
||||
else
|
||||
E:Print('Failed: ', tostring(object), ' is type: ', tableType,'. Requires table object.')
|
||||
end
|
||||
else
|
||||
_G.DevTools_Dump(object)
|
||||
end
|
||||
end
|
||||
|
||||
function E:AddNonPetBattleFrames()
|
||||
if InCombatLockdown() then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames, E.AddNonPetBattleFrames)
|
||||
return
|
||||
elseif E:IsEventRegisteredForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames) then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.AddNonPetBattleFrames, E.AddNonPetBattleFrames)
|
||||
end
|
||||
|
||||
for object, data in pairs(E.FrameLocks) do
|
||||
local parent, strata
|
||||
if type(data) == 'table' then
|
||||
parent, strata = data.parent, data.strata
|
||||
elseif data == true then
|
||||
parent = _G.UIParent
|
||||
end
|
||||
|
||||
local obj = _G[object] or object
|
||||
obj:SetParent(parent)
|
||||
if strata then
|
||||
obj:SetFrameStrata(strata)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:RemoveNonPetBattleFrames()
|
||||
if InCombatLockdown() then
|
||||
E:RegisterEventForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames, E.RemoveNonPetBattleFrames)
|
||||
return
|
||||
elseif E:IsEventRegisteredForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames) then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_DISABLED', E.RemoveNonPetBattleFrames, E.RemoveNonPetBattleFrames)
|
||||
end
|
||||
|
||||
for object in pairs(E.FrameLocks) do
|
||||
local obj = _G[object] or object
|
||||
obj:SetParent(E.HiddenFrame)
|
||||
end
|
||||
end
|
||||
|
||||
function E:RegisterPetBattleHideFrames(object, originalParent, originalStrata)
|
||||
if not object or not originalParent then
|
||||
E:Print('Error. Usage: RegisterPetBattleHideFrames(object, originalParent, originalStrata)')
|
||||
return
|
||||
end
|
||||
|
||||
object = _G[object] or object
|
||||
|
||||
--If already doing pokemon
|
||||
if C_PetBattles_IsInBattle() then
|
||||
object:SetParent(E.HiddenFrame)
|
||||
end
|
||||
|
||||
E.FrameLocks[object] = {
|
||||
parent = originalParent,
|
||||
strata = originalStrata or nil,
|
||||
}
|
||||
end
|
||||
|
||||
function E:UnregisterPetBattleHideFrames(object)
|
||||
if not object then
|
||||
E:Print('Error. Usage: UnregisterPetBattleHideFrames(object)')
|
||||
return
|
||||
end
|
||||
|
||||
object = _G[object] or object
|
||||
|
||||
--Check if object was registered to begin with
|
||||
if not E.FrameLocks[object] then return end
|
||||
|
||||
--Change parent of object back to original parent
|
||||
local originalParent = E.FrameLocks[object].parent
|
||||
if originalParent then
|
||||
object:SetParent(originalParent)
|
||||
end
|
||||
|
||||
--Change strata of object back to original
|
||||
local originalStrata = E.FrameLocks[object].strata
|
||||
if originalStrata then
|
||||
object:SetFrameStrata(originalStrata)
|
||||
end
|
||||
|
||||
--Remove object from table
|
||||
E.FrameLocks[object] = nil
|
||||
end
|
||||
|
||||
function E:RegisterObjectForVehicleLock(object, originalParent)
|
||||
if not object or not originalParent then
|
||||
E:Print('Error. Usage: RegisterObjectForVehicleLock(object, originalParent)')
|
||||
return
|
||||
end
|
||||
|
||||
object = _G[object] or object
|
||||
--Entering/Exiting vehicles will often happen in combat.
|
||||
--For this reason we cannot allow protected objects.
|
||||
if object.IsProtected and object:IsProtected() then
|
||||
E:Print('Error. Object is protected and cannot be changed in combat.')
|
||||
return
|
||||
end
|
||||
|
||||
--Check if we are already in a vehicles
|
||||
if UnitHasVehicleUI('player') then
|
||||
object:SetParent(E.HiddenFrame)
|
||||
end
|
||||
|
||||
--Add object to table
|
||||
E.VehicleLocks[object] = originalParent
|
||||
end
|
||||
|
||||
function E:UnregisterObjectForVehicleLock(object)
|
||||
if not object then
|
||||
E:Print('Error. Usage: UnregisterObjectForVehicleLock(object)')
|
||||
return
|
||||
end
|
||||
|
||||
object = _G[object] or object
|
||||
--Check if object was registered to begin with
|
||||
if not E.VehicleLocks[object] then
|
||||
return
|
||||
end
|
||||
|
||||
--Change parent of object back to original parent
|
||||
local originalParent = E.VehicleLocks[object]
|
||||
if originalParent then
|
||||
object:SetParent(originalParent)
|
||||
end
|
||||
|
||||
--Remove object from table
|
||||
E.VehicleLocks[object] = nil
|
||||
end
|
||||
|
||||
function E:EnterVehicleHideFrames(_, unit)
|
||||
if unit ~= 'player' then return end
|
||||
for object in pairs(E.VehicleLocks) do
|
||||
object:SetParent(E.HiddenFrame)
|
||||
end
|
||||
end
|
||||
|
||||
function E:ExitVehicleShowFrames(_, unit)
|
||||
if unit ~= 'player' then return end
|
||||
for object, originalParent in pairs(E.VehicleLocks) do
|
||||
object:SetParent(originalParent)
|
||||
end
|
||||
end
|
||||
|
||||
function E:RequestBGInfo()
|
||||
RequestBattlefieldScoreData()
|
||||
end
|
||||
|
||||
function E:PLAYER_ENTERING_WORLD(_, initLogin, isReload)
|
||||
self:CheckRole()
|
||||
|
||||
if initLogin or not ElvDB.LuaErrorDisabledAddOns then
|
||||
ElvDB.LuaErrorDisabledAddOns = {}
|
||||
end
|
||||
|
||||
if initLogin or isReload then
|
||||
self:CheckIncompatible()
|
||||
end
|
||||
|
||||
if not self.MediaUpdated then
|
||||
self:UpdateMedia()
|
||||
self.MediaUpdated = true
|
||||
end
|
||||
|
||||
local _, instanceType = GetInstanceInfo()
|
||||
if instanceType == 'pvp' then
|
||||
self.BGTimer = self:ScheduleRepeatingTimer('RequestBGInfo', 5)
|
||||
self:RequestBGInfo()
|
||||
elseif self.BGTimer then
|
||||
self:CancelTimer(self.BGTimer)
|
||||
self.BGTimer = nil
|
||||
end
|
||||
end
|
||||
|
||||
function E:PLAYER_REGEN_ENABLED()
|
||||
if self.CVarUpdate then
|
||||
for cvarName, value in pairs(self.LockedCVars) do
|
||||
if not self.IgnoredCVars[cvarName] and (GetCVar(cvarName) ~= value) then
|
||||
SetCVar(cvarName, value)
|
||||
end
|
||||
end
|
||||
|
||||
self.CVarUpdate = nil
|
||||
end
|
||||
|
||||
if self.ShowOptionsUI then
|
||||
self:ToggleOptionsUI()
|
||||
|
||||
self.ShowOptionsUI = nil
|
||||
end
|
||||
end
|
||||
|
||||
function E:PLAYER_REGEN_DISABLED()
|
||||
local err
|
||||
|
||||
if IsAddOnLoaded('ElvUI_OptionsUI') then
|
||||
local ACD = self.Libs.AceConfigDialog
|
||||
if ACD and ACD.OpenFrames and ACD.OpenFrames.ElvUI then
|
||||
ACD:Close('ElvUI')
|
||||
err = true
|
||||
end
|
||||
end
|
||||
|
||||
if self.CreatedMovers then
|
||||
for name in pairs(self.CreatedMovers) do
|
||||
local mover = _G[name]
|
||||
if mover and mover:IsShown() then
|
||||
mover:Hide()
|
||||
err = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if err then
|
||||
self:Print(ERR_NOT_IN_COMBAT)
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetUnitBattlefieldFaction(unit)
|
||||
local englishFaction, localizedFaction = UnitFactionGroup(unit)
|
||||
|
||||
-- this might be a rated BG or wargame and if so the player's faction might be altered
|
||||
-- should also apply if `player` is a mercenary.
|
||||
if unit == 'player' then
|
||||
if C_PvP_IsRatedBattleground() or IsWargame() then
|
||||
englishFaction = PLAYER_FACTION_GROUP[GetBattlefieldArenaFaction()]
|
||||
localizedFaction = (englishFaction == 'Alliance' and FACTION_ALLIANCE) or FACTION_HORDE
|
||||
elseif UnitIsMercenary(unit) then
|
||||
if englishFaction == 'Alliance' then
|
||||
englishFaction, localizedFaction = 'Horde', FACTION_HORDE
|
||||
else
|
||||
englishFaction, localizedFaction = 'Alliance', FACTION_ALLIANCE
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return englishFaction, localizedFaction
|
||||
end
|
||||
|
||||
function E:NEUTRAL_FACTION_SELECT_RESULT()
|
||||
E.myfaction, E.myLocalizedFaction = UnitFactionGroup('player')
|
||||
end
|
||||
|
||||
function E:PLAYER_LEVEL_UP(_, level)
|
||||
E.mylevel = level
|
||||
end
|
||||
|
||||
function E:LoadAPI()
|
||||
E:RegisterEvent('PLAYER_LEVEL_UP')
|
||||
E:RegisterEvent('PLAYER_ENTERING_WORLD')
|
||||
E:RegisterEvent('PLAYER_REGEN_ENABLED')
|
||||
E:RegisterEvent('PLAYER_REGEN_DISABLED')
|
||||
E:RegisterEvent('NEUTRAL_FACTION_SELECT_RESULT')
|
||||
E:RegisterEvent('PET_BATTLE_CLOSE', 'AddNonPetBattleFrames')
|
||||
E:RegisterEvent('PET_BATTLE_OPENING_START', 'RemoveNonPetBattleFrames')
|
||||
E:RegisterEvent('PLAYER_SPECIALIZATION_CHANGED', 'CheckRole')
|
||||
E:RegisterEvent('UNIT_ENTERED_VEHICLE', 'EnterVehicleHideFrames')
|
||||
E:RegisterEvent('UNIT_EXITED_VEHICLE', 'ExitVehicleShowFrames')
|
||||
E:RegisterEvent('UI_SCALE_CHANGED', 'PixelScaleChanged')
|
||||
|
||||
do -- setup cropIcon texCoords
|
||||
local opt = E.db.general.cropIcon
|
||||
local modifier = 0.04 * opt
|
||||
for i, v in ipairs(E.TexCoords) do
|
||||
if i % 2 == 0 then
|
||||
E.TexCoords[i] = v - modifier
|
||||
else
|
||||
E.TexCoords[i] = v + modifier
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not strfind(date(), '04/01/') then
|
||||
E.global.aprilFools = nil
|
||||
end
|
||||
|
||||
if _G.OrderHallCommandBar then
|
||||
E:HandleCommandBar()
|
||||
else
|
||||
local frame = CreateFrame('Frame')
|
||||
frame:RegisterEvent('ADDON_LOADED')
|
||||
frame:SetScript('OnEvent', function(Frame, event, addon)
|
||||
if event == 'ADDON_LOADED' and addon == 'Blizzard_OrderHallUI' then
|
||||
if InCombatLockdown() then
|
||||
Frame:RegisterEvent('PLAYER_REGEN_ENABLED')
|
||||
else
|
||||
E:HandleCommandBar()
|
||||
end
|
||||
Frame:UnregisterEvent(event)
|
||||
elseif event == 'PLAYER_REGEN_ENABLED' then
|
||||
E:HandleCommandBar()
|
||||
Frame:UnregisterEvent(event)
|
||||
end
|
||||
end)
|
||||
end
|
||||
end
|
||||
353
Core/Animation.lua
Normal file
353
Core/Animation.lua
Normal file
@@ -0,0 +1,353 @@
|
||||
------------------------------------------------------------------------
|
||||
-- Animation Functions
|
||||
------------------------------------------------------------------------
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local _G = _G
|
||||
local random, next, unpack, strsub = random, next, unpack, strsub
|
||||
|
||||
E.AnimShake = {{-9,7,-7,12}, {-5,9,-9,5}, {-5,7,-7,5}, {-9,9,-9,9}, {-5,7,-7,5}, {-9,7,-9,5}}
|
||||
E.AnimShakeH = {-5,5,-2,5,-2,5}
|
||||
|
||||
function E:FlashLoopFinished(requested)
|
||||
if not requested then self:Play() end
|
||||
end
|
||||
|
||||
function E:RandomAnimShake(index)
|
||||
local s = E.AnimShake[index]
|
||||
return random(s[1], s[2]), random(s[3], s[4])
|
||||
end
|
||||
|
||||
--TEST
|
||||
--[[local t = UIParent:CreateFontString(nil, 'OVERLAY', 'GameTooltipText')
|
||||
t:SetText(0)
|
||||
t:Point('CENTER')
|
||||
t:FontTemplate(nil, 20)
|
||||
E:SetUpAnimGroup(t, 'Number', 10, 5)
|
||||
|
||||
local b = CreateFrame('BUTTON', nil, UIParent)
|
||||
b:Point('CENTER', 0, -100)
|
||||
b:SetTemplate()
|
||||
b:Size(40,30)
|
||||
b:EnableMouse(true)
|
||||
b:SetScript('OnClick', function()
|
||||
if t:GetText() == 10 then
|
||||
t.NumberAnim:SetChange(0)
|
||||
t.NumberAnimGroup:Play()
|
||||
else
|
||||
t.NumberAnim:SetChange(10)
|
||||
t.NumberAnimGroup:Play()
|
||||
end
|
||||
end)]]
|
||||
|
||||
function E:SetUpAnimGroup(obj, Type, ...)
|
||||
if not Type then Type = 'Flash' end
|
||||
|
||||
if strsub(Type, 1, 5) == 'Flash' then
|
||||
obj.anim = obj:CreateAnimationGroup('Flash')
|
||||
obj.anim.fadein = obj.anim:CreateAnimation('ALPHA', 'FadeIn')
|
||||
obj.anim.fadein:SetFromAlpha(0)
|
||||
obj.anim.fadein:SetToAlpha(1)
|
||||
obj.anim.fadein:SetOrder(2)
|
||||
|
||||
obj.anim.fadeout = obj.anim:CreateAnimation('ALPHA', 'FadeOut')
|
||||
obj.anim.fadeout:SetFromAlpha(1)
|
||||
obj.anim.fadeout:SetToAlpha(0)
|
||||
obj.anim.fadeout:SetOrder(1)
|
||||
|
||||
if Type == 'FlashLoop' then
|
||||
obj.anim:SetScript('OnFinished', E.FlashLoopFinished)
|
||||
end
|
||||
elseif strsub(Type, 1, 5) == 'Shake' then
|
||||
local shake = obj:CreateAnimationGroup(Type)
|
||||
shake:SetLooping('REPEAT')
|
||||
shake.path = shake:CreateAnimation('Path')
|
||||
|
||||
if Type == 'Shake' then
|
||||
shake.path:SetDuration(0.7)
|
||||
obj.shake = shake
|
||||
elseif Type == 'ShakeH' then
|
||||
shake.path:SetDuration(2)
|
||||
obj.shakeh = shake
|
||||
end
|
||||
|
||||
for i = 1, 6 do
|
||||
shake.path[i] = shake.path:CreateControlPoint()
|
||||
shake.path[i]:SetOrder(i)
|
||||
|
||||
if Type == 'Shake' then
|
||||
shake.path[i]:SetOffset(E:RandomAnimShake(i))
|
||||
else
|
||||
shake.path[i]:SetOffset(E.AnimShakeH[i], 0)
|
||||
end
|
||||
end
|
||||
elseif Type == 'Elastic' then
|
||||
local width, height, duration, loop = ...
|
||||
obj.elastic = _G.CreateAnimationGroup(obj)
|
||||
|
||||
for i = 1, 4 do
|
||||
local anim = obj.elastic:CreateAnimation(i < 3 and 'width' or 'height')
|
||||
anim:SetChange((i==1 and width*0.45) or (i==2 and width) or (i==3 and height*0.45) or height)
|
||||
anim:SetEasing('inout-elastic')
|
||||
anim:SetDuration(duration)
|
||||
obj.elastic[i] = anim
|
||||
end
|
||||
|
||||
obj.elastic[1]:SetScript('OnFinished', function(anim) anim:Stop() obj.elastic[2]:Play() end)
|
||||
obj.elastic[3]:SetScript('OnFinished', function(anim) anim:Stop() obj.elastic[4]:Play() end)
|
||||
obj.elastic[2]:SetScript('OnFinished', function(anim) anim:Stop() if loop then obj.elastic[1]:Play() end end)
|
||||
obj.elastic[4]:SetScript('OnFinished', function(anim) anim:Stop() if loop then obj.elastic[3]:Play() end end)
|
||||
elseif Type == 'Number' then
|
||||
local endingNumber, duration = ...
|
||||
obj.NumberAnimGroup = _G.CreateAnimationGroup(obj)
|
||||
obj.NumberAnim = obj.NumberAnimGroup:CreateAnimation('number')
|
||||
obj.NumberAnim:SetChange(endingNumber)
|
||||
obj.NumberAnim:SetEasing('in-circular')
|
||||
obj.NumberAnim:SetDuration(duration)
|
||||
else
|
||||
local x, y, duration, customName = ...
|
||||
if not customName then customName = 'anim' end
|
||||
|
||||
local anim = obj:CreateAnimationGroup('Move_In')
|
||||
obj[customName] = anim
|
||||
|
||||
anim.in1 = anim:CreateAnimation('Translation')
|
||||
anim.in1:SetDuration(0)
|
||||
anim.in1:SetOrder(1)
|
||||
anim.in1:SetOffset(x, y)
|
||||
|
||||
anim.in2 = anim:CreateAnimation('Translation')
|
||||
anim.in2:SetDuration(duration)
|
||||
anim.in2:SetOrder(2)
|
||||
anim.in2:SetSmoothing('OUT')
|
||||
anim.in2:SetOffset(-x, -y)
|
||||
|
||||
anim.out1 = obj:CreateAnimationGroup('Move_Out')
|
||||
anim.out1:SetScript('OnFinished', function() obj:Hide() end)
|
||||
|
||||
anim.out2 = anim.out1:CreateAnimation('Translation')
|
||||
anim.out2:SetDuration(duration)
|
||||
anim.out2:SetOrder(1)
|
||||
anim.out2:SetSmoothing('IN')
|
||||
anim.out2:SetOffset(x, y)
|
||||
end
|
||||
end
|
||||
|
||||
function E:Elasticize(obj, width, height)
|
||||
if not obj.elastic then
|
||||
if not width then width = obj:GetWidth() end
|
||||
if not height then height = obj:GetHeight() end
|
||||
E:SetUpAnimGroup(obj, 'Elastic', width, height, 2, false)
|
||||
end
|
||||
|
||||
obj.elastic[1]:Play()
|
||||
obj.elastic[3]:Play()
|
||||
end
|
||||
|
||||
function E:StopElasticize(obj)
|
||||
if obj.elastic then
|
||||
obj.elastic[1]:Stop(true)
|
||||
obj.elastic[3]:Stop(true)
|
||||
end
|
||||
end
|
||||
|
||||
function E:Shake(obj)
|
||||
if not obj.shake then
|
||||
E:SetUpAnimGroup(obj, 'Shake')
|
||||
end
|
||||
|
||||
obj.shake:Play()
|
||||
end
|
||||
|
||||
function E:StopShake(obj)
|
||||
if obj.shake then
|
||||
obj.shake:Finish()
|
||||
end
|
||||
end
|
||||
|
||||
function E:ShakeHorizontal(obj)
|
||||
if not obj.shakeh then
|
||||
E:SetUpAnimGroup(obj, 'ShakeH')
|
||||
end
|
||||
|
||||
obj.shakeh:Play()
|
||||
end
|
||||
|
||||
function E:StopShakeHorizontal(obj)
|
||||
if obj.shakeh then
|
||||
obj.shakeh:Finish()
|
||||
end
|
||||
end
|
||||
|
||||
function E:Flash(obj, duration, loop)
|
||||
if not obj.anim then
|
||||
E:SetUpAnimGroup(obj, loop and 'FlashLoop' or 'Flash')
|
||||
end
|
||||
|
||||
if not obj.anim:IsPlaying() then
|
||||
obj.anim.fadein:SetDuration(duration)
|
||||
obj.anim.fadeout:SetDuration(duration)
|
||||
obj.anim:Play()
|
||||
end
|
||||
end
|
||||
|
||||
function E:StopFlash(obj)
|
||||
if obj.anim and obj.anim:IsPlaying() then
|
||||
obj.anim:Stop()
|
||||
end
|
||||
end
|
||||
|
||||
function E:SlideIn(obj, customName)
|
||||
if not customName then customName = 'anim' end
|
||||
if not obj[customName] then return end
|
||||
|
||||
obj[customName].out1:Stop()
|
||||
obj[customName]:Play()
|
||||
obj:Show()
|
||||
end
|
||||
|
||||
function E:SlideOut(obj, customName)
|
||||
if not customName then customName = 'anim' end
|
||||
if not obj[customName] then return end
|
||||
|
||||
obj[customName]:Finish()
|
||||
obj[customName]:Stop()
|
||||
obj[customName].out1:Play()
|
||||
end
|
||||
|
||||
local FADEFRAMES, FADEMANAGER = {}, CreateFrame('FRAME')
|
||||
FADEMANAGER.delay = 0.025
|
||||
|
||||
function E:UIFrameFade_OnUpdate(elapsed)
|
||||
FADEMANAGER.timer = (FADEMANAGER.timer or 0) + elapsed
|
||||
|
||||
if FADEMANAGER.timer > FADEMANAGER.delay then
|
||||
FADEMANAGER.timer = 0
|
||||
|
||||
for frame, info in next, FADEFRAMES do
|
||||
-- Reset the timer if there isn't one, this is just an internal counter
|
||||
if frame:IsVisible() then
|
||||
info.fadeTimer = (info.fadeTimer or 0) + (elapsed + FADEMANAGER.delay)
|
||||
else
|
||||
info.fadeTimer = info.timeToFade + 1
|
||||
end
|
||||
|
||||
-- If the fadeTimer is less then the desired fade time then set the alpha otherwise hold the fade state, call the finished function, or just finish the fade
|
||||
if info.fadeTimer < info.timeToFade then
|
||||
if info.mode == 'IN' then
|
||||
frame:SetAlpha((info.fadeTimer / info.timeToFade) * info.diffAlpha + info.startAlpha)
|
||||
else
|
||||
frame:SetAlpha(((info.timeToFade - info.fadeTimer) / info.timeToFade) * info.diffAlpha + info.endAlpha)
|
||||
end
|
||||
else
|
||||
frame:SetAlpha(info.endAlpha)
|
||||
|
||||
-- If there is a fadeHoldTime then wait until its passed to continue on
|
||||
if info.fadeHoldTime and info.fadeHoldTime > 0 then
|
||||
info.fadeHoldTime = info.fadeHoldTime - elapsed
|
||||
else
|
||||
-- Complete the fade and call the finished function if there is one
|
||||
E:UIFrameFadeRemoveFrame(frame)
|
||||
|
||||
if info.finishedFunc then
|
||||
if info.finishedArgs then
|
||||
info.finishedFunc(unpack(info.finishedArgs))
|
||||
else -- optional method
|
||||
info.finishedFunc(info.finishedArg1, info.finishedArg2, info.finishedArg3, info.finishedArg4, info.finishedArg5)
|
||||
end
|
||||
|
||||
if not info.finishedFuncKeep then
|
||||
info.finishedFunc = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not next(FADEFRAMES) then
|
||||
FADEMANAGER:SetScript('OnUpdate', nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Generic fade function
|
||||
function E:UIFrameFade(frame, info)
|
||||
if not frame or frame:IsForbidden() then return end
|
||||
|
||||
frame.fadeInfo = info
|
||||
|
||||
if not info.mode then
|
||||
info.mode = 'IN'
|
||||
end
|
||||
|
||||
if info.mode == 'IN' then
|
||||
if not info.startAlpha then info.startAlpha = 0 end
|
||||
if not info.endAlpha then info.endAlpha = 1 end
|
||||
if not info.diffAlpha then info.diffAlpha = info.endAlpha - info.startAlpha end
|
||||
else
|
||||
if not info.startAlpha then info.startAlpha = 1 end
|
||||
if not info.endAlpha then info.endAlpha = 0 end
|
||||
if not info.diffAlpha then info.diffAlpha = info.startAlpha - info.endAlpha end
|
||||
end
|
||||
|
||||
frame:SetAlpha(info.startAlpha)
|
||||
|
||||
if not frame:IsProtected() then
|
||||
frame:Show()
|
||||
end
|
||||
|
||||
if not FADEFRAMES[frame] then
|
||||
FADEFRAMES[frame] = info -- read below comment
|
||||
FADEMANAGER:SetScript('OnUpdate', E.UIFrameFade_OnUpdate)
|
||||
else
|
||||
FADEFRAMES[frame] = info -- keep these both, we need this updated in the event its changed to another ref from a plugin or sth, don't move it up!
|
||||
end
|
||||
end
|
||||
|
||||
-- Convenience function to do a simple fade in
|
||||
function E:UIFrameFadeIn(frame, timeToFade, startAlpha, endAlpha)
|
||||
if not frame or frame:IsForbidden() then return end
|
||||
|
||||
if frame.FadeObject then
|
||||
frame.FadeObject.fadeTimer = nil
|
||||
else
|
||||
frame.FadeObject = {}
|
||||
end
|
||||
|
||||
frame.FadeObject.mode = 'IN'
|
||||
frame.FadeObject.timeToFade = timeToFade
|
||||
frame.FadeObject.startAlpha = startAlpha
|
||||
frame.FadeObject.endAlpha = endAlpha
|
||||
frame.FadeObject.diffAlpha = endAlpha - startAlpha
|
||||
|
||||
E:UIFrameFade(frame, frame.FadeObject)
|
||||
end
|
||||
|
||||
-- Convenience function to do a simple fade out
|
||||
function E:UIFrameFadeOut(frame, timeToFade, startAlpha, endAlpha)
|
||||
if not frame or frame:IsForbidden() then return end
|
||||
|
||||
if frame.FadeObject then
|
||||
frame.FadeObject.fadeTimer = nil
|
||||
else
|
||||
frame.FadeObject = {}
|
||||
end
|
||||
|
||||
frame.FadeObject.mode = 'OUT'
|
||||
frame.FadeObject.timeToFade = timeToFade
|
||||
frame.FadeObject.startAlpha = startAlpha
|
||||
frame.FadeObject.endAlpha = endAlpha
|
||||
frame.FadeObject.diffAlpha = startAlpha - endAlpha
|
||||
|
||||
E:UIFrameFade(frame, frame.FadeObject)
|
||||
end
|
||||
|
||||
function E:UIFrameFadeRemoveFrame(frame)
|
||||
if frame and FADEFRAMES[frame] then
|
||||
if frame.FadeObject then
|
||||
frame.FadeObject.fadeTimer = nil
|
||||
end
|
||||
|
||||
FADEFRAMES[frame] = nil
|
||||
end
|
||||
end
|
||||
355
Core/AprilFools.lua
Normal file
355
Core/AprilFools.lua
Normal file
@@ -0,0 +1,355 @@
|
||||
------------------------------------------------------------------------
|
||||
-- Collection of previous april fools pranks
|
||||
-- Harlem Shake: Try it out with the command /harlemshake
|
||||
-- Hello Kitty: Try it out with the command /hellokitty (pay attention to the popups, read what it says)
|
||||
------------------------------------------------------------------------
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local UF = E:GetModule('UnitFrames')
|
||||
local AB = E:GetModule('ActionBars')
|
||||
|
||||
local _G = _G
|
||||
local pairs = pairs
|
||||
local wipe, tinsert = wipe, tinsert
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local DoEmote = DoEmote
|
||||
local GetCVar, SetCVar = GetCVar, SetCVar
|
||||
local NUM_PET_ACTION_SLOTS = NUM_PET_ACTION_SLOTS
|
||||
local PlayMusic, StopMusic = PlayMusic, StopMusic
|
||||
-- GLOBALS: ElvUI_StaticPopup1, ElvUI_StaticPopup1Button1, ElvUI_StanceBar
|
||||
|
||||
--Harlem Shake (Activate with command: /harlemshake)
|
||||
--People really seemed to like this one. We got a lot of positive responses.
|
||||
do
|
||||
function E:StopHarlemShake()
|
||||
E.isMassiveShaking = nil
|
||||
StopMusic()
|
||||
SetCVar('Sound_EnableAllSound', self.oldEnableAllSound)
|
||||
SetCVar('Sound_EnableMusic', self.oldEnableMusic)
|
||||
|
||||
self:StopShakeHorizontal(ElvUI_StaticPopup1)
|
||||
for _, object in pairs(self.massiveShakeObjects) do
|
||||
if object then
|
||||
self:StopShake(object)
|
||||
end
|
||||
end
|
||||
|
||||
if E.massiveShakeTimer then
|
||||
E:CancelTimer(E.massiveShakeTimer)
|
||||
end
|
||||
|
||||
E.global.aprilFools = true
|
||||
E:StaticPopup_Hide('HARLEM_SHAKE')
|
||||
wipe(self.massiveShakeObjects)
|
||||
DoEmote('Dance')
|
||||
end
|
||||
|
||||
function E:DoTheHarlemShake()
|
||||
E.isMassiveShaking = true
|
||||
ElvUI_StaticPopup1Button1:Enable()
|
||||
|
||||
for _, object in pairs(self.massiveShakeObjects) do
|
||||
if object and not object:IsForbidden() and object:IsShown() then
|
||||
self:Shake(object)
|
||||
end
|
||||
end
|
||||
|
||||
E.massiveShakeTimer = E:ScheduleTimer('StopHarlemShake', 42.5)
|
||||
end
|
||||
|
||||
function E:BeginHarlemShake()
|
||||
DoEmote('Dance')
|
||||
ElvUI_StaticPopup1Button1:Disable()
|
||||
self:ShakeHorizontal(ElvUI_StaticPopup1)
|
||||
self.oldEnableAllSound = GetCVar('Sound_EnableAllSound')
|
||||
self.oldEnableMusic = GetCVar('Sound_EnableMusic')
|
||||
|
||||
SetCVar('Sound_EnableAllSound', 1)
|
||||
SetCVar('Sound_EnableMusic', 1)
|
||||
PlayMusic(E.Media.Sounds.HarlemShake)
|
||||
E:ScheduleTimer('DoTheHarlemShake', 15.5)
|
||||
|
||||
self.massiveShakeObjects = {}
|
||||
tinsert(self.massiveShakeObjects, _G.GameTooltip)
|
||||
tinsert(self.massiveShakeObjects, _G.Minimap)
|
||||
tinsert(self.massiveShakeObjects, _G.ObjectiveTrackerFrame)
|
||||
tinsert(self.massiveShakeObjects, _G.LeftChatPanel)
|
||||
tinsert(self.massiveShakeObjects, _G.RightChatPanel)
|
||||
|
||||
for unit in pairs(UF.units) do
|
||||
tinsert(self.massiveShakeObjects, UF[unit])
|
||||
end
|
||||
for _, header in pairs(UF.headers) do
|
||||
tinsert(self.massiveShakeObjects, header)
|
||||
end
|
||||
|
||||
for _, bar in pairs(AB.handledBars) do
|
||||
for i = 1, #bar.buttons do
|
||||
tinsert(self.massiveShakeObjects, bar.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
if ElvUI_StanceBar then
|
||||
for i = 1, #ElvUI_StanceBar.buttons do
|
||||
tinsert(self.massiveShakeObjects, ElvUI_StanceBar.buttons[i])
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1, NUM_PET_ACTION_SLOTS do
|
||||
local button = _G['PetActionButton'..i]
|
||||
if button then
|
||||
tinsert(self.massiveShakeObjects, button)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:HarlemShakeToggle()
|
||||
self:StaticPopup_Show('HARLEM_SHAKE')
|
||||
end
|
||||
end
|
||||
|
||||
--Hello Kitty (Activate with command: /hellokitty)
|
||||
--This is one of those pranks where you either love it or hate it I think
|
||||
--Unfortunately there was a bug which caused some of the hello kitty changes to stick,
|
||||
-- when they should have reverted to the original settings. This bug was fixed later on.
|
||||
do
|
||||
local function OnDragStart(self)
|
||||
self:StartMoving()
|
||||
end
|
||||
|
||||
local function OnDragStop(self)
|
||||
self:StopMovingOrSizing()
|
||||
end
|
||||
|
||||
local function OnUpdate(self, elapsed)
|
||||
if self.elapsed and self.elapsed > 0.1 then
|
||||
self.tex:SetTexCoord((self.curFrame - 1) * 0.1, 0, (self.curFrame - 1) * 0.1, 1, self.curFrame * 0.1, 0, self.curFrame * 0.1, 1)
|
||||
|
||||
if self.countUp then
|
||||
self.curFrame = self.curFrame + 1
|
||||
else
|
||||
self.curFrame = self.curFrame - 1
|
||||
end
|
||||
|
||||
if self.curFrame > 10 then
|
||||
self.countUp = false
|
||||
self.curFrame = 9
|
||||
elseif self.curFrame < 1 then
|
||||
self.countUp = true
|
||||
self.curFrame = 2
|
||||
end
|
||||
self.elapsed = 0
|
||||
else
|
||||
self.elapsed = (self.elapsed or 0) + elapsed
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetupHelloKitty()
|
||||
if not self.db.tempSettings then
|
||||
self.db.tempSettings = {}
|
||||
end
|
||||
|
||||
--Store old settings
|
||||
local t = self.db.tempSettings
|
||||
local c = self.db.general.backdropcolor
|
||||
if self:HelloKittyFixCheck() then
|
||||
E:HelloKittyFix()
|
||||
else
|
||||
self.oldEnableAllSound = GetCVar('Sound_EnableAllSound')
|
||||
self.oldEnableMusic = GetCVar('Sound_EnableMusic')
|
||||
|
||||
t.backdropcolor = {r = c.r, g = c.g, b = c.b}
|
||||
c = self.db.general.backdropfadecolor
|
||||
t.backdropfadecolor = {r = c.r, g = c.g, b = c.b, a = c.a}
|
||||
c = self.db.general.bordercolor
|
||||
t.bordercolor = {r = c.r, g = c.g, b = c.b}
|
||||
c = self.db.general.valuecolor
|
||||
t.valuecolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
t.panelBackdropNameLeft = self.db.chat.panelBackdropNameLeft
|
||||
t.panelBackdropNameRight = self.db.chat.panelBackdropNameRight
|
||||
|
||||
c = self.db.unitframe.colors.health
|
||||
t.health = {r = c.r, g = c.g, b = c.b}
|
||||
t.healthclass = self.db.unitframe.colors.healthclass
|
||||
|
||||
c = self.db.unitframe.colors.castColor
|
||||
t.castColor = {r = c.r, g = c.g, b = c.b}
|
||||
t.transparentCastbar = self.db.unitframe.colors.transparentCastbar
|
||||
|
||||
c = self.db.unitframe.colors.auraBarBuff
|
||||
t.auraBarBuff = {r = c.r, g = c.g, b = c.b}
|
||||
t.transparentAurabars = self.db.unitframe.colors.transparentAurabars
|
||||
|
||||
--Apply new settings
|
||||
self.db.general.backdropfadecolor = {r =131/255, g =36/255, b = 130/255, a = 0.36}
|
||||
self.db.general.backdropcolor = {r = 223/255, g = 76/255, b = 188/255}
|
||||
self.db.general.bordercolor = {r = 223/255, g = 217/255, b = 47/255}
|
||||
self.db.general.valuecolor = {r = 223/255, g = 217/255, b = 47/255}
|
||||
|
||||
self.db.chat.panelBackdropNameLeft = E.Media.Textures.HelloKittyChat
|
||||
self.db.chat.panelBackdropNameRight = E.Media.Textures.HelloKittyChat
|
||||
|
||||
self.db.unitframe.colors.castColor = {r = 223/255, g = 76/255, b = 188/255}
|
||||
self.db.unitframe.colors.transparentCastbar = true
|
||||
|
||||
self.db.unitframe.colors.auraBarBuff = {r = 223/255, g = 76/255, b = 188/255}
|
||||
self.db.unitframe.colors.transparentAurabars = true
|
||||
|
||||
self.db.unitframe.colors.health = {r = 223/255, g = 76/255, b = 188/255}
|
||||
self.db.unitframe.colors.healthclass = false
|
||||
|
||||
SetCVar('Sound_EnableAllSound', 1)
|
||||
SetCVar('Sound_EnableMusic', 1)
|
||||
PlayMusic(E.Media.Sounds.HelloKitty)
|
||||
E:StaticPopup_Show('HELLO_KITTY_END')
|
||||
|
||||
self.db.general.kittys = true
|
||||
self:CreateKittys()
|
||||
|
||||
self:StaggeredUpdateAll(nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
function E:RestoreHelloKitty()
|
||||
--Store old settings
|
||||
self.db.general.kittys = false
|
||||
if _G.HelloKittyLeft then
|
||||
_G.HelloKittyLeft:Hide()
|
||||
_G.HelloKittyRight:Hide()
|
||||
end
|
||||
|
||||
if not(self.db.tempSettings) then return end
|
||||
if self:HelloKittyFixCheck() then
|
||||
self:HelloKittyFix()
|
||||
self.db.tempSettings = nil
|
||||
return
|
||||
end
|
||||
local c = self.db.tempSettings.backdropcolor
|
||||
self.db.general.backdropcolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
c = self.db.tempSettings.backdropfadecolor
|
||||
self.db.general.backdropfadecolor = {r = c.r, g = c.g, b = c.b, a = (c.a or 0.8)}
|
||||
|
||||
c = self.db.tempSettings.bordercolor
|
||||
self.db.general.bordercolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
c = self.db.tempSettings.valuecolor
|
||||
self.db.general.valuecolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
self.db.chat.panelBackdropNameLeft = self.db.tempSettings.panelBackdropNameLeft
|
||||
self.db.chat.panelBackdropNameRight = self.db.tempSettings.panelBackdropNameRight
|
||||
|
||||
c = self.db.tempSettings.health
|
||||
self.db.unitframe.colors.health = {r = c.r, g = c.g, b = c.b}
|
||||
self.db.unitframe.colors.healthclass = self.db.tempSettings.healthclass
|
||||
|
||||
c = self.db.tempSettings.castColor
|
||||
self.db.unitframe.colors.castColor = {r = c.r, g = c.g, b = c.b}
|
||||
self.db.unitframe.colors.transparentCastbar = self.db.tempSettings.transparentCastbar
|
||||
|
||||
c = self.db.tempSettings.auraBarBuff
|
||||
self.db.unitframe.colors.auraBarBuff = {r = c.r, g = c.g, b = c.b}
|
||||
self.db.unitframe.colors.transparentAurabars = self.db.tempSettings.transparentAurabars
|
||||
|
||||
self.db.tempSettings = nil
|
||||
|
||||
self:StaggeredUpdateAll(nil, true)
|
||||
end
|
||||
|
||||
function E:CreateKittys()
|
||||
if _G.HelloKittyLeft then
|
||||
_G.HelloKittyLeft:Show()
|
||||
_G.HelloKittyRight:Show()
|
||||
return
|
||||
end
|
||||
local helloKittyLeft = CreateFrame('Frame', 'HelloKittyLeft', _G.UIParent)
|
||||
helloKittyLeft:Size(120, 128)
|
||||
helloKittyLeft:SetMovable(true)
|
||||
helloKittyLeft:EnableMouse(true)
|
||||
helloKittyLeft:RegisterForDrag('LeftButton')
|
||||
helloKittyLeft:Point('BOTTOMLEFT', _G.LeftChatPanel, 'BOTTOMRIGHT', 2, -4)
|
||||
helloKittyLeft.tex = helloKittyLeft:CreateTexture(nil, 'OVERLAY')
|
||||
helloKittyLeft.tex:SetAllPoints()
|
||||
helloKittyLeft.tex:SetTexture(E.Media.Textures.HelloKitty)
|
||||
helloKittyLeft.tex:SetTexCoord(0, 0, 0, 1, 0, 0, 0, 1)
|
||||
helloKittyLeft.curFrame = 1
|
||||
helloKittyLeft.countUp = true
|
||||
helloKittyLeft:SetClampedToScreen(true)
|
||||
helloKittyLeft:SetScript('OnDragStart', OnDragStart)
|
||||
helloKittyLeft:SetScript('OnDragStop', OnDragStop)
|
||||
helloKittyLeft:SetScript('OnUpdate', OnUpdate)
|
||||
|
||||
local helloKittyRight = CreateFrame('Frame', 'HelloKittyRight', _G.UIParent)
|
||||
helloKittyRight:Size(120, 128)
|
||||
helloKittyRight:SetMovable(true)
|
||||
helloKittyRight:EnableMouse(true)
|
||||
helloKittyRight:RegisterForDrag('LeftButton')
|
||||
helloKittyRight:Point('BOTTOMRIGHT', _G.RightChatPanel, 'BOTTOMLEFT', -2, -4)
|
||||
helloKittyRight.tex = helloKittyRight:CreateTexture(nil, 'OVERLAY')
|
||||
helloKittyRight.tex:SetAllPoints()
|
||||
helloKittyRight.tex:SetTexture(E.Media.Textures.HelloKitty)
|
||||
helloKittyRight.tex:SetTexCoord(0, 0, 0, 1, 0, 0, 0, 1)
|
||||
helloKittyRight.curFrame = 10
|
||||
helloKittyRight.countUp = false
|
||||
helloKittyRight:SetClampedToScreen(true)
|
||||
helloKittyRight:SetScript('OnDragStart', OnDragStart)
|
||||
helloKittyRight:SetScript('OnDragStop', OnDragStop)
|
||||
helloKittyRight:SetScript('OnUpdate', OnUpdate)
|
||||
end
|
||||
|
||||
--When it bugged out for a user the command '/hellokittyfix' attempted to restore the changed settings to default
|
||||
function E:HelloKittyFixCheck(secondCheck)
|
||||
local t = self.db.tempSettings
|
||||
if not t and not secondCheck then t = self.db.general end
|
||||
if t and t.backdropcolor then
|
||||
return self:Round(t.backdropcolor.r, 2) == 0.87 and self:Round(t.backdropcolor.g, 2) == 0.3 and self:Round(t.backdropcolor.b, 2) == 0.74
|
||||
end
|
||||
end
|
||||
|
||||
function E:HelloKittyFix()
|
||||
local c = P.general.backdropcolor
|
||||
self.db.general.backdropcolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
c = P.general.backdropfadecolor
|
||||
self.db.general.backdropfadecolor = {r = c.r, g = c.g, b = c.b, a = (c.a or 0.8)}
|
||||
|
||||
c = P.general.bordercolor
|
||||
self.db.general.bordercolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
c = P.general.valuecolor
|
||||
self.db.general.valuecolor = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
self.db.chat.panelBackdropNameLeft = ''
|
||||
self.db.chat.panelBackdropNameRight = ''
|
||||
|
||||
c = P.unitframe.colors.health
|
||||
self.db.unitframe.colors.health = {r = c.r, g = c.g, b = c.b}
|
||||
|
||||
c = P.unitframe.colors.castColor
|
||||
self.db.unitframe.colors.castColor = {r = c.r, g = c.g, b = c.b}
|
||||
self.db.unitframe.colors.transparentCastbar = false
|
||||
|
||||
c = P.unitframe.colors.castColor
|
||||
self.db.unitframe.colors.auraBarBuff = {r = c.r, g = c.g, b = c.b}
|
||||
self.db.unitframe.colors.transparentAurabars = false
|
||||
|
||||
if _G.HelloKittyLeft then
|
||||
_G.HelloKittyLeft:Hide()
|
||||
_G.HelloKittyRight:Hide()
|
||||
self.db.general.kittys = nil
|
||||
return
|
||||
end
|
||||
|
||||
self.db.tempSettings = nil
|
||||
self:StaggeredUpdateAll(nil, true)
|
||||
end
|
||||
|
||||
function E:HelloKittyToggle()
|
||||
if _G.HelloKittyLeft and _G.HelloKittyLeft:IsShown() then
|
||||
self:RestoreHelloKitty()
|
||||
else
|
||||
self:StaticPopup_Show('HELLO_KITTY')
|
||||
end
|
||||
end
|
||||
end
|
||||
300
Core/Commands.lua
Normal file
300
Core/Commands.lua
Normal file
@@ -0,0 +1,300 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)) --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local DT = E:GetModule('DataTexts')
|
||||
local AB = E:GetModule('ActionBars')
|
||||
|
||||
local _G = _G
|
||||
local tonumber, type, pairs, select = tonumber, type, pairs, select
|
||||
local lower, split, format, wipe, next, print = strlower, strsplit, format, wipe, next, print
|
||||
|
||||
local debugprofilestop = debugprofilestop
|
||||
local EnableAddOn = EnableAddOn
|
||||
local GetAddOnCPUUsage = GetAddOnCPUUsage
|
||||
local GetAddOnInfo = GetAddOnInfo
|
||||
local GetNumAddOns = GetNumAddOns
|
||||
local GetCVarBool = GetCVarBool
|
||||
local DisableAddOn = DisableAddOn
|
||||
local GetGuildRosterInfo = GetGuildRosterInfo
|
||||
local GetGuildRosterLastOnline = GetGuildRosterLastOnline
|
||||
local GetNumGuildMembers = GetNumGuildMembers
|
||||
local GuildControlGetNumRanks = GuildControlGetNumRanks
|
||||
local GuildControlGetRankName = GuildControlGetRankName
|
||||
local GuildUninvite = GuildUninvite
|
||||
local ResetCPUUsage = ResetCPUUsage
|
||||
local SendChatMessage = SendChatMessage
|
||||
local ReloadUI = ReloadUI
|
||||
local SetCVar = SetCVar
|
||||
local UpdateAddOnCPUUsage = UpdateAddOnCPUUsage
|
||||
-- GLOBALS: ElvUIGrid, ElvDB
|
||||
|
||||
function E:Grid(msg)
|
||||
msg = msg and tonumber(msg)
|
||||
if type(msg) == 'number' and (msg <= 256 and msg >= 4) then
|
||||
E.db.gridSize = msg
|
||||
E:Grid_Show()
|
||||
elseif ElvUIGrid and ElvUIGrid:IsShown() then
|
||||
E:Grid_Hide()
|
||||
else
|
||||
E:Grid_Show()
|
||||
end
|
||||
end
|
||||
|
||||
function E:LuaError(msg)
|
||||
local switch = lower(msg)
|
||||
if switch == 'on' or switch == '1' then
|
||||
for i=1, GetNumAddOns() do
|
||||
local name = GetAddOnInfo(i)
|
||||
if name ~= 'ElvUI' and name ~= 'ElvUI_OptionsUI' and E:IsAddOnEnabled(name) then
|
||||
DisableAddOn(name, E.myname)
|
||||
ElvDB.LuaErrorDisabledAddOns[name] = i
|
||||
end
|
||||
end
|
||||
|
||||
SetCVar('scriptErrors', 1)
|
||||
ReloadUI()
|
||||
elseif switch == 'off' or switch == '0' then
|
||||
if switch == 'off' then
|
||||
SetCVar('scriptErrors', 0)
|
||||
E:Print('Lua errors off.')
|
||||
end
|
||||
|
||||
if next(ElvDB.LuaErrorDisabledAddOns) then
|
||||
for name in pairs(ElvDB.LuaErrorDisabledAddOns) do
|
||||
EnableAddOn(name, E.myname)
|
||||
end
|
||||
|
||||
wipe(ElvDB.LuaErrorDisabledAddOns)
|
||||
ReloadUI()
|
||||
end
|
||||
else
|
||||
E:Print('/luaerror on - /luaerror off')
|
||||
end
|
||||
end
|
||||
|
||||
local function OnCallback(command)
|
||||
_G.MacroEditBox:GetScript('OnEvent')(_G.MacroEditBox, 'EXECUTE_CHAT_LINE', command)
|
||||
end
|
||||
|
||||
function E:DelayScriptCall(msg)
|
||||
local secs, command = msg:match('^(%S+)%s+(.*)$')
|
||||
secs = tonumber(secs)
|
||||
if not secs or (#command == 0) then
|
||||
self:Print('usage: /in <seconds> <command>')
|
||||
self:Print('example: /in 1.5 /say hi')
|
||||
else
|
||||
E:Delay(secs, OnCallback, command)
|
||||
end
|
||||
end
|
||||
|
||||
-- make this a locale later?
|
||||
local MassKickMessage = 'Guild Cleanup Results: Removed all guild members below rank %s, that have a minimal level of %s, and have not been online for at least: %s days.'
|
||||
function E:MassGuildKick(msg)
|
||||
local minLevel, minDays, minRankIndex = split(',', msg)
|
||||
minRankIndex = tonumber(minRankIndex)
|
||||
minLevel = tonumber(minLevel)
|
||||
minDays = tonumber(minDays)
|
||||
|
||||
if not minLevel or not minDays then
|
||||
E:Print('Usage: /cleanguild <minLevel>, <minDays>, [<minRankIndex>]')
|
||||
return
|
||||
end
|
||||
|
||||
if minDays > 31 then
|
||||
E:Print('Maximum days value must be below 32.')
|
||||
return
|
||||
end
|
||||
|
||||
if not minRankIndex then
|
||||
minRankIndex = GuildControlGetNumRanks() - 1
|
||||
end
|
||||
|
||||
for i = 1, GetNumGuildMembers() do
|
||||
local name, _, rankIndex, level, _, _, note, officerNote, connected, _, classFileName = GetGuildRosterInfo(i)
|
||||
local minLevelx = minLevel
|
||||
|
||||
if classFileName == 'DEATHKNIGHT' then
|
||||
minLevelx = minLevelx + 55
|
||||
end
|
||||
|
||||
if not connected then
|
||||
local years, months, days = GetGuildRosterLastOnline(i)
|
||||
if days ~= nil and ((years > 0 or months > 0 or days >= minDays) and rankIndex >= minRankIndex)
|
||||
and note ~= nil and officerNote ~= nil and (level <= minLevelx) then
|
||||
GuildUninvite(name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
SendChatMessage(format(MassKickMessage, GuildControlGetRankName(minRankIndex), minLevel, minDays), 'GUILD')
|
||||
end
|
||||
|
||||
local num_frames = 0
|
||||
local function OnUpdate()
|
||||
num_frames = num_frames + 1
|
||||
end
|
||||
local f = CreateFrame('Frame')
|
||||
f:Hide()
|
||||
f:SetScript('OnUpdate', OnUpdate)
|
||||
|
||||
local toggleMode, debugTimer, cpuImpactMessage = false, 0, 'Consumed %sms per frame. Each frame took %sms to render.'
|
||||
function E:GetCPUImpact()
|
||||
if not GetCVarBool('scriptProfile') then
|
||||
E:Print('For `/cpuimpact` to work, you need to enable script profiling via: `/console scriptProfile 1` then reload. Disable after testing by setting it back to 0.')
|
||||
return
|
||||
end
|
||||
|
||||
if not toggleMode then
|
||||
ResetCPUUsage()
|
||||
toggleMode, num_frames, debugTimer = true, 0, debugprofilestop()
|
||||
self:Print('CPU Impact being calculated, type /cpuimpact to get results when you are ready.')
|
||||
f:Show()
|
||||
else
|
||||
f:Hide()
|
||||
local ms_passed = debugprofilestop() - debugTimer
|
||||
UpdateAddOnCPUUsage()
|
||||
|
||||
local per, passed = ((num_frames == 0 and 0) or (GetAddOnCPUUsage('ElvUI') / num_frames)), ((num_frames == 0 and 0) or (ms_passed / num_frames))
|
||||
self:Print(format(cpuImpactMessage, per and per > 0 and format('%.3f', per) or 0, passed and passed > 0 and format('%.3f', passed) or 0))
|
||||
toggleMode = false
|
||||
end
|
||||
end
|
||||
|
||||
function E:EHelp()
|
||||
print(L["EHELP_COMMANDS"])
|
||||
end
|
||||
|
||||
local BLIZZARD_ADDONS = {
|
||||
'Blizzard_AchievementUI',
|
||||
'Blizzard_AdventureMap',
|
||||
'Blizzard_ArchaeologyUI',
|
||||
'Blizzard_ArenaUI',
|
||||
'Blizzard_ArtifactUI',
|
||||
'Blizzard_AuctionUI',
|
||||
'Blizzard_AuthChallengeUI',
|
||||
'Blizzard_BarbershopUI',
|
||||
'Blizzard_BattlefieldMinimap',
|
||||
'Blizzard_BindingUI',
|
||||
'Blizzard_BlackMarketUI',
|
||||
'Blizzard_BoostTutorial',
|
||||
'Blizzard_Calendar',
|
||||
'Blizzard_ChallengesUI',
|
||||
'Blizzard_ClassTrial',
|
||||
'Blizzard_ClientSavedVariables',
|
||||
'Blizzard_Collections',
|
||||
'Blizzard_CombatLog',
|
||||
'Blizzard_CombatText',
|
||||
'Blizzard_CompactRaidFrames',
|
||||
'Blizzard_CUFProfiles',
|
||||
'Blizzard_DeathRecap',
|
||||
'Blizzard_DebugTools',
|
||||
'Blizzard_EncounterJournal',
|
||||
'Blizzard_FlightMap',
|
||||
'Blizzard_GarrisonTemplates',
|
||||
'Blizzard_GarrisonUI',
|
||||
'Blizzard_GlyphUI',
|
||||
'Blizzard_GMChatUI',
|
||||
'Blizzard_GMSurveyUI',
|
||||
'Blizzard_GuildBankUI',
|
||||
'Blizzard_GuildControlUI',
|
||||
'Blizzard_GuildUI',
|
||||
'Blizzard_InspectUI',
|
||||
'Blizzard_ItemSocketingUI',
|
||||
'Blizzard_ItemUpgradeUI',
|
||||
'Blizzard_LookingForGuildUI',
|
||||
'Blizzard_MacroUI',
|
||||
'Blizzard_MapCanvas',
|
||||
'Blizzard_MovePad',
|
||||
'Blizzard_NamePlates',
|
||||
'Blizzard_ObjectiveTracker',
|
||||
'Blizzard_ObliterumUI',
|
||||
'Blizzard_OrderHallUI',
|
||||
'Blizzard_PetBattleUI',
|
||||
'Blizzard_PVPUI',
|
||||
'Blizzard_QuestChoice',
|
||||
'Blizzard_RaidUI',
|
||||
'Blizzard_SecureTransferUI',
|
||||
'Blizzard_SharedMapDataProviders',
|
||||
'Blizzard_SocialUI',
|
||||
'Blizzard_StoreUI',
|
||||
'Blizzard_TalentUI',
|
||||
'Blizzard_TalkingHeadUI',
|
||||
'Blizzard_TimeManager',
|
||||
'Blizzard_TokenUI',
|
||||
'Blizzard_TradeSkillUI',
|
||||
'Blizzard_TrainerUI',
|
||||
'Blizzard_Tutorial',
|
||||
'Blizzard_TutorialTemplates',
|
||||
'Blizzard_VoidStorageUI',
|
||||
'Blizzard_WowTokenUI'
|
||||
}
|
||||
function E:EnableBlizzardAddOns()
|
||||
for _, addon in pairs(BLIZZARD_ADDONS) do
|
||||
local reason = select(5, GetAddOnInfo(addon))
|
||||
if reason == 'DISABLED' then
|
||||
EnableAddOn(addon)
|
||||
E:Print('The following addon was re-enabled:', addon)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
do -- Blizzard Commands
|
||||
local SlashCmdList = _G.SlashCmdList
|
||||
|
||||
-- DeveloperConsole (without starting with `-console`)
|
||||
if not SlashCmdList.DEVCON then
|
||||
local DevConsole = _G.DeveloperConsole
|
||||
if DevConsole then
|
||||
_G.SLASH_DEVCON1 = '/devcon'
|
||||
SlashCmdList.DEVCON = function()
|
||||
DevConsole:Toggle()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- ReloadUI: /rl, /reloadui, /reload NOTE: /reload is from SLASH_RELOAD
|
||||
if not SlashCmdList.RELOADUI then
|
||||
_G.SLASH_RELOADUI1 = '/rl'
|
||||
_G.SLASH_RELOADUI2 = '/reloadui'
|
||||
SlashCmdList.RELOADUI = _G.ReloadUI
|
||||
end
|
||||
end
|
||||
|
||||
function E:DBConvertProfile()
|
||||
E.db.dbConverted = nil
|
||||
E:DBConversions()
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
function E:LoadCommands()
|
||||
self:RegisterChatCommand('in', 'DelayScriptCall')
|
||||
self:RegisterChatCommand('ec', 'ToggleOptionsUI')
|
||||
self:RegisterChatCommand('elvui', 'ToggleOptionsUI')
|
||||
self:RegisterChatCommand('cpuimpact', 'GetCPUImpact')
|
||||
self:RegisterChatCommand('cpuusage', 'GetTopCPUFunc')
|
||||
-- cpuusage args: module, showall, delay, minCalls
|
||||
--- Example1: /cpuusage all
|
||||
--- Example2: /cpuusage Bags true
|
||||
--- Example3: /cpuusage UnitFrames nil 50 25
|
||||
---- Note: showall, delay, and minCalls will default if not set
|
||||
---- arg1 can be 'all' this will scan all registered modules!
|
||||
|
||||
self:RegisterChatCommand('hdt', DT.HyperDT)
|
||||
self:RegisterChatCommand('bgstats', DT.ToggleBattleStats)
|
||||
self:RegisterChatCommand('hellokitty', 'HelloKittyToggle')
|
||||
self:RegisterChatCommand('hellokittyfix', 'HelloKittyFix')
|
||||
self:RegisterChatCommand('harlemshake', 'HarlemShakeToggle')
|
||||
self:RegisterChatCommand('luaerror', 'LuaError')
|
||||
self:RegisterChatCommand('egrid', 'Grid')
|
||||
self:RegisterChatCommand('moveui', 'ToggleMoveMode')
|
||||
self:RegisterChatCommand('resetui', 'ResetUI')
|
||||
self:RegisterChatCommand('cleanguild', 'MassGuildKick')
|
||||
self:RegisterChatCommand('enableblizzard', 'EnableBlizzardAddOns')
|
||||
self:RegisterChatCommand('estatus', 'ShowStatusReport')
|
||||
self:RegisterChatCommand('ehelp', 'EHelp')
|
||||
self:RegisterChatCommand('ecommands', 'EHelp')
|
||||
self:RegisterChatCommand('efixdb', 'DBConvertProfile')
|
||||
-- self:RegisterChatCommand('aprilfools', '') --Don't need this until next april fools
|
||||
|
||||
if E.private.actionbar.enable then
|
||||
self:RegisterChatCommand('kb', AB.ActivateBindMode)
|
||||
end
|
||||
end
|
||||
1226
Core/Config.lua
Normal file
1226
Core/Config.lua
Normal file
File diff suppressed because it is too large
Load Diff
347
Core/Cooldowns.lua
Normal file
347
Core/Cooldowns.lua
Normal file
@@ -0,0 +1,347 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local AB = E:GetModule('ActionBars')
|
||||
local LSM = E.Libs.LSM
|
||||
|
||||
local next, ipairs, pairs = next, ipairs, pairs
|
||||
local floor, tinsert = floor, tinsert
|
||||
|
||||
local GetTime = GetTime
|
||||
local CreateFrame = CreateFrame
|
||||
local hooksecurefunc = hooksecurefunc
|
||||
|
||||
local ICON_SIZE = 36 --the normal size for an icon (don't change this)
|
||||
local FONT_SIZE = 20 --the base font size to use at a scale of 1
|
||||
local MIN_SCALE = 0.5 --the minimum scale we want to show cooldown counts at, anything below this will be hidden
|
||||
local MIN_DURATION = 1.5 --the minimum duration to show cooldown text for
|
||||
|
||||
function E:Cooldown_TextThreshold(cd, now)
|
||||
if cd.parent and cd.parent.textThreshold and cd.endTime then
|
||||
return (cd.endTime - now) >= cd.parent.textThreshold
|
||||
end
|
||||
end
|
||||
|
||||
function E:Cooldown_BelowScale(cd)
|
||||
if cd.parent then
|
||||
if cd.parent.hideText then return true end
|
||||
if cd.parent.skipScale then return end
|
||||
end
|
||||
|
||||
return cd.fontScale and (cd.fontScale < MIN_SCALE)
|
||||
end
|
||||
|
||||
function E:Cooldown_OnUpdate(elapsed)
|
||||
local forced = elapsed == -1
|
||||
if forced then
|
||||
self.nextUpdate = 0
|
||||
elseif self.nextUpdate > 0 then
|
||||
self.nextUpdate = self.nextUpdate - elapsed
|
||||
return
|
||||
end
|
||||
|
||||
if not E:Cooldown_IsEnabled(self) then
|
||||
E:Cooldown_StopTimer(self)
|
||||
else
|
||||
local now = GetTime()
|
||||
if self.endCooldown and now >= self.endCooldown then
|
||||
E:Cooldown_StopTimer(self)
|
||||
elseif E:Cooldown_BelowScale(self) then
|
||||
self.text:SetText('')
|
||||
if not forced then
|
||||
self.nextUpdate = 500
|
||||
end
|
||||
elseif E:Cooldown_TextThreshold(self, now) then
|
||||
self.text:SetText('')
|
||||
if not forced then
|
||||
self.nextUpdate = 1
|
||||
end
|
||||
elseif self.endTime then
|
||||
local value, id, nextUpdate, remainder = E:GetTimeInfo(self.endTime - now, self.threshold, self.hhmmThreshold, self.mmssThreshold)
|
||||
if not forced then
|
||||
self.nextUpdate = nextUpdate
|
||||
end
|
||||
|
||||
local style = E.TimeFormats[id]
|
||||
if style then
|
||||
local which = (self.textColors and 2 or 1) + (self.showSeconds and 0 or 2)
|
||||
if self.textColors then
|
||||
self.text:SetFormattedText(style[which], value, self.textColors[id], remainder)
|
||||
else
|
||||
self.text:SetFormattedText(style[which], value, remainder)
|
||||
end
|
||||
end
|
||||
|
||||
local color = not self.skipTextColor and self.timeColors[id]
|
||||
if color then self.text:SetTextColor(color.r, color.g, color.b) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:Cooldown_OnSizeChanged(cd, width, force)
|
||||
local scale = width and (floor(width + 0.5) / ICON_SIZE)
|
||||
|
||||
-- dont bother updating when the fontScale is the same, unless we are passing the force arg
|
||||
if scale and (scale == cd.fontScale) and (force ~= true) then return end
|
||||
cd.fontScale = scale
|
||||
|
||||
-- this is needed because of skipScale variable, we wont allow a font size under the minscale
|
||||
if cd.fontScale and (cd.fontScale < MIN_SCALE) then
|
||||
scale = MIN_SCALE
|
||||
end
|
||||
|
||||
if cd.customFont then -- override font
|
||||
cd.text:FontTemplate(cd.customFont, (scale * cd.customFontSize), cd.customFontOutline)
|
||||
elseif scale then -- default, no override
|
||||
cd.text:FontTemplate(nil, (scale * FONT_SIZE), 'OUTLINE')
|
||||
else -- this should never happen but just incase
|
||||
cd.text:FontTemplate()
|
||||
end
|
||||
end
|
||||
|
||||
function E:Cooldown_IsEnabled(cd)
|
||||
if cd.forceEnabled then
|
||||
return true
|
||||
elseif cd.forceDisabled then
|
||||
return false
|
||||
elseif cd.reverseToggle ~= nil then
|
||||
return cd.reverseToggle
|
||||
else
|
||||
return E.db.cooldown.enable
|
||||
end
|
||||
end
|
||||
|
||||
function E:Cooldown_ForceUpdate(cd)
|
||||
E.Cooldown_OnUpdate(cd, -1)
|
||||
cd:Show()
|
||||
end
|
||||
|
||||
function E:Cooldown_StopTimer(cd)
|
||||
cd:Hide()
|
||||
end
|
||||
|
||||
function E:Cooldown_Options(timer, db, parent)
|
||||
local threshold, colors, icolors, hhmm, mmss, fonts
|
||||
if parent and db.override then
|
||||
threshold = db.threshold
|
||||
icolors = db.useIndicatorColor and E.TimeIndicatorColors[parent.CooldownOverride]
|
||||
colors = E.TimeColors[parent.CooldownOverride]
|
||||
end
|
||||
|
||||
if db.checkSeconds then
|
||||
hhmm, mmss = db.hhmmThreshold, db.mmssThreshold
|
||||
end
|
||||
|
||||
timer.timeColors = colors or E.TimeColors
|
||||
timer.threshold = threshold or E.db.cooldown.threshold or E.TimeThreshold
|
||||
timer.textColors = icolors or (E.db.cooldown.useIndicatorColor and E.TimeIndicatorColors)
|
||||
timer.hhmmThreshold = hhmm or (E.db.cooldown.checkSeconds and E.db.cooldown.hhmmThreshold)
|
||||
timer.mmssThreshold = mmss or (E.db.cooldown.checkSeconds and E.db.cooldown.mmssThreshold)
|
||||
timer.hideBlizzard = db.hideBlizzard or E.db.cooldown.hideBlizzard
|
||||
|
||||
if db.reverse ~= nil then
|
||||
timer.reverseToggle = (E.db.cooldown.enable and not db.reverse) or (db.reverse and not E.db.cooldown.enable)
|
||||
else
|
||||
timer.reverseToggle = nil
|
||||
end
|
||||
|
||||
if timer.CooldownOverride ~= 'auras' then
|
||||
if (db ~= E.db.cooldown) and db.fonts and db.fonts.enable then
|
||||
fonts = db.fonts -- custom fonts override default fonts
|
||||
elseif E.db.cooldown.fonts and E.db.cooldown.fonts.enable then
|
||||
fonts = E.db.cooldown.fonts -- default global font override
|
||||
end
|
||||
|
||||
if fonts and fonts.enable then
|
||||
timer.customFont = LSM:Fetch('font', fonts.font)
|
||||
timer.customFontSize = fonts.fontSize
|
||||
timer.customFontOutline = fonts.fontOutline
|
||||
else
|
||||
timer.customFont = nil
|
||||
timer.customFontSize = nil
|
||||
timer.customFontOutline = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:CreateCooldownTimer(parent)
|
||||
local timer = CreateFrame('Frame', nil, parent)
|
||||
timer:Hide()
|
||||
timer:SetAllPoints()
|
||||
timer.parent = parent
|
||||
parent.timer = timer
|
||||
|
||||
local text = timer:CreateFontString(nil, 'OVERLAY')
|
||||
text:Point('CENTER', 1, 1)
|
||||
text:SetJustifyH('CENTER')
|
||||
timer.text = text
|
||||
|
||||
-- can be used to modify elements created from this function
|
||||
if parent.CooldownPreHook then
|
||||
parent.CooldownPreHook(parent)
|
||||
end
|
||||
|
||||
-- cooldown override settings
|
||||
local db = (parent.CooldownOverride and E.db[parent.CooldownOverride]) or E.db
|
||||
if db and db.cooldown then
|
||||
E:Cooldown_Options(timer, db.cooldown, parent)
|
||||
|
||||
-- prevent LibActionBar from showing blizzard CD when the CD timer is created
|
||||
if parent.CooldownOverride == 'actionbar' then
|
||||
AB:ToggleCountDownNumbers(nil, nil, parent)
|
||||
end
|
||||
end
|
||||
|
||||
E:ToggleBlizzardCooldownText(parent, timer)
|
||||
|
||||
-- keep an eye on the size so we can rescale the font if needed
|
||||
E:Cooldown_OnSizeChanged(timer, parent:GetWidth())
|
||||
parent:SetScript('OnSizeChanged', function(_, width)
|
||||
E:Cooldown_OnSizeChanged(timer, width)
|
||||
end)
|
||||
|
||||
-- keep this after Cooldown_OnSizeChanged
|
||||
timer:SetScript('OnUpdate', E.Cooldown_OnUpdate)
|
||||
|
||||
return timer
|
||||
end
|
||||
|
||||
E.RegisteredCooldowns = {}
|
||||
function E:OnSetCooldown(start, duration)
|
||||
if not self.forceDisabled and (start and duration) and (duration > MIN_DURATION) then
|
||||
local timer = self.timer or E:CreateCooldownTimer(self)
|
||||
timer.start = start
|
||||
timer.duration = duration
|
||||
timer.endTime = start + duration
|
||||
timer.endCooldown = timer.endTime - 0.05
|
||||
E:Cooldown_ForceUpdate(timer)
|
||||
elseif self.timer then
|
||||
E:Cooldown_StopTimer(self.timer)
|
||||
end
|
||||
end
|
||||
|
||||
function E:RegisterCooldown(cooldown)
|
||||
if not cooldown.isHooked then
|
||||
hooksecurefunc(cooldown, 'SetCooldown', E.OnSetCooldown)
|
||||
cooldown.isHooked = true
|
||||
end
|
||||
|
||||
if not cooldown.isRegisteredCooldown then
|
||||
local module = (cooldown.CooldownOverride or 'global')
|
||||
if not E.RegisteredCooldowns[module] then E.RegisteredCooldowns[module] = {} end
|
||||
|
||||
tinsert(E.RegisteredCooldowns[module], cooldown)
|
||||
cooldown.isRegisteredCooldown = true
|
||||
end
|
||||
end
|
||||
|
||||
function E:ToggleBlizzardCooldownText(cd, timer, request)
|
||||
-- we should hide the blizzard cooldown text when ours are enabled
|
||||
if timer and cd and cd.SetHideCountdownNumbers then
|
||||
local forceHide = cd.hideText or timer.hideBlizzard
|
||||
if request then
|
||||
return forceHide or E:Cooldown_IsEnabled(timer)
|
||||
else
|
||||
cd:SetHideCountdownNumbers(forceHide or E:Cooldown_IsEnabled(timer))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetCooldownColors(db)
|
||||
if not db then db = E.db.cooldown end -- just incase someone calls this without a first arg use the global
|
||||
local c13 = E:RGBToHex(db.hhmmColorIndicator.r, db.hhmmColorIndicator.g, db.hhmmColorIndicator.b) -- color for timers that are soon to expire
|
||||
local c12 = E:RGBToHex(db.mmssColorIndicator.r, db.mmssColorIndicator.g, db.mmssColorIndicator.b) -- color for timers that are soon to expire
|
||||
local c11 = E:RGBToHex(db.expireIndicator.r, db.expireIndicator.g, db.expireIndicator.b) -- color for timers that are soon to expire
|
||||
local c10 = E:RGBToHex(db.secondsIndicator.r, db.secondsIndicator.g, db.secondsIndicator.b) -- color for timers that have seconds remaining
|
||||
local c9 = E:RGBToHex(db.minutesIndicator.r, db.minutesIndicator.g, db.minutesIndicator.b) -- color for timers that have minutes remaining
|
||||
local c8 = E:RGBToHex(db.hoursIndicator.r, db.hoursIndicator.g, db.hoursIndicator.b) -- color for timers that have hours remaining
|
||||
local c7 = E:RGBToHex(db.daysIndicator.r, db.daysIndicator.g, db.daysIndicator.b) -- color for timers that have days remaining
|
||||
local c6 = db.hhmmColor -- HH:MM color
|
||||
local c5 = db.mmssColor -- MM:SS color
|
||||
local c4 = db.expiringColor -- color for timers that are soon to expire
|
||||
local c3 = db.secondsColor -- color for timers that have seconds remaining
|
||||
local c2 = db.minutesColor -- color for timers that have minutes remaining
|
||||
local c1 = db.hoursColor -- color for timers that have hours remaining
|
||||
local c0 = db.daysColor -- color for timers that have days remaining
|
||||
return c0, c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13
|
||||
end
|
||||
|
||||
function E:UpdateCooldownOverride(module)
|
||||
local cooldowns = (module and E.RegisteredCooldowns[module])
|
||||
if not cooldowns or not next(cooldowns) then return end
|
||||
|
||||
local blizzText
|
||||
for _, parent in ipairs(cooldowns) do
|
||||
local db = (parent.CooldownOverride and E.db[parent.CooldownOverride]) or E.db
|
||||
if db and db.cooldown then
|
||||
local timer = parent.isHooked and parent.isRegisteredCooldown and parent.timer
|
||||
local cd = timer or parent
|
||||
|
||||
-- cooldown override settings
|
||||
E:Cooldown_Options(cd, db.cooldown, parent)
|
||||
|
||||
-- update font on cooldowns
|
||||
if timer and cd then -- has a parent, these are timers from RegisterCooldown
|
||||
E:Cooldown_OnSizeChanged(cd, parent:GetWidth(), true)
|
||||
|
||||
E:ToggleBlizzardCooldownText(parent, cd)
|
||||
if (not blizzText) and parent.CooldownOverride == 'actionbar' then
|
||||
blizzText = true
|
||||
end
|
||||
elseif cd.text then
|
||||
if cd.customFont then
|
||||
cd.text:FontTemplate(cd.customFont, cd.customFontSize, cd.customFontOutline)
|
||||
elseif parent.CooldownOverride == 'auras' then
|
||||
-- parent.auraType defined in `A:UpdateHeader` and `A:CreateIcon`
|
||||
local fontDB = parent.auraType and db[parent.auraType]
|
||||
if fontDB and fontDB.timeFont then
|
||||
cd.text:FontTemplate(LSM:Fetch('font', fontDB.timeFont), fontDB.timeFontSize, fontDB.timeFontOutline)
|
||||
end
|
||||
end
|
||||
|
||||
-- force update top aura cooldowns
|
||||
if parent.CooldownOverride == 'auras' then
|
||||
E:Cooldown_ForceUpdate(parent)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if blizzText and AB.handledBars then
|
||||
for _, bar in pairs(AB.handledBars) do
|
||||
if bar then
|
||||
AB:ToggleCountDownNumbers(bar)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:UpdateCooldownSettings(module)
|
||||
local db, timeColors, textColors = E.db.cooldown, E.TimeColors, E.TimeIndicatorColors
|
||||
|
||||
-- update the module timecolors if the config called it but ignore 'global' and 'all':
|
||||
-- global is the main call from config, all is the core file calls
|
||||
local isModule = module and (module ~= 'global' and module ~= 'all') and E.db[module] and E.db[module].cooldown
|
||||
if isModule then
|
||||
if not E.TimeColors[module] then E.TimeColors[module] = {} end
|
||||
if not E.TimeIndicatorColors[module] then E.TimeIndicatorColors[module] = {} end
|
||||
db, timeColors, textColors = E.db[module].cooldown, E.TimeColors[module], E.TimeIndicatorColors[module]
|
||||
end
|
||||
|
||||
timeColors[0], timeColors[1], timeColors[2], timeColors[3], timeColors[4], timeColors[5], timeColors[6], textColors[0], textColors[1], textColors[2], textColors[3], textColors[4], textColors[5], textColors[6] = E:GetCooldownColors(db)
|
||||
|
||||
if isModule then
|
||||
E:UpdateCooldownOverride(module)
|
||||
elseif module == 'global' then -- this is only a call from the config change
|
||||
for key in pairs(E.RegisteredCooldowns) do
|
||||
E:UpdateCooldownOverride(key)
|
||||
end
|
||||
end
|
||||
|
||||
-- okay update the other override settings if it was one of the core file calls
|
||||
if module and (module == 'all') then
|
||||
E:UpdateCooldownSettings('bags')
|
||||
E:UpdateCooldownSettings('nameplates')
|
||||
E:UpdateCooldownSettings('actionbar')
|
||||
E:UpdateCooldownSettings('unitframe')
|
||||
E:UpdateCooldownSettings('auras')
|
||||
end
|
||||
end
|
||||
1820
Core/Core.lua
Normal file
1820
Core/Core.lua
Normal file
File diff suppressed because it is too large
Load Diff
628
Core/Distributor.lua
Normal file
628
Core/Distributor.lua
Normal file
@@ -0,0 +1,628 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local D = E:GetModule('Distributor')
|
||||
local NP = E:GetModule('NamePlates')
|
||||
local LibCompress = E.Libs.Compress
|
||||
local LibBase64 = E.Libs.Base64
|
||||
|
||||
local _G = _G
|
||||
local tonumber, type, gsub, pairs, pcall, loadstring = tonumber, type, gsub, pairs, pcall, loadstring
|
||||
local len, format, split, find = strlen, format, strsplit, strfind
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local IsInRaid, UnitInRaid = IsInRaid, UnitInRaid
|
||||
local IsInGroup, UnitInParty = IsInGroup, UnitInParty
|
||||
local LE_PARTY_CATEGORY_HOME = LE_PARTY_CATEGORY_HOME
|
||||
local LE_PARTY_CATEGORY_INSTANCE = LE_PARTY_CATEGORY_INSTANCE
|
||||
local ACCEPT, CANCEL, YES, NO = ACCEPT, CANCEL, YES, NO
|
||||
-- GLOBALS: ElvDB, ElvPrivateDB
|
||||
|
||||
local REQUEST_PREFIX = 'ELVUI_REQUEST'
|
||||
local REPLY_PREFIX = 'ELVUI_REPLY'
|
||||
local TRANSFER_PREFIX = 'ELVUI_TRANSFER'
|
||||
local TRANSFER_COMPLETE_PREFIX = 'ELVUI_COMPLETE'
|
||||
|
||||
-- The active downloads
|
||||
local Downloads = {}
|
||||
local Uploads = {}
|
||||
|
||||
function D:Initialize()
|
||||
self.Initialized = true
|
||||
|
||||
D:UpdateSettings()
|
||||
|
||||
self.statusBar = CreateFrame('StatusBar', 'ElvUI_Download', E.UIParent)
|
||||
self.statusBar:CreateBackdrop()
|
||||
self.statusBar:SetStatusBarTexture(E.media.normTex)
|
||||
self.statusBar:SetStatusBarColor(0.95, 0.15, 0.15)
|
||||
self.statusBar:Size(250, 18)
|
||||
self.statusBar.text = self.statusBar:CreateFontString(nil, 'OVERLAY')
|
||||
self.statusBar.text:FontTemplate()
|
||||
self.statusBar.text:Point('CENTER')
|
||||
self.statusBar:Hide()
|
||||
E:RegisterStatusBar(self.statusBar)
|
||||
end
|
||||
|
||||
function D:UpdateSettings()
|
||||
if E.global.general.allowDistributor then
|
||||
self:RegisterComm(REQUEST_PREFIX)
|
||||
self:RegisterEvent('CHAT_MSG_ADDON')
|
||||
else
|
||||
self:UnregisterComm(REQUEST_PREFIX)
|
||||
self:UnregisterEvent('CHAT_MSG_ADDON')
|
||||
end
|
||||
end
|
||||
|
||||
-- Used to start uploads
|
||||
function D:Distribute(target, otherServer, isGlobal)
|
||||
local profileKey, data
|
||||
if not isGlobal then
|
||||
profileKey = ElvDB.profileKeys and ElvDB.profileKeys[E.mynameRealm]
|
||||
data = ElvDB.profiles[profileKey]
|
||||
else
|
||||
profileKey = 'global'
|
||||
data = ElvDB.global
|
||||
end
|
||||
|
||||
if not data then return end
|
||||
|
||||
local serialData = self:Serialize(data)
|
||||
local length = len(serialData)
|
||||
local message = format('%s:%d:%s', profileKey, length, target)
|
||||
|
||||
Uploads[profileKey] = {serialData = serialData, target = target}
|
||||
|
||||
if otherServer then
|
||||
if IsInRaid() and UnitInRaid('target') then
|
||||
self:SendCommMessage(REQUEST_PREFIX, message, (not IsInRaid(LE_PARTY_CATEGORY_HOME) and IsInRaid(LE_PARTY_CATEGORY_INSTANCE)) and 'INSTANCE_CHAT' or 'RAID')
|
||||
elseif IsInGroup() and UnitInParty('target') then
|
||||
self:SendCommMessage(REQUEST_PREFIX, message, (not IsInGroup(LE_PARTY_CATEGORY_HOME) and IsInGroup(LE_PARTY_CATEGORY_INSTANCE)) and 'INSTANCE_CHAT' or 'PARTY')
|
||||
else
|
||||
E:Print(L["Must be in group with the player if he isn't on the same server as you."])
|
||||
return
|
||||
end
|
||||
else
|
||||
self:SendCommMessage(REQUEST_PREFIX, message, 'WHISPER', target)
|
||||
end
|
||||
|
||||
self:RegisterComm(REPLY_PREFIX)
|
||||
E:StaticPopup_Show('DISTRIBUTOR_WAITING')
|
||||
end
|
||||
|
||||
function D:CHAT_MSG_ADDON(_, prefix, message, _, sender)
|
||||
if prefix ~= TRANSFER_PREFIX or not Downloads[sender] then return end
|
||||
local cur = len(message)
|
||||
local max = Downloads[sender].length
|
||||
Downloads[sender].current = Downloads[sender].current + cur
|
||||
|
||||
if Downloads[sender].current > max then
|
||||
Downloads[sender].current = max
|
||||
end
|
||||
|
||||
self.statusBar:SetValue(Downloads[sender].current)
|
||||
end
|
||||
|
||||
function D:OnCommReceived(prefix, msg, dist, sender)
|
||||
if prefix == REQUEST_PREFIX then
|
||||
local profile, length, sendTo = split(':', msg)
|
||||
|
||||
if dist ~= 'WHISPER' and sendTo ~= E.myname then
|
||||
return
|
||||
end
|
||||
|
||||
if self.statusBar:IsShown() then
|
||||
self:SendCommMessage(REPLY_PREFIX, profile..':NO', dist, sender)
|
||||
return
|
||||
end
|
||||
|
||||
local textString = format(L["%s is attempting to share the profile %s with you. Would you like to accept the request?"], sender, profile)
|
||||
if profile == 'global' then
|
||||
textString = format(L["%s is attempting to share his filters with you. Would you like to accept the request?"], sender)
|
||||
end
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_RESPONSE = {
|
||||
text = textString,
|
||||
OnAccept = function()
|
||||
self.statusBar:SetMinMaxValues(0, length)
|
||||
self.statusBar:SetValue(0)
|
||||
self.statusBar.text:SetFormattedText(L["Data From: %s"], sender)
|
||||
E:StaticPopupSpecial_Show(self.statusBar)
|
||||
self:SendCommMessage(REPLY_PREFIX, profile..':YES', dist, sender)
|
||||
end,
|
||||
OnCancel = function()
|
||||
self:SendCommMessage(REPLY_PREFIX, profile..':NO', dist, sender)
|
||||
end,
|
||||
button1 = ACCEPT,
|
||||
button2 = CANCEL,
|
||||
timeout = 30,
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
}
|
||||
|
||||
E:StaticPopup_Show('DISTRIBUTOR_RESPONSE')
|
||||
|
||||
Downloads[sender] = {
|
||||
current = 0,
|
||||
length = tonumber(length),
|
||||
profile = profile,
|
||||
}
|
||||
|
||||
self:RegisterComm(TRANSFER_PREFIX)
|
||||
elseif prefix == REPLY_PREFIX then
|
||||
self:UnregisterComm(REPLY_PREFIX)
|
||||
E:StaticPopup_Hide('DISTRIBUTOR_WAITING')
|
||||
|
||||
local profileKey, response = split(':', msg)
|
||||
if response == 'YES' then
|
||||
self:RegisterComm(TRANSFER_COMPLETE_PREFIX)
|
||||
self:SendCommMessage(TRANSFER_PREFIX, Uploads[profileKey].serialData, dist, Uploads[profileKey].target)
|
||||
else
|
||||
E:StaticPopup_Show('DISTRIBUTOR_REQUEST_DENIED')
|
||||
end
|
||||
|
||||
Uploads[profileKey] = nil
|
||||
elseif prefix == TRANSFER_PREFIX then
|
||||
self:UnregisterComm(TRANSFER_PREFIX)
|
||||
E:StaticPopupSpecial_Hide(self.statusBar)
|
||||
|
||||
local profileKey = Downloads[sender].profile
|
||||
local success, data = self:Deserialize(msg)
|
||||
|
||||
if success then
|
||||
local textString = format(L["Profile download complete from %s, would you like to load the profile %s now?"], sender, profileKey)
|
||||
|
||||
if profileKey == 'global' then
|
||||
textString = format(L["Filter download complete from %s, would you like to apply changes now?"], sender)
|
||||
else
|
||||
if not ElvDB.profiles[profileKey] then
|
||||
ElvDB.profiles[profileKey] = data
|
||||
else
|
||||
textString = format(L["Profile download complete from %s, but the profile %s already exists. Change the name or else it will overwrite the existing profile."], sender, profileKey)
|
||||
E.PopupDialogs.DISTRIBUTOR_CONFIRM = {
|
||||
text = textString,
|
||||
button1 = ACCEPT,
|
||||
hasEditBox = 1,
|
||||
editBoxWidth = 350,
|
||||
maxLetters = 127,
|
||||
OnAccept = function(popup)
|
||||
ElvDB.profiles[popup.editBox:GetText()] = data
|
||||
E.Libs.AceAddon:GetAddon('ElvUI').data:SetProfile(popup.editBox:GetText())
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
Downloads[sender] = nil
|
||||
end,
|
||||
OnShow = function(popup) popup.editBox:SetText(profileKey) popup.editBox:SetFocus() end,
|
||||
timeout = 0,
|
||||
exclusive = 1,
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
preferredIndex = 3
|
||||
}
|
||||
|
||||
E:StaticPopup_Show('DISTRIBUTOR_CONFIRM')
|
||||
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'COMPLETE', dist, sender)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_CONFIRM = {
|
||||
text = textString,
|
||||
OnAccept = function()
|
||||
if profileKey == 'global' then
|
||||
E:CopyTable(ElvDB.global, data)
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
else
|
||||
E.Libs.AceAddon:GetAddon('ElvUI').data:SetProfile(profileKey)
|
||||
end
|
||||
Downloads[sender] = nil
|
||||
end,
|
||||
OnCancel = function()
|
||||
Downloads[sender] = nil
|
||||
end,
|
||||
button1 = YES,
|
||||
button2 = NO,
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
}
|
||||
|
||||
E:StaticPopup_Show('DISTRIBUTOR_CONFIRM')
|
||||
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'COMPLETE', dist, sender)
|
||||
else
|
||||
E:StaticPopup_Show('DISTRIBUTOR_FAILED')
|
||||
self:SendCommMessage(TRANSFER_COMPLETE_PREFIX, 'FAILED', dist, sender)
|
||||
end
|
||||
elseif prefix == TRANSFER_COMPLETE_PREFIX then
|
||||
self:UnregisterComm(TRANSFER_COMPLETE_PREFIX)
|
||||
if msg == 'COMPLETE' then
|
||||
E:StaticPopup_Show('DISTRIBUTOR_SUCCESS')
|
||||
else
|
||||
E:StaticPopup_Show('DISTRIBUTOR_FAILED')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Keys that should not be exported
|
||||
local blacklistedKeys = {
|
||||
profile = {
|
||||
gridSize = true,
|
||||
general = {
|
||||
numberPrefixStyle = true
|
||||
},
|
||||
chat = {
|
||||
hideVoiceButtons = true
|
||||
}
|
||||
},
|
||||
private = {},
|
||||
global = {
|
||||
profileCopy = true,
|
||||
general = {
|
||||
AceGUI = true,
|
||||
UIScale = true,
|
||||
locale = true,
|
||||
version = true,
|
||||
eyefinity = true,
|
||||
ultrawide = true,
|
||||
disableTutorialButtons = true,
|
||||
showMissingTalentAlert = true,
|
||||
allowDistributor = true
|
||||
},
|
||||
chat = {
|
||||
classColorMentionExcludedNames = true
|
||||
},
|
||||
datatexts = {
|
||||
newPanelInfo = true
|
||||
},
|
||||
nameplate = {
|
||||
effectiveHealth = true,
|
||||
effectivePower = true,
|
||||
effectiveAura = true,
|
||||
effectiveHealthSpeed = true,
|
||||
effectivePowerSpeed = true,
|
||||
effectiveAuraSpeed = true,
|
||||
filters = true
|
||||
},
|
||||
unitframe = {
|
||||
aurafilters = true,
|
||||
aurawatch = true,
|
||||
effectiveHealth = true,
|
||||
effectivePower = true,
|
||||
effectiveAura = true,
|
||||
effectiveHealthSpeed = true,
|
||||
effectivePowerSpeed = true,
|
||||
effectiveAuraSpeed = true,
|
||||
spellRangeCheck = true
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
--Keys that auto or user generated tables.
|
||||
D.GeneratedKeys = {
|
||||
profile = {
|
||||
movers = true,
|
||||
nameplates = { -- this is supposed to have an 's' because yeah, oh well
|
||||
filters = true
|
||||
},
|
||||
datatexts = {
|
||||
panels = true,
|
||||
},
|
||||
unitframe = {
|
||||
units = {} -- required for the scope below for customTexts
|
||||
}
|
||||
},
|
||||
private = {
|
||||
theme = true,
|
||||
install_complete = true
|
||||
},
|
||||
global = {
|
||||
datatexts = {
|
||||
customPanels = true,
|
||||
customCurrencies = true
|
||||
},
|
||||
unitframe = {
|
||||
aurafilters = true,
|
||||
aurawatch = true
|
||||
},
|
||||
nameplate = {
|
||||
filters = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
do
|
||||
local units = D.GeneratedKeys.profile.unitframe.units
|
||||
for unit in pairs(P.unitframe.units) do
|
||||
units[unit] = {customTexts = true}
|
||||
end
|
||||
end
|
||||
|
||||
local function GetProfileData(profileType)
|
||||
if not profileType or type(profileType) ~= 'string' then
|
||||
E:Print('Bad argument #1 to "GetProfileData" (string expected)')
|
||||
return
|
||||
end
|
||||
|
||||
local profileData, profileKey = {}
|
||||
if profileType == 'profile' then
|
||||
--Copy current profile data
|
||||
profileKey = ElvDB.profileKeys and ElvDB.profileKeys[E.mynameRealm]
|
||||
profileData = E:CopyTable(profileData, ElvDB.profiles[profileKey])
|
||||
|
||||
--This table will also hold all default values, not just the changed settings.
|
||||
--This makes the table huge, and will cause the WoW client to lock up for several seconds.
|
||||
--We compare against the default table and remove all duplicates from our table. The table is now much smaller.
|
||||
profileData = E:RemoveTableDuplicates(profileData, P, D.GeneratedKeys.profile)
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.profile)
|
||||
elseif profileType == 'private' then
|
||||
local privateKey = ElvPrivateDB.profileKeys and ElvPrivateDB.profileKeys[E.mynameRealm]
|
||||
profileData = E:CopyTable(profileData, ElvPrivateDB.profiles[privateKey])
|
||||
profileData = E:RemoveTableDuplicates(profileData, V, D.GeneratedKeys.private)
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.private)
|
||||
profileKey = 'private'
|
||||
elseif profileType == 'global' then
|
||||
profileData = E:CopyTable(profileData, ElvDB.global)
|
||||
profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.global)
|
||||
profileKey = 'global'
|
||||
elseif profileType == 'filters' then
|
||||
profileData.unitframe = {}
|
||||
profileData.unitframe.aurafilters = {}
|
||||
profileData.unitframe.aurafilters = E:CopyTable(profileData.unitframe.aurafilters, ElvDB.global.unitframe.aurafilters)
|
||||
profileData.unitframe.aurawatch = {}
|
||||
profileData.unitframe.aurawatch = E:CopyTable(profileData.unitframe.aurawatch, ElvDB.global.unitframe.aurawatch)
|
||||
profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
|
||||
profileKey = 'filters'
|
||||
elseif profileType == 'styleFilters' then
|
||||
profileKey = 'styleFilters'
|
||||
profileData.nameplate = {}
|
||||
profileData.nameplate.filters = {}
|
||||
profileData.nameplate.filters = E:CopyTable(profileData.nameplate.filters, ElvDB.global.nameplate.filters)
|
||||
NP:StyleFilterClearDefaults(profileData.nameplate.filters)
|
||||
profileData = E:RemoveTableDuplicates(profileData, G, D.GeneratedKeys.global)
|
||||
end
|
||||
|
||||
return profileKey, profileData
|
||||
end
|
||||
|
||||
local function GetProfileExport(profileType, exportFormat)
|
||||
local profileExport, exportString
|
||||
local profileKey, profileData = GetProfileData(profileType)
|
||||
|
||||
if not profileKey or not profileData or (profileData and type(profileData) ~= 'table') then
|
||||
E:Print('Error getting data from "GetProfileData"')
|
||||
return
|
||||
end
|
||||
|
||||
if exportFormat == 'text' then
|
||||
local serialData = D:Serialize(profileData)
|
||||
exportString = D:CreateProfileExport(serialData, profileType, profileKey)
|
||||
local compressedData = LibCompress:Compress(exportString)
|
||||
local encodedData = LibBase64:Encode(compressedData)
|
||||
profileExport = encodedData
|
||||
|
||||
elseif exportFormat == 'luaTable' then
|
||||
exportString = E:TableToLuaString(profileData)
|
||||
profileExport = D:CreateProfileExport(exportString, profileType, profileKey)
|
||||
|
||||
elseif exportFormat == 'luaPlugin' then
|
||||
profileExport = E:ProfileTableToPluginFormat(profileData, profileType)
|
||||
end
|
||||
|
||||
return profileKey, profileExport
|
||||
end
|
||||
|
||||
function D:CreateProfileExport(dataString, profileType, profileKey)
|
||||
local returnString
|
||||
|
||||
if profileType == 'profile' then
|
||||
returnString = format('%s::%s::%s', dataString, profileType, profileKey)
|
||||
else
|
||||
returnString = format('%s::%s', dataString, profileType)
|
||||
end
|
||||
|
||||
return returnString
|
||||
end
|
||||
|
||||
function D:GetImportStringType(dataString)
|
||||
local stringType = ''
|
||||
|
||||
if LibBase64:IsBase64(dataString) then
|
||||
stringType = 'Base64'
|
||||
elseif find(dataString, '{') then --Basic check to weed out obviously wrong strings
|
||||
stringType = 'Table'
|
||||
end
|
||||
|
||||
return stringType
|
||||
end
|
||||
|
||||
function D:Decode(dataString)
|
||||
local profileInfo, profileType, profileKey, profileData
|
||||
local stringType = self:GetImportStringType(dataString)
|
||||
|
||||
if stringType == 'Base64' then
|
||||
local decodedData = LibBase64:Decode(dataString)
|
||||
local decompressedData, decompressedMessage = LibCompress:Decompress(decodedData)
|
||||
|
||||
if not decompressedData then
|
||||
E:Print('Error decompressing data:', decompressedMessage)
|
||||
return
|
||||
end
|
||||
|
||||
local serializedData, success
|
||||
serializedData, profileInfo = E:SplitString(decompressedData, '^^::') -- '^^' indicates the end of the AceSerializer string
|
||||
|
||||
if not profileInfo then
|
||||
E:Print('Error importing profile. String is invalid or corrupted!')
|
||||
return
|
||||
end
|
||||
|
||||
serializedData = format('%s%s', serializedData, '^^') --Add back the AceSerializer terminator
|
||||
profileType, profileKey = E:SplitString(profileInfo, '::')
|
||||
success, profileData = D:Deserialize(serializedData)
|
||||
|
||||
if not success then
|
||||
E:Print('Error deserializing:', profileData)
|
||||
return
|
||||
end
|
||||
elseif stringType == 'Table' then
|
||||
local profileDataAsString
|
||||
profileDataAsString, profileInfo = E:SplitString(dataString, '}::') -- '}::' indicates the end of the table
|
||||
|
||||
if not profileInfo then
|
||||
E:Print('Error extracting profile info. Invalid import string!')
|
||||
return
|
||||
end
|
||||
|
||||
if not profileDataAsString then
|
||||
E:Print('Error extracting profile data. Invalid import string!')
|
||||
return
|
||||
end
|
||||
|
||||
profileDataAsString = format('%s%s', profileDataAsString, '}') --Add back the missing '}'
|
||||
profileDataAsString = gsub(profileDataAsString, '\124\124', '\124') --Remove escape pipe characters
|
||||
profileType, profileKey = E:SplitString(profileInfo, '::')
|
||||
|
||||
local profileMessage
|
||||
local profileToTable = loadstring(format('%s %s', 'return', profileDataAsString))
|
||||
if profileToTable then profileMessage, profileData = pcall(profileToTable) end
|
||||
|
||||
if profileMessage and (not profileData or type(profileData) ~= 'table') then
|
||||
E:Print('Error converting lua string to table:', profileMessage)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
return profileType, profileKey, profileData
|
||||
end
|
||||
|
||||
local function SetImportedProfile(profileType, profileKey, profileData, force)
|
||||
if profileType == 'profile' then
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.profile) --Remove unwanted options from import
|
||||
|
||||
if not ElvDB.profiles[profileKey] or force then
|
||||
if force and E.data.keys.profile == profileKey then
|
||||
--Overwriting an active profile doesn't update when calling SetProfile
|
||||
--So make it look like we use a different profile
|
||||
E.data.keys.profile = profileKey..'_Temp'
|
||||
end
|
||||
|
||||
ElvDB.profiles[profileKey] = profileData
|
||||
|
||||
--Calling SetProfile will now update all settings correctly
|
||||
E.data:SetProfile(profileKey)
|
||||
else
|
||||
E:StaticPopup_Show('IMPORT_PROFILE_EXISTS', nil, nil, {profileKey = profileKey, profileType = profileType, profileData = profileData})
|
||||
end
|
||||
elseif profileType == 'private' then
|
||||
local privateKey = ElvPrivateDB.profileKeys and ElvPrivateDB.profileKeys[E.mynameRealm]
|
||||
if privateKey then
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.private) --Remove unwanted options from import
|
||||
ElvPrivateDB.profiles[privateKey] = profileData
|
||||
E:StaticPopup_Show('IMPORT_RL')
|
||||
end
|
||||
elseif profileType == 'global' then
|
||||
profileData = E:FilterTableFromBlacklist(profileData, blacklistedKeys.global) --Remove unwanted options from import
|
||||
E:CopyTable(ElvDB.global, profileData)
|
||||
E:StaticPopup_Show('IMPORT_RL')
|
||||
elseif profileType == 'filters' then
|
||||
E:CopyTable(ElvDB.global.unitframe, profileData.unitframe)
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
elseif profileType == 'styleFilters' then
|
||||
E:CopyTable(ElvDB.global.nameplate, profileData.nameplate)
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
end
|
||||
end
|
||||
|
||||
function D:ExportProfile(profileType, exportFormat)
|
||||
if not profileType or not exportFormat then
|
||||
E:Print('Bad argument to "ExportProfile" (string expected)')
|
||||
return
|
||||
end
|
||||
|
||||
local profileKey, profileExport = GetProfileExport(profileType, exportFormat)
|
||||
|
||||
return profileKey, profileExport
|
||||
end
|
||||
|
||||
function D:ImportProfile(dataString)
|
||||
local profileType, profileKey, profileData = self:Decode(dataString)
|
||||
|
||||
if not profileData or type(profileData) ~= 'table' then
|
||||
E:Print('Error: something went wrong when converting string to table!')
|
||||
return
|
||||
end
|
||||
|
||||
if profileType and ((profileType == 'profile' and profileKey) or profileType ~= 'profile') then
|
||||
SetImportedProfile(profileType, profileKey, profileData)
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_SUCCESS = {
|
||||
text = L["Your profile was successfully recieved by the player."],
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
button1 = _G.OKAY,
|
||||
}
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_WAITING = {
|
||||
text = L["Profile request sent. Waiting for response from player."],
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
timeout = 20,
|
||||
}
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_REQUEST_DENIED = {
|
||||
text = L["Request was denied by user."],
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
button1 = _G.OKAY,
|
||||
}
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_FAILED = {
|
||||
text = L["Lord! It's a miracle! The download up and vanished like a fart in the wind! Try Again!"],
|
||||
whileDead = 1,
|
||||
hideOnEscape = 1,
|
||||
button1 = _G.OKAY,
|
||||
}
|
||||
|
||||
E.PopupDialogs.DISTRIBUTOR_RESPONSE = {}
|
||||
E.PopupDialogs.DISTRIBUTOR_CONFIRM = {}
|
||||
|
||||
E.PopupDialogs.IMPORT_PROFILE_EXISTS = {
|
||||
text = L["The profile you tried to import already exists. Choose a new name or accept to overwrite the existing profile."],
|
||||
button1 = ACCEPT,
|
||||
button2 = CANCEL,
|
||||
hasEditBox = 1,
|
||||
editBoxWidth = 350,
|
||||
maxLetters = 127,
|
||||
OnAccept = function(self, data)
|
||||
SetImportedProfile(data.profileType, self.editBox:GetText(), data.profileData, true)
|
||||
end,
|
||||
EditBoxOnTextChanged = function(self)
|
||||
if self:GetText() == '' then
|
||||
self:GetParent().button1:Disable()
|
||||
else
|
||||
self:GetParent().button1:Enable()
|
||||
end
|
||||
end,
|
||||
OnShow = function(self, data)
|
||||
self.editBox:SetText(data.profileKey)
|
||||
self.editBox:SetFocus()
|
||||
end,
|
||||
timeout = 0,
|
||||
whileDead = 1,
|
||||
hideOnEscape = true,
|
||||
preferredIndex = 3
|
||||
}
|
||||
|
||||
E.PopupDialogs.IMPORT_RL = {
|
||||
text = L["You have imported settings which may require a UI reload to take effect. Reload now?"],
|
||||
button1 = ACCEPT,
|
||||
button2 = CANCEL,
|
||||
OnAccept = _G.ReloadUI,
|
||||
timeout = 0,
|
||||
whileDead = 1,
|
||||
hideOnEscape = false,
|
||||
preferredIndex = 3
|
||||
}
|
||||
|
||||
E:RegisterModule(D:GetName())
|
||||
86
Core/Dropdown.lua
Normal file
86
Core/Dropdown.lua
Normal file
@@ -0,0 +1,86 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local _G = _G
|
||||
local tinsert = tinsert
|
||||
local ToggleFrame = ToggleFrame
|
||||
local GetCursorPosition = GetCursorPosition
|
||||
local CreateFrame = CreateFrame
|
||||
|
||||
local PADDING = 10
|
||||
local BUTTON_HEIGHT = 16
|
||||
local BUTTON_WIDTH = 135
|
||||
|
||||
local function OnClick(btn)
|
||||
btn.func()
|
||||
btn:GetParent():Hide()
|
||||
end
|
||||
|
||||
local function OnEnter(btn)
|
||||
btn.hoverTex:Show()
|
||||
end
|
||||
|
||||
local function OnLeave(btn)
|
||||
btn.hoverTex:Hide()
|
||||
end
|
||||
|
||||
function E:DropDown(list, frame, xOffset, yOffset)
|
||||
if not frame.buttons then
|
||||
frame.buttons = {}
|
||||
frame:SetFrameStrata('DIALOG')
|
||||
frame:SetClampedToScreen(true)
|
||||
tinsert(_G.UISpecialFrames, frame:GetName())
|
||||
frame:Hide()
|
||||
end
|
||||
|
||||
xOffset = xOffset or 0
|
||||
yOffset = yOffset or 0
|
||||
|
||||
for i = 1, #frame.buttons do
|
||||
frame.buttons[i]:Hide()
|
||||
end
|
||||
|
||||
for i = 1, #list do
|
||||
if not frame.buttons[i] then
|
||||
frame.buttons[i] = CreateFrame('Button', nil, frame)
|
||||
|
||||
frame.buttons[i].hoverTex = frame.buttons[i]:CreateTexture(nil, 'OVERLAY')
|
||||
frame.buttons[i].hoverTex:SetAllPoints()
|
||||
frame.buttons[i].hoverTex:SetTexture([[Interface\QuestFrame\UI-QuestTitleHighlight]])
|
||||
frame.buttons[i].hoverTex:SetBlendMode('ADD')
|
||||
frame.buttons[i].hoverTex:Hide()
|
||||
|
||||
frame.buttons[i].text = frame.buttons[i]:CreateFontString(nil, 'BORDER')
|
||||
frame.buttons[i].text:SetAllPoints()
|
||||
frame.buttons[i].text:FontTemplate(nil, nil, '')
|
||||
frame.buttons[i].text:SetJustifyH('LEFT')
|
||||
|
||||
frame.buttons[i]:SetScript('OnEnter', OnEnter)
|
||||
frame.buttons[i]:SetScript('OnLeave', OnLeave)
|
||||
end
|
||||
|
||||
frame.buttons[i]:Show()
|
||||
frame.buttons[i]:Height(BUTTON_HEIGHT)
|
||||
frame.buttons[i]:Width(BUTTON_WIDTH)
|
||||
frame.buttons[i].text:SetText(list[i].text)
|
||||
frame.buttons[i].func = list[i].func
|
||||
frame.buttons[i]:SetScript('OnClick', OnClick)
|
||||
|
||||
if i == 1 then
|
||||
frame.buttons[i]:Point('TOPLEFT', frame, 'TOPLEFT', PADDING, -PADDING)
|
||||
else
|
||||
frame.buttons[i]:Point('TOPLEFT', frame.buttons[i-1], 'BOTTOMLEFT')
|
||||
end
|
||||
end
|
||||
|
||||
frame:Height((#list * BUTTON_HEIGHT) + PADDING * 2)
|
||||
frame:Width(BUTTON_WIDTH + PADDING * 2)
|
||||
|
||||
local UIScale = _G.UIParent:GetScale()
|
||||
local x, y = GetCursorPosition()
|
||||
x = x/UIScale
|
||||
y = y/UIScale
|
||||
frame:ClearAllPoints()
|
||||
frame:Point('TOPLEFT', _G.UIParent, 'BOTTOMLEFT', x + xOffset, y + yOffset)
|
||||
|
||||
ToggleFrame(frame)
|
||||
end
|
||||
162
Core/Fonts.lua
Normal file
162
Core/Fonts.lua
Normal file
@@ -0,0 +1,162 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local LSM = E.Libs.LSM
|
||||
|
||||
local _G = _G
|
||||
local min, max = min, max
|
||||
local strmatch = strmatch
|
||||
|
||||
local function SetFont(obj, font, size, style, sr, sg, sb, sa, sox, soy, r, g, b)
|
||||
if not obj then return end
|
||||
obj:SetFont(font, size, style)
|
||||
|
||||
if sr and sg and sb then
|
||||
obj:SetShadowColor(sr, sg, sb, sa)
|
||||
end
|
||||
|
||||
if sox and soy then
|
||||
obj:SetShadowOffset(sox, soy)
|
||||
end
|
||||
|
||||
if r and g and b then
|
||||
obj:SetTextColor(r, g, b)
|
||||
elseif r then
|
||||
obj:SetAlpha(r)
|
||||
end
|
||||
end
|
||||
|
||||
local chatFontHeights = {6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20}
|
||||
function E:UpdateBlizzardFonts()
|
||||
local NORMAL = E.media.normFont
|
||||
local NUMBER = E.media.normFont
|
||||
local COMBAT = LSM:Fetch('font', E.private.general.dmgfont)
|
||||
local NAMEFONT = LSM:Fetch('font', E.private.general.namefont)
|
||||
local BUBBLE = LSM:Fetch('font', E.private.general.chatBubbleFont)
|
||||
|
||||
_G.CHAT_FONT_HEIGHTS = chatFontHeights
|
||||
|
||||
if (E.eyefinity or E.ultrawide) then COMBAT = E.Media.Fonts.Invisible end -- set an invisible font for xp, honor kill, etc
|
||||
if E.private.general.replaceNameFont then _G.UNIT_NAME_FONT = NAMEFONT end
|
||||
if E.private.general.replaceCombatFont then _G.DAMAGE_TEXT_FONT = COMBAT end
|
||||
if E.private.general.replaceBlizzFonts then
|
||||
_G.STANDARD_TEXT_FONT = NORMAL
|
||||
--_G.NAMEPLATE_FONT = NAMEFONT
|
||||
|
||||
local size = E.db.general.fontSize
|
||||
local enormous = size * 1.9
|
||||
local mega = size * 1.7
|
||||
local huge = size * 1.5
|
||||
local large = size * 1.3
|
||||
local medium = size * 1.1
|
||||
local small = size * 0.9
|
||||
local tiny = size * 0.8
|
||||
|
||||
local s = not E.private.general.unifiedBlizzFonts
|
||||
local mono = strmatch(E.db.general.fontStyle, 'MONOCHROME') and 'MONOCHROME' or ''
|
||||
local thick, outline = mono..'THICKOUTLINE', mono..'OUTLINE'
|
||||
|
||||
SetFont(_G.ChatBubbleFont, BUBBLE, E.private.general.chatBubbleFontSize, E.private.general.chatBubbleFontOutline) -- 13
|
||||
SetFont(_G.AchievementFont_Small, NORMAL, s and small or size) -- 10 Achiev dates
|
||||
SetFont(_G.BossEmoteNormalHuge, NORMAL, 24) -- Talent Title
|
||||
SetFont(_G.CoreAbilityFont, NORMAL, 26) -- 32 Core abilities(title)
|
||||
SetFont(_G.DestinyFontHuge, NORMAL, 32) -- Garrison Mission Report
|
||||
SetFont(_G.DestinyFontMed, NORMAL, 14) -- Added in 7.3.5 used for ?
|
||||
SetFont(_G.Fancy12Font, NORMAL, 12) -- Added in 7.3.5 used for ?
|
||||
SetFont(_G.Fancy14Font, NORMAL, 14) -- Added in 7.3.5 used for ?
|
||||
SetFont(_G.Fancy22Font, NORMAL, s and 22 or 20) -- Talking frame Title font
|
||||
SetFont(_G.Fancy24Font, NORMAL, s and 24 or 20) -- Artifact frame - weapon name
|
||||
SetFont(_G.FriendsFont_11, NORMAL, 11)
|
||||
SetFont(_G.FriendsFont_Large, NORMAL, s and large or size) -- 14
|
||||
SetFont(_G.FriendsFont_Normal, NORMAL, size) -- 12
|
||||
SetFont(_G.FriendsFont_Small, NORMAL, s and small or size) -- 10
|
||||
SetFont(_G.FriendsFont_UserText, NORMAL, size) -- 11
|
||||
SetFont(_G.Game10Font_o1, NORMAL, 10, 'OUTLINE')
|
||||
SetFont(_G.Game120Font, NORMAL, 120)
|
||||
SetFont(_G.Game12Font, NORMAL, 12) -- PVP Stuff
|
||||
SetFont(_G.Game13FontShadow, NORMAL, s and 13 or 14) -- InspectPvpFrame
|
||||
SetFont(_G.Game15Font_o1, NORMAL, 15) -- CharacterStatsPane (ItemLevelFrame)
|
||||
SetFont(_G.Game16Font, NORMAL, 16) -- Added in 7.3.5 used for ?
|
||||
SetFont(_G.Game18Font, NORMAL, 18) -- MissionUI Bonus Chance
|
||||
SetFont(_G.Game24Font, NORMAL, 24) -- Garrison Mission level (in detail frame)
|
||||
SetFont(_G.Game30Font, NORMAL, 30) -- Mission Level
|
||||
SetFont(_G.Game40Font, NORMAL, 40)
|
||||
SetFont(_G.Game42Font, NORMAL, 42) -- PVP Stuff
|
||||
SetFont(_G.Game46Font, NORMAL, 46) -- Added in 7.3.5 used for ?
|
||||
SetFont(_G.Game48Font, NORMAL, 48)
|
||||
SetFont(_G.Game48FontShadow, NORMAL, 48)
|
||||
SetFont(_G.Game60Font, NORMAL, 60)
|
||||
SetFont(_G.Game72Font, NORMAL, 72)
|
||||
SetFont(_G.GameFont_Gigantic, NORMAL, 32) -- Used at the install steps
|
||||
SetFont(_G.GameFontHighlightMedium, NORMAL, s and medium or 15) -- 14 Fix QuestLog Title mouseover
|
||||
SetFont(_G.GameFontHighlightSmall2, NORMAL, s and small or size) -- 11 Skill or Recipe description on TradeSkill frame
|
||||
SetFont(_G.GameFontNormalHuge2, NORMAL, s and huge or 24) -- 24 Mythic weekly best dungeon name
|
||||
SetFont(_G.GameFontNormalLarge, NORMAL, s and large or 16) -- 16
|
||||
SetFont(_G.GameFontNormalLarge2, NORMAL, s and large or 15) -- 18 Garrison Follower Names
|
||||
SetFont(_G.GameFontNormalMed1, NORMAL, s and medium or 14) -- 13 WoW Token Info
|
||||
SetFont(_G.GameFontNormalMed2, NORMAL, s and medium or medium) -- 14 Quest tracker
|
||||
SetFont(_G.GameFontNormalMed3, NORMAL, s and medium or 15) -- 14
|
||||
SetFont(_G.GameFontNormalSmall2, NORMAL, s and small or 12) -- 11 MissionUI Followers names
|
||||
SetFont(_G.GameTooltipHeader, NORMAL, size) -- 14
|
||||
SetFont(_G.InvoiceFont_Med, NORMAL, s and size or 12) -- 12 Mail
|
||||
SetFont(_G.InvoiceFont_Small, NORMAL, s and small or size) -- 10 Mail
|
||||
SetFont(_G.MailFont_Large, NORMAL, 14) -- 10 Mail
|
||||
SetFont(_G.Number11Font, NORMAL, 11)
|
||||
SetFont(_G.Number11Font, NUMBER, 11)
|
||||
SetFont(_G.Number12Font, NORMAL, 12)
|
||||
SetFont(_G.Number12Font_o1, NUMBER, 12, 'OUTLINE')
|
||||
SetFont(_G.Number13Font, NUMBER, 13)
|
||||
SetFont(_G.Number13FontGray, NUMBER, 13)
|
||||
SetFont(_G.Number13FontWhite, NUMBER, 13)
|
||||
SetFont(_G.Number13FontYellow, NUMBER, 13)
|
||||
SetFont(_G.Number14FontGray, NUMBER, 14)
|
||||
SetFont(_G.Number14FontWhite, NUMBER, 14)
|
||||
SetFont(_G.Number15Font, NORMAL, 15)
|
||||
SetFont(_G.Number18Font, NUMBER, 18)
|
||||
SetFont(_G.Number18FontWhite, NUMBER, 18)
|
||||
SetFont(_G.NumberFont_Outline_Huge, NUMBER, s and huge or 28, thick) -- 30
|
||||
SetFont(_G.NumberFont_Outline_Large, NUMBER, s and large or 15, outline) -- 16
|
||||
SetFont(_G.NumberFont_Outline_Med, NUMBER, medium, 'OUTLINE') -- 14
|
||||
SetFont(_G.NumberFont_OutlineThick_Mono_Small, NUMBER, size, 'OUTLINE') -- 12
|
||||
SetFont(_G.NumberFont_Shadow_Med, NORMAL, s and medium or size) -- 14 Chat EditBox
|
||||
SetFont(_G.NumberFont_Shadow_Small, NORMAL, s and small or size) -- 12
|
||||
SetFont(_G.NumberFontNormalSmall, NORMAL, s and small or 11, 'OUTLINE') -- 12 Calendar, EncounterJournal
|
||||
SetFont(_G.PriceFont, NORMAL, 13)
|
||||
SetFont(_G.PVPArenaTextString, NORMAL, 22, outline)
|
||||
SetFont(_G.PVPInfoTextString, NORMAL, 22, outline)
|
||||
SetFont(_G.QuestFont, NORMAL, size) -- 13
|
||||
SetFont(_G.QuestFont_Enormous, NORMAL, s and enormous or 24) -- 30 Garrison Titles
|
||||
SetFont(_G.QuestFont_Huge, NORMAL, s and huge or 15) -- 18 Quest rewards title(Rewards)
|
||||
SetFont(_G.QuestFont_Large, NORMAL, s and large or 14) -- 14
|
||||
SetFont(_G.QuestFont_Shadow_Huge, NORMAL, s and huge or 15) -- 18 Quest Title
|
||||
SetFont(_G.QuestFont_Shadow_Small, NORMAL, s and size or 14) -- 14
|
||||
SetFont(_G.QuestFont_Super_Huge, NORMAL, s and mega or 22) -- 24
|
||||
SetFont(_G.ReputationDetailFont, NORMAL, size) -- 10 Rep Desc when clicking a rep
|
||||
SetFont(_G.SpellFont_Small, NORMAL, 10)
|
||||
SetFont(_G.SubSpellFont, NORMAL, 10) -- Spellbook Sub Names
|
||||
SetFont(_G.SubZoneTextFont, NORMAL, 24, outline) -- 26 World Map(SubZone)
|
||||
SetFont(_G.SubZoneTextString, NORMAL, 25, outline) -- 26
|
||||
SetFont(_G.SystemFont_Huge1, NORMAL, 20) -- Garrison Mission XP
|
||||
SetFont(_G.SystemFont_Huge1_Outline, NORMAL, 18, outline) -- 20 Garrison Mission Chance
|
||||
SetFont(_G.SystemFont_Large, NORMAL, s and 16 or 15)
|
||||
SetFont(_G.SystemFont_Med1, NORMAL, size) -- 12
|
||||
SetFont(_G.SystemFont_Med3, NORMAL, medium) -- 14
|
||||
SetFont(_G.SystemFont_Outline, NORMAL, s and size or 13, outline) -- 13 Pet level on World map
|
||||
SetFont(_G.SystemFont_Outline_Small, NUMBER, s and small or size, 'OUTLINE') -- 10
|
||||
SetFont(_G.SystemFont_OutlineThick_Huge2, NORMAL, s and huge or 20, thick) -- 22
|
||||
SetFont(_G.SystemFont_OutlineThick_WTF, NORMAL, s and enormous or 32, outline) -- 32 World Map
|
||||
SetFont(_G.SystemFont_Shadow_Huge1, NORMAL, 20, outline) -- Raid Warning, Boss emote frame too
|
||||
SetFont(_G.SystemFont_Shadow_Huge3, NORMAL, 22) -- 25 FlightMap
|
||||
SetFont(_G.SystemFont_Shadow_Huge4, NORMAL, 27, nil, nil, nil, nil, nil, 1, -1)
|
||||
SetFont(_G.SystemFont_Shadow_Large, NORMAL, 15)
|
||||
SetFont(_G.SystemFont_Shadow_Large2, NORMAL, 18) -- Auction House ItemDisplay
|
||||
SetFont(_G.SystemFont_Shadow_Large_Outline, NUMBER, 20, 'OUTLINE') -- 16
|
||||
SetFont(_G.SystemFont_Shadow_Med1, NORMAL, size) -- 12
|
||||
SetFont(_G.SystemFont_Shadow_Med2, NORMAL, s and medium or 14.3) -- 14 Shows Order resourses on OrderHallTalentFrame
|
||||
SetFont(_G.SystemFont_Shadow_Med3, NORMAL, medium) -- 14
|
||||
SetFont(_G.SystemFont_Shadow_Small, NORMAL, small) -- 10
|
||||
SetFont(_G.SystemFont_Small, NORMAL, s and small or size) -- 10
|
||||
SetFont(_G.SystemFont_Tiny, NORMAL, s and tiny or size) -- 09
|
||||
SetFont(_G.Tooltip_Med, NORMAL, size) -- 12
|
||||
SetFont(_G.Tooltip_Small, NORMAL, s and small or size) -- 10
|
||||
SetFont(_G.ZoneTextString, NORMAL, s and enormous or 32, outline) -- 32
|
||||
end
|
||||
end
|
||||
882
Core/Install.lua
Normal file
882
Core/Install.lua
Normal file
@@ -0,0 +1,882 @@
|
||||
local E, L, V, P, G =unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB, Localize Underscore
|
||||
local NP = E:GetModule('NamePlates')
|
||||
local UF = E:GetModule('UnitFrames')
|
||||
local CH = E:GetModule('Chat')
|
||||
local S = E:GetModule('Skins')
|
||||
|
||||
local _G = _G
|
||||
local unpack = unpack
|
||||
local format = format
|
||||
local pairs = pairs
|
||||
local ipairs = ipairs
|
||||
local tinsert = tinsert
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local SetCVar = SetCVar
|
||||
local PlaySound = PlaySound
|
||||
local ReloadUI = ReloadUI
|
||||
local UIFrameFadeOut = UIFrameFadeOut
|
||||
local ChatFrame_AddMessageGroup = ChatFrame_AddMessageGroup
|
||||
local ChatFrame_RemoveAllMessageGroups = ChatFrame_RemoveAllMessageGroups
|
||||
local ChatFrame_AddChannel = ChatFrame_AddChannel
|
||||
local ChatFrame_RemoveChannel = ChatFrame_RemoveChannel
|
||||
local ChangeChatColor = ChangeChatColor
|
||||
local ToggleChatColorNamesByClassGroup = ToggleChatColorNamesByClassGroup
|
||||
local FCF_ResetChatWindows = FCF_ResetChatWindows
|
||||
local FCF_UnDockFrame = FCF_UnDockFrame
|
||||
local FCF_OpenNewWindow = FCF_OpenNewWindow
|
||||
local FCF_SavePositionAndDimensions = FCF_SavePositionAndDimensions
|
||||
local FCF_SetWindowName = FCF_SetWindowName
|
||||
local FCF_StopDragging = FCF_StopDragging
|
||||
local FCF_SetChatWindowFontSize = FCF_SetChatWindowFontSize
|
||||
local CLASS, CONTINUE, PREVIOUS = CLASS, CONTINUE, PREVIOUS
|
||||
local LOOT, GENERAL, TRADE = LOOT, GENERAL, TRADE
|
||||
local GUILD_EVENT_LOG = GUILD_EVENT_LOG
|
||||
-- GLOBALS: ElvUIInstallFrame
|
||||
|
||||
local CURRENT_PAGE = 0
|
||||
local MAX_PAGE = 9
|
||||
|
||||
local PLAYER_NAME = format('%s-%s', E.myname, E:ShortenRealm(E.myrealm))
|
||||
local ELV_TOONS = {
|
||||
['Elv-Spirestone'] = true,
|
||||
['Elvz-Spirestone'] = true,
|
||||
['Fleshlite-Spirestone'] = true,
|
||||
['Elvidan-Spirestone'] = true,
|
||||
['Elvilas-Spirestone'] = true,
|
||||
['Fraku-Spirestone'] = true,
|
||||
['Jarvix-Spirestone'] = true,
|
||||
['Watermelon-Spirestone'] = true,
|
||||
['Zinxbe-Spirestone'] = true,
|
||||
['Whorlock-Spirestone'] = true,
|
||||
}
|
||||
|
||||
function E:SetupChat(noDisplayMsg)
|
||||
FCF_ResetChatWindows()
|
||||
FCF_OpenNewWindow(LOOT)
|
||||
FCF_UnDockFrame(_G.ChatFrame3)
|
||||
|
||||
for _, name in ipairs(_G.CHAT_FRAMES) do
|
||||
local frame = _G[name]
|
||||
local id = frame:GetID()
|
||||
|
||||
if E.private.chat.enable then
|
||||
CH:FCFTab_UpdateColors(CH:GetTab(_G[name]))
|
||||
end
|
||||
|
||||
-- move general bottom left
|
||||
if id == 1 then
|
||||
frame:ClearAllPoints()
|
||||
frame:Point('BOTTOMLEFT', _G.LeftChatToggleButton, 'TOPLEFT', 1, 3)
|
||||
elseif id == 3 then
|
||||
frame:ClearAllPoints()
|
||||
frame:Point('BOTTOMLEFT', _G.RightChatDataPanel, 'TOPLEFT', 1, 3)
|
||||
end
|
||||
|
||||
FCF_SavePositionAndDimensions(frame)
|
||||
FCF_StopDragging(frame)
|
||||
FCF_SetChatWindowFontSize(nil, frame, 12)
|
||||
|
||||
-- rename windows general because moved to chat #3
|
||||
if id == 1 then
|
||||
FCF_SetWindowName(frame, GENERAL)
|
||||
elseif id == 2 then
|
||||
FCF_SetWindowName(frame, GUILD_EVENT_LOG)
|
||||
elseif id == 3 then
|
||||
FCF_SetWindowName(frame, LOOT..' / '..TRADE)
|
||||
end
|
||||
end
|
||||
|
||||
-- keys taken from `ChatTypeGroup` but doesnt add: 'OPENING', 'TRADESKILLS', 'PET_INFO', 'COMBAT_MISC_INFO', 'COMMUNITIES_CHANNEL', 'PET_BATTLE_COMBAT_LOG', 'PET_BATTLE_INFO', 'TARGETICONS'
|
||||
local chatGroup = { 'SYSTEM', 'CHANNEL', 'SAY', 'EMOTE', 'YELL', 'WHISPER', 'PARTY', 'PARTY_LEADER', 'RAID', 'RAID_LEADER', 'RAID_WARNING', 'INSTANCE_CHAT', 'INSTANCE_CHAT_LEADER', 'GUILD', 'OFFICER', 'MONSTER_SAY', 'MONSTER_YELL', 'MONSTER_EMOTE', 'MONSTER_WHISPER', 'MONSTER_BOSS_EMOTE', 'MONSTER_BOSS_WHISPER', 'ERRORS', 'AFK', 'DND', 'IGNORED', 'BG_HORDE', 'BG_ALLIANCE', 'BG_NEUTRAL', 'ACHIEVEMENT', 'GUILD_ACHIEVEMENT', 'BN_WHISPER', 'BN_INLINE_TOAST_ALERT' }
|
||||
ChatFrame_RemoveAllMessageGroups(_G.ChatFrame1)
|
||||
for _, v in ipairs(chatGroup) do
|
||||
ChatFrame_AddMessageGroup(_G.ChatFrame1, v)
|
||||
end
|
||||
|
||||
-- keys taken from `ChatTypeGroup` which weren't added above to ChatFrame1
|
||||
chatGroup = { 'COMBAT_XP_GAIN', 'COMBAT_HONOR_GAIN', 'COMBAT_FACTION_CHANGE', 'SKILL', 'LOOT', 'CURRENCY', 'MONEY' }
|
||||
ChatFrame_RemoveAllMessageGroups(_G.ChatFrame3)
|
||||
for _, v in ipairs(chatGroup) do
|
||||
ChatFrame_AddMessageGroup(_G.ChatFrame3, v)
|
||||
end
|
||||
|
||||
ChatFrame_AddChannel(_G.ChatFrame1, GENERAL)
|
||||
ChatFrame_RemoveChannel(_G.ChatFrame1, TRADE)
|
||||
ChatFrame_AddChannel(_G.ChatFrame3, TRADE)
|
||||
|
||||
-- set the chat groups names in class color to enabled for all chat groups which players names appear
|
||||
chatGroup = { 'SAY', 'EMOTE', 'YELL', 'WHISPER', 'PARTY', 'PARTY_LEADER', 'RAID', 'RAID_LEADER', 'RAID_WARNING', 'INSTANCE_CHAT', 'INSTANCE_CHAT_LEADER', 'GUILD', 'OFFICER', 'ACHIEVEMENT', 'GUILD_ACHIEVEMENT', 'COMMUNITIES_CHANNEL' }
|
||||
for i = 1, _G.MAX_WOW_CHAT_CHANNELS do
|
||||
tinsert(chatGroup, 'CHANNEL'..i)
|
||||
end
|
||||
for _, v in ipairs(chatGroup) do
|
||||
ToggleChatColorNamesByClassGroup(true, v)
|
||||
end
|
||||
|
||||
-- Adjust Chat Colors
|
||||
ChangeChatColor('CHANNEL1', 195/255, 230/255, 232/255) -- General
|
||||
ChangeChatColor('CHANNEL2', 232/255, 158/255, 121/255) -- Trade
|
||||
ChangeChatColor('CHANNEL3', 232/255, 228/255, 121/255) -- Local Defense
|
||||
|
||||
if E.private.chat.enable then
|
||||
CH:PositionChats()
|
||||
end
|
||||
|
||||
if E.db.RightChatPanelFaded then
|
||||
_G.RightChatToggleButton:Click()
|
||||
end
|
||||
|
||||
if E.db.LeftChatPanelFaded then
|
||||
_G.LeftChatToggleButton:Click()
|
||||
end
|
||||
|
||||
if ELV_TOONS[PLAYER_NAME] then
|
||||
SetCVar('scriptErrors', 1)
|
||||
end
|
||||
|
||||
if _G.InstallStepComplete and not noDisplayMsg then
|
||||
_G.InstallStepComplete.message = L["Chat Set"]
|
||||
_G.InstallStepComplete:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetupCVars(noDisplayMsg)
|
||||
SetCVar('statusTextDisplay', 'BOTH')
|
||||
SetCVar('screenshotQuality', 10)
|
||||
SetCVar('chatMouseScroll', 1)
|
||||
SetCVar('chatStyle', 'classic')
|
||||
SetCVar('whisperMode', 'inline')
|
||||
SetCVar('wholeChatWindowClickable', 0)
|
||||
SetCVar('showTutorials', 0)
|
||||
SetCVar('showNPETutorials', 0)
|
||||
SetCVar('UberTooltips', 1)
|
||||
SetCVar('threatWarning', 3)
|
||||
SetCVar('alwaysShowActionBars', 1)
|
||||
SetCVar('lockActionBars', 1)
|
||||
SetCVar('spamFilter', 0)
|
||||
SetCVar('cameraDistanceMaxZoomFactor', 2.6)
|
||||
SetCVar('showQuestTrackingTooltips', 1)
|
||||
SetCVar('fstack_preferParentKeys', 0) --Add back the frame names via fstack!
|
||||
|
||||
NP:CVarReset()
|
||||
|
||||
_G.InterfaceOptionsActionBarsPanelPickupActionKeyDropDown:SetValue('SHIFT')
|
||||
_G.InterfaceOptionsActionBarsPanelPickupActionKeyDropDown:RefreshValue()
|
||||
|
||||
if _G.InstallStepComplete and not noDisplayMsg then
|
||||
_G.InstallStepComplete.message = L["CVars Set"]
|
||||
_G.InstallStepComplete:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetColor(r, g, b, a)
|
||||
return { r = r, b = b, g = g, a = a }
|
||||
end
|
||||
|
||||
function E:SetupTheme(theme, noDisplayMsg)
|
||||
E.private.theme = theme
|
||||
|
||||
local classColor
|
||||
|
||||
--Set colors
|
||||
if theme == 'classic' then
|
||||
E.db.general.bordercolor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.31, .31, .31))
|
||||
E.db.general.backdropcolor = E:GetColor(.1, .1, .1)
|
||||
E.db.general.backdropfadecolor = E:GetColor(0.13, 0.13, 0.13, 0.69)
|
||||
E.db.unitframe.colors.borderColor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.31, .31, .31))
|
||||
E.db.unitframe.colors.healthclass = false
|
||||
E.db.unitframe.colors.health = E:GetColor(.31, .31, .31)
|
||||
E.db.unitframe.colors.auraBarBuff = E:GetColor(.31, .31, .31)
|
||||
E.db.unitframe.colors.castColor = E:GetColor(.31, .31, .31)
|
||||
E.db.unitframe.colors.castClassColor = false
|
||||
elseif theme == 'class' then
|
||||
classColor = E:ClassColor(E.myclass, true)
|
||||
|
||||
E.db.general.bordercolor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.31, .31, .31))
|
||||
E.db.general.backdropcolor = E:GetColor(.1, .1, .1)
|
||||
E.db.general.backdropfadecolor = E:GetColor(.06, .06, .06, .8)
|
||||
E.db.unitframe.colors.borderColor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.31, .31, .31))
|
||||
E.db.unitframe.colors.auraBarBuff = E:GetColor(classColor.r, classColor.g, classColor.b)
|
||||
E.db.unitframe.colors.healthclass = true
|
||||
E.db.unitframe.colors.castClassColor = true
|
||||
else
|
||||
E.db.general.bordercolor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.1, .1, .1))
|
||||
E.db.general.backdropcolor = E:GetColor(.1, .1, .1)
|
||||
E.db.general.backdropfadecolor = E:GetColor(.054, .054, .054, .8)
|
||||
E.db.unitframe.colors.borderColor = (E.PixelMode and E:GetColor(0, 0, 0) or E:GetColor(.1, .1, .1))
|
||||
E.db.unitframe.colors.auraBarBuff = E:GetColor(.1, .1, .1)
|
||||
E.db.unitframe.colors.healthclass = false
|
||||
E.db.unitframe.colors.health = E:GetColor(.1, .1, .1)
|
||||
E.db.unitframe.colors.castColor = E:GetColor(.1, .1, .1)
|
||||
E.db.unitframe.colors.castClassColor = false
|
||||
end
|
||||
|
||||
--Value Color
|
||||
if theme == 'class' then
|
||||
E.db.general.valuecolor = E:GetColor(classColor.r, classColor.g, classColor.b)
|
||||
else
|
||||
E.db.general.valuecolor = E:GetColor(23/255, 132/255, 209/255)
|
||||
end
|
||||
|
||||
E:UpdateStart(true, true)
|
||||
|
||||
if _G.InstallStepComplete and not noDisplayMsg then
|
||||
_G.InstallStepComplete.message = L["Theme Set"]
|
||||
_G.InstallStepComplete:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetupLayout(layout, noDataReset, noDisplayMsg)
|
||||
if not noDataReset then
|
||||
E.db.layoutSet = layout
|
||||
E.db.layoutSetting = layout
|
||||
|
||||
--Unitframes
|
||||
E:CopyTable(E.db.unitframe.units, P.unitframe.units)
|
||||
|
||||
--Shared base layout, tweaks to individual layouts will be below
|
||||
E:ResetMovers()
|
||||
if not E.db.movers then
|
||||
E.db.movers = {}
|
||||
end
|
||||
|
||||
--ActionBars
|
||||
E.db.actionbar.bar1.buttons = 8
|
||||
E.db.actionbar.bar1.buttonsize = 50
|
||||
E.db.actionbar.bar1.buttonspacing = 1
|
||||
E.db.actionbar.bar2.buttons = 9
|
||||
E.db.actionbar.bar2.buttonsize = 38
|
||||
E.db.actionbar.bar2.buttonspacing = 1
|
||||
E.db.actionbar.bar2.enabled = true
|
||||
E.db.actionbar.bar2.visibility = '[petbattle] hide; show'
|
||||
E.db.actionbar.bar3.buttons = 8
|
||||
E.db.actionbar.bar3.buttonsize = 50
|
||||
E.db.actionbar.bar3.buttonspacing = 1
|
||||
E.db.actionbar.bar3.buttonsPerRow = 10
|
||||
E.db.actionbar.bar3.visibility = '[petbattle] hide; show'
|
||||
E.db.actionbar.bar4.enabled = false
|
||||
E.db.actionbar.bar4.visibility = '[petbattle] hide; show'
|
||||
E.db.actionbar.bar5.enabled = false
|
||||
E.db.actionbar.bar5.visibility = '[petbattle] hide; show'
|
||||
E.db.actionbar.bar6.visibility = '[petbattle] hide; show'
|
||||
--Auras
|
||||
E.db.auras.buffs.countFontSize = 10
|
||||
E.db.auras.buffs.size = 40
|
||||
E.db.auras.debuffs.countFontSize = 10
|
||||
E.db.auras.debuffs.size = 40
|
||||
--Bags
|
||||
E.db.bags.bagSize = 42
|
||||
E.db.bags.bagWidth = 474
|
||||
E.db.bags.bankSize = 42
|
||||
E.db.bags.bankWidth = 474
|
||||
E.db.bags.itemLevelCustomColorEnable = true
|
||||
E.db.bags.scrapIcon = true
|
||||
--Chat
|
||||
E.db.chat.fontSize = 10
|
||||
E.db.chat.separateSizes = false
|
||||
E.db.chat.panelHeight = 236
|
||||
E.db.chat.panelWidth = 472
|
||||
E.db.chat.tabFontSize = 10
|
||||
--DataTexts
|
||||
E.db.datatexts.panels.LeftChatDataPanel[3] = 'QuickJoin'
|
||||
--General
|
||||
E.db.general.bonusObjectivePosition = 'AUTO'
|
||||
E.db.general.minimap.size = 220
|
||||
E.db.general.objectiveFrameHeight = 400
|
||||
E.db.general.talkingHeadFrameScale = 1
|
||||
E.db.general.totems.growthDirection = 'HORIZONTAL'
|
||||
E.db.general.totems.size = 50
|
||||
E.db.general.totems.spacing = 8
|
||||
|
||||
--Movers
|
||||
for mover, position in pairs(E.LayoutMoverPositions.ALL) do
|
||||
E.db.movers[mover] = position
|
||||
E:SaveMoverDefaultPosition(mover)
|
||||
end
|
||||
--Tooltip
|
||||
E.db.tooltip.healthBar.fontOutline = 'MONOCHROMEOUTLINE'
|
||||
E.db.tooltip.healthBar.height = 12
|
||||
--UnitFrames
|
||||
E.db.unitframe.smoothbars = true
|
||||
E.db.unitframe.thinBorders = true
|
||||
--Player
|
||||
E.db.unitframe.units.player.aurabar.height = 26
|
||||
E.db.unitframe.units.player.buffs.perrow = 7
|
||||
E.db.unitframe.units.player.castbar.height = 40
|
||||
E.db.unitframe.units.player.castbar.insideInfoPanel = false
|
||||
E.db.unitframe.units.player.castbar.width = 405
|
||||
E.db.unitframe.units.player.classbar.height = 14
|
||||
E.db.unitframe.units.player.debuffs.perrow = 7
|
||||
E.db.unitframe.units.player.disableMouseoverGlow = true
|
||||
E.db.unitframe.units.player.healPrediction.showOverAbsorbs = false
|
||||
E.db.unitframe.units.player.health.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.player.height = 82
|
||||
E.db.unitframe.units.player.infoPanel.enable = true
|
||||
E.db.unitframe.units.player.power.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.player.power.height = 22
|
||||
--Target
|
||||
E.db.unitframe.units.target.aurabar.height = 26
|
||||
E.db.unitframe.units.target.buffs.anchorPoint = 'TOPLEFT'
|
||||
E.db.unitframe.units.target.buffs.perrow = 7
|
||||
E.db.unitframe.units.target.castbar.height = 40
|
||||
E.db.unitframe.units.target.castbar.insideInfoPanel = false
|
||||
E.db.unitframe.units.target.castbar.width = 405
|
||||
E.db.unitframe.units.target.debuffs.anchorPoint = 'TOPLEFT'
|
||||
E.db.unitframe.units.target.debuffs.attachTo = 'FRAME'
|
||||
E.db.unitframe.units.target.debuffs.enable = false
|
||||
E.db.unitframe.units.target.debuffs.maxDuration = 0
|
||||
E.db.unitframe.units.target.debuffs.perrow = 7
|
||||
E.db.unitframe.units.target.disableMouseoverGlow = true
|
||||
E.db.unitframe.units.target.healPrediction.showOverAbsorbs = false
|
||||
E.db.unitframe.units.target.health.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.target.height = 82
|
||||
E.db.unitframe.units.target.infoPanel.enable = true
|
||||
E.db.unitframe.units.target.name.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.target.orientation = 'LEFT'
|
||||
E.db.unitframe.units.target.power.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.target.power.height = 22
|
||||
--TargetTarget
|
||||
E.db.unitframe.units.targettarget.debuffs.anchorPoint = 'TOPRIGHT'
|
||||
E.db.unitframe.units.targettarget.debuffs.enable = false
|
||||
E.db.unitframe.units.targettarget.disableMouseoverGlow = true
|
||||
E.db.unitframe.units.targettarget.power.enable = false
|
||||
E.db.unitframe.units.targettarget.raidicon.attachTo = 'LEFT'
|
||||
E.db.unitframe.units.targettarget.raidicon.enable = false
|
||||
E.db.unitframe.units.targettarget.raidicon.xOffset = 2
|
||||
E.db.unitframe.units.targettarget.raidicon.yOffset = 0
|
||||
E.db.unitframe.units.targettarget.threatStyle = 'GLOW'
|
||||
E.db.unitframe.units.targettarget.width = 270
|
||||
--Focus
|
||||
E.db.unitframe.units.focus.castbar.width = 270
|
||||
E.db.unitframe.units.focus.width = 270
|
||||
--Pet
|
||||
E.db.unitframe.units.pet.castbar.iconSize = 32
|
||||
E.db.unitframe.units.pet.castbar.width = 270
|
||||
E.db.unitframe.units.pet.debuffs.anchorPoint = 'TOPRIGHT'
|
||||
E.db.unitframe.units.pet.debuffs.enable = true
|
||||
E.db.unitframe.units.pet.disableTargetGlow = false
|
||||
E.db.unitframe.units.pet.infoPanel.height = 14
|
||||
E.db.unitframe.units.pet.portrait.camDistanceScale = 2
|
||||
E.db.unitframe.units.pet.width = 270
|
||||
--Boss
|
||||
E.db.unitframe.units.boss.buffs.maxDuration = 300
|
||||
E.db.unitframe.units.boss.buffs.sizeOverride = 27
|
||||
E.db.unitframe.units.boss.buffs.yOffset = 16
|
||||
E.db.unitframe.units.boss.castbar.width = 246
|
||||
E.db.unitframe.units.boss.debuffs.maxDuration = 300
|
||||
E.db.unitframe.units.boss.debuffs.numrows = 1
|
||||
E.db.unitframe.units.boss.debuffs.sizeOverride = 27
|
||||
E.db.unitframe.units.boss.debuffs.yOffset = -16
|
||||
E.db.unitframe.units.boss.height = 60
|
||||
E.db.unitframe.units.boss.infoPanel.height = 17
|
||||
E.db.unitframe.units.boss.portrait.camDistanceScale = 2
|
||||
E.db.unitframe.units.boss.portrait.width = 45
|
||||
E.db.unitframe.units.boss.width = 246
|
||||
--Party
|
||||
E.db.unitframe.units.party.height = 74
|
||||
E.db.unitframe.units.party.power.height = 13
|
||||
E.db.unitframe.units.party.rdebuffs.font = 'PT Sans Narrow'
|
||||
E.db.unitframe.units.party.width = 231
|
||||
--Raid
|
||||
E.db.unitframe.units.raid.growthDirection = 'RIGHT_UP'
|
||||
E.db.unitframe.units.raid.infoPanel.enable = true
|
||||
E.db.unitframe.units.raid.name.attachTextTo = 'InfoPanel'
|
||||
E.db.unitframe.units.raid.name.position = 'BOTTOMLEFT'
|
||||
E.db.unitframe.units.raid.name.xOffset = 2
|
||||
E.db.unitframe.units.raid.numGroups = 8
|
||||
E.db.unitframe.units.raid.rdebuffs.font = 'PT Sans Narrow'
|
||||
E.db.unitframe.units.raid.rdebuffs.size = 30
|
||||
E.db.unitframe.units.raid.rdebuffs.xOffset = 30
|
||||
E.db.unitframe.units.raid.rdebuffs.yOffset = 25
|
||||
E.db.unitframe.units.raid.resurrectIcon.attachTo = 'BOTTOMRIGHT'
|
||||
E.db.unitframe.units.raid.roleIcon.attachTo = 'InfoPanel'
|
||||
E.db.unitframe.units.raid.roleIcon.position = 'BOTTOMRIGHT'
|
||||
E.db.unitframe.units.raid.roleIcon.size = 12
|
||||
E.db.unitframe.units.raid.roleIcon.xOffset = 0
|
||||
E.db.unitframe.units.raid.visibility = '[@raid6,noexists] hide;show'
|
||||
E.db.unitframe.units.raid.width = 92
|
||||
--Raid40
|
||||
E.db.unitframe.units.raid40.enable = false
|
||||
E.db.unitframe.units.raid40.rdebuffs.font = 'PT Sans Narrow'
|
||||
|
||||
--[[
|
||||
Layout Tweaks will be handled below,
|
||||
These are changes that deviate from the shared base layout.
|
||||
]]
|
||||
if E.LayoutMoverPositions[layout] then
|
||||
for mover, position in pairs(E.LayoutMoverPositions[layout]) do
|
||||
E.db.movers[mover] = position
|
||||
E:SaveMoverDefaultPosition(mover)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
|
||||
if _G.InstallStepComplete and not noDisplayMsg then
|
||||
_G.InstallStepComplete.message = L["Layout Set"]
|
||||
_G.InstallStepComplete:Show()
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetupAuras(style, noDisplayMsg)
|
||||
local frame = UF.player
|
||||
E:CopyTable(E.db.unitframe.units.player.buffs, P.unitframe.units.player.buffs)
|
||||
E:CopyTable(E.db.unitframe.units.player.debuffs, P.unitframe.units.player.debuffs)
|
||||
E:CopyTable(E.db.unitframe.units.player.aurabar, P.unitframe.units.player.aurabar)
|
||||
if frame then
|
||||
UF:Configure_AllAuras(frame)
|
||||
UF:Configure_AuraBars(frame)
|
||||
end
|
||||
|
||||
frame = UF.target
|
||||
E:CopyTable(E.db.unitframe.units.target.buffs, P.unitframe.units.target.buffs)
|
||||
E:CopyTable(E.db.unitframe.units.target.debuffs, P.unitframe.units.target.debuffs)
|
||||
E:CopyTable(E.db.unitframe.units.target.aurabar, P.unitframe.units.target.aurabar)
|
||||
if frame then
|
||||
UF:Configure_AllAuras(frame)
|
||||
UF:Configure_AuraBars(frame)
|
||||
end
|
||||
|
||||
frame = UF.focus
|
||||
E:CopyTable(E.db.unitframe.units.focus.buffs, P.unitframe.units.focus.buffs)
|
||||
E:CopyTable(E.db.unitframe.units.focus.debuffs, P.unitframe.units.focus.debuffs)
|
||||
E:CopyTable(E.db.unitframe.units.focus.aurabar, P.unitframe.units.focus.aurabar)
|
||||
if frame then
|
||||
UF:Configure_AllAuras(frame)
|
||||
UF:Configure_AuraBars(frame)
|
||||
end
|
||||
|
||||
if not style then
|
||||
--PLAYER
|
||||
E.db.unitframe.units.player.buffs.enable = true
|
||||
E.db.unitframe.units.player.buffs.attachTo = 'FRAME'
|
||||
E.db.unitframe.units.player.debuffs.attachTo = 'BUFFS'
|
||||
E.db.unitframe.units.player.aurabar.enable = false
|
||||
if E.private.unitframe.enable then
|
||||
UF:CreateAndUpdateUF('player')
|
||||
end
|
||||
|
||||
--TARGET
|
||||
E.db.unitframe.units.target.debuffs.enable = true
|
||||
E.db.unitframe.units.target.aurabar.enable = false
|
||||
if E.private.unitframe.enable then
|
||||
UF:CreateAndUpdateUF('target')
|
||||
end
|
||||
end
|
||||
|
||||
if _G.InstallStepComplete and not noDisplayMsg then
|
||||
_G.InstallStepComplete.message = L["Auras Set"]
|
||||
_G.InstallStepComplete:Show()
|
||||
end
|
||||
end
|
||||
|
||||
local function InstallComplete()
|
||||
E.private.install_complete = E.version
|
||||
|
||||
ReloadUI()
|
||||
end
|
||||
|
||||
local function ResetAll()
|
||||
_G.InstallNextButton:Disable()
|
||||
_G.InstallPrevButton:Disable()
|
||||
_G.InstallOption1Button:Hide()
|
||||
_G.InstallOption1Button:SetScript('OnClick', nil)
|
||||
_G.InstallOption1Button:SetText('')
|
||||
_G.InstallOption2Button:Hide()
|
||||
_G.InstallOption2Button:SetScript('OnClick', nil)
|
||||
_G.InstallOption2Button:SetText('')
|
||||
_G.InstallOption3Button:Hide()
|
||||
_G.InstallOption3Button:SetScript('OnClick', nil)
|
||||
_G.InstallOption3Button:SetText('')
|
||||
_G.InstallOption4Button:Hide()
|
||||
_G.InstallOption4Button:SetScript('OnClick', nil)
|
||||
_G.InstallOption4Button:SetText('')
|
||||
_G.InstallSlider:Hide()
|
||||
_G.InstallSlider.Min:SetText('')
|
||||
_G.InstallSlider.Max:SetText('')
|
||||
_G.InstallSlider.Cur:SetText('')
|
||||
ElvUIInstallFrame.SubTitle:SetText('')
|
||||
ElvUIInstallFrame.Desc1:SetText('')
|
||||
ElvUIInstallFrame.Desc2:SetText('')
|
||||
ElvUIInstallFrame.Desc3:SetText('')
|
||||
ElvUIInstallFrame:Size(550, 400)
|
||||
end
|
||||
|
||||
function E:SetPage(PageNum)
|
||||
CURRENT_PAGE = PageNum
|
||||
ResetAll()
|
||||
|
||||
_G.InstallStatus.anim.progress:SetChange(PageNum)
|
||||
_G.InstallStatus.anim.progress:Play()
|
||||
_G.InstallStatus.text:SetText(CURRENT_PAGE..' / '..MAX_PAGE)
|
||||
|
||||
local r, g, b = E:ColorGradient(CURRENT_PAGE / MAX_PAGE, 1, 0, 0, 1, 1, 0, 0, 1, 0)
|
||||
ElvUIInstallFrame.Status:SetStatusBarColor(r, g, b)
|
||||
|
||||
if PageNum == MAX_PAGE then
|
||||
_G.InstallNextButton:Disable()
|
||||
else
|
||||
_G.InstallNextButton:Enable()
|
||||
end
|
||||
|
||||
if PageNum == 1 then
|
||||
_G.InstallPrevButton:Disable()
|
||||
else
|
||||
_G.InstallPrevButton:Enable()
|
||||
end
|
||||
|
||||
local InstallOption1Button = _G.InstallOption1Button
|
||||
local InstallOption2Button = _G.InstallOption2Button
|
||||
local InstallOption3Button = _G.InstallOption3Button
|
||||
local InstallSlider = _G.InstallSlider
|
||||
|
||||
local f = ElvUIInstallFrame
|
||||
if PageNum == 1 then
|
||||
f.SubTitle:SetFormattedText(L["Welcome to ElvUI version %s!"], E.version)
|
||||
f.Desc1:SetText(L["This install process will help you learn some of the features in ElvUI has to offer and also prepare your user interface for usage."])
|
||||
f.Desc2:SetText(L["The in-game configuration menu can be accessed by typing the /ec command. Press the button below if you wish to skip the installation process."])
|
||||
f.Desc3:SetText(L["Please press the continue button to go onto the next step."])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', InstallComplete)
|
||||
InstallOption1Button:SetText(L["Skip Process"])
|
||||
elseif PageNum == 2 then
|
||||
f.SubTitle:SetText(L["CVars"])
|
||||
f.Desc1:SetText(L["This part of the installation process sets up your World of Warcraft default options it is recommended you should do this step for everything to behave properly."])
|
||||
f.Desc2:SetText(L["Please click the button below to setup your CVars."])
|
||||
f.Desc3:SetText(L["Importance: |cffFF3333High|r"])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E:SetupCVars() end)
|
||||
InstallOption1Button:SetText(L["Setup CVars"])
|
||||
elseif PageNum == 3 then
|
||||
f.SubTitle:SetText(L["Chat"])
|
||||
f.Desc1:SetText(L["This part of the installation process sets up your chat windows names, positions and colors."])
|
||||
f.Desc2:SetText(L["The chat windows function the same as Blizzard standard chat windows, you can right click the tabs and drag them around, rename, etc. Please click the button below to setup your chat windows."])
|
||||
f.Desc3:SetText(L["Importance: |cffD3CF00Medium|r"])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E:SetupChat() end)
|
||||
InstallOption1Button:SetText(L["Setup Chat"])
|
||||
elseif PageNum == 4 then
|
||||
f.SubTitle:SetText(L["Profile Settings Setup"])
|
||||
f.Desc1:SetText(L["Please click the button below to setup your Profile Settings."])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function()
|
||||
E.data:SetProfile('Default')
|
||||
E:NextPage()
|
||||
end)
|
||||
InstallOption1Button:SetText(L["Shared Profile"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', function()
|
||||
E.data:SetProfile(E.mynameRealm)
|
||||
E:NextPage()
|
||||
end)
|
||||
InstallOption2Button:SetText(L["New Profile"])
|
||||
elseif PageNum == 5 then
|
||||
f.SubTitle:SetText(L["Theme Setup"])
|
||||
f.Desc1:SetText(L["Choose a theme layout you wish to use for your initial setup."])
|
||||
f.Desc2:SetText(L["You can always change fonts and colors of any element of ElvUI from the in-game configuration."])
|
||||
f.Desc3:SetText(L["Importance: |cFF33FF33Low|r"])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E:SetupTheme('classic') end)
|
||||
InstallOption1Button:SetText(L["Classic"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', function() E:SetupTheme('default') end)
|
||||
InstallOption2Button:SetText(L["Dark"])
|
||||
InstallOption3Button:Show()
|
||||
InstallOption3Button:SetScript('OnClick', function() E:SetupTheme('class') end)
|
||||
InstallOption3Button:SetText(CLASS)
|
||||
elseif PageNum == 6 then
|
||||
f.SubTitle:SetText(_G.UISCALE)
|
||||
f.Desc1:SetFormattedText(L["Adjust the UI Scale to fit your screen, press the autoscale button to set the UI Scale automatically."])
|
||||
InstallSlider:Show()
|
||||
InstallSlider:SetValueStep(0.01)
|
||||
InstallSlider:SetObeyStepOnDrag(true)
|
||||
InstallSlider:SetMinMaxValues(0.4, 1.15)
|
||||
|
||||
local value = E.global.general.UIScale
|
||||
InstallSlider:SetValue(value)
|
||||
InstallSlider.Cur:SetText(value)
|
||||
InstallSlider:SetScript('OnValueChanged', function(self)
|
||||
local val = E:Round(self:GetValue(), 2)
|
||||
E.global.general.UIScale = val
|
||||
InstallSlider.Cur:SetText(val)
|
||||
end)
|
||||
|
||||
InstallSlider.Min:SetText(0.4)
|
||||
InstallSlider.Max:SetText(1.15)
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function()
|
||||
local scale = E:PixelBestSize()
|
||||
|
||||
-- this is to just keep the slider in place, the values need updated again afterwards
|
||||
InstallSlider:SetValue(scale)
|
||||
|
||||
-- update the values with deeper accuracy
|
||||
E.global.general.UIScale = scale
|
||||
InstallSlider.Cur:SetText(E.global.general.UIScale)
|
||||
end)
|
||||
|
||||
InstallOption1Button:SetText(L["Auto Scale"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', E.PixelScaleChanged)
|
||||
|
||||
InstallOption2Button:SetText(L["Preview"])
|
||||
f.Desc3:SetText(L["Importance: |cffFF3333High|r"])
|
||||
elseif PageNum == 7 then
|
||||
f.SubTitle:SetText(L["Layout"])
|
||||
f.Desc1:SetText(L["You can now choose what layout you wish to use based on your combat role."])
|
||||
f.Desc2:SetText(L["This will change the layout of your unitframes and actionbars."])
|
||||
f.Desc3:SetText(L["Importance: |cffD3CF00Medium|r"])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E.db.layoutSet = nil; E:SetupLayout('tank') end)
|
||||
InstallOption1Button:SetText(L["Tank / Physical DPS"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', function() E.db.layoutSet = nil; E:SetupLayout('healer') end)
|
||||
InstallOption2Button:SetText(L["Healer"])
|
||||
InstallOption3Button:Show()
|
||||
InstallOption3Button:SetScript('OnClick', function() E.db.layoutSet = nil; E:SetupLayout('dpsCaster') end)
|
||||
InstallOption3Button:SetText(L["Caster DPS"])
|
||||
elseif PageNum == 8 then
|
||||
f.SubTitle:SetText(L["Auras"])
|
||||
f.Desc1:SetText(L["Select the type of aura system you want to use with ElvUI's unitframes. Set to Aura Bar & Icons to use both aura bars and icons, set to icons only to only see icons."])
|
||||
f.Desc2:SetText(L["If you have an icon or aurabar that you don't want to display simply hold down shift and right click the icon for it to disapear."])
|
||||
f.Desc3:SetText(L["Importance: |cffD3CF00Medium|r"])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E:SetupAuras(true) end)
|
||||
InstallOption1Button:SetText(L["Aura Bars & Icons"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', function() E:SetupAuras() end)
|
||||
InstallOption2Button:SetText(L["Icons Only"])
|
||||
elseif PageNum == 9 then
|
||||
f.SubTitle:SetText(L["Installation Complete"])
|
||||
f.Desc1:SetText(L["You are now finished with the installation process. If you are in need of technical support please visit us at http://www.tukui.org."])
|
||||
f.Desc2:SetText(L["Please click the button below so you can setup variables and ReloadUI."])
|
||||
InstallOption1Button:Show()
|
||||
InstallOption1Button:SetScript('OnClick', function() E:StaticPopup_Show('ELVUI_EDITBOX', nil, nil, 'https://discord.gg/xFWcfgE') end)
|
||||
InstallOption1Button:SetText(L["Discord"])
|
||||
InstallOption2Button:Show()
|
||||
InstallOption2Button:SetScript('OnClick', InstallComplete)
|
||||
InstallOption2Button:SetText(L["Finished"])
|
||||
ElvUIInstallFrame:Size(550, 350)
|
||||
end
|
||||
end
|
||||
|
||||
function E:NextPage()
|
||||
if CURRENT_PAGE ~= MAX_PAGE then
|
||||
CURRENT_PAGE = CURRENT_PAGE + 1
|
||||
E:SetPage(CURRENT_PAGE)
|
||||
end
|
||||
end
|
||||
|
||||
function E:PreviousPage()
|
||||
if CURRENT_PAGE ~= 1 then
|
||||
CURRENT_PAGE = CURRENT_PAGE - 1
|
||||
E:SetPage(CURRENT_PAGE)
|
||||
end
|
||||
end
|
||||
|
||||
--Install UI
|
||||
function E:Install()
|
||||
if not _G.InstallStepComplete then
|
||||
local imsg = CreateFrame('Frame', 'InstallStepComplete', E.UIParent)
|
||||
imsg:Size(418, 72)
|
||||
imsg:Point('TOP', 0, -190)
|
||||
imsg:Hide()
|
||||
imsg:SetScript('OnShow', function(f)
|
||||
if f.message then
|
||||
PlaySound(888)
|
||||
f.text:SetText(f.message)
|
||||
UIFrameFadeOut(f, 3.5, 1, 0)
|
||||
E:Delay(4, f.Hide, f)
|
||||
f.message = nil
|
||||
else
|
||||
f:Hide()
|
||||
end
|
||||
end)
|
||||
|
||||
imsg.firstShow = false
|
||||
|
||||
imsg.bg = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.bg:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.bg:Point('BOTTOM')
|
||||
imsg.bg:Size(326, 103)
|
||||
imsg.bg:SetTexCoord(0.00195313, 0.63867188, 0.03710938, 0.23828125)
|
||||
imsg.bg:SetVertexColor(1, 1, 1, 0.6)
|
||||
|
||||
imsg.lineTop = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.lineTop:SetDrawLayer('BACKGROUND', 2)
|
||||
imsg.lineTop:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.lineTop:Point('TOP')
|
||||
imsg.lineTop:Size(418, 7)
|
||||
imsg.lineTop:SetTexCoord(0.00195313, 0.81835938, 0.01953125, 0.03320313)
|
||||
|
||||
imsg.lineBottom = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.lineBottom:SetDrawLayer('BACKGROUND', 2)
|
||||
imsg.lineBottom:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.lineBottom:Point('BOTTOM')
|
||||
imsg.lineBottom:Size(418, 7)
|
||||
imsg.lineBottom:SetTexCoord(0.00195313, 0.81835938, 0.01953125, 0.03320313)
|
||||
|
||||
imsg.text = imsg:CreateFontString(nil, 'ARTWORK', 'GameFont_Gigantic')
|
||||
imsg.text:Point('BOTTOM', 0, 12)
|
||||
imsg.text:SetTextColor(1, 0.82, 0)
|
||||
imsg.text:SetJustifyH('CENTER')
|
||||
end
|
||||
|
||||
--Create Frame
|
||||
if not ElvUIInstallFrame then
|
||||
local f = CreateFrame('Button', 'ElvUIInstallFrame', E.UIParent, 'BackdropTemplate')
|
||||
f.SetPage = E.SetPage
|
||||
f:Size(550, 400)
|
||||
f:SetTemplate('Transparent')
|
||||
f:Point('CENTER')
|
||||
f:SetFrameStrata('TOOLTIP')
|
||||
|
||||
f:SetMovable(true)
|
||||
f:EnableMouse(true)
|
||||
f:RegisterForDrag('LeftButton')
|
||||
f:SetScript('OnDragStart', function(frame) frame:StartMoving() frame:SetUserPlaced(false) end)
|
||||
f:SetScript('OnDragStop', function(frame) frame:StopMovingOrSizing() end)
|
||||
|
||||
f.Title = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Title:FontTemplate(nil, 17, nil)
|
||||
f.Title:Point('TOP', 0, -5)
|
||||
f.Title:SetText(L["ElvUI Installation"])
|
||||
|
||||
f.Next = CreateFrame('Button', 'InstallNextButton', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Next:Size(110, 25)
|
||||
f.Next:Point('BOTTOMRIGHT', -5, 5)
|
||||
f.Next:SetText(CONTINUE)
|
||||
f.Next:Disable()
|
||||
f.Next:SetScript('OnClick', E.NextPage)
|
||||
S:HandleButton(f.Next, true)
|
||||
|
||||
f.Prev = CreateFrame('Button', 'InstallPrevButton', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Prev:Size(110, 25)
|
||||
f.Prev:Point('BOTTOMLEFT', 5, 5)
|
||||
f.Prev:SetText(PREVIOUS)
|
||||
f.Prev:Disable()
|
||||
f.Prev:SetScript('OnClick', E.PreviousPage)
|
||||
S:HandleButton(f.Prev, true)
|
||||
|
||||
f.Status = CreateFrame('StatusBar', 'InstallStatus', f)
|
||||
f.Status:SetFrameLevel(f.Status:GetFrameLevel() + 2)
|
||||
f.Status:CreateBackdrop()
|
||||
f.Status:SetStatusBarTexture(E.media.normTex)
|
||||
E:RegisterStatusBar(f.Status)
|
||||
f.Status:SetStatusBarColor(1, 0, 0)
|
||||
f.Status:SetMinMaxValues(0, MAX_PAGE)
|
||||
f.Status:Point('TOPLEFT', f.Prev, 'TOPRIGHT', 6, -2)
|
||||
f.Status:Point('BOTTOMRIGHT', f.Next, 'BOTTOMLEFT', -6, 2)
|
||||
|
||||
-- Setup StatusBar Animation
|
||||
f.Status.anim = _G.CreateAnimationGroup(f.Status)
|
||||
f.Status.anim.progress = f.Status.anim:CreateAnimation('Progress')
|
||||
f.Status.anim.progress:SetEasing('Out')
|
||||
f.Status.anim.progress:SetDuration(.3)
|
||||
|
||||
f.Status.text = f.Status:CreateFontString(nil, 'OVERLAY')
|
||||
f.Status.text:FontTemplate(nil, 14, 'OUTLINE')
|
||||
f.Status.text:Point('CENTER')
|
||||
f.Status.text:SetText(CURRENT_PAGE..' / '..MAX_PAGE)
|
||||
|
||||
f.Slider = CreateFrame('Slider', 'InstallSlider', f, 'BackdropTemplate')
|
||||
f.Slider:SetOrientation('HORIZONTAL')
|
||||
f.Slider:Height(15)
|
||||
f.Slider:Width(400)
|
||||
f.Slider:SetHitRectInsets(0, 0, -10, 0)
|
||||
f.Slider:Point('CENTER', 0, 45)
|
||||
S:HandleSliderFrame(f.Slider)
|
||||
f.Slider:Hide()
|
||||
|
||||
f.Slider.Min = f.Slider:CreateFontString(nil, 'ARTWORK', 'GameFontHighlightSmall')
|
||||
f.Slider.Min:Point('RIGHT', f.Slider, 'LEFT', -3, 0)
|
||||
f.Slider.Max = f.Slider:CreateFontString(nil, 'ARTWORK', 'GameFontHighlightSmall')
|
||||
f.Slider.Max:Point('LEFT', f.Slider, 'RIGHT', 3, 0)
|
||||
f.Slider.Cur = f.Slider:CreateFontString(nil, 'ARTWORK', 'GameFontHighlightSmall')
|
||||
f.Slider.Cur:Point('BOTTOM', f.Slider, 'TOP', 0, 10)
|
||||
f.Slider.Cur:FontTemplate(nil, 30, nil)
|
||||
|
||||
f.Option1 = CreateFrame('Button', 'InstallOption1Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option1:Size(160, 30)
|
||||
f.Option1:Point('BOTTOM', 0, 45)
|
||||
f.Option1:SetText('')
|
||||
f.Option1:Hide()
|
||||
S:HandleButton(f.Option1, true)
|
||||
|
||||
f.Option2 = CreateFrame('Button', 'InstallOption2Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option2:Size(110, 30)
|
||||
f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45)
|
||||
f.Option2:SetText('')
|
||||
f.Option2:Hide()
|
||||
f.Option2:SetScript('OnShow', function() f.Option1:Width(110); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOMRIGHT', f, 'BOTTOM', -4, 45) end)
|
||||
f.Option2:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45) end)
|
||||
S:HandleButton(f.Option2, true)
|
||||
|
||||
f.Option3 = CreateFrame('Button', 'InstallOption3Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option3:Size(100, 30)
|
||||
f.Option3:Point('LEFT', f.Option2, 'RIGHT', 4, 0)
|
||||
f.Option3:SetText('')
|
||||
f.Option3:Hide()
|
||||
f.Option3:SetScript('OnShow', function() f.Option1:Width(100); f.Option1:ClearAllPoints(); f.Option1:Point('RIGHT', f.Option2, 'LEFT', -4, 0); f.Option2:Width(100); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOM', f, 'BOTTOM', 0, 45) end)
|
||||
f.Option3:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45); f.Option2:Width(110); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45) end)
|
||||
S:HandleButton(f.Option3, true)
|
||||
|
||||
f.Option4 = CreateFrame('Button', 'InstallOption4Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option4:Size(100, 30)
|
||||
f.Option4:Point('LEFT', f.Option3, 'RIGHT', 4, 0)
|
||||
f.Option4:SetText('')
|
||||
f.Option4:Hide()
|
||||
f.Option4:SetScript('OnShow', function()
|
||||
f.Option1:Width(100)
|
||||
f.Option2:Width(100)
|
||||
f.Option1:ClearAllPoints()
|
||||
f.Option1:Point('RIGHT', f.Option2, 'LEFT', -4, 0)
|
||||
f.Option2:ClearAllPoints()
|
||||
f.Option2:Point('BOTTOMRIGHT', f, 'BOTTOM', -4, 45)
|
||||
end)
|
||||
f.Option4:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45); f.Option2:Width(110); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45) end)
|
||||
S:HandleButton(f.Option4, true)
|
||||
|
||||
f.SubTitle = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.SubTitle:FontTemplate(nil, 15, nil)
|
||||
f.SubTitle:Point('TOP', 0, -40)
|
||||
|
||||
f.Desc1 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc1:FontTemplate()
|
||||
f.Desc1:Point('TOPLEFT', 20, -75)
|
||||
f.Desc1:Width(f:GetWidth() - 40)
|
||||
|
||||
f.Desc2 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc2:FontTemplate()
|
||||
f.Desc2:Point('TOPLEFT', 20, -125)
|
||||
f.Desc2:Width(f:GetWidth() - 40)
|
||||
|
||||
f.Desc3 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc3:FontTemplate()
|
||||
f.Desc3:Point('TOPLEFT', 20, -175)
|
||||
f.Desc3:Width(f:GetWidth() - 40)
|
||||
|
||||
local close = CreateFrame('Button', 'InstallCloseButton', f, 'UIPanelCloseButton, BackdropTemplate')
|
||||
close:Point('TOPRIGHT', f, 'TOPRIGHT')
|
||||
close:SetScript('OnClick', function() f:Hide() end)
|
||||
S:HandleCloseButton(close)
|
||||
|
||||
local logo = f:CreateTexture('InstallTutorialImage', 'OVERLAY')
|
||||
logo:Size(256, 128)
|
||||
logo:SetTexture(E.Media.Textures.LogoTop)
|
||||
logo:Point('BOTTOM', 0, 70)
|
||||
f.tutorialImage = logo
|
||||
|
||||
local logo2 = f:CreateTexture('InstallTutorialImage2', 'OVERLAY')
|
||||
logo2:Size(256, 128)
|
||||
logo2:SetTexture(E.Media.Textures.LogoBottom)
|
||||
logo2:Point('BOTTOM', 0, 70)
|
||||
f.tutorialImage2 = logo2
|
||||
end
|
||||
|
||||
ElvUIInstallFrame.tutorialImage:SetVertexColor(unpack(E.media.rgbvaluecolor))
|
||||
ElvUIInstallFrame:Show()
|
||||
E:NextPage()
|
||||
end
|
||||
220
Core/ItemLevel.lua
Normal file
220
Core/ItemLevel.lua
Normal file
@@ -0,0 +1,220 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local _G = _G
|
||||
local tinsert, strfind, strmatch = tinsert, strfind, strmatch
|
||||
local select, tonumber, format = select, tonumber, format
|
||||
local next, max, wipe = next, max, wipe
|
||||
local utf8sub = string.utf8sub
|
||||
|
||||
local UnitIsUnit = UnitIsUnit
|
||||
local GetCVarBool = GetCVarBool
|
||||
local GetItemInfo = GetItemInfo
|
||||
local GetAverageItemLevel = GetAverageItemLevel
|
||||
local GetInventoryItemLink = GetInventoryItemLink
|
||||
local GetInventoryItemTexture = GetInventoryItemTexture
|
||||
local GetInspectSpecialization = GetInspectSpecialization
|
||||
local RETRIEVING_ITEM_INFO = RETRIEVING_ITEM_INFO
|
||||
|
||||
local ITEM_SPELL_TRIGGER_ONEQUIP = ITEM_SPELL_TRIGGER_ONEQUIP
|
||||
local ESSENCE_DESCRIPTION = GetSpellDescription(277253)
|
||||
|
||||
local MATCH_ITEM_LEVEL = ITEM_LEVEL:gsub('%%d', '(%%d+)')
|
||||
local MATCH_ITEM_LEVEL_ALT = ITEM_LEVEL_ALT:gsub('%%d(%s?)%(%%d%)', '%%d+%1%%((%%d+)%%)')
|
||||
local MATCH_ENCHANT = ENCHANTED_TOOLTIP_LINE:gsub('%%s', '(.+)')
|
||||
local X2_INVTYPES, X2_EXCEPTIONS, ARMOR_SLOTS = {
|
||||
INVTYPE_2HWEAPON = true,
|
||||
INVTYPE_RANGEDRIGHT = true,
|
||||
INVTYPE_RANGED = true,
|
||||
}, {
|
||||
[2] = 19, -- wands, use INVTYPE_RANGEDRIGHT, but are 1H
|
||||
}, {1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}
|
||||
|
||||
function E:InspectGearSlot(line, lineText, slotInfo)
|
||||
local enchant = strmatch(lineText, MATCH_ENCHANT)
|
||||
if enchant then
|
||||
slotInfo.enchantText = enchant
|
||||
slotInfo.enchantTextShort = utf8sub(enchant, 1, 18)
|
||||
|
||||
local lr, lg, lb = line:GetTextColor()
|
||||
slotInfo.enchantColors[1] = lr
|
||||
slotInfo.enchantColors[2] = lg
|
||||
slotInfo.enchantColors[3] = lb
|
||||
end
|
||||
|
||||
local itemLevel = lineText and (strmatch(lineText, MATCH_ITEM_LEVEL_ALT) or strmatch(lineText, MATCH_ITEM_LEVEL))
|
||||
if itemLevel then
|
||||
slotInfo.iLvl = tonumber(itemLevel)
|
||||
|
||||
local tr, tg, tb = _G.ElvUI_ScanTooltipTextLeft1:GetTextColor()
|
||||
slotInfo.itemLevelColors[1] = tr
|
||||
slotInfo.itemLevelColors[2] = tg
|
||||
slotInfo.itemLevelColors[3] = tb
|
||||
end
|
||||
end
|
||||
|
||||
function E:CollectEssenceInfo(index, lineText, slotInfo)
|
||||
local step = 1
|
||||
local essence = slotInfo.essences[step]
|
||||
if essence and next(essence) and (strfind(lineText, ITEM_SPELL_TRIGGER_ONEQUIP, nil, true) and strfind(lineText, ESSENCE_DESCRIPTION, nil, true)) then
|
||||
for i = 5, 2, -1 do
|
||||
local line = _G['ElvUI_ScanTooltipTextLeft'..index - i]
|
||||
local text = line and line:GetText()
|
||||
|
||||
if text and (not strmatch(text, '^[ +]')) and essence and next(essence) then
|
||||
local r, g, b = line:GetTextColor()
|
||||
|
||||
essence[4] = E:RGBToHex(r, g, b)
|
||||
essence[5] = text
|
||||
|
||||
step = step + 1
|
||||
essence = slotInfo.essences[step]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetGearSlotInfo(unit, slot, deepScan)
|
||||
local tt = E.ScanTooltip
|
||||
tt:SetOwner(_G.UIParent, 'ANCHOR_NONE')
|
||||
tt:SetInventoryItem(unit, slot)
|
||||
tt:Show()
|
||||
|
||||
if not tt.slotInfo then tt.slotInfo = {} else wipe(tt.slotInfo) end
|
||||
local slotInfo = tt.slotInfo
|
||||
|
||||
if deepScan then
|
||||
slotInfo.gems, slotInfo.essences = E:ScanTooltipTextures()
|
||||
|
||||
if not tt.enchantColors then tt.enchantColors = {} else wipe(tt.enchantColors) end
|
||||
if not tt.itemLevelColors then tt.itemLevelColors = {} else wipe(tt.itemLevelColors) end
|
||||
slotInfo.enchantColors = tt.enchantColors
|
||||
slotInfo.itemLevelColors = tt.itemLevelColors
|
||||
|
||||
for x = 1, tt:NumLines() do
|
||||
local line = _G['ElvUI_ScanTooltipTextLeft'..x]
|
||||
if line then
|
||||
local lineText = line:GetText()
|
||||
if x == 1 and lineText == RETRIEVING_ITEM_INFO then
|
||||
return 'tooSoon'
|
||||
else
|
||||
E:InspectGearSlot(line, lineText, slotInfo)
|
||||
E:CollectEssenceInfo(x, lineText, slotInfo)
|
||||
end
|
||||
end
|
||||
end
|
||||
else
|
||||
local firstLine = _G.ElvUI_ScanTooltipTextLeft1:GetText()
|
||||
if firstLine == RETRIEVING_ITEM_INFO then
|
||||
return 'tooSoon'
|
||||
end
|
||||
|
||||
local colorblind = GetCVarBool('colorblindmode') and 4 or 3
|
||||
for x = 2, colorblind do
|
||||
local line = _G['ElvUI_ScanTooltipTextLeft'..x]
|
||||
if line then
|
||||
local lineText = line:GetText()
|
||||
local itemLevel = lineText and (strmatch(lineText, MATCH_ITEM_LEVEL_ALT) or strmatch(lineText, MATCH_ITEM_LEVEL))
|
||||
if itemLevel then
|
||||
slotInfo.iLvl = tonumber(itemLevel)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tt:Hide()
|
||||
|
||||
return slotInfo
|
||||
end
|
||||
|
||||
--Credit ls & Acidweb
|
||||
function E:CalculateAverageItemLevel(iLevelDB, unit)
|
||||
local spec = GetInspectSpecialization(unit)
|
||||
local isOK, total, link = true, 0
|
||||
|
||||
if not spec or spec == 0 then
|
||||
isOK = false
|
||||
end
|
||||
|
||||
-- Armor
|
||||
for _, id in next, ARMOR_SLOTS do
|
||||
link = GetInventoryItemLink(unit, id)
|
||||
if link then
|
||||
local cur = iLevelDB[id]
|
||||
if cur and cur > 0 then
|
||||
total = total + cur
|
||||
end
|
||||
elseif GetInventoryItemTexture(unit, id) then
|
||||
isOK = false
|
||||
end
|
||||
end
|
||||
|
||||
-- Main hand
|
||||
local mainItemLevel, mainQuality, mainEquipLoc, mainItemClass, mainItemSubClass, _ = 0
|
||||
link = GetInventoryItemLink(unit, 16)
|
||||
if link then
|
||||
mainItemLevel = iLevelDB[16]
|
||||
_, _, mainQuality, _, _, _, _, _, mainEquipLoc, _, _, mainItemClass, mainItemSubClass = GetItemInfo(link)
|
||||
elseif GetInventoryItemTexture(unit, 16) then
|
||||
isOK = false
|
||||
end
|
||||
|
||||
-- Off hand
|
||||
local offItemLevel, offEquipLoc = 0
|
||||
link = GetInventoryItemLink(unit, 17)
|
||||
if link then
|
||||
offItemLevel = iLevelDB[17]
|
||||
_, _, _, _, _, _, _, _, offEquipLoc = GetItemInfo(link)
|
||||
elseif GetInventoryItemTexture(unit, 17) then
|
||||
isOK = false
|
||||
end
|
||||
|
||||
if mainItemLevel and offItemLevel then
|
||||
if mainQuality == 6 or (not offEquipLoc and X2_INVTYPES[mainEquipLoc] and X2_EXCEPTIONS[mainItemClass] ~= mainItemSubClass and spec ~= 72) then
|
||||
mainItemLevel = max(mainItemLevel, offItemLevel)
|
||||
total = total + mainItemLevel * 2
|
||||
else
|
||||
total = total + mainItemLevel + offItemLevel
|
||||
end
|
||||
end
|
||||
|
||||
-- at the beginning of an arena match no info might be available,
|
||||
-- so despite having equipped gear a person may appear naked
|
||||
if total == 0 then
|
||||
isOK = false
|
||||
end
|
||||
|
||||
return isOK and format('%0.2f', E:Round(total / 16, 2))
|
||||
end
|
||||
|
||||
function E:GetPlayerItemLevel()
|
||||
return format('%0.2f', E:Round((select(2, GetAverageItemLevel())), 2))
|
||||
end
|
||||
|
||||
do
|
||||
local iLevelDB, tryAgain = {}, {}
|
||||
function E:GetUnitItemLevel(unit)
|
||||
if UnitIsUnit('player', unit) then
|
||||
return E:GetPlayerItemLevel()
|
||||
end
|
||||
|
||||
if next(iLevelDB) then wipe(iLevelDB) end
|
||||
if next(tryAgain) then wipe(tryAgain) end
|
||||
|
||||
for i = 1, 17 do
|
||||
if i ~= 4 then
|
||||
local slotInfo = E:GetGearSlotInfo(unit, i)
|
||||
if slotInfo == 'tooSoon' then
|
||||
tinsert(tryAgain, i)
|
||||
else
|
||||
iLevelDB[i] = slotInfo.iLvl
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if next(tryAgain) then
|
||||
return 'tooSoon', unit, tryAgain, iLevelDB
|
||||
end
|
||||
|
||||
return E:CalculateAverageItemLevel(iLevelDB, unit)
|
||||
end
|
||||
end
|
||||
26
Core/Load_Core.xml
Normal file
26
Core/Load_Core.xml
Normal file
@@ -0,0 +1,26 @@
|
||||
<Ui xmlns='http://www.blizzard.com/wow/ui/'>
|
||||
<Script file='Core.lua'/>
|
||||
<Script file='Math.lua'/>
|
||||
<Script file='API.lua'/>
|
||||
<Script file='AprilFools.lua'/>
|
||||
<Script file='Fonts.lua'/>
|
||||
<Script file='Install.lua'/>
|
||||
<Script file='PluginInstaller.lua'/>
|
||||
<Script file='PixelPerfect.lua'/>
|
||||
<Script file='Toolkit.lua'/>
|
||||
<Script file='StatusReport.lua'/>
|
||||
<Script file='Commands.lua'/>
|
||||
<Script file='StaticPopups.lua'/>
|
||||
<Script file='Animation.lua'/>
|
||||
<Script file='Smoothie.lua'/>
|
||||
<Script file='Movers.lua'/>
|
||||
<Script file='Config.lua'/>
|
||||
<Script file='Tutorials.lua'/>
|
||||
<Script file='Distributor.lua'/>
|
||||
<Script file='Dropdown.lua'/>
|
||||
<Script file='ItemLevel.lua'/>
|
||||
<Script file='Cooldowns.lua'/>
|
||||
<Script file='MapInfo.lua'/>
|
||||
<Script file='ModuleCopy.lua'/>
|
||||
<Script file='Tags.lua'/>
|
||||
</Ui>
|
||||
170
Core/MapInfo.lua
Normal file
170
Core/MapInfo.lua
Normal file
@@ -0,0 +1,170 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local pairs = pairs
|
||||
local IsFalling = IsFalling
|
||||
local CreateFrame = CreateFrame
|
||||
local UnitPosition = UnitPosition
|
||||
local GetUnitSpeed = GetUnitSpeed
|
||||
local CreateVector2D = CreateVector2D
|
||||
local GetRealZoneText = GetRealZoneText
|
||||
local GetMinimapZoneText = GetMinimapZoneText
|
||||
|
||||
local C_Map_GetMapInfo = C_Map.GetMapInfo
|
||||
local C_Map_GetBestMapForUnit = C_Map.GetBestMapForUnit
|
||||
local C_Map_GetWorldPosFromMapPos = C_Map.GetWorldPosFromMapPos
|
||||
|
||||
local Enum_UIMapType = Enum.UIMapType
|
||||
local MapUtil_GetMapParentInfo = MapUtil.GetMapParentInfo
|
||||
|
||||
E.MapInfo = {}
|
||||
|
||||
function E:MapInfo_Update()
|
||||
local mapID = C_Map_GetBestMapForUnit('player')
|
||||
|
||||
local mapInfo = mapID and C_Map_GetMapInfo(mapID)
|
||||
E.MapInfo.name = (mapInfo and mapInfo.name) or nil
|
||||
E.MapInfo.mapType = (mapInfo and mapInfo.mapType) or nil
|
||||
E.MapInfo.parentMapID = (mapInfo and mapInfo.parentMapID) or nil
|
||||
|
||||
E.MapInfo.mapID = mapID or nil
|
||||
E.MapInfo.zoneText = (mapID and E:GetZoneText(mapID)) or nil
|
||||
E.MapInfo.subZoneText = GetMinimapZoneText() or nil
|
||||
E.MapInfo.realZoneText = GetRealZoneText() or nil
|
||||
|
||||
local continent = mapID and MapUtil_GetMapParentInfo(mapID, Enum_UIMapType.Continent, true)
|
||||
E.MapInfo.continentParentMapID = (continent and continent.parentMapID) or nil
|
||||
E.MapInfo.continentMapType = (continent and continent.mapType) or nil
|
||||
E.MapInfo.continentMapID = (continent and continent.mapID) or nil
|
||||
E.MapInfo.continentName = (continent and continent.name) or nil
|
||||
|
||||
E:MapInfo_CoordsUpdate()
|
||||
end
|
||||
|
||||
local coordsWatcher = CreateFrame('Frame')
|
||||
function E:MapInfo_CoordsStart()
|
||||
E.MapInfo.coordsWatching = true
|
||||
E.MapInfo.coordsFalling = nil
|
||||
coordsWatcher:SetScript('OnUpdate', E.MapInfo_OnUpdate)
|
||||
|
||||
if E.MapInfo.coordsStopTimer then
|
||||
E:CancelTimer(E.MapInfo.coordsStopTimer)
|
||||
E.MapInfo.coordsStopTimer = nil
|
||||
end
|
||||
end
|
||||
|
||||
function E:MapInfo_CoordsStopWatching()
|
||||
E.MapInfo.coordsWatching = nil
|
||||
E.MapInfo.coordsStopTimer = nil
|
||||
coordsWatcher:SetScript('OnUpdate', nil)
|
||||
end
|
||||
|
||||
function E:MapInfo_CoordsStop(event)
|
||||
if event == 'CRITERIA_UPDATE' then
|
||||
if not E.MapInfo.coordsFalling then return end -- stop if we weren't falling
|
||||
if (GetUnitSpeed('player') or 0) > 0 then return end -- we are still moving!
|
||||
E.MapInfo.coordsFalling = nil -- we were falling!
|
||||
elseif (event == 'PLAYER_STOPPED_MOVING' or event == 'PLAYER_CONTROL_GAINED') and IsFalling() then
|
||||
E.MapInfo.coordsFalling = true
|
||||
return
|
||||
end
|
||||
|
||||
if not E.MapInfo.coordsStopTimer then
|
||||
E.MapInfo.coordsStopTimer = E:ScheduleTimer('MapInfo_CoordsStopWatching', 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
function E:MapInfo_CoordsUpdate()
|
||||
if E.MapInfo.mapID then
|
||||
E.MapInfo.x, E.MapInfo.y = E:GetPlayerMapPos(E.MapInfo.mapID)
|
||||
else
|
||||
E.MapInfo.x, E.MapInfo.y = nil, nil
|
||||
end
|
||||
|
||||
if E.MapInfo.x and E.MapInfo.y then
|
||||
E.MapInfo.xText = E:Round(100 * E.MapInfo.x, 2)
|
||||
E.MapInfo.yText = E:Round(100 * E.MapInfo.y, 2)
|
||||
else
|
||||
E.MapInfo.xText, E.MapInfo.yText = nil, nil
|
||||
end
|
||||
end
|
||||
|
||||
function E:MapInfo_OnUpdate(elapsed)
|
||||
self.lastUpdate = (self.lastUpdate or 0) + elapsed
|
||||
if self.lastUpdate > 0.1 then
|
||||
E:MapInfo_CoordsUpdate()
|
||||
self.lastUpdate = 0
|
||||
end
|
||||
end
|
||||
|
||||
-- This code fixes C_Map.GetPlayerMapPosition memory leak.
|
||||
-- Fix stolen from NDui (and modified by Simpy). Credit: siweia.
|
||||
local mapRects, tempVec2D = {}, CreateVector2D(0, 0)
|
||||
function E:GetPlayerMapPos(mapID)
|
||||
tempVec2D.x, tempVec2D.y = UnitPosition('player')
|
||||
if not tempVec2D.x then return end
|
||||
|
||||
local mapRect = mapRects[mapID]
|
||||
if not mapRect then
|
||||
local _, pos1 = C_Map_GetWorldPosFromMapPos(mapID, CreateVector2D(0, 0))
|
||||
local _, pos2 = C_Map_GetWorldPosFromMapPos(mapID, CreateVector2D(1, 1))
|
||||
if not pos1 or not pos2 then return end
|
||||
|
||||
mapRect = {pos1, pos2}
|
||||
mapRect[2]:Subtract(mapRect[1])
|
||||
mapRects[mapID] = mapRect
|
||||
end
|
||||
tempVec2D:Subtract(mapRect[1])
|
||||
|
||||
return (tempVec2D.y/mapRect[2].y), (tempVec2D.x/mapRect[2].x)
|
||||
end
|
||||
|
||||
-- Code taken from LibTourist-3.0 and rewritten to fit our purpose
|
||||
local localizedMapNames = {}
|
||||
local ZoneIDToContinentName = {
|
||||
[104] = 'Outland',
|
||||
[107] = 'Outland',
|
||||
}
|
||||
local MapIdLookupTable = {
|
||||
[101] = 'Outland',
|
||||
[104] = 'Shadowmoon Valley',
|
||||
[107] = 'Nagrand',
|
||||
}
|
||||
local function LocalizeZoneNames()
|
||||
local mapInfo
|
||||
for mapID, englishName in pairs(MapIdLookupTable) do
|
||||
mapInfo = C_Map_GetMapInfo(mapID)
|
||||
-- Add combination of English and localized name to lookup table
|
||||
if mapInfo and mapInfo.name and not localizedMapNames[englishName] then
|
||||
localizedMapNames[englishName] = mapInfo.name
|
||||
end
|
||||
end
|
||||
end
|
||||
LocalizeZoneNames()
|
||||
|
||||
--Add ' (Outland)' to the end of zone name for Nagrand and Shadowmoon Valley, if mapID matches Outland continent.
|
||||
--We can then use this function when we need to compare the players own zone against return values from stuff like GetFriendInfo and GetGuildRosterInfo,
|
||||
--which adds the ' (Outland)' part unlike the GetRealZoneText() API.
|
||||
function E:GetZoneText(mapID)
|
||||
if not (mapID and E.MapInfo.name) then return end
|
||||
|
||||
local continent, zoneName = ZoneIDToContinentName[mapID]
|
||||
if continent and continent == 'Outland' then
|
||||
if E.MapInfo.name == localizedMapNames.Nagrand or E.MapInfo.name == 'Nagrand' then
|
||||
zoneName = localizedMapNames.Nagrand..' ('..localizedMapNames.Outland..')'
|
||||
elseif E.MapInfo.name == localizedMapNames['Shadowmoon Valley'] or E.MapInfo.name == 'Shadowmoon Valley' then
|
||||
zoneName = localizedMapNames['Shadowmoon Valley']..' ('..localizedMapNames.Outland..')'
|
||||
end
|
||||
end
|
||||
|
||||
return zoneName or E.MapInfo.name
|
||||
end
|
||||
|
||||
E:RegisterEvent('CRITERIA_UPDATE', 'MapInfo_CoordsStop') -- when the player goes into an animation (landing)
|
||||
E:RegisterEvent('PLAYER_STARTED_MOVING', 'MapInfo_CoordsStart')
|
||||
E:RegisterEvent('PLAYER_STOPPED_MOVING', 'MapInfo_CoordsStop')
|
||||
E:RegisterEvent('PLAYER_CONTROL_LOST', 'MapInfo_CoordsStart')
|
||||
E:RegisterEvent('PLAYER_CONTROL_GAINED', 'MapInfo_CoordsStop')
|
||||
E:RegisterEventForObject('LOADING_SCREEN_DISABLED', E.MapInfo, E.MapInfo_Update)
|
||||
E:RegisterEventForObject('ZONE_CHANGED_NEW_AREA', E.MapInfo, E.MapInfo_Update)
|
||||
E:RegisterEventForObject('ZONE_CHANGED_INDOORS', E.MapInfo, E.MapInfo_Update)
|
||||
E:RegisterEventForObject('ZONE_CHANGED', E.MapInfo, E.MapInfo_Update)
|
||||
515
Core/Math.lua
Normal file
515
Core/Math.lua
Normal file
@@ -0,0 +1,515 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local tinsert, tremove, next, wipe, ipairs = tinsert, tremove, next, wipe, ipairs
|
||||
local select, tonumber, type, unpack, strmatch = select, tonumber, type, unpack, strmatch
|
||||
local modf, atan2, ceil, floor, abs, sqrt, mod = math.modf, atan2, ceil, floor, abs, sqrt, mod
|
||||
local format, strsub, strupper, gsub, gmatch = format, strsub, strupper, gsub, gmatch
|
||||
local tostring, pairs, utf8sub, utf8len = tostring, pairs, string.utf8sub, string.utf8len
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local UnitPosition = UnitPosition
|
||||
local GetPlayerFacing = GetPlayerFacing
|
||||
local BreakUpLargeNumbers = BreakUpLargeNumbers
|
||||
local GetScreenWidth, GetScreenHeight = GetScreenWidth, GetScreenHeight
|
||||
local C_Timer_After = C_Timer.After
|
||||
|
||||
E.ShortPrefixValues = {}
|
||||
E.ShortPrefixStyles = {
|
||||
TCHINESE = {{1e8,'億'}, {1e4,'萬'}},
|
||||
CHINESE = {{1e8,'亿'}, {1e4,'万'}},
|
||||
ENGLISH = {{1e12,'T'}, {1e9,'B'}, {1e6,'M'}, {1e3,'K'}},
|
||||
GERMAN = {{1e12,'Bio'}, {1e9,'Mrd'}, {1e6,'Mio'}, {1e3,'Tsd'}},
|
||||
KOREAN = {{1e8,'억'}, {1e4,'만'}, {1e3,'천'}},
|
||||
METRIC = {{1e12,'T'}, {1e9,'G'}, {1e6,'M'}, {1e3,'k'}}
|
||||
}
|
||||
|
||||
E.GetFormattedTextStyles = {
|
||||
CURRENT = '%s',
|
||||
CURRENT_MAX = '%s - %s',
|
||||
CURRENT_PERCENT = '%s - %.1f%%',
|
||||
CURRENT_MAX_PERCENT = '%s - %s | %.1f%%',
|
||||
PERCENT = '%.1f%%',
|
||||
DEFICIT = '-%s',
|
||||
}
|
||||
|
||||
function E:BuildPrefixValues()
|
||||
if next(E.ShortPrefixValues) then wipe(E.ShortPrefixValues) end
|
||||
|
||||
E.ShortPrefixValues = E:CopyTable(E.ShortPrefixValues, E.ShortPrefixStyles[E.db.general.numberPrefixStyle])
|
||||
E.ShortValueDec = format('%%.%df', E.db.general.decimalLength or 1)
|
||||
|
||||
for _, style in ipairs(E.ShortPrefixValues) do
|
||||
style[3] = E.ShortValueDec..style[2]
|
||||
end
|
||||
|
||||
local dec = tostring(E.db.general.decimalLength or 1)
|
||||
for style, str in pairs(E.GetFormattedTextStyles) do
|
||||
E.GetFormattedTextStyles[style] = gsub(str, '%d', dec)
|
||||
end
|
||||
end
|
||||
|
||||
--Return short value of a number
|
||||
function E:ShortValue(value, dec)
|
||||
local abs_value = value<0 and -value or value
|
||||
local decimal = dec and format('%%.%df', tonumber(dec) or 0)
|
||||
|
||||
for i = 1, #E.ShortPrefixValues do
|
||||
if abs_value >= E.ShortPrefixValues[i][1] then
|
||||
if decimal then
|
||||
return format(decimal..E.ShortPrefixValues[i][2], value / E.ShortPrefixValues[i][1])
|
||||
else
|
||||
return format(E.ShortPrefixValues[i][3], value / E.ShortPrefixValues[i][1])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return format('%.0f', value)
|
||||
end
|
||||
|
||||
function E:IsEvenNumber(num)
|
||||
return num % 2 == 0
|
||||
end
|
||||
|
||||
-- http://www.wowwiki.com/ColorGradient
|
||||
function E:ColorGradient(perc, ...)
|
||||
if perc >= 1 then
|
||||
return select(select('#', ...) - 2, ...)
|
||||
elseif perc <= 0 then
|
||||
return ...
|
||||
end
|
||||
|
||||
local num = select('#', ...) / 3
|
||||
local segment, relperc = modf(perc*(num-1))
|
||||
local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...)
|
||||
|
||||
return r1+(r2-r1)*relperc, g1+(g2-g1)*relperc, b1+(b2-b1)*relperc
|
||||
end
|
||||
|
||||
-- Text Gradient by Simpy
|
||||
function E:TextGradient(text, ...)
|
||||
local msg, len, idx = '', utf8len(text), 0
|
||||
|
||||
for i = 1, len do
|
||||
local x = utf8sub(text, i, i)
|
||||
if strmatch(x, '%s') then
|
||||
msg = msg .. x
|
||||
idx = idx + 1
|
||||
else
|
||||
local num = select('#', ...) / 3
|
||||
local segment, relperc = modf((idx/len)*num)
|
||||
local r1, g1, b1, r2, g2, b2 = select((segment*3)+1, ...)
|
||||
|
||||
if not r2 then
|
||||
msg = msg .. E:RGBToHex(r1, g1, b1, nil, x..'|r')
|
||||
else
|
||||
msg = msg .. E:RGBToHex(r1+(r2-r1)*relperc, g1+(g2-g1)*relperc, b1+(b2-b1)*relperc, nil, x..'|r')
|
||||
idx = idx + 1
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return msg
|
||||
end
|
||||
|
||||
-- quick convert function: (nil or table to populate, 'ff0000', '00ff00', '0000ff', ...) to get (1,0,0, 0,1,0, 0,0,1, ...)
|
||||
function E:HexsToRGBs(rgb, ...)
|
||||
if not rgb then rgb = {} end
|
||||
for i = 1, select('#', ...) do
|
||||
local x, r, g, b = #rgb, E:HexToRGB(select(i, ...))
|
||||
rgb[x+1], rgb[x+2], rgb[x+3] = r/255, g/255, b/255
|
||||
end
|
||||
|
||||
return unpack(rgb)
|
||||
end
|
||||
|
||||
--Return rounded number
|
||||
function E:Round(num, idp)
|
||||
if type(num) ~= 'number' then
|
||||
return num, idp
|
||||
end
|
||||
|
||||
if idp and idp > 0 then
|
||||
local mult = 10 ^ idp
|
||||
return floor(num * mult + 0.5) / mult
|
||||
end
|
||||
|
||||
return floor(num + 0.5)
|
||||
end
|
||||
|
||||
--Truncate a number off to n places
|
||||
function E:Truncate(v, decimals)
|
||||
return v - (v % (0.1 ^ (decimals or 0)))
|
||||
end
|
||||
|
||||
--RGB to Hex
|
||||
function E:RGBToHex(r, g, b, header, ending)
|
||||
r = r <= 1 and r >= 0 and r or 1
|
||||
g = g <= 1 and g >= 0 and g or 1
|
||||
b = b <= 1 and b >= 0 and b or 1
|
||||
return format('%s%02x%02x%02x%s', header or '|cff', r*255, g*255, b*255, ending or '')
|
||||
end
|
||||
|
||||
--Hex to RGB
|
||||
function E:HexToRGB(hex)
|
||||
local a, r, g, b = strmatch(hex, '^|?c?(%x%x)(%x%x)(%x%x)(%x?%x?)|?r?$')
|
||||
if not a then return 0, 0, 0, 0 end
|
||||
if b == '' then r, g, b, a = a, r, g, 'ff' end
|
||||
|
||||
return tonumber(r, 16), tonumber(g, 16), tonumber(b, 16), tonumber(a, 16)
|
||||
end
|
||||
|
||||
--From http://wow.gamepedia.com/UI_coordinates
|
||||
function E:FramesOverlap(frameA, frameB)
|
||||
if not frameA or not frameB then return end
|
||||
|
||||
local sA, sB = frameA:GetEffectiveScale(), frameB:GetEffectiveScale()
|
||||
if not sA or not sB then return end
|
||||
|
||||
local frameALeft, frameARight, frameABottom, frameATop = frameA:GetLeft(), frameA:GetRight(), frameA:GetBottom(), frameA:GetTop()
|
||||
local frameBLeft, frameBRight, frameBBottom, frameBTop = frameB:GetLeft(), frameB:GetRight(), frameB:GetBottom(), frameB:GetTop()
|
||||
if not (frameALeft and frameARight and frameABottom and frameATop) then return end
|
||||
if not (frameBLeft and frameBRight and frameBBottom and frameBTop) then return end
|
||||
|
||||
return ((frameALeft*sA) < (frameBRight*sB)) and ((frameBLeft*sB) < (frameARight*sA)) and ((frameABottom*sA) < (frameBTop*sB)) and ((frameBBottom*sB) < (frameATop*sA))
|
||||
end
|
||||
|
||||
function E:GetScreenQuadrant(frame)
|
||||
local x, y = frame:GetCenter()
|
||||
local screenWidth = GetScreenWidth()
|
||||
local screenHeight = GetScreenHeight()
|
||||
|
||||
if not (x and y) then
|
||||
return 'UNKNOWN', frame:GetName()
|
||||
end
|
||||
|
||||
local point
|
||||
if (x > (screenWidth / 3) and x < (screenWidth / 3)*2) and y > (screenHeight / 3)*2 then
|
||||
point = 'TOP'
|
||||
elseif x < (screenWidth / 3) and y > (screenHeight / 3)*2 then
|
||||
point = 'TOPLEFT'
|
||||
elseif x > (screenWidth / 3)*2 and y > (screenHeight / 3)*2 then
|
||||
point = 'TOPRIGHT'
|
||||
elseif (x > (screenWidth / 3) and x < (screenWidth / 3)*2) and y < (screenHeight / 3) then
|
||||
point = 'BOTTOM'
|
||||
elseif x < (screenWidth / 3) and y < (screenHeight / 3) then
|
||||
point = 'BOTTOMLEFT'
|
||||
elseif x > (screenWidth / 3)*2 and y < (screenHeight / 3) then
|
||||
point = 'BOTTOMRIGHT'
|
||||
elseif x < (screenWidth / 3) and (y > (screenHeight / 3) and y < (screenHeight / 3)*2) then
|
||||
point = 'LEFT'
|
||||
elseif x > (screenWidth / 3)*2 and y < (screenHeight / 3)*2 and y > (screenHeight / 3) then
|
||||
point = 'RIGHT'
|
||||
else
|
||||
point = 'CENTER'
|
||||
end
|
||||
|
||||
return point
|
||||
end
|
||||
|
||||
function E:GetXYOffset(position, forcedX, forcedY)
|
||||
local default = E.Spacing
|
||||
local x, y = forcedX or default, forcedY or forcedX or default
|
||||
|
||||
if position == 'TOP' then
|
||||
return 0, y
|
||||
elseif position == 'TOPLEFT' then
|
||||
return x, y
|
||||
elseif position == 'TOPRIGHT' then
|
||||
return -x, y
|
||||
elseif position == 'BOTTOM' then
|
||||
return 0, -y
|
||||
elseif position == 'BOTTOMLEFT' then
|
||||
return x, -y
|
||||
elseif position == 'BOTTOMRIGHT' then
|
||||
return -x, -y
|
||||
elseif position == 'LEFT' then
|
||||
return -x, 0
|
||||
elseif position == 'RIGHT' then
|
||||
return x, 0
|
||||
elseif position == 'CENTER' then
|
||||
return 0, 0
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetFormattedText(style, min, max, dec)
|
||||
if max == 0 then max = 1 end
|
||||
|
||||
if style == 'CURRENT' or ((style == 'CURRENT_MAX' or style == 'CURRENT_MAX_PERCENT' or style == 'CURRENT_PERCENT') and min == max) then
|
||||
return format(E.GetFormattedTextStyles.CURRENT, E:ShortValue(min, dec))
|
||||
else
|
||||
local useStyle = E.GetFormattedTextStyles[style]
|
||||
if not useStyle then return end
|
||||
|
||||
if style == 'DEFICIT' then
|
||||
local deficit = max - min
|
||||
return (deficit > 0 and format(useStyle, E:ShortValue(deficit, dec))) or ''
|
||||
elseif style == 'CURRENT_MAX' then
|
||||
return format(useStyle, E:ShortValue(min, dec), E:ShortValue(max, dec))
|
||||
elseif style == 'PERCENT' or style == 'CURRENT_PERCENT' or style == 'CURRENT_MAX_PERCENT' then
|
||||
if dec then useStyle = gsub(useStyle, '%d', tonumber(dec) or 0) end
|
||||
local perc = min / max * 100
|
||||
|
||||
if style == 'PERCENT' then
|
||||
return format(useStyle, perc)
|
||||
elseif style == 'CURRENT_PERCENT' then
|
||||
return format(useStyle, E:ShortValue(min, dec), perc)
|
||||
elseif style == 'CURRENT_MAX_PERCENT' then
|
||||
return format(useStyle, E:ShortValue(min, dec), E:ShortValue(max, dec), perc)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:ShortenString(str, numChars, dots)
|
||||
local bytes = #str
|
||||
if bytes <= numChars then
|
||||
return str
|
||||
else
|
||||
local len, pos = 0, 1
|
||||
while pos <= bytes do
|
||||
len = len + 1
|
||||
local c = str:byte(pos)
|
||||
if c > 0 and c <= 127 then
|
||||
pos = pos + 1
|
||||
elseif c >= 192 and c <= 223 then
|
||||
pos = pos + 2
|
||||
elseif c >= 224 and c <= 239 then
|
||||
pos = pos + 3
|
||||
elseif c >= 240 and c <= 247 then
|
||||
pos = pos + 4
|
||||
end
|
||||
if len == numChars then
|
||||
break
|
||||
end
|
||||
end
|
||||
|
||||
if len == numChars and pos <= bytes then
|
||||
return strsub(str, 1, pos - 1)..(dots and '...' or '')
|
||||
else
|
||||
return str
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:AbbreviateString(str, allUpper)
|
||||
local newString = ''
|
||||
for word in gmatch(str, '[^%s]+') do
|
||||
word = utf8sub(word, 1, 1) --get only first letter of each word
|
||||
if allUpper then word = strupper(word) end
|
||||
newString = newString..word
|
||||
end
|
||||
|
||||
return newString
|
||||
end
|
||||
|
||||
function E:WaitFunc(elapse)
|
||||
local i = 1
|
||||
while i <= #E.WaitTable do
|
||||
local data = E.WaitTable[i]
|
||||
if data[1] > elapse then
|
||||
data[1], i = data[1] - elapse, i + 1
|
||||
else
|
||||
tremove(E.WaitTable, i)
|
||||
data[2](unpack(data[3]))
|
||||
|
||||
if #E.WaitTable == 0 then
|
||||
E.WaitFrame:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
E.WaitTable = {}
|
||||
E.WaitFrame = CreateFrame('Frame', 'ElvUI_WaitFrame', _G.UIParent)
|
||||
E.WaitFrame:SetScript('OnUpdate', E.WaitFunc)
|
||||
|
||||
--Add time before calling a function
|
||||
function E:Delay(delay, func, ...)
|
||||
if type(delay) ~= 'number' or type(func) ~= 'function' then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Restrict to the lowest time that the C_Timer API allows us
|
||||
if delay < 0.01 then delay = 0.01 end
|
||||
|
||||
if select('#', ...) <= 0 then
|
||||
C_Timer_After(delay, func)
|
||||
else
|
||||
tinsert(E.WaitTable,{delay,func,{...}})
|
||||
E.WaitFrame:Show()
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function E:StringTitle(str)
|
||||
return gsub(str, '(.)', strupper, 1)
|
||||
end
|
||||
|
||||
E.TimeThreshold = 3
|
||||
|
||||
E.TimeColors = { --aura time colors
|
||||
[0] = '|cffeeeeee', --days
|
||||
[1] = '|cffeeeeee', --hours
|
||||
[2] = '|cffeeeeee', --minutes
|
||||
[3] = '|cffeeeeee', --seconds
|
||||
[4] = '|cfffe0000', --expire (fade timer)
|
||||
[5] = '|cff909090', --mmss
|
||||
[6] = '|cff707070', --hhmm
|
||||
}
|
||||
|
||||
E.TimeFormats = { -- short / indicator color
|
||||
[0] = {'%dd', '%d%sd|r'},
|
||||
[1] = {'%dh', '%d%sh|r'},
|
||||
[2] = {'%dm', '%d%sm|r'},
|
||||
[3] = {'%ds', '%d%ss|r'},
|
||||
[4] = {'%.1fs', '%.1f%ss|r'},
|
||||
[5] = {'%d:%02d', '%d%s:|r%02d'}, --mmss
|
||||
[6] = {'%d:%02d', '%d%s:|r%02d'}, --hhmm
|
||||
}
|
||||
|
||||
for _, x in pairs(E.TimeFormats) do
|
||||
x[3] = gsub(x[1], 's$', '') -- 1 without seconds
|
||||
x[4] = gsub(x[2], '%%ss', '%%s') -- 2 without seconds
|
||||
end
|
||||
|
||||
E.TimeIndicatorColors = {
|
||||
[0] = '|cff00b3ff',
|
||||
[1] = '|cff00b3ff',
|
||||
[2] = '|cff00b3ff',
|
||||
[3] = '|cff00b3ff',
|
||||
[4] = '|cff00b3ff',
|
||||
[5] = '|cff00b3ff',
|
||||
[6] = '|cff00b3ff',
|
||||
}
|
||||
|
||||
local DAY, HOUR, MINUTE = 86400, 3600, 60 --used for calculating aura time text
|
||||
local DAYISH, HOURISH, MINUTEISH = HOUR * 23.5, MINUTE * 59.5, 59.5 --used for caclculating aura time at transition points
|
||||
local HALFDAYISH, HALFHOURISH, HALFMINUTEISH = DAY/2 + 0.5, HOUR/2 + 0.5, MINUTE/2 + 0.5 --used for calculating next update times
|
||||
|
||||
-- will return the the value to display, the formatter id to use and calculates the next update for the Aura
|
||||
function E:GetTimeInfo(s, threshhold, hhmm, mmss)
|
||||
if s < MINUTE then
|
||||
if s >= threshhold then
|
||||
return floor(s), 3, 0.51
|
||||
else
|
||||
return s, 4, 0.051
|
||||
end
|
||||
elseif s < HOUR then
|
||||
if mmss and s < mmss then
|
||||
return s/MINUTE, 5, 0.51, s%MINUTE
|
||||
else
|
||||
local minutes = floor((s/MINUTE)+.5)
|
||||
if hhmm and s < (hhmm * MINUTE) then
|
||||
return s/HOUR, 6, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH), minutes%MINUTE
|
||||
else
|
||||
return ceil(s / MINUTE), 2, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH)
|
||||
end
|
||||
end
|
||||
elseif s < DAY then
|
||||
if mmss and s < mmss then
|
||||
return s/MINUTE, 5, 0.51, s%MINUTE
|
||||
elseif hhmm and s < (hhmm * MINUTE) then
|
||||
local minutes = floor((s/MINUTE)+.5)
|
||||
return s/HOUR, 6, minutes > 1 and (s - (minutes*MINUTE - HALFMINUTEISH)) or (s - MINUTEISH), minutes%MINUTE
|
||||
else
|
||||
local hours = floor((s/HOUR)+.5)
|
||||
return ceil(s / HOUR), 1, hours > 1 and (s - (hours*HOUR - HALFHOURISH)) or (s - HOURISH)
|
||||
end
|
||||
else
|
||||
local days = floor((s/DAY)+.5)
|
||||
return ceil(s / DAY), 0, days > 1 and (s - (days*DAY - HALFDAYISH)) or (s - DAYISH)
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetDistance(unit1, unit2)
|
||||
local x1, y1, _, map1 = UnitPosition(unit1)
|
||||
if not x1 then return end
|
||||
|
||||
local x2, y2, _, map2 = UnitPosition(unit2)
|
||||
if not x2 then return end
|
||||
|
||||
if map1 ~= map2 then return end
|
||||
|
||||
local dX = x2 - x1
|
||||
local dY = y2 - y1
|
||||
local distance = sqrt(dX * dX + dY * dY)
|
||||
return distance, atan2(dY, dX) - GetPlayerFacing()
|
||||
end
|
||||
|
||||
--Money text formatting, code taken from Scrooge by thelibrarian ( http://www.wowace.com/addons/scrooge/ )
|
||||
local COLOR_COPPER, COLOR_SILVER, COLOR_GOLD = '|cffeda55f', '|cffc7c7cf', '|cffffd700'
|
||||
local ICON_COPPER = [[|TInterface\MoneyFrame\UI-CopperIcon:12:12|t]]
|
||||
local ICON_SILVER = [[|TInterface\MoneyFrame\UI-SilverIcon:12:12|t]]
|
||||
local ICON_GOLD = [[|TInterface\MoneyFrame\UI-GoldIcon:12:12|t]]
|
||||
function E:FormatMoney(amount, style, textonly)
|
||||
local coppername = textonly and L["copperabbrev"] or ICON_COPPER
|
||||
local silvername = textonly and L["silverabbrev"] or ICON_SILVER
|
||||
local goldname = textonly and L["goldabbrev"] or ICON_GOLD
|
||||
|
||||
local value = abs(amount)
|
||||
local gold = floor(value / 10000)
|
||||
local silver = floor(mod(value / 100, 100))
|
||||
local copper = floor(mod(value, 100))
|
||||
|
||||
if not style or style == 'SMART' then
|
||||
local str = ''
|
||||
if gold > 0 then str = format('%d%s%s', gold, goldname, (silver > 0 or copper > 0) and ' ' or '') end
|
||||
if silver > 0 then str = format('%s%d%s%s', str, silver, silvername, copper > 0 and ' ' or '') end
|
||||
if copper > 0 or value == 0 then str = format('%s%d%s', str, copper, coppername) end
|
||||
return str
|
||||
end
|
||||
|
||||
if style == 'FULL' then
|
||||
if gold > 0 then
|
||||
return format('%d%s %d%s %d%s', gold, goldname, silver, silvername, copper, coppername)
|
||||
elseif silver > 0 then
|
||||
return format('%d%s %d%s', silver, silvername, copper, coppername)
|
||||
else
|
||||
return format('%d%s', copper, coppername)
|
||||
end
|
||||
elseif style == 'SHORT' then
|
||||
if gold > 0 then
|
||||
return format('%.1f%s', amount / 10000, goldname)
|
||||
elseif silver > 0 then
|
||||
return format('%.1f%s', amount / 100, silvername)
|
||||
else
|
||||
return format('%d%s', amount, coppername)
|
||||
end
|
||||
elseif style == 'SHORTINT' then
|
||||
if gold > 0 then
|
||||
return format('%d%s', gold, goldname)
|
||||
elseif silver > 0 then
|
||||
return format('%d%s', silver, silvername)
|
||||
else
|
||||
return format('%d%s', copper, coppername)
|
||||
end
|
||||
elseif style == 'CONDENSED' then
|
||||
if gold > 0 then
|
||||
return format('%s%d|r.%s%02d|r.%s%02d|r', COLOR_GOLD, gold, COLOR_SILVER, silver, COLOR_COPPER, copper)
|
||||
elseif silver > 0 then
|
||||
return format('%s%d|r.%s%02d|r', COLOR_SILVER, silver, COLOR_COPPER, copper)
|
||||
else
|
||||
return format('%s%d|r', COLOR_COPPER, copper)
|
||||
end
|
||||
elseif style == 'BLIZZARD' then
|
||||
if gold > 0 then
|
||||
return format('%s%s %d%s %d%s', BreakUpLargeNumbers(gold), goldname, silver, silvername, copper, coppername)
|
||||
elseif silver > 0 then
|
||||
return format('%d%s %d%s', silver, silvername, copper, coppername)
|
||||
else
|
||||
return format('%d%s', copper, coppername)
|
||||
end
|
||||
elseif style == 'BLIZZARD2' then
|
||||
if gold > 0 then
|
||||
return format('%s%s %02d%s %02d%s', BreakUpLargeNumbers(gold), goldname, silver, silvername, copper, coppername)
|
||||
elseif silver > 0 then
|
||||
return format('%d%s %02d%s', silver, silvername, copper, coppername)
|
||||
else
|
||||
return format('%d%s', copper, coppername)
|
||||
end
|
||||
end
|
||||
|
||||
-- Shouldn't be here; punt
|
||||
return self:FormatMoney(amount, 'SMART')
|
||||
end
|
||||
251
Core/ModuleCopy.lua
Normal file
251
Core/ModuleCopy.lua
Normal file
@@ -0,0 +1,251 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local MC = E:GetModule('ModuleCopy')
|
||||
|
||||
local pairs, next, type = pairs, next, type
|
||||
local format, error = format, error
|
||||
-- GLOBALS: ElvDB
|
||||
|
||||
--This table to reserve settings names in E.global.profileCopy. Used in export/imports functions
|
||||
--Plugins can add own values for their internal settings for safechecks here
|
||||
MC.InternalOptions = {
|
||||
selected = true,
|
||||
movers = true,
|
||||
}
|
||||
|
||||
--Default template for a config group for a single module.
|
||||
--Contains header, general group toggle (shown only if the setting actually exists) and imports button.
|
||||
--Usage as seen in ElvUI_OptionsUI\modulecopy.lua
|
||||
function MC:CreateModuleConfigGroup(Name, section, pluginSection)
|
||||
local config = {
|
||||
order = 10,
|
||||
type = 'group',
|
||||
name = Name,
|
||||
args = {
|
||||
header = E.Libs.ACH:Header(Name, 0),
|
||||
general = {
|
||||
order = 1,
|
||||
type = 'toggle',
|
||||
name = L["General"],
|
||||
},
|
||||
spacer = E.Libs.ACH:Spacer(-4),
|
||||
import = {
|
||||
order = -3,
|
||||
type = 'execute',
|
||||
name = L["Import Now"],
|
||||
func = function()
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.text = format(L["You are going to copy settings for |cffD3CF00\"%s\"|r from |cff4beb2c\"%s\"|r profile to your current |cff4beb2c\"%s\"|r profile. Are you sure?"], Name, E.global.profileCopy.selected, ElvDB.profileKeys[E.mynameRealm])
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.OnAccept = function()
|
||||
MC:ImportFromProfile(section, pluginSection)
|
||||
end
|
||||
E:StaticPopup_Show('MODULE_COPY_CONFIRM')
|
||||
end,
|
||||
},
|
||||
export = {
|
||||
order = -2,
|
||||
type = 'execute',
|
||||
name = L["Export Now"],
|
||||
func = function()
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.text = format(L["You are going to copy settings for |cffD3CF00\"%s\"|r from your current |cff4beb2c\"%s\"|r profile to |cff4beb2c\"%s\"|r profile. Are you sure?"], Name, ElvDB.profileKeys[E.mynameRealm], E.global.profileCopy.selected)
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.OnAccept = function()
|
||||
MC:ExportToProfile(section, pluginSection)
|
||||
end
|
||||
E:StaticPopup_Show('MODULE_COPY_CONFIRM')
|
||||
end,
|
||||
},
|
||||
},
|
||||
}
|
||||
if pluginSection then
|
||||
config.args.general.hidden = function(info) return E.global.profileCopy[pluginSection][section][ info[#info] ] == nil end
|
||||
config.get = function(info) return E.global.profileCopy[pluginSection][section][ info[#info] ] end
|
||||
config.set = function(info, value) E.global.profileCopy[pluginSection][section][ info[#info] ] = value end
|
||||
else
|
||||
config.args.general.hidden = function(info) return E.global.profileCopy[section][ info[#info] ] == nil end
|
||||
config.get = function(info) return E.global.profileCopy[section][ info[#info] ] end
|
||||
config.set = function(info, value) E.global.profileCopy[section][ info[#info] ] = value end
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
function MC:CreateMoversConfigGroup()
|
||||
local config = {
|
||||
header = E.Libs.ACH:Header(L["On screen positions for different elements."], 0),
|
||||
spacer = E.Libs.ACH:Spacer(200),
|
||||
import = {
|
||||
order = 201,
|
||||
type = 'execute',
|
||||
name = L["Import Now"],
|
||||
func = function()
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.text = format(L["You are going to copy settings for |cffD3CF00\"%s\"|r from |cff4beb2c\"%s\"|r profile to your current |cff4beb2c\"%s\"|r profile. Are you sure?"], L["Movers"], E.global.profileCopy.selected, ElvDB.profileKeys[E.mynameRealm])
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.OnAccept = function()
|
||||
MC:CopyMovers('import')
|
||||
end
|
||||
E:StaticPopup_Show('MODULE_COPY_CONFIRM')
|
||||
end,
|
||||
},
|
||||
export = {
|
||||
order = 202,
|
||||
type = 'execute',
|
||||
name = L["Export Now"],
|
||||
func = function()
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.text = format(L["You are going to copy settings for |cffD3CF00\"%s\"|r from your current |cff4beb2c\"%s\"|r profile to |cff4beb2c\"%s\"|r profile. Are you sure?"], L["Movers"], ElvDB.profileKeys[E.mynameRealm], E.global.profileCopy.selected)
|
||||
E.PopupDialogs.MODULE_COPY_CONFIRM.OnAccept = function()
|
||||
MC:CopyMovers('export')
|
||||
end
|
||||
E:StaticPopup_Show('MODULE_COPY_CONFIRM')
|
||||
end,
|
||||
},
|
||||
}
|
||||
for moverName, data in pairs(E.CreatedMovers) do
|
||||
if not G.profileCopy.movers[moverName] then G.profileCopy.movers[moverName] = false end
|
||||
config[moverName] = {
|
||||
order = 1,
|
||||
type = 'toggle',
|
||||
name = data.mover.textString,
|
||||
get = function() return E.global.profileCopy.movers[moverName] end,
|
||||
set = function(_, value) E.global.profileCopy.movers[moverName] = value; end
|
||||
}
|
||||
end
|
||||
for moverName, data in pairs(E.DisabledMovers) do
|
||||
if not G.profileCopy.movers[moverName] then G.profileCopy.movers[moverName] = false end
|
||||
config[moverName] = {
|
||||
order = 1,
|
||||
type = 'toggle',
|
||||
name = data.mover.textString,
|
||||
get = function() return E.global.profileCopy.movers[moverName] end,
|
||||
set = function(_, value) E.global.profileCopy.movers[moverName] = value; end
|
||||
}
|
||||
end
|
||||
return config
|
||||
end
|
||||
|
||||
function MC:CopyTable(CopyFrom, CopyTo, CopyDefault, module)
|
||||
for key, value in pairs(CopyTo) do
|
||||
if type(value) ~= 'table' then
|
||||
if module == true or (type(module) == 'table' and (module.general == nil or (not CopyTo.general and module.general))) then --Some dark magic of a logic to figure out stuff
|
||||
--This check is to see if the profile we are copying from has keys absent from defaults.
|
||||
--If key exists, then copy. If not, then clear obsolite key from the profile.
|
||||
if CopyDefault[key] ~= nil then
|
||||
CopyTo[key] = CopyFrom[key] or CopyDefault[key]
|
||||
else
|
||||
CopyFrom[key] = nil
|
||||
end
|
||||
end
|
||||
else
|
||||
if module == true then --Copy over entire section of profile subgroup
|
||||
E:CopyTable(CopyTo, CopyDefault)
|
||||
E:CopyTable(CopyTo, CopyFrom)
|
||||
elseif type(module) == 'table' and module[key] ~= nil then
|
||||
--Making sure tables actually exist in profiles (e.g absent values in ElvDB.profiles are for default values)
|
||||
CopyFrom[key], CopyTo[key] = MC:TablesExist(CopyFrom[key], CopyTo[key], CopyDefault[key])
|
||||
--If key exists, then copy. If not, then clear obsolite key from the profile.
|
||||
--Someone should double check this logic. Cause for single keys it is fine, but I'm no sure bout whole tables @Darth
|
||||
if CopyFrom[key] ~= nil then
|
||||
MC:CopyTable(CopyFrom[key], CopyTo[key], CopyDefault[key], module[key])
|
||||
else
|
||||
CopyTo[key] = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--[[
|
||||
* Valid copy templates should be as follows:
|
||||
G.profileCopy[YourOptionGroupName] = {
|
||||
[SubGroupName1] = true,
|
||||
[SubGroupName2] = true,
|
||||
...
|
||||
}
|
||||
* For example:
|
||||
G.profileCopy.auras = {
|
||||
general = true,
|
||||
buffs = true,
|
||||
debuffs = true,
|
||||
cooldown = true,
|
||||
}
|
||||
* 'general' key can refer to a similar named subtable or all non-table variables inside your group
|
||||
* If you leave the table as G.profileCopy[YourOptionGroupName] = {}, this will result in no valid copy template error.
|
||||
* If set to G.profileCopy[YourOptionGroupName] = true, then this will copy everything without selecting any particular subcategory from your settings table.
|
||||
* Plugins can use 'pluginSection' argument to determain their own table if they keep settings apart from core ElvUI settings.
|
||||
-- Examples S&L uses 'sle' table, MerathilisUI uses 'mui' table, BenikUI uses 'benikui' and core table
|
||||
]]
|
||||
|
||||
function MC:TablesExist(CopyFrom, CopyTo, CopyDefault)
|
||||
if not CopyFrom then CopyFrom = CopyDefault end
|
||||
if not CopyTo then CopyTo = CopyDefault end
|
||||
return CopyFrom, CopyTo
|
||||
end
|
||||
|
||||
function MC:ImportFromProfile(section, pluginSection)
|
||||
--Some checks for the occasion someone passes wrong stuff
|
||||
if not section then error('No profile section provided. Usage MC:ImportFromProfile("section")') end
|
||||
if not pluginSection and MC.InternalOptions[section] then error(format('Section name could not be "%s". This name is reserved for internal setting'), section) end
|
||||
if pluginSection and (MC.InternalOptions[pluginSection] and MC.InternalOptions[pluginSection][section]) then error(format('Section name for plugin group "%s" could not be "%s". This name is reserved for internal setting'), pluginSection, section) end
|
||||
|
||||
local module = pluginSection and E.global.profileCopy[pluginSection][section] or E.global.profileCopy[section]
|
||||
if not module then error(format('Provided section name "%s" does not have a template for profile copy.', section)) end
|
||||
--Starting digging through the settings
|
||||
local CopyFrom = pluginSection and (ElvDB.profiles[E.global.profileCopy.selected][pluginSection] and ElvDB.profiles[E.global.profileCopy.selected][pluginSection][section] or P[pluginSection][section]) or ElvDB.profiles[E.global.profileCopy.selected][section]
|
||||
local CopyTo = pluginSection and E.db[pluginSection][section] or E.db[section]
|
||||
local CopyDefault = pluginSection and P[pluginSection][section] or P[section]
|
||||
--Making sure tables actually exist in profiles (e.g absent values in ElvDB.profiles are for default values)
|
||||
CopyFrom, CopyTo = MC:TablesExist(CopyFrom, CopyTo, CopyDefault)
|
||||
if type(module) == 'table' and next(module) then --This module is not an empty table
|
||||
MC:CopyTable(CopyFrom, CopyTo, CopyDefault, module)
|
||||
elseif type(module) == 'boolean' then --Copy over entire section of profile subgroup
|
||||
E:CopyTable(CopyTo, CopyDefault)
|
||||
E:CopyTable(CopyTo, CopyFrom)
|
||||
else
|
||||
error(format('Provided section name "%s" does not have a valid copy template.', section))
|
||||
end
|
||||
E:StaggeredUpdateAll(nil, true)
|
||||
end
|
||||
|
||||
function MC:ExportToProfile(section, pluginSection)
|
||||
--Some checks for the occasion someone passes wrong stuff
|
||||
if not section then error('No profile section provided. Usage MC:ExportToProfile("section")') end
|
||||
if not pluginSection and MC.InternalOptions[section] then error(format('Section name could not be "%s". This name is reserved for internal setting'), section) end
|
||||
if pluginSection and MC.InternalOptions[pluginSection][section] then error(format('Section name for plugin group "%s" could not be "%s". This name is reserved for internal setting'), pluginSection, section) end
|
||||
|
||||
local module = pluginSection and E.global.profileCopy[pluginSection][section] or E.global.profileCopy[section]
|
||||
if not module then error(format('Provided section name "%s" does not have a template for profile copy.', section)) end
|
||||
--Making sure tables actually exist
|
||||
if not ElvDB.profiles[E.global.profileCopy.selected][section] then ElvDB.profiles[E.global.profileCopy.selected][section] = {} end
|
||||
if not E.db[section] then E.db[section] = {} end
|
||||
--Starting digging through the settings
|
||||
local CopyFrom = pluginSection and E.db[pluginSection][section] or E.db[section]
|
||||
local CopyTo = pluginSection and ElvDB.profiles[E.global.profileCopy.selected][pluginSection][section] or ElvDB.profiles[E.global.profileCopy.selected][section]
|
||||
local CopyDefault = pluginSection and P[pluginSection][section] or P[section]
|
||||
if type(module) == 'table' and next(module) then --This module is not an empty table
|
||||
MC:CopyTable(CopyFrom, CopyTo, CopyDefault, module)
|
||||
elseif type(module) == 'boolean' then --Copy over entire section of profile subgroup
|
||||
E:CopyTable(CopyTo, CopyDefault)
|
||||
E:CopyTable(CopyTo, CopyFrom)
|
||||
else
|
||||
error(format('Provided section name "%s" does not have a valid copy template.', section))
|
||||
end
|
||||
end
|
||||
|
||||
function MC:CopyMovers(mode)
|
||||
if not E.db.movers then E.db.movers = {} end --Nothing was moved in cutrrent profile
|
||||
if not ElvDB.profiles[E.global.profileCopy.selected].movers then ElvDB.profiles[E.global.profileCopy.selected].movers = {} end --Nothing was moved in selected profile
|
||||
local CopyFrom, CopyTo
|
||||
if mode == 'export' then
|
||||
CopyFrom, CopyTo = E.db.movers, ElvDB.profiles[E.global.profileCopy.selected].movers
|
||||
else
|
||||
CopyFrom, CopyTo = ElvDB.profiles[E.global.profileCopy.selected].movers or {}, E.db.movers
|
||||
end
|
||||
|
||||
for moverName in pairs(E.CreatedMovers) do
|
||||
if E.global.profileCopy.movers[moverName] then
|
||||
CopyTo[moverName] = CopyFrom[moverName]
|
||||
end
|
||||
end
|
||||
E:SetMoversPositions()
|
||||
end
|
||||
|
||||
function MC:Initialize()
|
||||
self.Initialized = true
|
||||
end
|
||||
|
||||
E:RegisterModule(MC:GetName())
|
||||
488
Core/Movers.lua
Normal file
488
Core/Movers.lua
Normal file
@@ -0,0 +1,488 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local Sticky = E.Libs.SimpleSticky
|
||||
|
||||
local _G = _G
|
||||
local type, unpack, pairs, error, ipairs = type, unpack, pairs, error, ipairs
|
||||
local format, split, find, strupper = format, strsplit, strfind, strupper
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local IsShiftKeyDown = IsShiftKeyDown
|
||||
local InCombatLockdown = InCombatLockdown
|
||||
local IsControlKeyDown = IsControlKeyDown
|
||||
local ERR_NOT_IN_COMBAT = ERR_NOT_IN_COMBAT
|
||||
local hooksecurefunc = hooksecurefunc
|
||||
|
||||
E.CreatedMovers = {}
|
||||
E.DisabledMovers = {}
|
||||
|
||||
local function SizeChanged(frame, width, height)
|
||||
if InCombatLockdown() then return end
|
||||
frame.mover:SetSize(width, height)
|
||||
end
|
||||
|
||||
local function WidthChanged(frame, width)
|
||||
if InCombatLockdown() then return end
|
||||
frame.mover:SetWidth(width)
|
||||
end
|
||||
|
||||
local function HeightChanged(frame, height)
|
||||
if InCombatLockdown() then return end
|
||||
frame.mover:SetHeight(height)
|
||||
end
|
||||
|
||||
local function GetPoint(obj)
|
||||
local point, anchor, secondaryPoint, x, y = obj:GetPoint()
|
||||
if not anchor then anchor = E.UIParent end
|
||||
|
||||
return format('%s,%s,%s,%d,%d', point, anchor:GetName(), secondaryPoint, x and E:Round(x) or 0, y and E:Round(y) or 0)
|
||||
end
|
||||
|
||||
local function GetSettingPoints(name)
|
||||
local db = E.db.movers and E.db.movers[name]
|
||||
if db then
|
||||
local delim = (find(db, '\031') and '\031') or ','
|
||||
return split(delim, db)
|
||||
end
|
||||
end
|
||||
|
||||
local function UpdateCoords(self)
|
||||
local mover = self.child
|
||||
local x, y, _, nudgePoint, nudgeInversePoint = E:CalculateMoverPoints(mover)
|
||||
local coordX, coordY = E:GetXYOffset(nudgeInversePoint, 1)
|
||||
local nudgeFrame = _G.ElvUIMoverNudgeWindow
|
||||
|
||||
nudgeFrame:ClearAllPoints()
|
||||
nudgeFrame:SetPoint(nudgePoint, mover, nudgeInversePoint, coordX, coordY)
|
||||
E:UpdateNudgeFrame(mover, x, y)
|
||||
end
|
||||
|
||||
function E:SetMoverPoints(name, parent)
|
||||
local holder = E.CreatedMovers[name]
|
||||
if not holder then return end
|
||||
|
||||
local point1, relativeTo1, relativePoint1, xOffset1, yOffset1 = unpack(holder.parentPoint)
|
||||
local point2, relativeTo2, relativePoint2, xOffset2, yOffset2 = GetSettingPoints(name)
|
||||
if not _G[relativeTo2] then -- fallback to the parents original point (on create) if the setting doesn't exist
|
||||
point2, relativeTo2, relativePoint2, xOffset2, yOffset2 = point1, relativeTo1, relativePoint1, xOffset1, yOffset1
|
||||
end
|
||||
|
||||
if point2 then
|
||||
holder.mover:ClearAllPoints()
|
||||
holder.mover:SetPoint(point2, relativeTo2, relativePoint2, xOffset2, yOffset2)
|
||||
end
|
||||
|
||||
if parent then
|
||||
parent:ClearAllPoints()
|
||||
parent:SetPoint(point1, parent.mover, 0, 0)
|
||||
end
|
||||
end
|
||||
|
||||
local isDragging = false
|
||||
local coordFrame = CreateFrame('Frame')
|
||||
coordFrame:SetScript('OnUpdate', UpdateCoords)
|
||||
coordFrame:Hide()
|
||||
|
||||
local function HandlePostDrag(self, event)
|
||||
if self.postdrag and type(self.postdrag) == 'function' then
|
||||
self.postdrag(self, E:GetScreenQuadrant(self))
|
||||
end
|
||||
|
||||
if event then
|
||||
self:UnregisterAllEvents()
|
||||
end
|
||||
end
|
||||
|
||||
local function OnDragStart(self)
|
||||
if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
|
||||
|
||||
if _G.ElvUIGrid then
|
||||
E:UIFrameFadeIn(_G.ElvUIGrid, 0.75, _G.ElvUIGrid:GetAlpha(), 1)
|
||||
end
|
||||
|
||||
if E.db.general.stickyFrames then
|
||||
Sticky:StartMoving(self, E.snapBars, self.snapOffset, self.snapOffset, self.snapOffset, self.snapOffset)
|
||||
else
|
||||
self:StartMoving()
|
||||
end
|
||||
|
||||
coordFrame.child = self
|
||||
coordFrame:Show()
|
||||
isDragging = true
|
||||
end
|
||||
|
||||
local function OnDragStop(self)
|
||||
if InCombatLockdown() then E:Print(ERR_NOT_IN_COMBAT) return end
|
||||
|
||||
if _G.ElvUIGrid and E.ConfigurationMode then
|
||||
E:UIFrameFadeOut(_G.ElvUIGrid, 0.75, _G.ElvUIGrid:GetAlpha(), 0.4)
|
||||
end
|
||||
|
||||
if E.db.general.stickyFrames then
|
||||
Sticky:StopMoving(self)
|
||||
else
|
||||
self:StopMovingOrSizing()
|
||||
end
|
||||
|
||||
local x2, y2, p2 = E:CalculateMoverPoints(self)
|
||||
self:ClearAllPoints()
|
||||
self:SetPoint(p2, E.UIParent, p2, x2, y2)
|
||||
|
||||
E:SaveMoverPosition(self.name)
|
||||
|
||||
coordFrame.child = nil
|
||||
coordFrame:Hide()
|
||||
isDragging = false
|
||||
|
||||
HandlePostDrag(self)
|
||||
|
||||
self:SetUserPlaced(false)
|
||||
end
|
||||
|
||||
local function OnEnter(self)
|
||||
if isDragging then return end
|
||||
|
||||
for _, frame in pairs(E.CreatedMovers) do
|
||||
local mover = frame.mover
|
||||
if mover:IsShown() and mover ~= self then
|
||||
E:UIFrameFadeOut(mover, 0.75, mover:GetAlpha(), 0.5)
|
||||
end
|
||||
end
|
||||
|
||||
E.AssignFrameToNudge(self)
|
||||
|
||||
coordFrame.child = self
|
||||
coordFrame:GetScript('OnUpdate')(coordFrame)
|
||||
|
||||
self.text:SetTextColor(1, 1, 1)
|
||||
end
|
||||
|
||||
local function OnLeave(self)
|
||||
if isDragging then return end
|
||||
|
||||
for _, frame in pairs(E.CreatedMovers) do
|
||||
local mover = frame.mover
|
||||
if mover:IsShown() and mover ~= self then
|
||||
E:UIFrameFadeIn(mover, 0.75, mover:GetAlpha(), 1)
|
||||
end
|
||||
end
|
||||
|
||||
self.text:SetTextColor(unpack(E.media.rgbvaluecolor))
|
||||
end
|
||||
|
||||
local function OnMouseUp(_, button)
|
||||
if button == 'LeftButton' and not isDragging then
|
||||
_G.ElvUIMoverNudgeWindow:SetShown(not _G.ElvUIMoverNudgeWindow:IsShown())
|
||||
end
|
||||
end
|
||||
|
||||
local function OnMouseDown(self, button)
|
||||
if isDragging then
|
||||
OnDragStop(self)
|
||||
elseif button == 'RightButton' then
|
||||
if IsControlKeyDown() and self.textString then
|
||||
E:ResetMovers(self.textString) --Allow resetting of anchor by Ctrl+RightClick
|
||||
elseif IsShiftKeyDown() then
|
||||
self:Hide() --Allow hiding a mover temporarily
|
||||
elseif self.configString then
|
||||
E:ToggleOptionsUI(self.configString) --OpenConfig
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function OnMouseWheel(_, delta)
|
||||
if IsShiftKeyDown() then
|
||||
E:NudgeMover(delta)
|
||||
else
|
||||
E:NudgeMover(nil, delta)
|
||||
end
|
||||
end
|
||||
|
||||
local function OnShow(self, r, g, b)
|
||||
if not r then r, g, b = unpack(E.media.rgbvaluecolor) end
|
||||
|
||||
self.text:FontTemplate()
|
||||
self.text:SetTextColor(r, g, b)
|
||||
|
||||
self:SetBackdropBorderColor(r, g, b)
|
||||
self.forcedBorderColors = {r, g, b}
|
||||
end
|
||||
|
||||
local function UpdateColors()
|
||||
local r, g, b = unpack(E.media.rgbvaluecolor)
|
||||
for _, holder in pairs(E.CreatedMovers) do
|
||||
OnShow(holder.mover, r, g, b)
|
||||
end
|
||||
end
|
||||
E.valueColorUpdateFuncs[UpdateColors] = true
|
||||
|
||||
local function UpdateMover(name, parent, textString, overlay, snapOffset, postdrag, shouldDisable, configString, perferCorners, ignoreSizeChanged)
|
||||
if not (name and parent) then return end --If for some reason the parent isnt loaded yet, also require a name
|
||||
|
||||
local holder = E.CreatedMovers[name]
|
||||
if holder.Created then return end
|
||||
holder.Created = true
|
||||
|
||||
if overlay == nil then overlay = true end
|
||||
|
||||
local f = CreateFrame('Button', name, E.UIParent, 'BackdropTemplate')
|
||||
f:SetClampedToScreen(true)
|
||||
f:RegisterForDrag('LeftButton', 'RightButton')
|
||||
f:SetFrameLevel(parent:GetFrameLevel() + 1)
|
||||
f:SetFrameStrata(overlay and 'DIALOG' or 'BACKGROUND')
|
||||
f:EnableMouseWheel(true)
|
||||
f:SetMovable(true)
|
||||
f:SetTemplate('Transparent', nil, nil, true)
|
||||
f:SetSize(parent:GetSize())
|
||||
f:Hide()
|
||||
|
||||
local fs = f:CreateFontString(nil, 'OVERLAY')
|
||||
fs:FontTemplate()
|
||||
fs:SetPoint('CENTER')
|
||||
fs:SetText(textString or name)
|
||||
fs:SetJustifyH('CENTER')
|
||||
fs:SetTextColor(unpack(E.media.rgbvaluecolor))
|
||||
f:SetFontString(fs)
|
||||
|
||||
f.text = fs
|
||||
f.name = name
|
||||
f.parent = parent
|
||||
f.overlay = overlay
|
||||
f.postdrag = postdrag
|
||||
f.textString = textString or name
|
||||
f.snapOffset = snapOffset or -2
|
||||
f.shouldDisable = shouldDisable
|
||||
f.configString = configString
|
||||
f.perferCorners = perferCorners
|
||||
f.ignoreSizeChanged = ignoreSizeChanged
|
||||
|
||||
holder.mover = f
|
||||
parent.mover = f
|
||||
E.snapBars[#E.snapBars+1] = f
|
||||
|
||||
if not ignoreSizeChanged then
|
||||
hooksecurefunc(parent, 'SetSize', SizeChanged)
|
||||
hooksecurefunc(parent, 'SetWidth', WidthChanged)
|
||||
hooksecurefunc(parent, 'SetHeight', HeightChanged)
|
||||
end
|
||||
|
||||
E:SetMoverPoints(name, parent)
|
||||
|
||||
f:SetScript('OnDragStart', OnDragStart)
|
||||
f:SetScript('OnDragStop', OnDragStop)
|
||||
f:SetScript('OnEnter', OnEnter)
|
||||
f:SetScript('OnLeave', OnLeave)
|
||||
f:SetScript('OnMouseDown', OnMouseDown)
|
||||
f:SetScript('OnMouseUp', OnMouseUp)
|
||||
f:SetScript('OnMouseWheel', OnMouseWheel)
|
||||
f:SetScript('OnShow', OnShow)
|
||||
f:SetScript('OnEvent', HandlePostDrag)
|
||||
f:RegisterEvent('PLAYER_ENTERING_WORLD')
|
||||
end
|
||||
|
||||
function E:CalculateMoverPoints(mover, nudgeX, nudgeY)
|
||||
local centerX, centerY = E.UIParent:GetCenter()
|
||||
local width = E.UIParent:GetRight()
|
||||
local x, y = mover:GetCenter()
|
||||
|
||||
local point, nudgePoint, nudgeInversePoint = 'BOTTOM', 'BOTTOM', 'TOP'
|
||||
if y >= centerY then -- TOP: 1080p = 540
|
||||
point, nudgePoint, nudgeInversePoint = 'TOP', 'TOP', 'BOTTOM'
|
||||
y = -(E.UIParent:GetTop() - mover:GetTop())
|
||||
else
|
||||
y = mover:GetBottom()
|
||||
end
|
||||
|
||||
if x >= (width * 2 / 3) then -- RIGHT: 1080p = 1280
|
||||
point, nudgePoint, nudgeInversePoint = point..'RIGHT', 'RIGHT', 'LEFT'
|
||||
x = mover:GetRight() - width
|
||||
elseif x <= (width / 3) or mover.perferCorners then -- LEFT: 1080p = 640
|
||||
point, nudgePoint, nudgeInversePoint = point..'LEFT', 'LEFT', 'RIGHT'
|
||||
x = mover:GetLeft()
|
||||
else
|
||||
x = x - centerX
|
||||
end
|
||||
|
||||
--Update coordinates if nudged
|
||||
x = x + (nudgeX or 0)
|
||||
y = y + (nudgeY or 0)
|
||||
|
||||
return x, y, point, nudgePoint, nudgeInversePoint
|
||||
end
|
||||
|
||||
function E:HasMoverBeenMoved(name)
|
||||
return E.db.movers and E.db.movers[name]
|
||||
end
|
||||
|
||||
function E:SaveMoverPosition(name)
|
||||
local holder = E.CreatedMovers[name]
|
||||
if not holder then return end
|
||||
if not E.db.movers then E.db.movers = {} end
|
||||
E.db.movers[name] = GetPoint(holder.mover)
|
||||
end
|
||||
|
||||
function E:SetMoverSnapOffset(name, offset)
|
||||
local holder = E.CreatedMovers[name]
|
||||
if not holder then return end
|
||||
holder.mover.snapOffset = offset or -2
|
||||
holder.snapoffset = offset or -2
|
||||
end
|
||||
|
||||
function E:SetMoverLayoutPositionPoint(holder, name, parent)
|
||||
local layout = E.LayoutMoverPositions[E.db.layoutSetting]
|
||||
local layoutPoint = (layout and layout[name]) or E.LayoutMoverPositions.ALL[name]
|
||||
holder.layoutPoint = layoutPoint
|
||||
holder.point = layoutPoint or GetPoint(parent or holder.mover)
|
||||
|
||||
if parent then -- CreateMover call
|
||||
holder.parentPoint = {parent:GetPoint()}
|
||||
end
|
||||
end
|
||||
|
||||
function E:SaveMoverDefaultPosition(name)
|
||||
local holder = E.CreatedMovers[name]
|
||||
if not holder then return end
|
||||
|
||||
E:SetMoverLayoutPositionPoint(holder, name)
|
||||
|
||||
HandlePostDrag(holder.mover)
|
||||
end
|
||||
|
||||
function E:CreateMover(parent, name, textString, overlay, snapoffset, postdrag, types, shouldDisable, configString, perferCorners, ignoreSizeChanged)
|
||||
local holder = E.CreatedMovers[name]
|
||||
if holder == nil then
|
||||
holder = {}
|
||||
holder.types = {}
|
||||
|
||||
if types then
|
||||
for _, x in ipairs({split(',', types)}) do
|
||||
holder.types[x] = true
|
||||
end
|
||||
else
|
||||
holder.types.ALL = true
|
||||
holder.types.GENERAL = true
|
||||
end
|
||||
|
||||
E:SetMoverLayoutPositionPoint(holder, name, parent)
|
||||
E.CreatedMovers[name] = holder
|
||||
end
|
||||
|
||||
UpdateMover(name, parent, textString, overlay, snapoffset, postdrag, shouldDisable, configString, perferCorners, ignoreSizeChanged)
|
||||
end
|
||||
|
||||
function E:ToggleMovers(show, which)
|
||||
self.configMode = show
|
||||
|
||||
local upperText = strupper(which)
|
||||
for _, holder in pairs(E.CreatedMovers) do
|
||||
local isName = (holder.mover.name == which) or strupper(holder.mover.textString) == upperText
|
||||
if show and (isName or holder.types[upperText]) then
|
||||
holder.mover:Show()
|
||||
else
|
||||
holder.mover:Hide()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:GetMoverHolder(name)
|
||||
local created = self.CreatedMovers[name]
|
||||
local disabled = self.DisabledMovers[name]
|
||||
return created or disabled, not not disabled
|
||||
end
|
||||
|
||||
function E:DisableMover(name)
|
||||
if self.DisabledMovers[name] then return end
|
||||
|
||||
local holder = self.CreatedMovers[name]
|
||||
if not holder then
|
||||
error(format('mover %s doesnt exist', name or 'nil'))
|
||||
end
|
||||
|
||||
self.DisabledMovers[name] = {}
|
||||
for x, y in pairs(holder) do
|
||||
self.DisabledMovers[name][x] = y
|
||||
end
|
||||
|
||||
if self.configMode then
|
||||
holder.mover:Hide()
|
||||
end
|
||||
|
||||
self.CreatedMovers[name] = nil
|
||||
end
|
||||
|
||||
function E:EnableMover(name)
|
||||
if self.CreatedMovers[name] then return end
|
||||
|
||||
local holder = self.DisabledMovers[name]
|
||||
if not holder then
|
||||
error(format('mover %s doesnt exist', name or 'nil'))
|
||||
end
|
||||
|
||||
self.CreatedMovers[name] = {}
|
||||
for x, y in pairs(holder) do
|
||||
self.CreatedMovers[name][x] = y
|
||||
end
|
||||
|
||||
if self.configMode then
|
||||
holder.mover:Show()
|
||||
end
|
||||
|
||||
self.DisabledMovers[name] = nil
|
||||
end
|
||||
|
||||
function E:ResetMovers(arg)
|
||||
local all = not arg or arg == ''
|
||||
if all then self.db.movers = nil end
|
||||
|
||||
for name, holder in pairs(E.CreatedMovers) do
|
||||
if all or (holder.mover and holder.mover.textString == arg) then
|
||||
local point, anchor, secondaryPoint, x, y = split(',', holder.point)
|
||||
|
||||
local frame = holder.mover
|
||||
if point then
|
||||
frame:ClearAllPoints()
|
||||
frame:SetPoint(point, anchor, secondaryPoint, x, y)
|
||||
end
|
||||
|
||||
HandlePostDrag(frame)
|
||||
|
||||
if all then
|
||||
E:SaveMoverPosition(name)
|
||||
else
|
||||
if holder.layoutPoint then
|
||||
E:SaveMoverPosition(name)
|
||||
elseif self.db.movers then
|
||||
self.db.movers[name] = nil
|
||||
end
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--Profile Change
|
||||
function E:SetMoversPositions()
|
||||
--E:SetMoversPositions() is the first function called in E:StaggeredUpdateAll().
|
||||
--Because of that, we can allow ourselves to re-enable all disabled movers here,
|
||||
--as the subsequent updates to these elements will disable them again if needed.
|
||||
for name in pairs(E.DisabledMovers) do
|
||||
local disable = E.DisabledMovers[name].shouldDisable
|
||||
local shouldDisable = (disable and disable()) or false
|
||||
if not shouldDisable then E:EnableMover(name) end
|
||||
end
|
||||
|
||||
for name in pairs(E.CreatedMovers) do
|
||||
E:SetMoverPoints(name)
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetMoversClampedToScreen(value)
|
||||
for _, holder in pairs(E.CreatedMovers) do
|
||||
holder.mover:SetClampedToScreen(value)
|
||||
end
|
||||
end
|
||||
|
||||
function E:LoadMovers()
|
||||
for n, t in pairs(E.CreatedMovers) do
|
||||
UpdateMover(n, t.parent, t.textString, t.overlay, t.snapoffset, t.postdrag, t.shouldDisable, t.configString, t.perferCorners, t.ignoreSizeChanged)
|
||||
end
|
||||
end
|
||||
97
Core/PixelPerfect.lua
Normal file
97
Core/PixelPerfect.lua
Normal file
@@ -0,0 +1,97 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
local min, max, format = min, max, format
|
||||
|
||||
local UIParent = UIParent
|
||||
local InCombatLockdown = InCombatLockdown
|
||||
local GetPhysicalScreenSize = GetPhysicalScreenSize
|
||||
|
||||
function E:IsEyefinity(width, height)
|
||||
if E.global.general.eyefinity and width >= 3840 then
|
||||
--HQ resolution
|
||||
if width >= 9840 then return 3280 end --WQSXGA
|
||||
if width >= 7680 and width < 9840 then return 2560 end --WQXGA
|
||||
if width >= 5760 and width < 7680 then return 1920 end --WUXGA & HDTV
|
||||
if width >= 5040 and width < 5760 then return 1680 end --WSXGA+
|
||||
|
||||
--Adding height condition here to be sure it work with bezel compensation because WSXGA+ and UXGA/HD+ got approx same width
|
||||
if width >= 4800 and width < 5760 and height == 900 then return 1600 end --UXGA & HD+
|
||||
|
||||
--Low resolution screen
|
||||
if width >= 4320 and width < 4800 then return 1440 end --WSXGA
|
||||
if width >= 4080 and width < 4320 then return 1360 end --WXGA
|
||||
if width >= 3840 and width < 4080 then return 1224 end --SXGA & SXGA (UVGA) & WXGA & HDTV
|
||||
end
|
||||
end
|
||||
|
||||
function E:IsUltrawide(width, height)
|
||||
if E.global.general.ultrawide and width >= 2560 then
|
||||
--HQ Resolution
|
||||
if width >= 3440 and (height == 1440 or height == 1600) then return 2560 end --WQHD, DQHD, DQHD+ & WQHD+
|
||||
|
||||
--Low resolution
|
||||
if width >= 2560 and height == 1080 then return 1920 end --WFHD & DFHD
|
||||
end
|
||||
end
|
||||
|
||||
function E:UIScale(init) -- `init` will be the `event` if its triggered after combat
|
||||
if init == true then -- E.OnInitialize
|
||||
E.mult = (768 / E.screenheight) / E.global.general.UIScale
|
||||
elseif InCombatLockdown() then
|
||||
E:RegisterEventForObject('PLAYER_REGEN_ENABLED', E.UIScale, E.UIScale)
|
||||
else -- E.Initialize
|
||||
UIParent:SetScale(E.global.general.UIScale)
|
||||
|
||||
local width, height = E.screenwidth, E.screenheight
|
||||
E.eyefinity = E:IsEyefinity(width, height)
|
||||
E.ultrawide = E:IsUltrawide(width, height)
|
||||
|
||||
local testing, newWidth = false, E.eyefinity or E.ultrawide
|
||||
if testing then -- Resize E.UIParent if Eyefinity or UltraWide is on.
|
||||
-- Eyefinity / UltraWide Test: Resize the E.UIParent to be smaller than it should be, all objects inside should relocate.
|
||||
-- Dragging moveable frames outside the box and reloading the UI ensures that they are saving position correctly.
|
||||
local uiWidth, uiHeight = UIParent:GetSize()
|
||||
width, height = uiWidth-250, uiHeight-250
|
||||
elseif newWidth then -- Center E.UIParent
|
||||
local uiHeight = UIParent:GetHeight()
|
||||
width, height = newWidth / (height / uiHeight), uiHeight
|
||||
else
|
||||
width, height = UIParent:GetSize()
|
||||
end
|
||||
|
||||
E.UIParent:SetSize(width, height)
|
||||
E.UIParent.origHeight = E.UIParent:GetHeight()
|
||||
|
||||
if E:IsEventRegisteredForObject('PLAYER_REGEN_ENABLED', E.UIScale) then
|
||||
E:UnregisterEventForObject('PLAYER_REGEN_ENABLED', E.UIScale, E.UIScale)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function E:PixelBestSize()
|
||||
return max(0.4, min(1.15, 768 / E.screenheight))
|
||||
end
|
||||
|
||||
function E:PixelScaleChanged(event)
|
||||
if event == 'UI_SCALE_CHANGED' then
|
||||
E.screenwidth, E.screenheight = GetPhysicalScreenSize()
|
||||
E.resolution = format('%dx%d', E.screenwidth, E.screenheight)
|
||||
end
|
||||
|
||||
E:UIScale(true) -- Repopulate variables
|
||||
E:UIScale() -- Setup the scale
|
||||
|
||||
E:Config_UpdateSize(true) -- Reposition config
|
||||
end
|
||||
|
||||
local trunc = function(s) return s >= 0 and s-s%01 or s-s%-1 end
|
||||
local round = function(s) return s >= 0 and s-s%-1 or s-s%01 end
|
||||
function E:Scale(num)
|
||||
if E.mult == 1 then
|
||||
return num
|
||||
elseif E.mult < 1 then
|
||||
return trunc(num/E.mult) * E.mult
|
||||
else
|
||||
return round(num/E.mult) * E.mult
|
||||
end
|
||||
end
|
||||
507
Core/PluginInstaller.lua
Normal file
507
Core/PluginInstaller.lua
Normal file
@@ -0,0 +1,507 @@
|
||||
--[[--------------------------------------------------------------------
|
||||
* Plugins pass their info using the table like:
|
||||
addon = {
|
||||
Title = 'Your Own Title',
|
||||
Name = 'AddOnName',
|
||||
tutorialImage = 'TexturePath',
|
||||
tutorialImageSize = {x,y},
|
||||
tutorialImagetutorialImagePoint = {xOffset,yOffset},
|
||||
Pages = {
|
||||
function1,
|
||||
function2,
|
||||
function3,
|
||||
},
|
||||
StepTitles = {
|
||||
'Title 1',
|
||||
'Title 2',
|
||||
'Title 3',
|
||||
},
|
||||
StepTitlesColor = {r, g, b},
|
||||
StepTitlesColorSelected = {r, g, b},
|
||||
StepTitleWidth = 140,
|
||||
StepTitleButtonWidth = 130,
|
||||
StepTitleTextJustification = 'CENTER'
|
||||
}
|
||||
|
||||
E:GetModule('PluginInstaller'):Queue(addon)
|
||||
|
||||
* Title is wat displayed on top of the window. By default it's "ElvUI Plugin Installation"
|
||||
* Name is how your installation will be showin in 'pending list', Default is 'Unknown'
|
||||
* tutorialImage is a path to your own texture to use in frame. if not specified, then it will use ElvUI's one
|
||||
* Pages is a table to set up pages of your install where numbers are representing actual pages' order and function is what previously was used to set layout. For example
|
||||
|
||||
function function1()
|
||||
PluginInstallFrame.SubTitle:SetText('Title Text')
|
||||
PluginInstallFrame.Desc1:SetText('Desc 1 Tet')
|
||||
PluginInstallFrame.Desc2:SetText('Desc 2 Tet')
|
||||
PluginInstallFrame.Desc3:SetText('Desc 3 Tet')
|
||||
|
||||
PluginInstallFrame.Option1:Show()
|
||||
PluginInstallFrame.Option1:SetScript('OnClick', function() <Do Some Stuff> end)
|
||||
PluginInstallFrame.Option1:SetText('Text 1')
|
||||
|
||||
PluginInstallFrame.Option2:Show()
|
||||
PluginInstallFrame.Option2:SetScript('OnClick', function() <Do Some Other Stuff> end)
|
||||
PluginInstallFrame.Option2:SetText('Text 2')
|
||||
end
|
||||
|
||||
StepTitles - a table to specify 'titles' for your install steps.
|
||||
* If specified and number of lines here = number of pages then you'll get an additional frame to the right of main frame
|
||||
* with a list of steps (current one being highlighted), clicking on those will open respective step. BenikUI style of doing stuff.
|
||||
StepTitlesColor - a table with color values to color 'titles' when they are not active
|
||||
StepTitlesColorSelected - a table with color values to color 'titles' when they are active
|
||||
StepTitleWidth - Width of the steps frame on the right side
|
||||
StepTitleButtonWidth - Width of each step button in the steps frame
|
||||
StepTitleTextJustification - The justification of the text on each step button ('LEFT', 'RIGHT', 'CENTER'). Default: 'CENTER'
|
||||
--------------------------------------------------------------------]]--
|
||||
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB, Localize Underscore
|
||||
local PI = E:GetModule('PluginInstaller')
|
||||
local S = E:GetModule('Skins')
|
||||
|
||||
local _G = _G
|
||||
local pairs, unpack = pairs, unpack
|
||||
local tinsert, tremove, format = tinsert, tremove, format
|
||||
|
||||
local PlaySound = PlaySound
|
||||
local CreateFrame = CreateFrame
|
||||
local UIFrameFadeOut = UIFrameFadeOut
|
||||
local CONTINUE, PREVIOUS, UNKNOWN = CONTINUE, PREVIOUS, UNKNOWN
|
||||
-- GLOBALS: PluginInstallFrame
|
||||
|
||||
--Installation Functions
|
||||
PI.Installs = {}
|
||||
local f
|
||||
local BUTTON_HEIGHT = 20
|
||||
|
||||
local function ResetAll()
|
||||
f.Next:Disable()
|
||||
f.Prev:Disable()
|
||||
f.Option1:Hide()
|
||||
f.Option1:SetScript('OnClick', nil)
|
||||
f.Option1:SetText('')
|
||||
f.Option2:Hide()
|
||||
f.Option2:SetScript('OnClick', nil)
|
||||
f.Option2:SetText('')
|
||||
f.Option3:Hide()
|
||||
f.Option3:SetScript('OnClick', nil)
|
||||
f.Option3:SetText('')
|
||||
f.Option4:Hide()
|
||||
f.Option4:SetScript('OnClick', nil)
|
||||
f.Option4:SetText('')
|
||||
f.SubTitle:SetText('')
|
||||
f.Desc1:SetText('')
|
||||
f.Desc2:SetText('')
|
||||
f.Desc3:SetText('')
|
||||
f.Desc4:SetText('')
|
||||
f:Size(550, 400)
|
||||
if f.StepTitles then
|
||||
for i = 1, #f.side.Lines do f.side.Lines[i].text:SetText('') end
|
||||
end
|
||||
end
|
||||
|
||||
local function SetPage(PageNum, PrevPage)
|
||||
f.CurrentPage = PageNum
|
||||
f.PrevPage = PrevPage
|
||||
ResetAll()
|
||||
f.Status.anim.progress:SetChange(PageNum)
|
||||
f.Status.anim.progress:Play()
|
||||
|
||||
local r, g, b = E:ColorGradient(f.CurrentPage / f.MaxPage, 1, 0, 0, 1, 1, 0, 0, 1, 0)
|
||||
f.Status:SetStatusBarColor(r, g, b)
|
||||
|
||||
if PageNum == f.MaxPage then
|
||||
f.Next:Disable()
|
||||
else
|
||||
f.Next:Enable()
|
||||
end
|
||||
|
||||
if PageNum == 1 then
|
||||
f.Prev:Disable()
|
||||
else
|
||||
f.Prev:Enable()
|
||||
end
|
||||
|
||||
f.Pages[f.CurrentPage]()
|
||||
f.Status.text:SetFormattedText('%d / %d', f.CurrentPage, f.MaxPage)
|
||||
if f.StepTitles then
|
||||
for i = 1, #f.side.Lines do
|
||||
local line, color = f.side.Lines[i]
|
||||
line.text:SetText(f.StepTitles[i])
|
||||
if i == f.CurrentPage then
|
||||
color = f.StepTitlesColorSelected or {.09,.52,.82}
|
||||
else
|
||||
color = f.StepTitlesColor or {1,1,1}
|
||||
end
|
||||
line.text:SetTextColor(color[1] or color.r, color[2] or color.g, color[3] or color.b)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function NextPage()
|
||||
if f.CurrentPage ~= f.MaxPage then
|
||||
f.CurrentPage = f.CurrentPage + 1
|
||||
SetPage(f.CurrentPage, f.CurrentPage - 1)
|
||||
end
|
||||
end
|
||||
|
||||
local function PreviousPage()
|
||||
if f.CurrentPage ~= 1 then
|
||||
f.CurrentPage = f.CurrentPage - 1
|
||||
SetPage(f.CurrentPage, f.CurrentPage + 1)
|
||||
end
|
||||
end
|
||||
|
||||
function PI:CreateStepComplete()
|
||||
local imsg = CreateFrame('Frame', 'PluginInstallStepComplete', E.UIParent)
|
||||
imsg:Size(418, 72)
|
||||
imsg:Point('TOP', 0, -190)
|
||||
imsg:Hide()
|
||||
imsg:SetScript('OnShow', function(frame)
|
||||
if frame.message then
|
||||
PlaySound(888) -- LevelUp Sound
|
||||
frame.text:SetText(frame.message)
|
||||
UIFrameFadeOut(frame, 3.5, 1, 0)
|
||||
E:Delay(4, frame.Hide, frame)
|
||||
frame.message = nil
|
||||
else
|
||||
frame:Hide()
|
||||
end
|
||||
end)
|
||||
|
||||
imsg.firstShow = false
|
||||
|
||||
imsg.bg = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.bg:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.bg:Point('BOTTOM')
|
||||
imsg.bg:Size(326, 103)
|
||||
imsg.bg:SetTexCoord(0.00195313, 0.63867188, 0.03710938, 0.23828125)
|
||||
imsg.bg:SetVertexColor(1, 1, 1, 0.6)
|
||||
|
||||
imsg.lineTop = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.lineTop:SetDrawLayer('BACKGROUND', 2)
|
||||
imsg.lineTop:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.lineTop:Point('TOP')
|
||||
imsg.lineTop:Size(418, 7)
|
||||
imsg.lineTop:SetTexCoord(0.00195313, 0.81835938, 0.01953125, 0.03320313)
|
||||
|
||||
imsg.lineBottom = imsg:CreateTexture(nil, 'BACKGROUND')
|
||||
imsg.lineBottom:SetDrawLayer('BACKGROUND', 2)
|
||||
imsg.lineBottom:SetTexture([[Interface\LevelUp\LevelUpTex]])
|
||||
imsg.lineBottom:Point('BOTTOM')
|
||||
imsg.lineBottom:Size(418, 7)
|
||||
imsg.lineBottom:SetTexCoord(0.00195313, 0.81835938, 0.01953125, 0.03320313)
|
||||
|
||||
imsg.text = imsg:CreateFontString(nil, 'ARTWORK', 'GameFont_Gigantic')
|
||||
imsg.text:Point('BOTTOM', 0, 12)
|
||||
imsg.text:SetTextColor(1, 0.82, 0)
|
||||
imsg.text:SetJustifyH('CENTER')
|
||||
end
|
||||
|
||||
function PI:CreateFrame()
|
||||
f = CreateFrame('Button', 'PluginInstallFrame', E.UIParent, 'BackdropTemplate')
|
||||
f.SetPage = SetPage
|
||||
f:Size(550, 400)
|
||||
f:SetTemplate('Transparent')
|
||||
f:Point('CENTER')
|
||||
f:SetFrameStrata('TOOLTIP')
|
||||
f:SetMovable(true)
|
||||
|
||||
f.MoveFrame = CreateFrame('Frame', nil, f, 'TitleDragAreaTemplate')
|
||||
f.MoveFrame:Size(450, 50)
|
||||
f.MoveFrame:Point('TOP', f, 'TOP')
|
||||
|
||||
f.Title = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Title:FontTemplate(nil, 17, nil)
|
||||
f.Title:Point('TOP', 0, -5)
|
||||
|
||||
f.Next = CreateFrame('Button', 'PluginInstallNextButton', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Next:Size(110, 25)
|
||||
f.Next:Point('BOTTOMRIGHT', -5, 5)
|
||||
f.Next:SetText(CONTINUE)
|
||||
f.Next:Disable()
|
||||
f.Next:SetScript('OnClick', NextPage)
|
||||
S:HandleButton(f.Next)
|
||||
|
||||
f.Prev = CreateFrame('Button', 'PluginInstallPrevButton', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Prev:Size(110, 25)
|
||||
f.Prev:Point('BOTTOMLEFT', 5, 5)
|
||||
f.Prev:SetText(PREVIOUS)
|
||||
f.Prev:Disable()
|
||||
f.Prev:SetScript('OnClick', PreviousPage)
|
||||
S:HandleButton(f.Prev)
|
||||
|
||||
f.Status = CreateFrame('StatusBar', 'PluginInstallStatus', f)
|
||||
f.Status:SetFrameLevel(f.Status:GetFrameLevel() + 2)
|
||||
f.Status:CreateBackdrop(nil, true)
|
||||
f.Status:SetStatusBarTexture(E.media.normTex)
|
||||
f.Status:SetStatusBarColor(unpack(E.media.rgbvaluecolor))
|
||||
f.Status:Point('TOPLEFT', f.Prev, 'TOPRIGHT', 6, -2)
|
||||
f.Status:Point('BOTTOMRIGHT', f.Next, 'BOTTOMLEFT', -6, 2)
|
||||
-- Setup StatusBar Animation
|
||||
f.Status.anim = _G.CreateAnimationGroup(f.Status)
|
||||
f.Status.anim.progress = f.Status.anim:CreateAnimation('Progress')
|
||||
f.Status.anim.progress:SetEasing('Out')
|
||||
f.Status.anim.progress:SetDuration(.3)
|
||||
|
||||
f.Status.text = f.Status:CreateFontString(nil, 'OVERLAY')
|
||||
f.Status.text:FontTemplate(nil, 14, 'OUTLINE')
|
||||
f.Status.text:Point('CENTER')
|
||||
|
||||
f.Option1 = CreateFrame('Button', 'PluginInstallOption1Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option1:Size(160, 30)
|
||||
f.Option1:Point('BOTTOM', 0, 45)
|
||||
f.Option1:SetText('')
|
||||
f.Option1:Hide()
|
||||
S:HandleButton(f.Option1)
|
||||
|
||||
f.Option2 = CreateFrame('Button', 'PluginInstallOption2Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option2:Size(110, 30)
|
||||
f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45)
|
||||
f.Option2:SetText('')
|
||||
f.Option2:Hide()
|
||||
f.Option2:SetScript('OnShow', function() f.Option1:Width(110); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOMRIGHT', f, 'BOTTOM', -4, 45) end)
|
||||
f.Option2:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45) end)
|
||||
S:HandleButton(f.Option2)
|
||||
|
||||
f.Option3 = CreateFrame('Button', 'PluginInstallOption3Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option3:Size(100, 30)
|
||||
f.Option3:Point('LEFT', f.Option2, 'RIGHT', 4, 0)
|
||||
f.Option3:SetText('')
|
||||
f.Option3:Hide()
|
||||
f.Option3:SetScript('OnShow', function() f.Option1:Width(100); f.Option1:ClearAllPoints(); f.Option1:Point('RIGHT', f.Option2, 'LEFT', -4, 0); f.Option2:Width(100); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOM', f, 'BOTTOM', 0, 45) end)
|
||||
f.Option3:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45); f.Option2:Width(110); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45) end)
|
||||
S:HandleButton(f.Option3)
|
||||
|
||||
f.Option4 = CreateFrame('Button', 'PluginInstallOption4Button', f, 'UIPanelButtonTemplate, BackdropTemplate')
|
||||
f.Option4:Size(100, 30)
|
||||
f.Option4:Point('LEFT', f.Option3, 'RIGHT', 4, 0)
|
||||
f.Option4:SetText('')
|
||||
f.Option4:Hide()
|
||||
f.Option4:SetScript('OnShow', function()
|
||||
f.Option1:Width(100)
|
||||
f.Option2:Width(100)
|
||||
|
||||
f.Option1:ClearAllPoints()
|
||||
f.Option1:Point('RIGHT', f.Option2, 'LEFT', -4, 0)
|
||||
f.Option2:ClearAllPoints()
|
||||
f.Option2:Point('BOTTOMRIGHT', f, 'BOTTOM', -4, 45)
|
||||
end)
|
||||
f.Option4:SetScript('OnHide', function() f.Option1:Width(160); f.Option1:ClearAllPoints(); f.Option1:Point('BOTTOM', 0, 45); f.Option2:Width(110); f.Option2:ClearAllPoints(); f.Option2:Point('BOTTOMLEFT', f, 'BOTTOM', 4, 45) end)
|
||||
S:HandleButton(f.Option4)
|
||||
|
||||
f.SubTitle = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.SubTitle:FontTemplate(nil, 15, nil)
|
||||
f.SubTitle:Point('TOP', 0, -40)
|
||||
|
||||
f.Desc1 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc1:FontTemplate()
|
||||
f.Desc1:Point('TOPLEFT', 20, -75)
|
||||
f.Desc1:Width(f:GetWidth() - 40)
|
||||
|
||||
f.Desc2 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc2:FontTemplate()
|
||||
f.Desc2:Point('TOP', f.Desc1, 'BOTTOM', 0, -20)
|
||||
f.Desc2:Width(f:GetWidth() - 40)
|
||||
|
||||
f.Desc3 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc3:FontTemplate()
|
||||
f.Desc3:Point('TOP', f.Desc2, 'BOTTOM', 0, -20)
|
||||
f.Desc3:Width(f:GetWidth() - 40)
|
||||
|
||||
f.Desc4 = f:CreateFontString(nil, 'OVERLAY')
|
||||
f.Desc4:FontTemplate()
|
||||
f.Desc4:Point('TOP', f.Desc3, 'BOTTOM', 0, -20)
|
||||
f.Desc4:Width(f:GetWidth() - 40)
|
||||
|
||||
local close = CreateFrame('Button', 'PluginInstallCloseButton', f, 'UIPanelCloseButton, BackdropTemplate')
|
||||
close:Point('TOPRIGHT', f, 'TOPRIGHT')
|
||||
close:SetScript('OnClick', function() f:Hide() end)
|
||||
S:HandleCloseButton(close)
|
||||
|
||||
f.pending = CreateFrame('Frame', 'PluginInstallPendingButton', f)
|
||||
f.pending:Size(20, 20)
|
||||
f.pending:Point('TOPLEFT', f, 'TOPLEFT', 8, -8)
|
||||
f.pending.tex = f.pending:CreateTexture(nil, 'OVERLAY')
|
||||
f.pending.tex:Point('TOPLEFT', f.pending, 'TOPLEFT', 2, -2)
|
||||
f.pending.tex:Point('BOTTOMRIGHT', f.pending, 'BOTTOMRIGHT', -2, 2)
|
||||
f.pending.tex:SetTexture([[Interface\OptionsFrame\UI-OptionsFrame-NewFeatureIcon]])
|
||||
f.pending:CreateBackdrop('Transparent')
|
||||
f.pending:SetScript('OnEnter', function(button)
|
||||
_G.GameTooltip:SetOwner(button, 'ANCHOR_BOTTOMLEFT', E.PixelMode and -7 or -9)
|
||||
_G.GameTooltip:AddLine(L["List of installations in queue:"], 1, 1, 1)
|
||||
_G.GameTooltip:AddLine(' ')
|
||||
for i = 1, #PI.Installs do
|
||||
_G.GameTooltip:AddDoubleLine(format('%d. %s', i, (PI.Installs[i].Name or UNKNOWN)), i == 1 and format('|cff00FF00%s|r', L["In Progress"]) or format('|cffFF0000%s|r', L["Pending"]))
|
||||
end
|
||||
_G.GameTooltip:Show()
|
||||
end)
|
||||
f.pending:SetScript('OnLeave', function()
|
||||
_G.GameTooltip:Hide()
|
||||
end)
|
||||
|
||||
f.tutorialImage = f:CreateTexture('PluginInstallTutorialImage', 'OVERLAY')
|
||||
f.tutorialImage2 = f:CreateTexture('PluginInstallTutorialImage2', 'OVERLAY')
|
||||
|
||||
f.side = CreateFrame('Frame', 'PluginInstallTitleFrame', f, 'BackdropTemplate')
|
||||
f.side:SetTemplate('Transparent')
|
||||
f.side:Point('TOPLEFT', f, 'TOPRIGHT', E.PixelMode and 1 or 3, 0)
|
||||
f.side:Point('BOTTOMLEFT', f, 'BOTTOMRIGHT', E.PixelMode and 1 or 3, 0)
|
||||
f.side:Width(140)
|
||||
f.side.text = f.side:CreateFontString(nil, 'OVERLAY')
|
||||
f.side.text:Point('TOP', f.side, 'TOP', 0, -4)
|
||||
f.side.text:FontTemplate(nil, 18, 'OUTLINE')
|
||||
f.side.text:SetText(L["Steps"])
|
||||
f.side.Lines = {} --Table to keep shown lines
|
||||
f.side:Hide()
|
||||
for i = 1, 18 do
|
||||
local button = CreateFrame('Button', nil, f)
|
||||
if i == 1 then
|
||||
button:Point('TOP', f.side.text, 'BOTTOM', 0, -6)
|
||||
else
|
||||
button:Point('TOP', f.side.Lines[i - 1], 'BOTTOM')
|
||||
end
|
||||
button:Size(130, BUTTON_HEIGHT)
|
||||
button.text = button:CreateFontString(nil, 'OVERLAY')
|
||||
button.text:Point('TOPLEFT', button, 'TOPLEFT', 2, -2)
|
||||
button.text:Point('BOTTOMRIGHT', button, 'BOTTOMRIGHT', -2, 2)
|
||||
button.text:FontTemplate(nil, 14, 'OUTLINE')
|
||||
button:SetScript('OnClick', function() if i <= f.MaxPage then SetPage(i, f.CurrentPage) end end)
|
||||
button.text:SetText('')
|
||||
f.side.Lines[i] = button
|
||||
button:Hide()
|
||||
end
|
||||
|
||||
f:Hide()
|
||||
|
||||
f:SetScript('OnHide', function() PI:CloseInstall() end)
|
||||
end
|
||||
|
||||
function PI:Queue(addon)
|
||||
local addonIsQueued = false
|
||||
for _, v in pairs(self.Installs) do
|
||||
if v.Name == addon.Name then
|
||||
addonIsQueued = true
|
||||
end
|
||||
end
|
||||
|
||||
if not addonIsQueued then
|
||||
tinsert(self.Installs, #(self.Installs)+1, addon)
|
||||
self:RunInstall()
|
||||
end
|
||||
end
|
||||
|
||||
function PI:CloseInstall()
|
||||
tremove(self.Installs, 1)
|
||||
f.side:Hide()
|
||||
for i = 1, #f.side.Lines do
|
||||
f.side.Lines[i].text:SetText('')
|
||||
f.side.Lines[i]:Hide()
|
||||
end
|
||||
if #self.Installs > 0 then
|
||||
E:Delay(1, PI.RunInstall, PI)
|
||||
end
|
||||
end
|
||||
|
||||
function PI:RunInstall()
|
||||
if not E.private.install_complete then return end
|
||||
|
||||
local db = self.Installs[1]
|
||||
if db and not f:IsShown() and not (_G.ElvUIInstallFrame and _G.ElvUIInstallFrame:IsShown()) then
|
||||
f.StepTitles = nil
|
||||
f.StepTitlesColor = nil
|
||||
f.StepTitlesColorSelected = nil
|
||||
|
||||
f.CurrentPage = 0
|
||||
f.MaxPage = #(db.Pages)
|
||||
|
||||
f.Title:SetText(db.Title or L["ElvUI Plugin Installation"])
|
||||
f.Status:SetMinMaxValues(0, f.MaxPage)
|
||||
f.Status.text:SetText(f.CurrentPage..' / '..f.MaxPage)
|
||||
|
||||
-- Logo
|
||||
local LogoTop = db.tutorialImage or E.Media.Textures.LogoTop
|
||||
f.tutorialImage:SetTexture(LogoTop)
|
||||
f.tutorialImage:ClearAllPoints()
|
||||
if db.tutorialImageSize then
|
||||
f.tutorialImage:Size(db.tutorialImageSize[1], db.tutorialImageSize[2])
|
||||
else
|
||||
f.tutorialImage:Size(256, 128)
|
||||
end
|
||||
if db.tutorialImagePoint then
|
||||
f.tutorialImage:Point('BOTTOM', 0 + db.tutorialImagePoint[1], 70 + db.tutorialImagePoint[2])
|
||||
else
|
||||
f.tutorialImage:Point('BOTTOM', 0, 70)
|
||||
end
|
||||
if db.tutorialImageVertexColor then
|
||||
f.tutorialImage:SetVertexColor(unpack(db.tutorialImageVertexColor))
|
||||
elseif LogoTop == E.Media.Textures.LogoTop then
|
||||
f.tutorialImage:SetVertexColor(unpack(E.media.rgbvaluecolor))
|
||||
else
|
||||
f.tutorialImage:SetVertexColor(1, 1, 1)
|
||||
end
|
||||
|
||||
--Alt Logo
|
||||
if LogoTop == E.Media.Textures.LogoTop or db.tutorialImage2 then
|
||||
f.tutorialImage2:SetTexture(db.tutorialImage2 or E.Media.Textures.LogoBottom)
|
||||
f.tutorialImage2:ClearAllPoints()
|
||||
if db.tutorialImage2Size then
|
||||
f.tutorialImage2:Size(db.tutorialImage2Size[1], db.tutorialImage2Size[2])
|
||||
else
|
||||
f.tutorialImage2:Size(256, 128)
|
||||
end
|
||||
if db.tutorialImage2Point then
|
||||
f.tutorialImage2:Point('BOTTOM', 0 + db.tutorialImage2Point[1], 70 + db.tutorialImage2Point[2])
|
||||
else
|
||||
f.tutorialImage2:Point('BOTTOM', 0, 70)
|
||||
end
|
||||
if db.tutorialImage2VertexColor then
|
||||
f.tutorialImage2:SetVertexColor(unpack(db.tutorialImage2VertexColor))
|
||||
else
|
||||
f.tutorialImage2:SetVertexColor(1, 1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
f.Pages = db.Pages
|
||||
|
||||
f:Show()
|
||||
f:ClearAllPoints()
|
||||
f:Point('CENTER')
|
||||
|
||||
if db.StepTitles and #db.StepTitles == f.MaxPage then
|
||||
f:Point('CENTER', E.UIParent, 'CENTER', -((db.StepTitleWidth or 140)/2), 0)
|
||||
f.side:Width(db.StepTitleWidth or 140)
|
||||
f.side:Show()
|
||||
|
||||
for i = 1, #f.side.Lines do
|
||||
if db.StepTitles[i] then
|
||||
f.side.Lines[i]:Width(db.StepTitleButtonWidth or 130)
|
||||
f.side.Lines[i].text:SetJustifyH(db.StepTitleTextJustification or 'CENTER')
|
||||
f.side.Lines[i]:Show()
|
||||
end
|
||||
end
|
||||
|
||||
f.StepTitles = db.StepTitles
|
||||
f.StepTitlesColor = db.StepTitlesColor
|
||||
f.StepTitlesColorSelected = db.StepTitlesColorSelected
|
||||
end
|
||||
|
||||
NextPage()
|
||||
end
|
||||
|
||||
if #self.Installs > 1 then
|
||||
f.pending:Show()
|
||||
E:Flash(f.pending, 0.53, true)
|
||||
else
|
||||
f.pending:Hide()
|
||||
E:StopFlash(f.pending)
|
||||
end
|
||||
end
|
||||
|
||||
function PI:Initialize()
|
||||
PI.Initialized = true
|
||||
PI:CreateStepComplete()
|
||||
PI:CreateFrame()
|
||||
end
|
||||
|
||||
E:RegisterModule(PI:GetName())
|
||||
138
Core/Smoothie.lua
Normal file
138
Core/Smoothie.lua
Normal file
@@ -0,0 +1,138 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
|
||||
-- Credit: ls- (lightspark)
|
||||
local abs, next, Lerp = abs, next, Lerp
|
||||
local tonumber, assert = tonumber, assert
|
||||
local activeObjects = {}
|
||||
local handledObjects = {}
|
||||
local TARGET_FPS = 60
|
||||
local AMOUNT = 0.33
|
||||
|
||||
local function clamp(v, min, max)
|
||||
min = min or 0
|
||||
max = max or 1
|
||||
|
||||
if v > max then
|
||||
return max
|
||||
elseif v < min then
|
||||
return min
|
||||
end
|
||||
|
||||
return v
|
||||
end
|
||||
|
||||
local function isCloseEnough(new, target, range)
|
||||
if range > 0 then
|
||||
return abs((new - target) / range) <= 0.001
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local frame = CreateFrame('Frame')
|
||||
local function onUpdate(_, elapsed)
|
||||
for object, target in next, activeObjects do
|
||||
local new = Lerp(object._value, target, clamp(AMOUNT * elapsed * TARGET_FPS))
|
||||
if isCloseEnough(new, target, object._max - object._min) then
|
||||
new = target
|
||||
activeObjects[object] = nil
|
||||
end
|
||||
|
||||
object:SetValue_(new)
|
||||
object._value = new
|
||||
end
|
||||
end
|
||||
|
||||
local function bar_SetSmoothedValue(self, value)
|
||||
value = tonumber(value)
|
||||
|
||||
assert(value, 'bar_SetSmoothedValue requires (value) to be a number.')
|
||||
|
||||
self._value = self:GetValue()
|
||||
activeObjects[self] = clamp(value, self._min, self._max)
|
||||
end
|
||||
|
||||
local function bar_SetSmoothedMinMaxValues(self, min, max)
|
||||
min, max = tonumber(min), tonumber(max)
|
||||
|
||||
assert(min and max, 'bar_SetSmoothedMinMaxValues requires (min and max) to be a number.')
|
||||
|
||||
self:SetMinMaxValues_(min, max)
|
||||
|
||||
if self._max and self._max ~= max then
|
||||
local ratio = 1
|
||||
if max ~= 0 and self._max and self._max ~= 0 then
|
||||
ratio = max / (self._max or max)
|
||||
end
|
||||
|
||||
local target = activeObjects[self]
|
||||
if target then
|
||||
activeObjects[self] = target * ratio
|
||||
end
|
||||
|
||||
local cur = self._value
|
||||
if cur then
|
||||
self:SetValue_(cur * ratio)
|
||||
self._value = cur * ratio
|
||||
end
|
||||
end
|
||||
|
||||
self._min = min
|
||||
self._max = max
|
||||
end
|
||||
|
||||
local function SmoothBar(bar)
|
||||
bar._min, bar._max = bar:GetMinMaxValues()
|
||||
bar._value = bar:GetValue()
|
||||
|
||||
if not bar.SetValue_ then
|
||||
bar.SetValue_ = bar.SetValue
|
||||
bar.SetValue = bar_SetSmoothedValue
|
||||
end
|
||||
if not bar.SetMinMaxValues_ then
|
||||
bar.SetMinMaxValues_ = bar.SetMinMaxValues
|
||||
bar.SetMinMaxValues = bar_SetSmoothedMinMaxValues
|
||||
end
|
||||
|
||||
if not frame:GetScript('OnUpdate') then
|
||||
frame:SetScript('OnUpdate', onUpdate)
|
||||
end
|
||||
|
||||
handledObjects[bar] = true
|
||||
end
|
||||
|
||||
local function DesmoothBar(bar)
|
||||
if activeObjects[bar] then
|
||||
bar:SetValue_(activeObjects[bar])
|
||||
activeObjects[bar] = nil
|
||||
end
|
||||
|
||||
if handledObjects[bar] then
|
||||
handledObjects[bar] = nil
|
||||
end
|
||||
|
||||
if bar.SetValue_ then
|
||||
bar.SetValue = bar.SetValue_
|
||||
bar.SetValue_ = nil
|
||||
end
|
||||
if bar.SetMinMaxValues_ then
|
||||
bar.SetMinMaxValues = bar.SetMinMaxValues_
|
||||
bar.SetMinMaxValues_ = nil
|
||||
end
|
||||
|
||||
if not next(handledObjects) then
|
||||
frame:SetScript('OnUpdate', nil)
|
||||
end
|
||||
end
|
||||
|
||||
function E:SetSmoothingAmount(amount)
|
||||
AMOUNT = clamp(amount, 0.2, 0.8)
|
||||
end
|
||||
|
||||
function E:SetSmoothing(bar, enable)
|
||||
if enable then
|
||||
SmoothBar(bar)
|
||||
else
|
||||
DesmoothBar(bar)
|
||||
end
|
||||
end
|
||||
1238
Core/StaticPopups.lua
Normal file
1238
Core/StaticPopups.lua
Normal file
File diff suppressed because it is too large
Load Diff
343
Core/StatusReport.lua
Normal file
343
Core/StatusReport.lua
Normal file
@@ -0,0 +1,343 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local LSM = E.Libs.LSM
|
||||
|
||||
local wipe, sort, unpack = wipe, sort, unpack
|
||||
local next, pairs, tinsert = next, pairs, tinsert
|
||||
|
||||
local CreateFrame = CreateFrame
|
||||
local GetAddOnInfo = GetAddOnInfo
|
||||
local GetCVarBool = GetCVarBool
|
||||
local GetLocale = GetLocale
|
||||
local GetNumAddOns = GetNumAddOns
|
||||
local GetRealZoneText = GetRealZoneText
|
||||
local GetSpecialization = GetSpecialization
|
||||
local GetSpecializationInfo = GetSpecializationInfo
|
||||
local UNKNOWN = UNKNOWN
|
||||
|
||||
function E:AreOtherAddOnsEnabled()
|
||||
local EP, addons, plugins = E.Libs.EP.plugins
|
||||
|
||||
for i = 1, GetNumAddOns() do
|
||||
local name = GetAddOnInfo(i)
|
||||
if name ~= 'ElvUI' and name ~= 'ElvUI_OptionsUI' and E:IsAddOnEnabled(name) then
|
||||
if EP[name] then plugins = true else addons = true end
|
||||
end
|
||||
end
|
||||
|
||||
return addons, plugins
|
||||
end
|
||||
|
||||
function E:GetDisplayMode()
|
||||
return GetCVarBool('gxMaximize') and 'Fullscreen' or 'Windowed'
|
||||
end
|
||||
|
||||
local EnglishClassName = {
|
||||
DEATHKNIGHT = 'Death Knight',
|
||||
DEMONHUNTER = 'Demon Hunter',
|
||||
DRUID = 'Druid',
|
||||
HUNTER = 'Hunter',
|
||||
MAGE = 'Mage',
|
||||
MONK = 'Monk',
|
||||
PALADIN = 'Paladin',
|
||||
PRIEST = 'Priest',
|
||||
ROGUE = 'Rogue',
|
||||
SHAMAN = 'Shaman',
|
||||
WARLOCK = 'Warlock',
|
||||
WARRIOR = 'Warrior',
|
||||
}
|
||||
|
||||
local EnglishSpecName = {
|
||||
[250] = 'Blood',
|
||||
[251] = 'Frost',
|
||||
[252] = 'Unholy',
|
||||
[102] = 'Balance',
|
||||
[103] = 'Feral',
|
||||
[104] = 'Guardian',
|
||||
[105] = 'Restoration',
|
||||
[253] = 'Beast Mastery',
|
||||
[254] = 'Marksmanship',
|
||||
[255] = 'Survival',
|
||||
[62] = 'Arcane',
|
||||
[63] = 'Fire',
|
||||
[64] = 'Frost',
|
||||
[268] = 'Brewmaster',
|
||||
[270] = 'Mistweaver',
|
||||
[269] = 'Windwalker',
|
||||
[65] = 'Holy',
|
||||
[66] = 'Protection',
|
||||
[70] = 'Retribution',
|
||||
[256] = 'Discipline',
|
||||
[257] = 'Holy',
|
||||
[258] = 'Shadow',
|
||||
[259] = 'Assasination',
|
||||
[260] = 'Combat',
|
||||
[261] = 'Sublety',
|
||||
[262] = 'Elemental',
|
||||
[263] = 'Enhancement',
|
||||
[264] = 'Restoration',
|
||||
[265] = 'Affliction',
|
||||
[266] = 'Demonoligy',
|
||||
[267] = 'Destruction',
|
||||
[71] = 'Arms',
|
||||
[72] = 'Fury',
|
||||
[73] = 'Protection',
|
||||
[577] = 'Havoc',
|
||||
[581] = 'Vengeance',
|
||||
}
|
||||
|
||||
local function GetSpecName()
|
||||
return EnglishSpecName[GetSpecializationInfo(GetSpecialization())] or UNKNOWN
|
||||
end
|
||||
|
||||
function E:CreateStatusContent(num, width, parent, anchorTo, content)
|
||||
if not content then content = CreateFrame('Frame', nil, parent) end
|
||||
content:SetSize(width, (num * 20) + ((num-1)*5)) --20 height and 5 spacing
|
||||
content:SetPoint('TOP', anchorTo, 'BOTTOM')
|
||||
|
||||
local font = LSM:Fetch('font', 'Expressway')
|
||||
for i = 1, num do
|
||||
if not content['Line'..i] then
|
||||
local line = CreateFrame('Frame', nil, content)
|
||||
line:SetSize(width, 20)
|
||||
|
||||
local text = line:CreateFontString(nil, 'ARTWORK')
|
||||
text:SetAllPoints()
|
||||
text:SetJustifyH('LEFT')
|
||||
text:SetJustifyV('MIDDLE')
|
||||
text:FontTemplate(font, 14, 'OUTLINE')
|
||||
line.Text = text
|
||||
|
||||
if i == 1 then
|
||||
line:SetPoint('TOP', content, 'TOP')
|
||||
else
|
||||
line:SetPoint('TOP', content['Line'..(i-1)], 'BOTTOM', 0, -5)
|
||||
end
|
||||
|
||||
content['Line'..i] = line
|
||||
end
|
||||
end
|
||||
|
||||
return content
|
||||
end
|
||||
|
||||
local function CloseClicked()
|
||||
if E.StatusReportToggled then
|
||||
E.StatusReportToggled = nil
|
||||
E:ToggleOptionsUI()
|
||||
end
|
||||
end
|
||||
|
||||
function E:CreateStatusSection(width, height, headerWidth, headerHeight, parent, anchor1, anchorTo, anchor2, yOffset)
|
||||
local parentWidth, parentHeight = parent:GetSize()
|
||||
|
||||
if width > parentWidth then parent:Width(width + 25) end
|
||||
if height then parent:SetHeight(parentHeight + height) end
|
||||
|
||||
local section = CreateFrame('Frame', nil, parent)
|
||||
section:SetSize(width, height or 0)
|
||||
section:SetPoint(anchor1, anchorTo, anchor2, 0, yOffset)
|
||||
|
||||
local header = CreateFrame('Frame', nil, section)
|
||||
header:SetSize(headerWidth or width, headerHeight)
|
||||
header:SetPoint('TOP', section)
|
||||
section.Header = header
|
||||
|
||||
local font = LSM:Fetch('font', 'Expressway')
|
||||
local text = section.Header:CreateFontString(nil, 'ARTWORK')
|
||||
text:SetPoint('TOP')
|
||||
text:SetPoint('BOTTOM')
|
||||
text:SetJustifyH('CENTER')
|
||||
text:SetJustifyV('MIDDLE')
|
||||
text:FontTemplate(font, 18, 'OUTLINE')
|
||||
section.Header.Text = text
|
||||
|
||||
local leftDivider = section.Header:CreateTexture(nil, 'ARTWORK')
|
||||
leftDivider:SetHeight(8)
|
||||
leftDivider:SetPoint('LEFT', section.Header, 'LEFT', 5, 0)
|
||||
leftDivider:SetPoint('RIGHT', section.Header.Text, 'LEFT', -5, 0)
|
||||
leftDivider:SetTexture([[Interface\Tooltips\UI-Tooltip-Border]])
|
||||
leftDivider:SetTexCoord(0.81, 0.94, 0.5, 1)
|
||||
section.Header.LeftDivider = leftDivider
|
||||
|
||||
local rightDivider = section.Header:CreateTexture(nil, 'ARTWORK')
|
||||
rightDivider:SetHeight(8)
|
||||
rightDivider:SetPoint('RIGHT', section.Header, 'RIGHT', -5, 0)
|
||||
rightDivider:SetPoint('LEFT', section.Header.Text, 'RIGHT', 5, 0)
|
||||
rightDivider:SetTexture([[Interface\Tooltips\UI-Tooltip-Border]])
|
||||
rightDivider:SetTexCoord(0.81, 0.94, 0.5, 1)
|
||||
section.Header.RightDivider = rightDivider
|
||||
|
||||
return section
|
||||
end
|
||||
|
||||
function E:CreateStatusFrame()
|
||||
--Main frame
|
||||
local StatusFrame = CreateFrame('Frame', 'ElvUIStatusReport', E.UIParent)
|
||||
StatusFrame:SetPoint('CENTER', E.UIParent, 'CENTER')
|
||||
StatusFrame:SetFrameStrata('HIGH')
|
||||
StatusFrame:CreateBackdrop('Transparent', nil, true)
|
||||
StatusFrame.backdrop:SetBackdropColor(0, 0, 0, 0.6)
|
||||
StatusFrame:SetMovable(true)
|
||||
StatusFrame:SetSize(0, 35)
|
||||
StatusFrame:Hide()
|
||||
|
||||
--Plugin frame
|
||||
local PluginFrame = CreateFrame('Frame', 'ElvUIStatusPlugins', StatusFrame)
|
||||
PluginFrame:SetPoint('TOPLEFT', StatusFrame, 'TOPRIGHT', E:Scale(E.Border * 2 + 1), 0)
|
||||
PluginFrame:SetFrameStrata('HIGH')
|
||||
PluginFrame:CreateBackdrop('Transparent', nil, true)
|
||||
PluginFrame.backdrop:SetBackdropColor(0, 0, 0, 0.6)
|
||||
PluginFrame:SetSize(0, 25)
|
||||
StatusFrame.PluginFrame = PluginFrame
|
||||
|
||||
--Close button and script to retoggle the options.
|
||||
StatusFrame:CreateCloseButton()
|
||||
StatusFrame.CloseButton:HookScript('OnClick', CloseClicked)
|
||||
|
||||
--Title logo (drag to move frame)
|
||||
local titleLogoFrame = CreateFrame('Frame', nil, StatusFrame, 'TitleDragAreaTemplate')
|
||||
titleLogoFrame:SetPoint('CENTER', StatusFrame, 'TOP')
|
||||
titleLogoFrame:SetSize(240, 80)
|
||||
StatusFrame.TitleLogoFrame = titleLogoFrame
|
||||
|
||||
local LogoTop = StatusFrame.TitleLogoFrame:CreateTexture(nil, 'ARTWORK')
|
||||
LogoTop:SetPoint('CENTER', titleLogoFrame, 'TOP', 0, -36)
|
||||
LogoTop:SetTexture(E.Media.Textures.LogoTopSmall)
|
||||
LogoTop:SetSize(128, 64)
|
||||
titleLogoFrame.LogoTop = LogoTop
|
||||
|
||||
local LogoBottom = StatusFrame.TitleLogoFrame:CreateTexture(nil, 'ARTWORK')
|
||||
LogoBottom:SetPoint('CENTER', titleLogoFrame, 'TOP', 0, -36)
|
||||
LogoBottom:SetTexture(E.Media.Textures.LogoBottomSmall)
|
||||
LogoBottom:SetSize(128, 64)
|
||||
titleLogoFrame.LogoBottom = LogoBottom
|
||||
|
||||
--Sections
|
||||
StatusFrame.Section1 = E:CreateStatusSection(300, 125, nil, 30, StatusFrame, 'TOP', StatusFrame, 'TOP', -30)
|
||||
StatusFrame.Section2 = E:CreateStatusSection(300, 150, nil, 30, StatusFrame, 'TOP', StatusFrame.Section1, 'BOTTOM', 0)
|
||||
StatusFrame.Section3 = E:CreateStatusSection(300, 185, nil, 30, StatusFrame, 'TOP', StatusFrame.Section2, 'BOTTOM', 0)
|
||||
--StatusFrame.Section4 = E:CreateStatusSection(300, 60, nil, 30, StatusFrame, 'TOP', StatusFrame.Section3, 'BOTTOM', 0)
|
||||
PluginFrame.SectionP = E:CreateStatusSection(280, nil, nil, 30, PluginFrame, 'TOP', PluginFrame, 'TOP', -10)
|
||||
|
||||
--Section content
|
||||
StatusFrame.Section1.Content = E:CreateStatusContent(4, 260, StatusFrame.Section1, StatusFrame.Section1.Header)
|
||||
StatusFrame.Section2.Content = E:CreateStatusContent(5, 260, StatusFrame.Section2, StatusFrame.Section2.Header)
|
||||
StatusFrame.Section3.Content = E:CreateStatusContent(6, 260, StatusFrame.Section3, StatusFrame.Section3.Header)
|
||||
--StatusFrame.Section4.Content = CreateFrame('Frame', nil, StatusFrame.Section4)
|
||||
--StatusFrame.Section4.Content:SetSize(240, 25)
|
||||
--StatusFrame.Section4.Content:SetPoint('TOP', StatusFrame.Section4.Header, 'BOTTOM', 0, 0)
|
||||
|
||||
--Content lines
|
||||
StatusFrame.Section1.Content.Line3.Text:SetFormattedText('Recommended Scale: |cff4beb2c%s|r', E:PixelBestSize())
|
||||
StatusFrame.Section1.Content.Line4.Text:SetFormattedText('UI Scale Is: |cff4beb2c%s|r', E.global.general.UIScale)
|
||||
StatusFrame.Section2.Content.Line1.Text:SetFormattedText('Version of WoW: |cff4beb2c%s (build %s)|r', E.wowpatch, E.wowbuild)
|
||||
StatusFrame.Section2.Content.Line2.Text:SetFormattedText('Client Language: |cff4beb2c%s|r', GetLocale())
|
||||
StatusFrame.Section2.Content.Line5.Text:SetFormattedText('Using Mac Client: |cff4beb2c%s|r', (E.isMacClient == true and 'Yes' or 'No'))
|
||||
StatusFrame.Section3.Content.Line1.Text:SetFormattedText('Faction: |cff4beb2c%s|r', E.myfaction)
|
||||
StatusFrame.Section3.Content.Line2.Text:SetFormattedText('Race: |cff4beb2c%s|r', E.myrace)
|
||||
StatusFrame.Section3.Content.Line3.Text:SetFormattedText('Class: |cff4beb2c%s|r', EnglishClassName[E.myclass])
|
||||
|
||||
--[[Export buttons
|
||||
StatusFrame.Section4.Content.Button1 = CreateFrame('Button', nil, StatusFrame.Section4.Content, 'UIPanelButtonTemplate')
|
||||
StatusFrame.Section4.Content.Button1:SetSize(100, 25)
|
||||
StatusFrame.Section4.Content.Button1:SetPoint('LEFT', StatusFrame.Section4.Content, 'LEFT')
|
||||
StatusFrame.Section4.Content.Button1:SetText('Forum')
|
||||
StatusFrame.Section4.Content.Button1:SetButtonState('DISABLED')
|
||||
StatusFrame.Section4.Content.Button2 = CreateFrame('Button', nil, StatusFrame.Section4.Content, 'UIPanelButtonTemplate')
|
||||
StatusFrame.Section4.Content.Button2:SetSize(100, 25)
|
||||
StatusFrame.Section4.Content.Button2:SetPoint('RIGHT', StatusFrame.Section4.Content, 'RIGHT')
|
||||
StatusFrame.Section4.Content.Button2:SetText('Ticket')
|
||||
StatusFrame.Section4.Content.Button2:SetButtonState('DISABLED')
|
||||
Skins:HandleButton(StatusFrame.Section4.Content.Button1, true)
|
||||
Skins:HandleButton(StatusFrame.Section4.Content.Button2, true)]]
|
||||
|
||||
return StatusFrame
|
||||
end
|
||||
|
||||
local function pluginSort(a, b)
|
||||
local A, B = a.title or a.name, b.title or b.name
|
||||
if A and B then
|
||||
return E:StripString(A) < E:StripString(B)
|
||||
end
|
||||
end
|
||||
|
||||
local pluginData = {}
|
||||
function E:UpdateStatusFrame()
|
||||
local StatusFrame = E.StatusFrame
|
||||
local PluginFrame = StatusFrame.PluginFrame
|
||||
|
||||
--Section headers
|
||||
local valueColor = E.media.hexvaluecolor
|
||||
StatusFrame.Section1.Header.Text:SetFormattedText('%sAddOn Info|r', valueColor)
|
||||
StatusFrame.Section2.Header.Text:SetFormattedText('%sWoW Info|r', valueColor)
|
||||
StatusFrame.Section3.Header.Text:SetFormattedText('%sCharacter Info|r', valueColor)
|
||||
--StatusFrame.Section4.Header.Text:SetFormattedText('%sExport To|r', valueColor)
|
||||
|
||||
local PluginSection = PluginFrame.SectionP
|
||||
PluginSection.Header.Text:SetFormattedText('%sPlugins|r', valueColor)
|
||||
|
||||
local verWarning = E.recievedOutOfDateMessage and 'ff3333' or E.shownUpdatedWhileRunningPopup and 'ff9933'
|
||||
StatusFrame.Section1.Content.Line1.Text:SetFormattedText('Version of ElvUI: |cff%s%s|r', verWarning or '33ff33', E.version)
|
||||
|
||||
local addons, plugins = E:AreOtherAddOnsEnabled()
|
||||
StatusFrame.Section1.Content.Line2.Text:SetFormattedText('Other AddOns Enabled: |cff%s|r', (not addons and plugins and 'ff9933Plugins') or (addons and 'ff3333Yes') or '33ff33No')
|
||||
|
||||
local scale = E.global.general.UIScale
|
||||
StatusFrame.Section1.Content.Line4.Text:SetFormattedText('UI Scale Is: |cff%s%s|r', scale == E:PixelBestSize() and '33ff33' or 'ff9933', scale)
|
||||
|
||||
if plugins then
|
||||
wipe(pluginData)
|
||||
for _, data in pairs(E.Libs.EP.plugins) do
|
||||
if data and not data.isLib then
|
||||
tinsert(pluginData, data)
|
||||
end
|
||||
end
|
||||
|
||||
if next(pluginData) then
|
||||
sort(pluginData, pluginSort)
|
||||
|
||||
local count = #pluginData
|
||||
local width = PluginSection:GetWidth()
|
||||
PluginSection.Content = E:CreateStatusContent(count, width, PluginSection, PluginSection.Header, PluginSection.Content)
|
||||
|
||||
for i=1, count do
|
||||
local data = pluginData[i]
|
||||
local color = data.old and 'ff3333' or '33ff33'
|
||||
PluginSection.Content['Line'..i].Text:SetFormattedText('%s |cff888888v|r|cff%s%s|r', data.title or data.name, color, data.version)
|
||||
end
|
||||
|
||||
PluginFrame.SectionP:SetHeight(count * 20)
|
||||
PluginFrame:SetHeight(PluginSection.Content:GetHeight() + 50)
|
||||
PluginFrame:Show()
|
||||
else
|
||||
PluginFrame:Hide()
|
||||
end
|
||||
else
|
||||
PluginFrame:Hide()
|
||||
end
|
||||
|
||||
local Section2 = StatusFrame.Section2
|
||||
Section2.Content.Line3.Text:SetFormattedText('Display Mode: |cff4beb2c%s|r', E:GetDisplayMode())
|
||||
Section2.Content.Line4.Text:SetFormattedText('Resolution: |cff4beb2c%s|r', E.resolution)
|
||||
|
||||
local Section3 = StatusFrame.Section3
|
||||
Section3.Content.Line4.Text:SetFormattedText('Specialization: |cff4beb2c%s|r', GetSpecName())
|
||||
Section3.Content.Line5.Text:SetFormattedText('Level: |cff4beb2c%s|r', E.mylevel)
|
||||
Section3.Content.Line6.Text:SetFormattedText('Zone: |cff4beb2c%s|r', GetRealZoneText() or UNKNOWN)
|
||||
|
||||
StatusFrame.TitleLogoFrame.LogoTop:SetVertexColor(unpack(E.media.rgbvaluecolor))
|
||||
end
|
||||
|
||||
function E:ShowStatusReport()
|
||||
if not E.StatusFrame then
|
||||
E.StatusFrame = E:CreateStatusFrame()
|
||||
end
|
||||
|
||||
if not E.StatusFrame:IsShown() then
|
||||
E:UpdateStatusFrame()
|
||||
E.StatusFrame:Raise() --Set framelevel above everything else
|
||||
E.StatusFrame:Show()
|
||||
else
|
||||
E.StatusFrame:Hide()
|
||||
end
|
||||
end
|
||||
1373
Core/Tags.lua
Normal file
1373
Core/Tags.lua
Normal file
File diff suppressed because it is too large
Load Diff
457
Core/Toolkit.lua
Normal file
457
Core/Toolkit.lua
Normal file
@@ -0,0 +1,457 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local UF = E:GetModule('UnitFrames')
|
||||
local NP = E:GetModule('NamePlates')
|
||||
|
||||
local _G = _G
|
||||
local pairs, pcall = pairs, pcall
|
||||
local unpack, type, select, getmetatable = unpack, type, select, getmetatable
|
||||
local EnumerateFrames = EnumerateFrames
|
||||
local hooksecurefunc = hooksecurefunc
|
||||
local CreateFrame = CreateFrame
|
||||
|
||||
local backdropr, backdropg, backdropb, backdropa = 0, 0, 0, 1
|
||||
local borderr, borderg, borderb, bordera = 0, 0, 0, 1
|
||||
|
||||
-- 8.2 restricted frame check
|
||||
function E:SetPointsRestricted(frame)
|
||||
if frame and not pcall(frame.GetPoint, frame) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
function E:SafeGetPoint(frame)
|
||||
if frame and frame.GetPoint and not E:SetPointsRestricted(frame) then
|
||||
return frame:GetPoint()
|
||||
end
|
||||
end
|
||||
|
||||
local function WatchPixelSnap(frame, snap)
|
||||
if (frame and not frame:IsForbidden()) and frame.PixelSnapDisabled and snap then
|
||||
frame.PixelSnapDisabled = nil
|
||||
end
|
||||
end
|
||||
|
||||
local function DisablePixelSnap(frame)
|
||||
if (frame and not frame:IsForbidden()) and not frame.PixelSnapDisabled then
|
||||
if frame.SetSnapToPixelGrid then
|
||||
frame:SetSnapToPixelGrid(false)
|
||||
frame:SetTexelSnappingBias(0)
|
||||
elseif frame.GetStatusBarTexture then
|
||||
local texture = frame:GetStatusBarTexture()
|
||||
if texture and texture.SetSnapToPixelGrid then
|
||||
texture:SetSnapToPixelGrid(false)
|
||||
texture:SetTexelSnappingBias(0)
|
||||
end
|
||||
end
|
||||
|
||||
frame.PixelSnapDisabled = true
|
||||
end
|
||||
end
|
||||
|
||||
local function GetTemplate(template, isUnitFrameElement)
|
||||
backdropa, bordera = 1, 1
|
||||
|
||||
if template == 'ClassColor' then
|
||||
local color = E:ClassColor(E.myclass)
|
||||
borderr, borderg, borderb = color.r, color.g, color.b
|
||||
backdropr, backdropg, backdropb = unpack(E.media.backdropcolor)
|
||||
elseif template == 'Transparent' then
|
||||
borderr, borderg, borderb = unpack(isUnitFrameElement and E.media.unitframeBorderColor or E.media.bordercolor)
|
||||
backdropr, backdropg, backdropb, backdropa = unpack(E.media.backdropfadecolor)
|
||||
else
|
||||
borderr, borderg, borderb = unpack(isUnitFrameElement and E.media.unitframeBorderColor or E.media.bordercolor)
|
||||
backdropr, backdropg, backdropb = unpack(E.media.backdropcolor)
|
||||
end
|
||||
end
|
||||
|
||||
local function Size(frame, width, height, ...)
|
||||
local w = E:Scale(width)
|
||||
frame:SetSize(w, (height and E:Scale(height)) or w, ...)
|
||||
end
|
||||
|
||||
local function Width(frame, width, ...)
|
||||
frame:SetWidth(E:Scale(width), ...)
|
||||
end
|
||||
|
||||
local function Height(frame, height, ...)
|
||||
frame:SetHeight(E:Scale(height), ...)
|
||||
end
|
||||
|
||||
local function Point(obj, arg1, arg2, arg3, arg4, arg5, ...)
|
||||
if not arg2 then arg2 = obj:GetParent() end
|
||||
|
||||
if type(arg2)=='number' then arg2 = E:Scale(arg2) end
|
||||
if type(arg3)=='number' then arg3 = E:Scale(arg3) end
|
||||
if type(arg4)=='number' then arg4 = E:Scale(arg4) end
|
||||
if type(arg5)=='number' then arg5 = E:Scale(arg5) end
|
||||
|
||||
obj:SetPoint(arg1, arg2, arg3, arg4, arg5, ...)
|
||||
end
|
||||
|
||||
local function SetOutside(obj, anchor, xOffset, yOffset, anchor2, noScale)
|
||||
if not anchor then anchor = obj:GetParent() end
|
||||
|
||||
if not xOffset then xOffset = E.Border end
|
||||
if not yOffset then yOffset = E.Border end
|
||||
local x = (noScale and xOffset) or E:Scale(xOffset)
|
||||
local y = (noScale and yOffset) or E:Scale(yOffset)
|
||||
|
||||
if E:SetPointsRestricted(obj) or obj:GetPoint() then
|
||||
obj:ClearAllPoints()
|
||||
end
|
||||
|
||||
DisablePixelSnap(obj)
|
||||
obj:SetPoint('TOPLEFT', anchor, 'TOPLEFT', -x, y)
|
||||
obj:SetPoint('BOTTOMRIGHT', anchor2 or anchor, 'BOTTOMRIGHT', x, -y)
|
||||
end
|
||||
|
||||
local function SetInside(obj, anchor, xOffset, yOffset, anchor2, noScale)
|
||||
if not anchor then anchor = obj:GetParent() end
|
||||
|
||||
if not xOffset then xOffset = E.Border end
|
||||
if not yOffset then yOffset = E.Border end
|
||||
local x = (noScale and xOffset) or E:Scale(xOffset)
|
||||
local y = (noScale and yOffset) or E:Scale(yOffset)
|
||||
|
||||
if E:SetPointsRestricted(obj) or obj:GetPoint() then
|
||||
obj:ClearAllPoints()
|
||||
end
|
||||
|
||||
DisablePixelSnap(obj)
|
||||
obj:SetPoint('TOPLEFT', anchor, 'TOPLEFT', x, -y)
|
||||
obj:SetPoint('BOTTOMRIGHT', anchor2 or anchor, 'BOTTOMRIGHT', -x, y)
|
||||
end
|
||||
|
||||
local function SetTemplate(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement)
|
||||
GetTemplate(template, isUnitFrameElement)
|
||||
|
||||
frame.template = template or 'Default'
|
||||
frame.glossTex = glossTex
|
||||
frame.ignoreUpdates = ignoreUpdates
|
||||
frame.forcePixelMode = forcePixelMode
|
||||
frame.isUnitFrameElement = isUnitFrameElement
|
||||
frame.isNamePlateElement = isNamePlateElement
|
||||
|
||||
if template == 'NoBackdrop' then
|
||||
frame:SetBackdrop()
|
||||
else
|
||||
frame:SetBackdrop({
|
||||
edgeFile = E.media.blankTex,
|
||||
bgFile = glossTex and (type(glossTex) == 'string' and glossTex or E.media.glossTex) or E.media.blankTex,
|
||||
edgeSize = E:Scale(E.twoPixelsPlease and 2 or 1)
|
||||
})
|
||||
|
||||
if frame.callbackBackdropColor then
|
||||
frame:callbackBackdropColor()
|
||||
else
|
||||
frame:SetBackdropColor(backdropr, backdropg, backdropb, frame.customBackdropAlpha or (template == 'Transparent' and backdropa) or 1)
|
||||
end
|
||||
|
||||
local notPixelMode = not isUnitFrameElement and not isNamePlateElement and not E.PixelMode
|
||||
local notThinBorders = (isUnitFrameElement and not UF.thinBorders) or (isNamePlateElement and not NP.thinBorders)
|
||||
if (notPixelMode or notThinBorders) and not forcePixelMode then
|
||||
local backdrop = {
|
||||
edgeFile = E.media.blankTex,
|
||||
edgeSize = E:Scale(1)
|
||||
}
|
||||
|
||||
if not frame.iborder then
|
||||
local border = CreateFrame('Frame', nil, frame, 'BackdropTemplate')
|
||||
border:SetBackdrop(backdrop)
|
||||
border:SetBackdropBorderColor(0, 0, 0, 1)
|
||||
border:SetInside(frame, 1, 1)
|
||||
frame.iborder = border
|
||||
end
|
||||
|
||||
if not frame.oborder then
|
||||
local border = CreateFrame('Frame', nil, frame, 'BackdropTemplate')
|
||||
border:SetBackdrop(backdrop)
|
||||
border:SetBackdropBorderColor(0, 0, 0, 1)
|
||||
border:SetOutside(frame, 1, 1)
|
||||
frame.oborder = border
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if frame.forcedBorderColors then
|
||||
borderr, borderg, borderb, bordera = unpack(frame.forcedBorderColors)
|
||||
end
|
||||
|
||||
frame:SetBackdropBorderColor(borderr, borderg, borderb, bordera)
|
||||
|
||||
if not frame.ignoreUpdates then
|
||||
if frame.isUnitFrameElement then
|
||||
E.unitFrameElements[frame] = true
|
||||
else
|
||||
E.frames[frame] = true
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateBackdrop(frame, template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement)
|
||||
local parent = (frame.IsObjectType and frame:IsObjectType('Texture') and frame:GetParent()) or frame
|
||||
local backdrop = frame.backdrop or CreateFrame('Frame', nil, parent, 'BackdropTemplate')
|
||||
if not frame.backdrop then frame.backdrop = backdrop end
|
||||
|
||||
if forcePixelMode then
|
||||
backdrop:SetOutside(frame, E.twoPixelsPlease and 2 or 1, E.twoPixelsPlease and 2 or 1)
|
||||
else
|
||||
local border = (isUnitFrameElement and UF.BORDER) or (isNamePlateElement and NP.BORDER)
|
||||
backdrop:SetOutside(frame, border, border)
|
||||
end
|
||||
|
||||
backdrop:SetTemplate(template, glossTex, ignoreUpdates, forcePixelMode, isUnitFrameElement, isNamePlateElement)
|
||||
|
||||
local frameLevel = parent.GetFrameLevel and parent:GetFrameLevel()
|
||||
local frameLevelMinusOne = frameLevel and (frameLevel - 1)
|
||||
if frameLevelMinusOne and (frameLevelMinusOne >= 0) then
|
||||
backdrop:SetFrameLevel(frameLevelMinusOne)
|
||||
else
|
||||
backdrop:SetFrameLevel(0)
|
||||
end
|
||||
end
|
||||
|
||||
local function CreateShadow(frame, size, pass)
|
||||
if not pass and frame.shadow then return end
|
||||
if not size then size = 3 end
|
||||
|
||||
backdropr, backdropg, backdropb, borderr, borderg, borderb = 0, 0, 0, 0, 0, 0
|
||||
|
||||
local offset = (E.PixelMode and size) or (size + 1)
|
||||
local shadow = CreateFrame('Frame', nil, frame, 'BackdropTemplate')
|
||||
shadow:SetFrameLevel(1)
|
||||
shadow:SetFrameStrata(frame:GetFrameStrata())
|
||||
shadow:SetOutside(frame, offset, offset, nil, true)
|
||||
shadow:SetBackdrop({edgeFile = E.Media.Textures.GlowTex, edgeSize = size})
|
||||
shadow:SetBackdropColor(backdropr, backdropg, backdropb, 0)
|
||||
shadow:SetBackdropBorderColor(borderr, borderg, borderb, 0.9)
|
||||
|
||||
if pass then
|
||||
return shadow
|
||||
else
|
||||
frame.shadow = shadow
|
||||
end
|
||||
end
|
||||
|
||||
local function Kill(object)
|
||||
if object.UnregisterAllEvents then
|
||||
object:UnregisterAllEvents()
|
||||
object:SetParent(E.HiddenFrame)
|
||||
else
|
||||
object.Show = object.Hide
|
||||
end
|
||||
|
||||
object:Hide()
|
||||
end
|
||||
|
||||
local StripTexturesBlizzFrames = {
|
||||
'Inset',
|
||||
'inset',
|
||||
'InsetFrame',
|
||||
'LeftInset',
|
||||
'RightInset',
|
||||
'NineSlice',
|
||||
'BG',
|
||||
'border',
|
||||
'Border',
|
||||
'BorderFrame',
|
||||
'bottomInset',
|
||||
'BottomInset',
|
||||
'bgLeft',
|
||||
'bgRight',
|
||||
'FilligreeOverlay',
|
||||
'PortraitOverlay',
|
||||
'ArtOverlayFrame',
|
||||
'Portrait',
|
||||
'portrait',
|
||||
'ScrollFrameBorder',
|
||||
}
|
||||
|
||||
local STRIP_TEX = 'Texture'
|
||||
local STRIP_FONT = 'FontString'
|
||||
local function StripRegion(which, object, kill, alpha)
|
||||
if kill then
|
||||
object:Kill()
|
||||
elseif which == STRIP_TEX then
|
||||
object:SetTexture('')
|
||||
object:SetAtlas('')
|
||||
elseif which == STRIP_FONT then
|
||||
object:SetText('')
|
||||
end
|
||||
|
||||
if alpha then
|
||||
object:SetAlpha(0)
|
||||
end
|
||||
end
|
||||
|
||||
local function StripType(which, object, kill, alpha)
|
||||
if object:IsObjectType(which) then
|
||||
StripRegion(which, object, kill, alpha)
|
||||
else
|
||||
if which == STRIP_TEX then
|
||||
local FrameName = object.GetName and object:GetName()
|
||||
for _, Blizzard in pairs(StripTexturesBlizzFrames) do
|
||||
local BlizzFrame = object[Blizzard] or (FrameName and _G[FrameName..Blizzard])
|
||||
if BlizzFrame and BlizzFrame.StripTextures then
|
||||
BlizzFrame:StripTextures(kill, alpha)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if object.GetNumRegions then
|
||||
for i = 1, object:GetNumRegions() do
|
||||
local region = select(i, object:GetRegions())
|
||||
if region and region.IsObjectType and region:IsObjectType(which) then
|
||||
StripRegion(which, region, kill, alpha)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function StripTextures(object, kill, alpha)
|
||||
StripType(STRIP_TEX, object, kill, alpha)
|
||||
end
|
||||
|
||||
local function StripTexts(object, kill, alpha)
|
||||
StripType(STRIP_FONT, object, kill, alpha)
|
||||
end
|
||||
|
||||
local function FontTemplate(fs, font, size, style, skip)
|
||||
if not skip then -- ignore updates from UpdateFontTemplates
|
||||
fs.font, fs.fontSize, fs.fontStyle = font, size, style
|
||||
end
|
||||
|
||||
fs:SetFont(font or E.media.normFont, size or E.db.general.fontSize, style or E.db.general.fontStyle)
|
||||
|
||||
if style == 'NONE' then
|
||||
fs:SetShadowOffset(1, -0.5)
|
||||
fs:SetShadowColor(0, 0, 0, 1)
|
||||
else
|
||||
fs:SetShadowOffset(0, 0)
|
||||
fs:SetShadowColor(0, 0, 0, 0)
|
||||
end
|
||||
|
||||
E.texts[fs] = true
|
||||
end
|
||||
|
||||
local function StyleButton(button, noHover, noPushed, noChecked)
|
||||
if button.SetHighlightTexture and not button.hover and not noHover then
|
||||
local hover = button:CreateTexture()
|
||||
hover:SetInside()
|
||||
hover:SetBlendMode('ADD')
|
||||
hover:SetColorTexture(1, 1, 1, 0.3)
|
||||
button:SetHighlightTexture(hover)
|
||||
button.hover = hover
|
||||
end
|
||||
|
||||
if button.SetPushedTexture and not button.pushed and not noPushed then
|
||||
local pushed = button:CreateTexture()
|
||||
pushed:SetInside()
|
||||
pushed:SetBlendMode('ADD')
|
||||
pushed:SetColorTexture(0.9, 0.8, 0.1, 0.3)
|
||||
button:SetPushedTexture(pushed)
|
||||
button.pushed = pushed
|
||||
end
|
||||
|
||||
if button.SetCheckedTexture and not button.checked and not noChecked then
|
||||
local checked = button:CreateTexture()
|
||||
checked:SetInside()
|
||||
checked:SetBlendMode('ADD')
|
||||
checked:SetColorTexture(1, 1, 1, 0.3)
|
||||
button:SetCheckedTexture(checked)
|
||||
button.checked = checked
|
||||
end
|
||||
|
||||
local name = button.GetName and button:GetName()
|
||||
local cooldown = name and _G[name..'Cooldown']
|
||||
if cooldown then
|
||||
cooldown:ClearAllPoints()
|
||||
cooldown:SetInside()
|
||||
cooldown:SetDrawEdge(false)
|
||||
cooldown:SetSwipeColor(0, 0, 0, 1)
|
||||
end
|
||||
end
|
||||
|
||||
local CreateCloseButton
|
||||
do
|
||||
local CloseButtonOnClick = function(btn) btn:GetParent():Hide() end
|
||||
local CloseButtonOnEnter = function(btn) if btn.Texture then btn.Texture:SetVertexColor(unpack(E.media.rgbvaluecolor)) end end
|
||||
local CloseButtonOnLeave = function(btn) if btn.Texture then btn.Texture:SetVertexColor(1, 1, 1) end end
|
||||
CreateCloseButton = function(frame, size, offset, texture, backdrop)
|
||||
if frame.CloseButton then return end
|
||||
|
||||
local CloseButton = CreateFrame('Button', nil, frame)
|
||||
CloseButton:Size(size or 16)
|
||||
CloseButton:Point('TOPRIGHT', offset or -6, offset or -6)
|
||||
if backdrop then
|
||||
CloseButton:CreateBackdrop(nil, true)
|
||||
end
|
||||
|
||||
CloseButton.Texture = CloseButton:CreateTexture(nil, 'OVERLAY')
|
||||
CloseButton.Texture:SetAllPoints()
|
||||
CloseButton.Texture:SetTexture(texture or E.Media.Textures.Close)
|
||||
|
||||
CloseButton:SetScript('OnClick', CloseButtonOnClick)
|
||||
CloseButton:SetScript('OnEnter', CloseButtonOnEnter)
|
||||
CloseButton:SetScript('OnLeave', CloseButtonOnLeave)
|
||||
|
||||
frame.CloseButton = CloseButton
|
||||
end
|
||||
end
|
||||
|
||||
local function GetNamedChild(frame, childName, index)
|
||||
local name = frame and frame.GetName and frame:GetName()
|
||||
if not name or not childName then return nil end
|
||||
return _G[name..childName..(index or '')]
|
||||
end
|
||||
|
||||
local function addapi(object)
|
||||
local mt = getmetatable(object).__index
|
||||
if not object.Size then mt.Size = Size end
|
||||
if not object.Point then mt.Point = Point end
|
||||
if not object.SetOutside then mt.SetOutside = SetOutside end
|
||||
if not object.SetInside then mt.SetInside = SetInside end
|
||||
if not object.SetTemplate then mt.SetTemplate = SetTemplate end
|
||||
if not object.CreateBackdrop then mt.CreateBackdrop = CreateBackdrop end
|
||||
if not object.CreateShadow then mt.CreateShadow = CreateShadow end
|
||||
if not object.Kill then mt.Kill = Kill end
|
||||
if not object.Width then mt.Width = Width end
|
||||
if not object.Height then mt.Height = Height end
|
||||
if not object.FontTemplate then mt.FontTemplate = FontTemplate end
|
||||
if not object.StripTextures then mt.StripTextures = StripTextures end
|
||||
if not object.StripTexts then mt.StripTexts = StripTexts end
|
||||
if not object.StyleButton then mt.StyleButton = StyleButton end
|
||||
if not object.CreateCloseButton then mt.CreateCloseButton = CreateCloseButton end
|
||||
if not object.GetNamedChild then mt.GetNamedChild = GetNamedChild end
|
||||
if not object.DisabledPixelSnap and (mt.SetSnapToPixelGrid or mt.SetStatusBarTexture or mt.SetColorTexture or mt.SetVertexColor or mt.CreateTexture or mt.SetTexCoord or mt.SetTexture) then
|
||||
if mt.SetSnapToPixelGrid then hooksecurefunc(mt, 'SetSnapToPixelGrid', WatchPixelSnap) end
|
||||
if mt.SetStatusBarTexture then hooksecurefunc(mt, 'SetStatusBarTexture', DisablePixelSnap) end
|
||||
if mt.SetColorTexture then hooksecurefunc(mt, 'SetColorTexture', DisablePixelSnap) end
|
||||
if mt.SetVertexColor then hooksecurefunc(mt, 'SetVertexColor', DisablePixelSnap) end
|
||||
if mt.CreateTexture then hooksecurefunc(mt, 'CreateTexture', DisablePixelSnap) end
|
||||
if mt.SetTexCoord then hooksecurefunc(mt, 'SetTexCoord', DisablePixelSnap) end
|
||||
if mt.SetTexture then hooksecurefunc(mt, 'SetTexture', DisablePixelSnap) end
|
||||
mt.DisabledPixelSnap = true
|
||||
end
|
||||
end
|
||||
|
||||
local handled = {Frame = true}
|
||||
local object = CreateFrame('Frame')
|
||||
addapi(object)
|
||||
addapi(object:CreateTexture())
|
||||
addapi(object:CreateFontString())
|
||||
addapi(object:CreateMaskTexture())
|
||||
|
||||
object = EnumerateFrames()
|
||||
while object do
|
||||
if not object:IsForbidden() and not handled[object:GetObjectType()] then
|
||||
addapi(object)
|
||||
handled[object:GetObjectType()] = true
|
||||
end
|
||||
|
||||
object = EnumerateFrames(object)
|
||||
end
|
||||
|
||||
addapi(_G.GameFontNormal) --Add API to `CreateFont` objects without actually creating one
|
||||
addapi(CreateFrame('ScrollFrame')) --Hacky fix for issue on 7.1 PTR where scroll frames no longer seem to inherit the methods from the 'Frame' widget
|
||||
120
Core/Tutorials.lua
Normal file
120
Core/Tutorials.lua
Normal file
@@ -0,0 +1,120 @@
|
||||
local E, L, V, P, G = unpack(select(2, ...)); --Import: Engine, Locales, PrivateDB, ProfileDB, GlobalDB
|
||||
local Skins = E:GetModule('Skins')
|
||||
|
||||
local _G = _G
|
||||
local CreateFrame = CreateFrame
|
||||
local DISABLE = DISABLE
|
||||
local HIDE = HIDE
|
||||
-- GLOBALS: ElvUITutorialWindow
|
||||
|
||||
E.TutorialList = {
|
||||
L["Need help? Join our Discord: https://discord.gg/xFWcfgE"],
|
||||
L["You can enter the keybind mode by typing /kb"],
|
||||
L["Don't forget to backup your WTF folder, all your profiles and settings are in there."],
|
||||
L["If you are experiencing issues with ElvUI try disabling all your addons except ElvUI first."],
|
||||
L["You can access the copy chat and chat menu functions by left/right clicking on the icon in the top right corner of the chat panel."],
|
||||
L["You can see someones average item level inside the tooltip by holding shift and mousing over them."],
|
||||
L["To setup chat colors, chat channels and chat font size, right-click the chat tab name."],
|
||||
L["ElvUI has a dual spec feature which allows you to load different profiles based on your current spec on the fly. You can enable it in the profiles tab."],
|
||||
L["A raid marker feature is available by pressing Escape -> Keybinds. Scroll to the bottom -> ElvUI -> Raid Marker."],
|
||||
L["You can access the microbar by using middle mouse button on the minimap. You can also enable the MicroBar in the actionbar settings."],
|
||||
L["If you accidentally removed a default chat tab you can always re-run the chat part of the ElvUI installer."],
|
||||
L["You can quickly change your displayed DataTexts by mousing over them while holding ALT."],
|
||||
L["To quickly move around certain elements of the UI, type /moveui"],
|
||||
L["From time to time you should compare your ElvUI version against the most recent version on our website or the Tukui client."],
|
||||
L["To list all available ElvUI commands, type in chat /ehelp"]
|
||||
}
|
||||
|
||||
function E:SetNextTutorial()
|
||||
self.db.currentTutorial = self.db.currentTutorial or 0
|
||||
self.db.currentTutorial = self.db.currentTutorial + 1
|
||||
|
||||
if self.db.currentTutorial > #E.TutorialList then
|
||||
self.db.currentTutorial = 1
|
||||
end
|
||||
|
||||
ElvUITutorialWindow.desc:SetText(E.TutorialList[self.db.currentTutorial])
|
||||
end
|
||||
|
||||
function E:SetPrevTutorial()
|
||||
self.db.currentTutorial = self.db.currentTutorial or 0
|
||||
self.db.currentTutorial = self.db.currentTutorial - 1
|
||||
|
||||
if self.db.currentTutorial <= 0 then
|
||||
self.db.currentTutorial = #E.TutorialList
|
||||
end
|
||||
|
||||
ElvUITutorialWindow.desc:SetText(E.TutorialList[self.db.currentTutorial])
|
||||
end
|
||||
|
||||
function E:SpawnTutorialFrame()
|
||||
local f = CreateFrame('Frame', 'ElvUITutorialWindow', E.UIParent, 'BackdropTemplate')
|
||||
f:SetFrameStrata('DIALOG')
|
||||
f:SetToplevel(true)
|
||||
f:SetClampedToScreen(true)
|
||||
f:Width(360)
|
||||
f:Height(110)
|
||||
f:SetTemplate('Transparent')
|
||||
f:Hide()
|
||||
|
||||
local header = CreateFrame('Button', nil, f, 'BackdropTemplate')
|
||||
header:SetTemplate(nil, true)
|
||||
header:Width(120); header:Height(25)
|
||||
header:Point('CENTER', f, 'TOP')
|
||||
header:SetFrameLevel(header:GetFrameLevel() + 2)
|
||||
|
||||
local title = header:CreateFontString(nil, 'OVERLAY')
|
||||
title:FontTemplate()
|
||||
title:Point('CENTER', header, 'CENTER')
|
||||
title:SetText('ElvUI')
|
||||
|
||||
local desc = f:CreateFontString(nil, 'ARTWORK')
|
||||
desc:SetFontObject('GameFontHighlight')
|
||||
desc:SetJustifyV('TOP')
|
||||
desc:SetJustifyH('LEFT')
|
||||
desc:Point('TOPLEFT', 18, -32)
|
||||
desc:Point('BOTTOMRIGHT', -18, 30)
|
||||
f.desc = desc
|
||||
|
||||
f.disableButton = CreateFrame('CheckButton', f:GetName()..'DisableButton', f, 'OptionsCheckButtonTemplate, BackdropTemplate')
|
||||
_G[f.disableButton:GetName() .. 'Text']:SetText(DISABLE)
|
||||
f.disableButton:Point('BOTTOMLEFT')
|
||||
Skins:HandleCheckBox(f.disableButton)
|
||||
f.disableButton:SetScript('OnShow', function(btn) btn:SetChecked(E.db.hideTutorial) end)
|
||||
|
||||
f.disableButton:SetScript('OnClick', function(btn) E.db.hideTutorial = btn:GetChecked() end)
|
||||
|
||||
f.hideButton = CreateFrame('Button', f:GetName()..'HideButton', f, 'OptionsButtonTemplate, BackdropTemplate')
|
||||
f.hideButton:Point('BOTTOMRIGHT', -5, 5)
|
||||
Skins:HandleButton(f.hideButton)
|
||||
_G[f.hideButton:GetName() .. 'Text']:SetText(HIDE)
|
||||
f.hideButton:SetScript('OnClick', function(btn) E:StaticPopupSpecial_Hide(btn:GetParent()) end)
|
||||
|
||||
f.nextButton = CreateFrame('Button', f:GetName()..'NextButton', f, 'OptionsButtonTemplate, BackdropTemplate')
|
||||
f.nextButton:Point('RIGHT', f.hideButton, 'LEFT', -4, 0)
|
||||
f.nextButton:Width(20)
|
||||
Skins:HandleButton(f.nextButton)
|
||||
_G[f.nextButton:GetName() .. 'Text']:SetText('>')
|
||||
f.nextButton:SetScript('OnClick', function() E:SetNextTutorial() end)
|
||||
|
||||
f.prevButton = CreateFrame('Button', f:GetName()..'PrevButton', f, 'OptionsButtonTemplate, BackdropTemplate')
|
||||
f.prevButton:Point('RIGHT', f.nextButton, 'LEFT', -4, 0)
|
||||
f.prevButton:Width(20)
|
||||
Skins:HandleButton(f.prevButton)
|
||||
_G[f.prevButton:GetName() .. 'Text']:SetText('<')
|
||||
f.prevButton:SetScript('OnClick', function() E:SetPrevTutorial() end)
|
||||
|
||||
return f
|
||||
end
|
||||
|
||||
function E:Tutorials(forceShow)
|
||||
if (not forceShow and self.db.hideTutorial) or (not forceShow and not self.private.install_complete) then return end
|
||||
local f = ElvUITutorialWindow
|
||||
if not f then
|
||||
f = E:SpawnTutorialFrame()
|
||||
end
|
||||
|
||||
E:StaticPopupSpecial_Show(f)
|
||||
|
||||
self:SetNextTutorial()
|
||||
end
|
||||
Reference in New Issue
Block a user