initial commit

This commit is contained in:
Gitea
2020-11-13 14:27:50 -05:00
commit e2015fd9bb
581 changed files with 101308 additions and 0 deletions

634
Core/API.lua Normal file
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

347
Core/Cooldowns.lua Normal file
View 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

File diff suppressed because it is too large Load Diff

628
Core/Distributor.lua Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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

File diff suppressed because it is too large Load Diff

343
Core/StatusReport.lua Normal file
View 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

File diff suppressed because it is too large Load Diff

457
Core/Toolkit.lua Normal file
View 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
View 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