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

6188 lines
199 KiB

5 months ago
local dversion = 612
local major, minor = "DetailsFramework-1.0", dversion
3 years ago
local DF, oldminor = LibStub:NewLibrary(major, minor)
if (not DF) then
DetailsFrameworkCanLoad = false
return
end
3 years ago
_G["DetailsFramework"] = DF
---@cast DF detailsframework
local detailsFramework = DF
5 months ago
--store functions to call when the PLAYER_LOGIN event is triggered
detailsFramework.OnLoginSchedules = {}
local dfFrame = CreateFrame("frame")
dfFrame:RegisterEvent("PLAYER_LOGIN")
dfFrame:SetScript("OnEvent", function(self, event, ...)
if (event == "PLAYER_LOGIN") then
C_Timer.After(0, function()
for _, func in ipairs(detailsFramework.OnLoginSchedules) do
func()
end
end)
end
end)
DetailsFrameworkCanLoad = true
3 years ago
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local _
3 years ago
local type = type
local unpack = unpack
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local UnitPlayerControlled = UnitPlayerControlled
local UnitIsTapDenied = UnitIsTapDenied
-- TWW compatibility:
local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end
local GetSpellBookItemName = GetSpellBookItemName or C_SpellBook.GetSpellBookItemName
local GetNumSpellTabs = GetNumSpellTabs or C_SpellBook.GetNumSpellBookSkillLines
local GetSpellTabInfo = GetSpellTabInfo or function(tabLine) local skillLine = C_SpellBook.GetSpellBookSkillLineInfo(tabLine) if skillLine then return skillLine.name, skillLine.iconID, skillLine.itemIndexOffset, skillLine.numSpellBookItems, skillLine.isGuild, skillLine.offSpecID end end
local SpellBookItemTypeMap = Enum.SpellBookItemType and {[Enum.SpellBookItemType.Spell] = "SPELL", [Enum.SpellBookItemType.None] = "NONE", [Enum.SpellBookItemType.Flyout] = "FLYOUT", [Enum.SpellBookItemType.FutureSpell] = "FUTURESPELL", [Enum.SpellBookItemType.PetAction] = "PETACTION" } or {}
local GetSpellBookItemInfo = GetSpellBookItemInfo or function(...) local si = C_SpellBook.GetSpellBookItemInfo(...) if si then return SpellBookItemTypeMap[si.itemType] or "NONE", (si.itemType == Enum.SpellBookItemType.Flyout or si.itemType == Enum.SpellBookItemType.PetAction) and si.actionID or si.spellID or si.actionID, si end end
local SPELLBOOK_BANK_PLAYER = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or "player"
local SPELLBOOK_BANK_PET = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Pet or "pet"
local IsPassiveSpell = IsPassiveSpell or C_Spell.IsSpellPassive
local GetOverrideSpell = C_SpellBook and C_SpellBook.GetOverrideSpell or C_Spell.GetOverrideSpell or GetOverrideSpell
local HasPetSpells = HasPetSpells or C_SpellBook.HasPetSpells
5 months ago
local GetSpecialization = GetSpecialization or C_SpecializationInfo.GetSpecialization
local GetSpecializationInfo = GetSpecializationInfo or C_SpecializationInfo.GetSpecializationInfo
local spellBookPetEnum = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Pet or "pet"
SMALL_NUMBER = 0.000001
ALPHA_BLEND_AMOUNT = 0.8400251
--cache this stuff
local g, b, d, t = GetBuildInfo()
DF.BuildYear = tonumber(d:match("%d+$") or 0)
DF.GamePatch = g --string "10.2.7"
DF.BuildId = b --string "55000"
DF.Toc = t --number 100000
DF.Exp = floor(DF.Toc/10000)
local buildInfo = DF.Toc
DF.dversion = dversion
DF.AuthorInfo = {
Author = "",
Name = "Terciob", --terciob
Discord = "https://discord.gg/AGSzAZX",
Support = "www.patreon.com",
SearchVideos = "www.youtube.com",
}
3 years ago
function DF:Msg(msg, ...)
print("|cFFFFFFAA" .. (self.__name or "Details!Framework:") .. "|r ", msg, ...)
end
function DF:MsgWarning(msg, ...)
print("|cFFFFFFAA" .. (self.__name or "Details!Framework") .. "|r |cFFFFAA00[Warning]|r", msg, ...)
3 years ago
end
DF.DefaultRoundedCornerPreset = {
roundness = 6,
color = {.1, .1, .1, 0.98},
border_color = {.05, .05, .05, 0.834},
}
DF.internalFunctions = DF.internalFunctions or {}
local PixelUtil = PixelUtil or DFPixelUtil
if (not PixelUtil) then
3 years ago
--check if is in classic, TBC, or WotLK wow, if it is, build a replacement for PixelUtil
local gameVersion = GetBuildInfo()
3 years ago
if (gameVersion:match("%d") == "1" or gameVersion:match("%d") == "2" or gameVersion:match("%d") == "3") then
PixelUtil = {
3 years ago
SetWidth = function(self, width) self:SetWidth(width) end,
SetHeight = function(self, height) self:SetHeight(height) end,
SetSize = function(self, width, height) self:SetSize(width, height) end,
SetPoint = function(self, ...) self:SetPoint(...) end,
}
end
end
---return r, g, b, a for the default backdrop color used in addons
---@return number
---@return number
---@return number
---@return number
3 years ago
function DF:GetDefaultBackdropColor()
return 0.1215, 0.1176, 0.1294, 0.8
end
---return if the wow version the player is playing is dragonflight
---@return boolean
3 years ago
function DF.IsDragonflight()
if (buildInfo < 110000 and buildInfo >= 100000) then return true end
return false
3 years ago
end
---return if the wow version the player is playing is dragonflight or an expansion after it
---@return boolean
function DF.IsDragonflightAndBeyond()
return buildInfo >= 100000
end
5 months ago
function DF.ExpansionHasEvoker()
return buildInfo >= 100000
end
---return true if the wow version is Dragonflight or below
---@return boolean
function DF.IsDragonflightOrBelow()
return buildInfo < 110000
end
---return if the wow version the player is playing is a classic version of wow
---@return boolean
3 years ago
function DF.IsTimewalkWoW()
5 months ago
if (buildInfo < 60000) then return true end
return false
3 years ago
end
---return if the wow version the player is playing is the vanilla version of wow
---@return boolean
3 years ago
function DF.IsClassicWow()
if (buildInfo < 20000) then return true end
return false
end
---return true if the player is playing in the TBC version of wow
---@return boolean
function DF.IsTBCWow()
if (buildInfo < 30000 and buildInfo >= 20000) then return true end
return false
3 years ago
end
---return true if the player is playing in the WotLK version of wow
---@return boolean
3 years ago
function DF.IsWotLKWow()
if (buildInfo < 40000 and buildInfo >= 30000) then return true end
return false
3 years ago
end
---return true if the player is playing in the Cataclysm version of wow
---@return boolean
function DF.IsCataWow()
if (buildInfo < 50000 and buildInfo >= 40000) then return true end
return false
end
---return true if the player is playing in the Mists version of wow
---@return boolean
function DF.IsPandaWow()
if (buildInfo < 60000 and buildInfo >= 50000) then return true end
return false
end
---return true if the player is playing in the Warlords of Draenor version of wow
---@return boolean
function DF.IsWarlordsWow()
if (buildInfo < 70000 and buildInfo >= 60000) then return true end
return false
end
---return true if the player is playing in the Legion version of wow
---@return boolean
function DF.IsLegionWow()
if (buildInfo < 80000 and buildInfo >= 70000) then return true end
return false
end
---return true if the player is playing in the BFA version of wow
---@return boolean
function DF.IsBFAWow()
if (buildInfo < 90000 and buildInfo >= 80000) then return true end
return false
end
---return true if the player is playing in the Shadowlands version of wow
---@return boolean
3 years ago
function DF.IsShadowlandsWow()
if (buildInfo < 100000 and buildInfo >= 90000) then return true end
return false
end
---return if the wow version the player is playing is dragonflight
---@return boolean
function DF.IsDragonflightWow()
if (buildInfo < 110000 and buildInfo >= 100000) then return true end
return false
end
---return if the wow version the player is playing is the war within
---@return boolean
function DF.IsWarWow()
if (buildInfo < 120000 and buildInfo >= 110000) then return true end
return false
end
function DF.IsTWWWow()
return DF.IsWarWow()
end
---return true if the player is playing in the WotLK version of wow with the retail api
---@return boolean
function DF.IsNonRetailWowWithRetailAPI()
3 years ago
local _, _, _, buildInfo = GetBuildInfo()
5 months ago
if (buildInfo < 60000 and buildInfo >= 30401) or (buildInfo < 20000 and buildInfo >= 11404) then
3 years ago
return true
end
return false
end
DF.IsWotLKWowWithRetailAPI = DF.IsNonRetailWowWithRetailAPI -- this is still in use
function DF.ExpansionHasAugEvoker()
return DF.IsDragonflightWow() or DF.IsWarWow()
end
---for classic wow, get the role using the texture from the talents frame
local roleBySpecTextureName = {
DruidBalance = "DAMAGER",
DruidFeralCombat = "DAMAGER",
DruidRestoration = "HEALER",
3 years ago
HunterBeastMastery = "DAMAGER",
HunterMarksmanship = "DAMAGER",
HunterSurvival = "DAMAGER",
MageArcane = "DAMAGER",
MageFrost = "DAMAGER",
MageFire = "DAMAGER",
PaladinCombat = "DAMAGER",
PaladinHoly = "HEALER",
PaladinProtection = "TANK",
PriestHoly = "HEALER",
PriestDiscipline = "HEALER",
PriestShadow = "DAMAGER",
RogueAssassination = "DAMAGER",
RogueCombat = "DAMAGER",
RogueSubtlety = "DAMAGER",
ShamanElementalCombat = "DAMAGER",
ShamanEnhancement = "DAMAGER",
ShamanRestoration = "HEALER",
WarlockCurses = "DAMAGER",
WarlockDestruction = "DAMAGER",
WarlockSummoning = "DAMAGER",
WarriorArm = "DAMAGER",
WarriorArms = "DAMAGER",
WarriorFury = "DAMAGER",
WarriorProtection = "TANK",
3 years ago
DeathKnightBlood = "TANK",
DeathKnightFrost = "DAMAGER",
DeathKnightUnholy = "DAMAGER",
}
---classic, tbc and wotlk role guesser based on the weights of each talent tree
---@return string
function DF:GetRoleByClassicTalentTree()
if (not DF.IsTimewalkWoW()) then
return "NONE"
end
--amount of tabs existing
local numTabs = GetNumTalentTabs() or 3
--store the background textures for each tab
local pointsPerSpec = {}
for i = 1, (MAX_TALENT_TABS or 3) do
if (i <= numTabs) then
--tab information
local id, name, description, iconTexture, pointsSpent, fileName = GetTalentTabInfo(i)
if DF.IsClassicWow() and not fileName then
--On pre 1.15.3
name, iconTexture, pointsSpent, fileName = id, name, description, iconTexture
end
if (name) then
table.insert(pointsPerSpec, {name, pointsSpent, fileName})
end
end
end
local MIN_SPECS = 4
--put the spec with more talent point to the top
3 years ago
table.sort(pointsPerSpec, function(t1, t2) return t1[2] > t2[2] end)
--get the spec with more points spent
local spec = pointsPerSpec[1]
3 years ago
if (spec and spec[2] >= MIN_SPECS) then
local specName = spec[1]
local spentPoints = spec[2]
local specTexture = spec[3]
local role = roleBySpecTextureName[specTexture]
return role or "NONE"
end
3 years ago
return "DAMAGER"
end
5 months ago
local roleStringToNumber = {
["NONE"] = 0,
["TANK"] = 1,
["HEALER"] = 2,
["DAMAGER"] = 3,
["SUPPORT"] = 4,
}
local roleNumberToString = {
[0] = "NONE",
[1] = "TANK",
[2] = "HEALER",
[3] = "DAMAGER",
[4] = "SUPPORT",
}
function DF:ConvertRole(value, valueType)
if (valueType) then
if (type(valueType) == "string") then
valueType = roleNumberToString[valueType] or valueType
end
if (type(valueType) == "number") then
valueType = roleStringToNumber[valueType] or valueType
end
end
if (type(value) == "string") then
return roleStringToNumber[value] or 0
elseif (type(value) == "number") then
return roleNumberToString[value] or "NONE"
end
return value
end
---return the role of the unit, this is safe to use for all versions of wow
---@param unitId string
---@param bUseSupport boolean?
---@param specId number?
---@return string
function DF.UnitGroupRolesAssigned(unitId, bUseSupport, specId)
5 months ago
local role
5 months ago
if (specId == 1473 and bUseSupport) then
return "SUPPORT"
end
5 months ago
if (UnitGroupRolesAssigned) then
role = UnitGroupRolesAssigned(unitId)
end
5 months ago
if (role == "NONE") then
if (GetSpecialization) then
if (UnitIsUnit(unitId, "player")) then
local specializationIndex = GetSpecialization() or 0
local id, name, description, icon, role, primaryStat = GetSpecializationInfo(specializationIndex)
if (id == 1473 and bUseSupport) then
return "SUPPORT"
end
return id and role or "NONE"
end
else
--attempt to guess the role by the player spec
local classLoc, className = UnitClass(unitId)
if (className == "MAGE" or className == "ROGUE" or className == "HUNTER" or className == "WARLOCK") then
return "DAMAGER"
end
5 months ago
if (Details) then
--attempt to get the role from Details! Damage Meter
local guid = UnitGUID(unitId)
if (guid) then
role = Details.cached_roles[guid]
if (role) then
return role
end
end
end
5 months ago
if (UnitIsUnit(unitId, "player")) then
role = DF:GetRoleByClassicTalentTree()
end
end
end
return role
end
---return the specializationid of the player it self
---@return number|nil
function DF.GetSpecialization()
if (GetSpecialization) then
return GetSpecialization()
end
return nil
end
---return the specializationid using the specId
---@param specId unknown
function DF.GetSpecializationInfoByID(specId)
if (GetSpecializationInfoByID) then
return GetSpecializationInfoByID(specId)
end
return nil
end
3 years ago
function DF.GetSpecializationInfo(...)
if (GetSpecializationInfo) then
3 years ago
return GetSpecializationInfo(...)
end
return nil
end
3 years ago
function DF.GetSpecializationRole(...)
if (GetSpecializationRole) then
3 years ago
return GetSpecializationRole(...)
end
return nil
end
--[=[ dump of C_EncounterJournal
["GetEncountersOnMap"] = function,
["SetPreviewMythicPlusLevel"] = function,
["GetLootInfoByIndex"] = function,
["GetSlotFilter"] = function,
["IsEncounterComplete"] = function,
["SetTab"] = function,
["ResetSlotFilter"] = function,
["OnOpen"] = function,
["InstanceHasLoot"] = function,
["GetSectionIconFlags"] = function,
["SetPreviewPvpTier"] = function,
["GetEncounterJournalLink"] = function,
["GetInstanceForGameMap"] = function,
["GetSectionInfo"] = function,
["GetLootInfo"] = function,
["GetDungeonEntrancesForMap"] = function,
["OnClose"] = function,
["SetSlotFilter"] = function,
--]=]
--build dummy encounter journal functions if they doesn't exists
--this is done for compatibility with classic and if in the future EJ_ functions are moved to C_
---@class EncounterJournal : table
---@field EJ_GetInstanceForMap fun(mapId: number)
---@field EJ_GetInstanceInfo fun(journalInstanceID: number)
---@field EJ_SelectInstance fun(journalInstanceID: number)
---@field EJ_GetEncounterInfoByIndex fun(index: number, journalInstanceID: number?)
---@field EJ_GetEncounterInfo fun(journalEncounterID: number)
---@field EJ_SelectEncounter fun(journalEncounterID: number)
---@field EJ_GetSectionInfo fun(sectionID: number)
---@field EJ_GetCreatureInfo fun(index: number, journalEncounterID: number?)
---@field EJ_SetDifficulty fun(difficultyID: number)
---@field EJ_GetNumLoot fun(): number
DF.EncounterJournal = {
EJ_GetInstanceForMap = EJ_GetInstanceForMap or function() return nil end,
EJ_GetInstanceInfo = EJ_GetInstanceInfo or function() return nil end,
EJ_SelectInstance = EJ_SelectInstance or function() return nil end,
EJ_GetEncounterInfoByIndex = EJ_GetEncounterInfoByIndex or function() return nil end,
EJ_GetEncounterInfo = EJ_GetEncounterInfo or function() return nil end,
EJ_SelectEncounter = EJ_SelectEncounter or function() return nil end,
EJ_GetSectionInfo = EJ_GetSectionInfo or function() return nil end,
EJ_GetCreatureInfo = EJ_GetCreatureInfo or function() return nil end,
EJ_SetDifficulty = EJ_SetDifficulty or function() return nil end,
EJ_GetNumLoot = EJ_GetNumLoot or function() return 0 end,
}
3 years ago
--will always give a very random name for our widgets
local init_counter = math.random(1, 1000000)
DF.LabelNameCounter = DF.LabelNameCounter or init_counter
DF.PictureNameCounter = DF.PictureNameCounter or init_counter
DF.BarNameCounter = DF.BarNameCounter or init_counter
DF.DropDownCounter = DF.DropDownCounter or init_counter
DF.PanelCounter = DF.PanelCounter or init_counter
DF.SimplePanelCounter = DF.SimplePanelCounter or init_counter
DF.ButtonCounter = DF.ButtonCounter or init_counter
DF.SliderCounter = DF.SliderCounter or init_counter
DF.SwitchCounter = DF.SwitchCounter or init_counter
DF.SplitBarCounter = DF.SplitBarCounter or init_counter
DF.FRAMELEVEL_OVERLAY = 750
DF.FRAMELEVEL_BACKGROUND = 150
3 years ago
DF.FrameWorkVersion = tostring(dversion)
function DF:PrintVersion()
3 years ago
print("Details! Framework Version:", DF.FrameWorkVersion)
end
3 years ago
--get the working folder
do
3 years ago
local path = string.match(debugstack(1, 1, 0), "AddOns\\(.+)fw.lua")
if (path) then
DF.folder = "Interface\\AddOns\\" .. path
else
3 years ago
--if not found, try to use the last valid one
DF.folder = DF.folder or ""
end
end
DF.debug = false
3 years ago
function DF:GetFrameworkFolder()
return DF.folder
end
function DF:SetFrameworkDebugState(state)
DF.debug = state
end
DF.embeds = DF.embeds or {}
3 years ago
local embedFunctions = {
"RemoveRealName",
"table",
"BuildDropDownFontList",
"SetFontSize",
"SetFontFace",
"SetFontColor",
"GetFontSize",
"GetFontFace",
"SetFontOutline",
"trim",
"Msg",
"CreateFlashAnimation",
"Fade",
"NewColor",
"IsHtmlColor",
"ParseColors",
"BuildMenu",
"ShowTutorialAlertFrame",
"GetNpcIdFromGuid",
"SetAsOptionsPanel",
"GetPlayerRole",
"GetCharacterTalents",
"GetCharacterPvPTalents",
3 years ago
"CreateDropDown",
"CreateButton",
"CreateColorPickButton",
"CreateLabel",
"CreateBar",
"CreatePanel",
"CreateFillPanel",
"ColorPick",
"IconPick",
"CreateSimplePanel",
"CreateChartPanel",
"CreateImage",
"CreateScrollBar",
"CreateSwitch",
"CreateSlider",
"CreateSplitBar",
"CreateTextEntry",
"Create1PxPanel",
"CreateOptionsFrame",
"NewSpecialLuaEditorEntry",
"ShowPromptPanel",
"ShowTextPromptPanel",
"GetTemplate",
"InstallTemplate",
"GetFrameworkFolder",
"ShowPanicWarning",
"SetFrameworkDebugState",
"FindHighestParent",
"OpenInterfaceProfile",
"CreateInCombatTexture",
"CreateAnimationHub",
"CreateAnimation",
"CreateScrollBox",
"CreateBorder",
"FormatNumber",
"IntegerToTimer",
"QuickDispatch",
"Dispatch",
"CommaValue",
"RemoveRealmName",
"Trim",
"CreateGlowOverlay",
"CreateAnts",
"CreateFrameShake",
"RegisterScriptComm",
"SendScriptComm",
}
3 years ago
function DF:Embed(target)
for k, v in pairs(embedFunctions) do
target[v] = self[v]
end
self.embeds[target] = true
return target
end
3 years ago
function DF:FadeFrame(frame, t)
if (t == 0) then
frame.hidden = false
frame.faded = false
frame.fading_out = false
frame.fading_in = false
frame:Show()
3 years ago
frame:SetAlpha(1)
elseif (t == 1) then
frame.hidden = true
frame.faded = true
frame.fading_out = false
frame.fading_in = false
3 years ago
frame:SetAlpha(0)
frame:Hide()
end
end
------------------------------------------------------------------------------------------------------------
function DF:RandomBool(odds)
if (odds) then
local chance = math.random()
return chance <= odds
else
return math.random(1, 2) == 1
end
end
function DF:SetTexCoordFromAtlasInfo(texture, atlasInfo)
texture:SetTexCoord(atlasInfo.leftTexCoord, atlasInfo.rightTexCoord, atlasInfo.topTexCoord, atlasInfo.bottomTexCoord)
end
3 years ago
------------------------------------------------------------------------------------------------------------
--table
DF.table = {}
---find a value inside a table and return the index
---@param t table
---@param value any
---@return integer|nil
3 years ago
function DF.table.find(t, value)
for i = 1, #t do
if (t[i] == value) then
return i
end
end
end
---find a value inside a sub table
---@param index number
---@param value any
---@return integer|nil
function DF.table.findsubtable(t, index, value)
for i = 1, #t do
if (type(t[i]) == "table") then
if (t[i][index] == value) then
return i
end
end
end
end
---Loop through parent of the passed object, making a string with parentKeys separated by a dot.
---The loop continues until a parentKey is not found or if the frame has no parent (reach UIParent).
---@param self table
---@param object any
---@return string
function DF:GetParentKeyPath(object)
local parentKey = object:GetParentKey()
if (not parentKey) then
return ""
end
local path = "" .. parentKey
local parent = object:GetParent()
while (parent) do
parentKey = parent:GetParentKey()
if (parentKey) then
path = parentKey .. "." .. path
else
return path
end
parent = parent:GetParent()
end
return path
end
---Loop through the parent of the passed object, creating a string with parent names and parent keys separated by dots, if the object has no name.
---The loop continues until a parentName is not found or if the frame has no parent (reach UIParent).
---@param self table
---@param object any
---@return string
function DF:GetParentNamePath(object)
local parent = object
local path = ""
while (parent) do
local parentName = parent:GetName()
if (not parentName) then
local parentOfParent = parent:GetParent()
if (parentOfParent) then
local parentKey = parentOfParent:GetParentKey()
if (parentKey) then
parentName = parentKey
else
local result = path:gsub("%.$", "")
return result
end
end
end
if (parentName) then
path = parentName .. "." .. path
else
local result = path:gsub("%.$", "")
return result
end
parent = parent:GetParent()
end
local result = path:gsub("%.$", "")
return result
end
---get a value from a table using a path, e.g. getfrompath(tbl, "a.b.c") is the same as tbl.a.b.c
---@param t table
---@param path string
---@param subOffset number?
---@return any
function DF.table.getfrompath(t, path, subOffset)
if (path:match("%.") or path:match("%[")) then
local value
local offset = 0
for key in path:gmatch("[%w_]+") do
value = t[key] or t[tonumber(key)]
--check if the value is nil, if it is, the key does not exists in the table
if (not value) then
return
end
--update t for the next iteration
t = value
offset = offset + 1
if (subOffset == offset) then
return value
end
end
return value
else
return t[path] or t[tonumber(path)]
end
end
---set the value of a table using a path, e.g. setfrompath(tbl, "a.b.c", 10) is the same as tbl.a.b.c = 10
---@param t table
---@param path string
---@param value any
---@return boolean?
function DF.table.setfrompath(t, path, value)
if (path:match("%.") or path:match("%[")) then
local lastTable
local lastKey
5 months ago
--for key in path:gmatch("[%w_]+") do
for key in path:gmatch("[^%.%[%]]+") do
lastTable = t
lastKey = key
--update t for the next iteration
t = t[key] or t[tonumber(key)]
end
if (lastTable and lastKey) then
lastTable[lastKey] = value
return true
end
else
t[path] = value
return true
end
return false
end
---return the amount of keys in a table
---@param t table
---@return number
function DF.table.countkeys(t)
local count = 0
for _ in pairs(t) do
count = count + 1
end
return count
end
---find the value inside the table, and it it's not found, add it
---@param t table
---@param index integer|any
---@param value any
---@return boolean
3 years ago
function DF.table.addunique(t, index, value)
if (not value) then
value = index
index = #t + 1
end
for i = 1, #t do
if (t[i] == value) then
return false
end
end
3 years ago
table.insert(t, index, value)
return true
end
---get the table 't' and reverse the order of the values within it
---@param t table
---@return table
3 years ago
function DF.table.reverse(t)
local new = {}
local index = 1
for i = #t, 1, -1 do
3 years ago
new[index] = t[i]
index = index + 1
end
return new
end
---remove a value from an array table
---@param t table
---@param value any
---@return boolean
function DF.table.remove(t, value)
local bRemoved = false
local removedAmount = 0
for i = 1, #t do
if (t[i] == value) then
table.remove(t, i)
bRemoved = true
removedAmount = removedAmount + 1
end
end
return bRemoved, removedAmount
end
---copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, keys pointing to a UIObject are preserved
---@param t1 table
---@param t2 table
---@return table
3 years ago
function DF.table.duplicate(t1, t2)
for key, value in pairs(t2) do
if (key ~= "__index" and key ~= "__newindex") then
--preserve a UIObject passing it to the new table with copying it
3 years ago
if (type(value) == "table" and table.GetObjectType and table:GetObjectType()) then
t1[key] = value
elseif (type(value) == "table") then
t1[key] = t1[key] or {}
DF.table.copy(t1[key], t2[key])
else
t1[key] = value
end
end
end
return t1
end
---copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, threat UIObjects as regular tables
---@param t1 table
---@param t2 table
---@return table
function DF.table.copy(t1, t2)
for key, value in pairs(t2) do
if (key ~= "__index" and key ~= "__newindex") then
3 years ago
if (type(value) == "table") then
t1[key] = t1[key] or {}
DF.table.copy(t1[key], t2[key])
else
t1[key] = value
end
end
end
return t1
end
---copy from table2 to table1 overwriting values but do not copy data that cannot be compressed
---@param t1 table
---@param t2 table
---@return table
function DF.table.copytocompress(t1, t2)
for key, value in pairs(t2) do
if (key ~= "__index" and type(value) ~= "function") then
if (type(value) == "table") then
if (not value.GetObjectType) then
t1[key] = t1[key] or {}
DF.table.copytocompress(t1[key], t2[key])
end
else
t1 [key] = value
end
end
end
return t1
end
---remove from table1 the values that are also on table2
---@param table1 table the table to have the values removed
---@param table2 table the reference table
function DF.table.removeduplicate(table1, table2)
for key, value in pairs(table2) do
if (type(value) == "table") then
if (type(table1[key]) == "table") then
DF.table.removeduplicate(table1[key], value)
if (not next(table1[key])) then
table1[key] = nil
end
end
else
if (type(table1[key]) == "number" and type(value) == "number") then
if (DF:IsNearlyEqual(table1[key], value, 0.0001)) then
table1[key] = nil
end
else
if (table1[key] == value) then
table1[key] = nil
end
end
end
end
end
---add the indexes of table2 into the end of the table table1
---@param t1 table
---@param t2 table
---@return table
3 years ago
function DF.table.append(t1, t2)
for i = 1, #t2 do
t1[#t1+1] = t2[i]
end
return t1
end
5 months ago
---receive a table and N arguments, add each argument to the table
---@param t1 table
---@vararg any
function DF.table.inserts(t1, ...)
for i = 1, select("#", ...) do
t1[#t1+1] = select(i, ...)
end
return t1
end
---copy values that does exist on table2 but not on table1
---@param t1 table
---@param t2 table
---@return table
3 years ago
function DF.table.deploy(t1, t2)
for key, value in pairs(t2) do
if (type(value) == "table") then
5 months ago
--check the t1 type as sometimes the key isn't the same type on both tables
if (t1[key] == nil or type(t1[key]) == "table") then
t1[key] = t1[key] or {}
DF.table.deploy(t1[key], t2[key])
end
elseif (t1[key] == nil) then
t1[key] = value
end
end
return t1
end
--/run print (DetailsFramework.table.dump({{1, 2}, {2, 3}, {4, 5}}))
local function tableToString(t, resultString, deep, seenTables)
resultString = resultString or ""
deep = deep or 0
seenTables = seenTables or {}
if seenTables[t] then
resultString = resultString .. "--CIRCULAR REFERENCE\n"
return resultString
end
local space = string.rep(" ", deep)
seenTables[t] = true
for key, value in pairs(t) do
local valueType = type(value)
if (type(key) == "function") then
key = "#function#"
elseif (type(key) == "table") then
key = "#table#"
end
if (type(key) ~= "string" and type(key) ~= "number") then
key = "unknown?"
end
if (valueType == "table") then
local sUIObjectType = value.GetObjectType and value:GetObjectType()
if (sUIObjectType) then
if (type(key) == "number") then
resultString = resultString .. space .. "[" .. key .. "] = |cFFa9ffa9 " .. sUIObjectType .. " {|r\n"
else
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFa9ffa9 " .. sUIObjectType .. " {|r\n"
end
else
if (type(key) == "number") then
resultString = resultString .. space .. "[" .. key .. "] = |cFFa9ffa9 {|r\n"
else
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFa9ffa9 {|r\n"
end
end
resultString = resultString .. tableToString(value, nil, deep + 1, seenTables)
resultString = resultString .. space .. "|cFFa9ffa9},|r\n"
elseif (valueType == "string") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = \"|cFFfff1c1" .. value .. "|r\",\n"
elseif (valueType == "number") then
if (type(key) == "number") then
resultString = resultString .. space .. "[" .. key .. "] = |cFFffc1f4" .. value .. "|r,\n"
else
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF94CEA8" .. value .. "|r,\n"
end
elseif (valueType == "function") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFC586C0function|r,\n"
elseif (valueType == "boolean") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF99d0ff" .. (value and "true" or "false") .. "|r,\n"
end
end
return resultString
end
local function tableToStringSafe(t)
local seenTables = {}
return tableToString(t, nil, 0, seenTables)
end
---get the contends of table 't' and return it as a string
---@param t table
---@param resultString string
---@param deep integer
---@return string
3 years ago
function DF.table.dump(t, resultString, deep)
return tableToStringSafe(t)
end
---grab a text and split it into lines adding each line to an array table
---@param text string
---@return table
4 years ago
function DF:SplitTextInLines(text)
local lines = {}
local position = 1
local startScope, endScope = text:find("\n", position, true)
while (startScope) do
if (startScope ~= 1) then
table.insert(lines, text:sub(position, startScope-1))
4 years ago
end
position = endScope + 1
startScope, endScope = text:find("\n", position, true)
end
if (position <= #text) then
table.insert(lines, text:sub(position))
4 years ago
end
return lines
end
DF.strings = {}
---receive an array and output a string with the values separated by commas
---if bDoCompression is true, the string will be compressed using LibDeflate
---@param t table
---@param bDoCompression boolean|nil
---@return string
function DF.strings.tabletostring(t, bDoCompression)
local newString = ""
for i = 1, #t do
newString = newString .. t[i] .. ","
end
newString = newString:sub(1, -2)
if (bDoCompression) then
local LibDeflate = LibStub:GetLibrary("LibDeflate")
if (LibDeflate) then
newString = LibDeflate:CompressDeflate(newString, {level = 9})
end
end
return newString
end
function DF.strings.stringtotable(thisString, bDoCompression)
if (bDoCompression) then
local LibDeflate = LibStub:GetLibrary("LibDeflate")
if (LibDeflate) then
thisString = LibDeflate:DecompressDeflate(thisString)
end
end
local newTable = {strsplit(",", thisString)}
return newTable
end
4 years ago
local symbol_1K, symbol_10K, symbol_1B
if (GetLocale() == "koKR") then
symbol_1K, symbol_10K, symbol_1B = "", "", ""
3 years ago
elseif (GetLocale() == "zhCN") then
symbol_1K, symbol_10K, symbol_1B = "", "", "亿"
3 years ago
elseif (GetLocale() == "zhTW") then
symbol_1K, symbol_10K, symbol_1B = "", "", ""
end
---get the game localization and return which symbol need to be used after formatting numbers, this is for asian languages
---@return string
---@return string
---@return string
function DF:GetAsianNumberSymbols()
if (GetLocale() == "koKR") then
return "", "", ""
3 years ago
elseif (GetLocale() == "zhCN") then
return "", "", "亿"
3 years ago
elseif (GetLocale() == "zhTW") then
return "", "", ""
else
3 years ago
--return korean as default (if the language is western)
return "", "", ""
end
end
if (symbol_1K) then
---if symbol_1K is valid, the game has an Asian localization, 'DF.FormatNumber' will use Asian symbols to format numbers
---@param number number
---@return string
3 years ago
function DF.FormatNumber(number)
if (number > 99999999) then
return format("%.2f", number/100000000) .. symbol_1B
elseif (number > 999999) then
return format("%.2f", number/10000) .. symbol_10K
elseif (number > 99999) then
return floor(number/10000) .. symbol_10K
elseif (number > 9999) then
return format("%.1f", (number/10000)) .. symbol_10K
elseif (number > 999) then
return format("%.1f", (number/1000)) .. symbol_1K
end
3 years ago
return format("%.1f", number)
end
else
---if symbol_1K isn't valid, 'DF.FormatNumber' will use western symbols to format numbers
---@param number number
---@return string|number
function DF.FormatNumber(number)
3 years ago
if (number > 999999999) then
return format("%.2f", number/1000000000) .. "B"
elseif (number > 999999) then
return format("%.2f", number/1000000) .. "M"
elseif (number > 99999) then
return floor(number/1000) .. "K"
elseif (number > 999) then
return format("%.1f", (number/1000)) .. "K"
end
3 years ago
return floor(number)
end
end
---format a number with commas
---@param self table
---@param value number
---@return string
3 years ago
function DF:CommaValue(value)
if (not value) then
return "0"
end
3 years ago
value = floor(value)
if (value == 0) then
return "0"
end
3 years ago
--source http://richard.warburton.it
local left, num, right = string.match(value, '^([^%d]*%d)(%d*)(.-)$')
3 years ago
return left .. (num:reverse():gsub('(%d%d%d)','%1,'):reverse()) .. right
end
---call the function 'callback' for each group member passing the unitID and the extra arguments
---@param self table
---@param callback function
---@vararg any
3 years ago
function DF:GroupIterator(callback, ...)
if (IsInRaid()) then
for i = 1, GetNumGroupMembers() do
3 years ago
DF:QuickDispatch(callback, "raid" .. i, ...)
end
3 years ago
elseif (IsInGroup()) then
for i = 1, GetNumGroupMembers() - 1 do
3 years ago
DF:QuickDispatch(callback, "party" .. i, ...)
end
3 years ago
DF:QuickDispatch(callback, "player", ...)
else
3 years ago
DF:QuickDispatch(callback, "player", ...)
end
end
---receives an object and a percent amount, then calculate the return value by multiplying the min value of the object width or height by the percent received
---@param uiObject uiobject
---@param percent number
---@return number
function DF:GetSizeFromPercent(uiObject, percent)
local width, height = uiObject:GetSize()
local minValue = math.min(width, height)
return minValue * percent
end
---get an integer an format it as string with the time format 16:45
---@param self table
---@param value number
---@return string
function DF:IntegerToTimer(value) --~formattime
return "" .. math.floor(value/60) .. ":" .. string.format("%02.f", value%60)
end
---remove the realm name from a name
---@param self table
---@param name string
---@return string, number
3 years ago
function DF:RemoveRealmName(name)
return name:gsub(("%-.*"), "")
end
---remove the owner name of the pet or guardian
---@param self table
---@param name string
---@return string, number
function DF:RemoveOwnerName(name)
return name:gsub((" <.*"), "")
end
---remove realm and owner names also remove brackets from spell actors
---@param self table
---@param name string
---@return string
function DF:CleanUpName(name)
name = DF:RemoveRealmName(name)
name = DF:RemoveOwnerName(name)
name = name:gsub("%[%*%]%s", "")
--remove texture escape sequence
name = name:gsub("|T.-|t", "")
return name
end
---remove the realm name from a name
---@param self table
---@param name string
---@return string, number
3 years ago
function DF:RemoveRealName(name)
return name:gsub(("%-.*"), "")
end
---get the UIObject of type 'FontString' named fontString and set the font size to the maximum value of the arguments
---@param self table
---@param fontString fontstring
---@vararg number
3 years ago
function DF:SetFontSize(fontString, ...)
local font, _, flags = fontString:GetFont()
fontString:SetFont(font, math.max(...), flags)
end
---get the UIObject of type 'FontString' named fontString and set the font to the argument fontface
---@param self table
---@param fontString fontstring
---@param fontface string
3 years ago
function DF:SetFontFace(fontString, fontface)
if (fontface == "DEFAULT") then
DF:SetFontDefault(fontString)
return
end
3 years ago
local font = SharedMedia:Fetch("font", fontface, true)
if (font) then
fontface = font
end
local _, size, flags = fontString:GetFont()
return fontString:SetFont(fontface, size, flags)
end
local dummyFontString = UIParent:CreateFontString(nil, "background", "GameFontNormal")
local defaultFontFile = dummyFontString:GetFont()
5 months ago
function DF:GetTextWidth(text, size)
if (size) then
DF:SetFontSize(dummyFontString, size)
else
DF:SetFontSize(dummyFontString, 12)
end
dummyFontString:SetText(text)
return dummyFontString:GetStringWidth()
end
---get the UIObject of type 'FontString' and set the default game font into it
---@param self table
---@param fontString fontstring
function DF:SetFontDefault(fontString)
local _, size, flags = fontString:GetFont()
return fontString:SetFont(defaultFontFile, size, flags)
end
---get the FontString passed and set the font color
---@param self table
---@param fontString fontstring
---@param r any
---@param g number?
---@param b number?
---@param a number?
3 years ago
function DF:SetFontColor(fontString, r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
fontString:SetTextColor(r, g, b, a)
end
---get the FontString passed and set the font shadow color and offset
---@param self table
---@param fontString fontstring
---@param r any
---@param g number?
---@param b number?
---@param a number?
---@param x number?
---@param y number?
function DF:SetFontShadow(fontString, r, g, b, a, x, y)
3 years ago
r, g, b, a = DF:ParseColors(r, g, b, a)
fontString:SetShadowColor(r, g, b, a)
local offSetX, offSetY = fontString:GetShadowOffset()
x = x or offSetX
y = y or offSetY
fontString:SetShadowOffset(x, y)
end
---get the FontString object passed and set the rotation of the text shown
---@param self table
---@param fontString fontstring
---@param degrees number
function DF:SetFontRotation(fontString, degrees) --deprecated, use fontString:SetRotation(degrees) | retail use fontString:SetRotation(math.rad(degrees))
3 years ago
if (type(degrees) == "number") then
if (not fontString.__rotationAnimation) then
fontString.__rotationAnimation = DF:CreateAnimationHub(fontString)
fontString.__rotationAnimation.rotator = DF:CreateAnimation(fontString.__rotationAnimation, "rotation", 1, 0, 0)
fontString.__rotationAnimation.rotator:SetEndDelay(10^8)
fontString.__rotationAnimation.rotator:SetSmoothProgress(1)
end
fontString.__rotationAnimation.rotator:SetDegrees(degrees)
fontString.__rotationAnimation:Play()
fontString.__rotationAnimation:Pause()
end
3 years ago
end
---receives a string and a color and return the string wrapped with the color using |c and |r scape codes
---@param self table
---@param text string
---@param color any
---@return string
function DF:AddColorToText(text, color) --wrap text with a color
local r, g, b = DF:ParseColors(color)
if (not r) then
return text
end
local hexColor = DF:FormatColor("hex", r, g, b)
text = "|c" .. hexColor .. text .. "|r"
return text
end
5 months ago
function DF:GetClassColorByClassId(classId)
local classInfo = C_CreatureInfo.GetClassInfo(classId)
if (classInfo) then
local color = RAID_CLASS_COLORS[classInfo.classFile]
if (color) then
return color.r, color.g, color.b
else
return 1, 1, 1
end
end
return 1, 1, 1
end
---receives a string 'text' and a class name and return the string wrapped with the class color using |c and |r scape codes
---@param self table
---@param text string
5 months ago
---@param className class
---@return string
3 years ago
function DF:AddClassColorToText(text, className)
5 months ago
if (type(className) == "number") then
className = DF.ClassIndexToFileName[className]
end
3 years ago
if (type(className) ~= "string") then
return DF:RemoveRealName(text)
elseif (className == "UNKNOW" or className == "PET") then
return DF:RemoveRealName(text)
end
local color = RAID_CLASS_COLORS[className]
if (color) then
3 years ago
text = "|c" .. color.colorStr .. DF:RemoveRealName(text) .. "|r"
else
3 years ago
return DF:RemoveRealName(text)
end
3 years ago
return text
end
---returns the class icon texture coordinates and texture file path
5 months ago
---@param class string|number
---@return number, number, number, number, string
function DF:GetClassTCoordsAndTexture(class)
5 months ago
if (type(class) == "number") then
class = DF.ClassIndexToFileName[class]
end
local l, r, t, b = unpack(CLASS_ICON_TCOORDS[class])
return l, r, t, b, [[Interface\WORLDSTATEFRAME\Icons-Classes]]
end
---create a string with the spell icon and the spell name using |T|t scape codes to add the icon inside the string
---@param self table
---@param spellId any
---@return string
function DF:MakeStringFromSpellId(spellId)
local spellName, _, spellIcon = GetSpellInfo(spellId)
if (spellName) then
return "|T" .. spellIcon .. ":16:16:0:0:64:64:4:60:4:60|t " .. spellName
end
return ""
end
---wrap 'text' with the class icon of 'playerName' using |T|t scape codes
---@param text string
---@param playerName string
---@param englishClassName string this is the english class name, not the localized one, english class name is upper case
---@param useSpec boolean|nil
---@param iconSize number|nil
---@return string
function DF:AddClassIconToText(text, playerName, englishClassName, useSpec, iconSize)
4 years ago
local size = iconSize or 16
3 years ago
local spec
4 years ago
if (useSpec) then
if (Details) then
3 years ago
local GUID = UnitGUID(playerName)
if (GUID) then
spec = Details.cached_specs[GUID]
4 years ago
if (spec) then
spec = spec
end
end
else
if (type(useSpec) == "number") then
local specId, specName = GetSpecializationInfoByID(useSpec)
end
4 years ago
end
end
if (spec) then --if spec is valid, the user has Details! installed
local specString = ""
3 years ago
local L, R, T, B = unpack(Details.class_specs_coords[spec])
4 years ago
if (L) then
specString = "|TInterface\\AddOns\\Details\\images\\spec_icons_normal:" .. size .. ":" .. size .. ":0:0:512:512:" .. (L * 512) .. ":" .. (R * 512) .. ":" .. (T * 512) .. ":" .. (B * 512) .. "|t"
return specString .. " " .. text
end
end
if (englishClassName) then
4 years ago
local classString = ""
--Details.class_coords uses english class names as keys and the values are tables containing texture coordinates
local L, R, T, B = unpack(Details.class_coords[englishClassName])
4 years ago
if (L) then
local imageSize = 128
classString = "|TInterface\\AddOns\\Details\\images\\classes_small:" .. size .. ":" .. size .. ":0:0:" .. imageSize .. ":" .. imageSize .. ":" .. (L * imageSize) .. ":" .. (R * imageSize) .. ":" .. (T * imageSize) .. ":" .. (B * imageSize) .. "|t"
return classString .. " " .. text
end
end
return text
end
function DF:AddClassIconToString(text, engClass, size)
size = size or 16
local tcoords = CLASS_ICON_TCOORDS[engClass]
if (tcoords) then
local l, r, t, b = unpack(tcoords)
return "|TInterface\\Glues\\CharacterCreate\\UI-CharacterCreate-Classes:" .. size .. ":" .. size .. ":0:0:256:256:" .. (l * 256) .. ":" .. (r * 256) .. ":" .. (t * 256) .. ":" .. (b * 256) .. "|t " .. text
end
end
function DF:AddSpecIconToString(text, specId, size)
size = size or 16
if (not specId) then
--get the player specId
local specIndex = GetSpecialization()
specId = GetSpecializationInfo(specIndex)
if (not specId) then
return
end
end
local id, name, description, icon = GetSpecializationInfoByID(specId)
if (id) then
return "|T" .. icon .. ":" .. size .. ":" .. size .. ":0:0|t " .. text
end
end
---create a table with information about a texture (deprecated, use: DetailsFramework:CreateAtlas())
---@param texture any
---@param textureWidth any
---@param textureHeight any
---@param imageWidth any
---@param imageHeight any
---@param left any
---@param right any
---@param top any
---@param bottom any
---@return table
function DF:CreateTextureInfo(texture, textureWidth, textureHeight, left, right, top, bottom, imageWidth, imageHeight)
local textureInfo = {
texture = texture,
width = textureWidth or 16,
height = textureHeight or 16,
coords = {left or 0, right or 1, top or 0, bottom or 1},
}
textureInfo.imageWidth = imageWidth or textureInfo.width
textureInfo.imageHeight = imageHeight or textureInfo.height
return textureInfo
end
---add a texture to the start or end of a string
---@param text string
---@param textureInfo table
---@param bAddSpace any
---@param bAddAfterText any
---@return string
function DF:AddTextureToText(text, textureInfo, bAddSpace, bAddAfterText)
local texture = textureInfo.texture
local textureWidth = textureInfo.width
local textureHeight = textureInfo.height
local imageWidth = textureInfo.imageWidth or textureWidth
local imageHeight = textureInfo.imageHeight or textureHeight
local left, right, top, bottom = unpack(textureInfo.coords)
left = left or 0
right = right or 1
top = top or 0
bottom = bottom or 1
if (bAddAfterText) then
local newString = text .. (bAddSpace and " " or "") .. "|T" .. texture .. ":" .. textureWidth .. ":" .. textureHeight .. ":0:0:" .. imageWidth .. ":" .. imageHeight .. ":" .. (left * imageWidth) .. ":" .. (right * imageWidth) .. ":" .. (top * imageHeight) .. ":" .. (bottom * imageHeight) .. "|t"
return newString
else
local newString = "|T" .. texture .. ":" .. textureWidth .. ":" .. textureHeight .. ":0:0:" .. imageWidth .. ":" .. imageHeight .. ":" .. (left * imageWidth) .. ":" .. (right * imageWidth) .. ":" .. (top * imageHeight) .. ":" .. (bottom * imageHeight) .. "|t" .. (bAddSpace and " " or "") .. text
return newString
end
end
---return the size of a fontstring
---@param fontString table
---@return number
3 years ago
function DF:GetFontSize(fontString)
local _, size = fontString:GetFont()
return size
end
---return the font of a fontstring
---@param fontString table
---@return string
3 years ago
function DF:GetFontFace(fontString)
local fontface = fontString:GetFont()
return fontface
end
local ValidOutlines = {
["NONE"] = true,
["MONOCHROME"] = true,
["OUTLINE"] = true,
["THICKOUTLINE"] = true,
["OUTLINEMONOCHROME"] = true,
["THICKOUTLINEMONOCHROME"] = true,
}
3 years ago
DF.FontOutlineFlags = {
{"", "None"},
{"MONOCHROME", "Monochrome"},
{"OUTLINE", "Outline"},
{"THICKOUTLINE", "Thick Outline"},
{"OUTLINEMONOCHROME", "Outline & Monochrome"},
{"THICKOUTLINEMONOCHROME", "Thick Outline & Monochrome"},
}
---set the outline of a fontstring, outline is a black border around the text, can be "NONE", "MONOCHROME", "OUTLINE" or "THICKOUTLINE"
---@param fontString table
---@param outline outline
3 years ago
function DF:SetFontOutline(fontString, outline)
local font, fontSize = fontString:GetFont()
if (outline) then
if (type(outline) == "string") then
outline = outline:upper()
end
3 years ago
if (ValidOutlines[outline]) then
outline = outline
3 years ago
elseif (type(outline) == "boolean" and outline) then
outline = "OUTLINE"
3 years ago
elseif (type(outline) == "boolean" and not outline) then
outline = "" --"NONE"
3 years ago
elseif (outline == 1) then
outline = "OUTLINE"
3 years ago
elseif (outline == 2) then
outline = "THICKOUTLINE"
end
end
outline = (not outline or outline == "NONE") and "" or outline
fontString:SetFont(font, fontSize, outline)
end
---remove spaces from the start and end of the string
---@param string string
---@return string
3 years ago
function DF:Trim(string)
return DF:trim(string)
end
3 years ago
function DF:trim(string)
local from = string:match"^%s*()"
return from > #string and "" or string:match(".*%S", from)
end
---truncate removing at a maximum of 10 character from the string
---@param fontString table
---@param maxWidth number
function DF:TruncateTextSafe(fontString, maxWidth)
local text = fontString:GetText()
local numIterations = 10
while (fontString:GetStringWidth() > maxWidth) do
text = strsub(text, 1, #text-1)
fontString:SetText(text)
if (#text <= 1) then
break
end
numIterations = numIterations - 1
if (numIterations <= 0) then
break
end
end
text = DF:CleanTruncateUTF8String(text)
fontString:SetText(text)
end
---truncate removing characters from the string until the maxWidth is reach
---@param fontString table
---@param maxWidth number
3 years ago
function DF:TruncateText(fontString, maxWidth)
local text = fontString:GetText()
3 years ago
while (fontString:GetStringWidth() > maxWidth) do
3 years ago
text = strsub(text, 1, #text - 1)
fontString:SetText(text)
if (string.len(text) <= 1) then
break
end
end
3 years ago
text = DF:CleanTruncateUTF8String(text)
3 years ago
fontString:SetText(text)
end
---truncate removing text through a binary search with a max of 10 iterations
---@param fontString table
---@param maxWidth number
function DF:TruncateTextSafeBinarySearch(fontString, maxWidth)
local text = fontString:GetText()
if text == nil or text == '' then return end
if fontString:GetUnboundedStringWidth() > maxWidth then
local left = 1
local right = #text
local numIterations = 10
while left <= right and numIterations > 0 do
local middle = math.floor((left + right) * 0.5)
local substring = strsub(text, 1, middle)
fontString:SetText(substring)
if fontString:GetUnboundedStringWidth() <= maxWidth then
left = middle + 1
else
right = middle - 1
end
numIterations = numIterations - 1
end
text = strsub(text, 1, right)
end
fontString:SetText(DF:CleanTruncateUTF8String(text))
end
---truncate removing characters from the string until the maxWidth is reach
---@param fontString table
---@param maxWidth number
function DF:TruncateTextBinarySearch(fontString, maxWidth)
local text = fontString:GetText()
if text == nil or text == '' then return end
if fontString:GetUnboundedStringWidth() > maxWidth then
local left = 1
local right = #text
while left <= right do
local middle = math.floor((left + right) * 0.5)
local substring = strsub(text, 1, middle)
fontString:SetText(substring)
if fontString:GetUnboundedStringWidth() <= maxWidth then
left = middle + 1
else
right = middle - 1
end
end
text = strsub(text, 1, right)
end
fontString:SetText(DF:CleanTruncateUTF8String(text))
end
---@param text string
---@return string
function DF:CleanTruncateUTF8String(text)
if type(text) == "string" and text ~= "" then
local b1 = (#text > 0) and strbyte(strsub(text, #text, #text)) or nil
local b2 = (#text > 1) and strbyte(strsub(text, #text-1, #text)) or nil
local b3 = (#text > 2) and strbyte(strsub(text, #text-2, #text)) or nil
3 years ago
if b1 and b1 >= 194 and b1 <= 244 then
text = strsub (text, 1, #text - 1)
3 years ago
elseif b2 and b2 >= 224 and b2 <= 244 then
text = strsub (text, 1, #text - 2)
3 years ago
elseif b3 and b3 >= 240 and b3 <= 244 then
text = strsub (text, 1, #text - 3)
end
end
3 years ago
return text
end
---truncate the amount of numbers used to show the fraction part of a number
---@param number number
---@param fractionDigits number
---@return number
3 years ago
function DF:TruncateNumber(number, fractionDigits)
fractionDigits = fractionDigits or 2
local truncatedNumber = number
--local truncatedNumber = format("%." .. fractionDigits .. "f", number) --4x slower than:
--http://lua-users.org/wiki/SimpleRound
local mult = 10 ^ fractionDigits
if (number >= 0) then
truncatedNumber = floor(number * mult + 0.5) / mult
else
truncatedNumber = ceil(number * mult + 0.5) / mult
end
return truncatedNumber
end
5 months ago
function DF:GetCursorPosition()
local x, y = GetCursorPosition()
local scale = UIParent:GetEffectiveScale()
return x / scale, y / scale
end
---attempt to get the ID of an npc from a GUID
---@param GUID string
---@return number
3 years ago
function DF:GetNpcIdFromGuid(GUID)
local npcId = select(6, strsplit("-", GUID ))
if (npcId) then
npcId = tonumber(npcId)
return npcId or 0
end
return 0
end
3 years ago
function DF.SortOrder1(t1, t2)
return t1[1] > t2[1]
end
3 years ago
function DF.SortOrder2(t1, t2)
return t1[2] > t2[2]
end
3 years ago
function DF.SortOrder3(t1, t2)
return t1[3] > t2[3]
end
3 years ago
function DF.SortOrder1R(t1, t2)
return t1[1] < t2[1]
end
3 years ago
function DF.SortOrder2R(t1, t2)
return t1[2] < t2[2]
end
3 years ago
function DF.SortOrder3R(t1, t2)
return t1[3] < t2[3]
end
---return a list of spells from the player spellbook
---@return table<string, boolean> spellNamesInSpellBook
---@return spellid[] spellIdsInSpellBook
function DF:GetSpellBookSpells()
local spellNamesInSpellBook = {}
3 years ago
local spellIdsInSpellBook = {}
for i = 1, GetNumSpellTabs() do
local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(i)
3 years ago
if (offspecId == 0 and tabTexture ~= 136830) then --don't add spells found in the General tab
offset = offset + 1
local tabEnd = offset + numSpells
for j = offset, tabEnd - 1 do
local spellType, spellId = GetSpellBookItemInfo(j, SPELLBOOK_BANK_PLAYER)
if (spellId) then
if (spellType ~= "FLYOUT") then
local spellName = GetSpellInfo(spellId)
if (spellName) then
spellNamesInSpellBook[spellName] = true
3 years ago
spellIdsInSpellBook[#spellIdsInSpellBook+1] = spellId
end
else
local _, _, numSlots, isKnown = GetFlyoutInfo(spellId)
if (isKnown and numSlots > 0) then
for k = 1, numSlots do
local spellID, overrideSpellID, isKnown = GetFlyoutSlotInfo(spellId, k)
if (isKnown) then
local spellName = GetSpellInfo(spellID)
spellNamesInSpellBook[spellName] = true
3 years ago
spellIdsInSpellBook[#spellIdsInSpellBook+1] = spellID
end
end
end
end
end
end
end
end
3 years ago
return spellNamesInSpellBook, spellIdsInSpellBook
end
5 months ago
function DF:GetHeroTalentId()
local configId = C_ClassTalents.GetActiveConfigID()
if (not configId) then
return 0
end
local configInfo = C_Traits.GetConfigInfo(configId)
for treeIndex, treeId in ipairs(configInfo.treeIDs) do
local treeNodes = C_Traits.GetTreeNodes(treeId)
for nodeIdIndex, treeNodeID in ipairs(treeNodes) do
local traitNodeInfo = C_Traits.GetNodeInfo(configId, treeNodeID)
if (traitNodeInfo) then
local activeEntry = traitNodeInfo.activeEntry
if (activeEntry) then
local entryId = activeEntry.entryID
local rank = activeEntry.rank
if (rank > 0) then
local entryInfo = C_Traits.GetEntryInfo(configId, entryId)
if (not entryInfo.definitionID and entryInfo.subTreeID) then
return entryInfo.subTreeID
end
end
end
end
end
end
return 0
end
---return a table of passive talents, format: [spellId] = true
---@return {Name: string, ID: number, Texture: any, IsSelected: boolean}[]
function DF:GetAllTalents()
local allTalents = {}
local configId = C_ClassTalents.GetActiveConfigID()
if (configId) then
local configInfo = C_Traits.GetConfigInfo(configId)
--get the spells from the SPEC from talents
for treeIndex, treeId in ipairs(configInfo.treeIDs) do
local treeNodes = C_Traits.GetTreeNodes(treeId)
for nodeIdIndex, treeNodeID in ipairs(treeNodes) do
local traitNodeInfo = C_Traits.GetNodeInfo(configId, treeNodeID)
if (traitNodeInfo) then
local activeEntry = traitNodeInfo.activeEntry
local entryIds = traitNodeInfo.entryIDs
for i = 1, #entryIds do
local entryId = entryIds[i] --number
local traitEntryInfo = C_Traits.GetEntryInfo(configId, entryId)
local borderTypes = Enum.TraitNodeEntryType
if (traitEntryInfo.type) then -- == borderTypes.SpendCircle
local definitionId = traitEntryInfo.definitionID
if definitionId then
local traitDefinitionInfo = C_Traits.GetDefinitionInfo(definitionId)
local spellId = traitDefinitionInfo.overriddenSpellID or traitDefinitionInfo.spellID
local spellName, _, spellTexture = GetSpellInfo(spellId)
if (spellName) then
local talentInfo = {Name = spellName, ID = spellId, Texture = spellTexture, IsSelected = (activeEntry and activeEntry.rank and activeEntry.rank > 0) or false}
allTalents[#allTalents+1] = talentInfo
end
end
end
end
end
end
end
end
return allTalents
end
---return a table where keys are spellIds (number) and the value is true
---@return table<number, boolean>
function DF:GetAvailableSpells()
local completeListOfSpells = {}
--this line might not be compatible with classic
5 months ago
local specId, specName, _, specIconTexture = GetSpecializationInfo(GetSpecialization())
--local classNameLoc, className, classId = UnitClass("player") --not in use
local locPlayerRace, playerRace, playerRaceId = UnitRace("player")
--get racials from the general tab
local generalTabIndex = 1
local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(generalTabIndex)
offset = offset + 1
local tabEnd = offset + numSpells
for entryOffset = offset, tabEnd - 1 do
local spellType, spellId = GetSpellBookItemInfo(entryOffset, SPELLBOOK_BANK_PLAYER)
local spellData = LIB_OPEN_RAID_COOLDOWNS_INFO[spellId]
if (spellData) then
local raceId = spellData.raceid
if (raceId) then
if (type(raceId) == "table") then
if (raceId[playerRaceId]) then
spellId = GetOverrideSpell(spellId)
local spellName = GetSpellInfo(spellId)
local bIsPassive = IsPassiveSpell(spellId, SPELLBOOK_BANK_PLAYER)
if (spellName and not bIsPassive) then
completeListOfSpells[spellId] = true
end
end
elseif (type(raceId) == "number") then
if (raceId == playerRaceId) then
spellId = GetOverrideSpell(spellId)
local spellName = GetSpellInfo(spellId)
local bIsPassive = IsPassiveSpell(spellId, SPELLBOOK_BANK_PLAYER)
if (spellName and not bIsPassive) then
completeListOfSpells[spellId] = true
end
end
end
end
end
end
5 months ago
local spellBookPlayerEnum = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or "player"
--get spells from the Spec spellbook
5 months ago
for i = 1, GetNumSpellTabs() do --called "lines" in new v11 api
local tabName, tabTexture, offset, numSpells, isGuild, offSpecId, shouldHide, specID = GetSpellTabInfo(i)
5 months ago
if (tabTexture == specIconTexture) then
offset = offset + 1
local tabEnd = offset + numSpells
--local bIsOffSpec = offSpecId ~= 0
for entryOffset = offset, tabEnd - 1 do
local spellType, spellId = GetSpellBookItemInfo(entryOffset, spellBookPlayerEnum)
if (spellId) then
if (spellType == "SPELL" or spellType == 1) then
spellId = GetOverrideSpell(spellId)
local spellName = GetSpellInfo(spellId)
local bIsPassive = IsPassiveSpell(entryOffset, spellBookPlayerEnum)
if (spellName and not bIsPassive) then
completeListOfSpells[spellId] = true --bIsOffSpec == false
end
end
end
end
end
end
5 months ago
local CONST_SPELLBOOK_CLASSSPELLS_TABID = 2
local CONST_SPELLBOOK_GENERAL_TABID = 1
--get class shared spells from the spell book
5 months ago
--[=
local tabName, tabTexture, offset, numSpells, isGuild, offSpecId = GetSpellTabInfo(CONST_SPELLBOOK_CLASSSPELLS_TABID)
offset = offset + 1
local tabEnd = offset + numSpells
5 months ago
--local bIsOffSpec = offSpecId ~= 0
for entryOffset = offset, tabEnd - 1 do
5 months ago
local spellType, spellId = GetSpellBookItemInfo(entryOffset, spellBookPlayerEnum)
if (spellId) then
5 months ago
if (spellType == "SPELL" or spellType == 1) then
spellId = GetOverrideSpell(spellId)
local spellName = GetSpellInfo(spellId)
5 months ago
local bIsPassive = IsPassiveSpell(spellId, spellBookPlayerEnum)
if (spellName and not bIsPassive) then
5 months ago
completeListOfSpells[spellId] = true --bIsOffSpec == false
end
end
end
end
--]=]
local getNumPetSpells = function()
--'HasPetSpells' contradicts the name and return the amount of pet spells available instead of a boolean
return HasPetSpells()
end
--get pet spells from the pet spellbook
local numPetSpells = getNumPetSpells()
if (numPetSpells) then
for i = 1, numPetSpells do
5 months ago
local spellName, _, unmaskedSpellId = GetSpellBookItemName(i, spellBookPetEnum)
if (unmaskedSpellId) then
unmaskedSpellId = GetOverrideSpell(unmaskedSpellId)
5 months ago
local bIsPassive = IsPassiveSpell(i, spellBookPetEnum)
if (spellName and not bIsPassive) then
completeListOfSpells[unmaskedSpellId] = true
end
end
end
end
return completeListOfSpells
end
3 years ago
------------------------------------------------------------------------------------------------------------------------
--flash animation
3 years ago
local onFinishFlashAnimation = function(self)
if (self.showWhenDone) then
3 years ago
self.frame:SetAlpha(1)
else
3 years ago
self.frame:SetAlpha(0)
self.frame:Hide()
end
3 years ago
if (self.onFinishFunc) then
3 years ago
self:onFinishFunc(self.frame)
end
end
3 years ago
local stopAnimation_Method = function(self)
local FlashAnimation = self.FlashAnimation
FlashAnimation:Stop()
end
3 years ago
local startFlash_Method = function(self, fadeInTime, fadeOutTime, flashDuration, showWhenDone, flashInHoldTime, flashOutHoldTime, loopType)
local flashAnimation = self.FlashAnimation
local fadeIn = flashAnimation.fadeIn
local fadeOut = flashAnimation.fadeOut
fadeIn:Stop()
fadeOut:Stop()
3 years ago
fadeIn:SetDuration(fadeInTime or 1)
fadeIn:SetEndDelay(flashInHoldTime or 0)
fadeOut:SetDuration(fadeOutTime or 1)
fadeOut:SetEndDelay(flashOutHoldTime or 0)
flashAnimation.duration = flashDuration
flashAnimation.loopTime = flashAnimation:GetDuration()
flashAnimation.finishAt = GetTime() + flashDuration
flashAnimation.showWhenDone = showWhenDone
flashAnimation:SetLooping(loopType or "REPEAT")
self:Show()
3 years ago
self:SetAlpha(0)
flashAnimation:Play()
end
---create a flash animation for a frame
---@param frame table
---@param onFinishFunc function?
---@param onLoopFunc function?
3 years ago
function DF:CreateFlashAnimation(frame, onFinishFunc, onLoopFunc)
local flashAnimation = frame:CreateAnimationGroup()
flashAnimation.fadeOut = flashAnimation:CreateAnimation("Alpha")
flashAnimation.fadeOut:SetOrder(1)
flashAnimation.fadeOut:SetFromAlpha(0)
flashAnimation.fadeOut:SetToAlpha(1)
flashAnimation.fadeIn = flashAnimation:CreateAnimation("Alpha")
flashAnimation.fadeIn:SetOrder(2)
flashAnimation.fadeIn:SetFromAlpha(1)
flashAnimation.fadeIn:SetToAlpha(0)
frame.FlashAnimation = flashAnimation
flashAnimation.frame = frame
flashAnimation.onFinishFunc = onFinishFunc
flashAnimation:SetScript("OnLoop", onLoopFunc)
flashAnimation:SetScript("OnFinished", onFinishFlashAnimation)
frame.Flash = startFlash_Method
frame.Stop = stopAnimation_Method
return flashAnimation
end
5 months ago
local onStartPunchAnimation = function(animationGroup)
local parent = animationGroup:GetParent()
animationGroup.parentWidth = parent:GetWidth()
animationGroup.parentHeight = parent:GetHeight()
end
local onStopPunchAnimation = function(animationGroup)
local parent = animationGroup:GetParent()
parent:SetWidth(animationGroup.parentWidth)
parent:SetHeight(animationGroup.parentHeight)
end
function DF:CreatePunchAnimation(frame, scale)
scale = scale or 1.1
scale = math.min(scale, 1.9)
local animationHub = DF:CreateAnimationHub(frame, onStartPunchAnimation, onStopPunchAnimation)
local scaleUp = DF:CreateAnimation(animationHub, "scale", 1, 0.05, 1, 1, scale, scale, "center", 0, 0)
local scaleDown = DF:CreateAnimation(animationHub, "scale", 2, 0.05, 1, 1, 1-(scale - 1), 1-(scale - 1), "center", 0, 0)
return animationHub
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--anchoring
function DF:CheckPoints(point1, point2, point3, point4, point5, object)
if (not point1 and not point2) then
return "topleft", object.widget:GetParent(), "topleft", 0, 0
end
3 years ago
if (type(point1) == "string") then
local frameGlobal = _G[point1]
if (frameGlobal and type(frameGlobal) == "table" and frameGlobal.GetObjectType) then
return DF:CheckPoints(frameGlobal, point2, point3, point4, point5, object)
end
3 years ago
elseif (type(point2) == "string") then
local frameGlobal = _G[point2]
if (frameGlobal and type(frameGlobal) == "table" and frameGlobal.GetObjectType) then
return DF:CheckPoints(point1, frameGlobal, point3, point4, point5, object)
end
end
if (type(point1) == "string" and type(point2) == "table") then --setpoint("left", frame, _, _, _)
if (not point3 or type(point3) == "number") then --setpoint("left", frame, 10, 10)
point1, point2, point3, point4, point5 = point1, point2, point1, point3, point4
end
3 years ago
elseif (type(point1) == "string" and type(point2) == "number") then --setpoint("topleft", x, y)
point1, point2, point3, point4, point5 = point1, object.widget:GetParent(), point1, point2, point3
3 years ago
elseif (type(point1) == "number") then --setpoint(x, y)
point1, point2, point3, point4, point5 = "topleft", object.widget:GetParent(), "topleft", point1, point2
3 years ago
elseif (type(point1) == "table") then --setpoint(frame, x, y)
point1, point2, point3, point4, point5 = "topleft", point1, "topleft", point2, point3
end
3 years ago
if (not point2) then
point2 = object.widget:GetParent()
elseif (point2.dframework) then
point2 = point2.widget
end
3 years ago
return point1 or "topleft", point2, point3 or "topleft", point4 or 0, point5 or 0
end
3 years ago
---@class df_anchor : table
---@field side number 1-8: topleft to top (clockwise); 9: center; 10-13: inside left right top bottom; 14-17: inside topleft, bottomleft bottomright topright
---@field x number
---@field y number
DF.AnchorPoints = {
"Top Left",
"Left",
"Bottom Left",
"Bottom",
"Bottom Right",
"Right",
"Top Right",
"Top",
"Center", --9
"Inside Left", --10
"Inside Right", --11
"Inside Top", --12
"Inside Bottom", --13
"Inside Top Left", --14
"Inside Bottom Left", --15
"Inside Bottom Right", --16
"Inside Top Right", --17
}
DF.AnchorPointsByIndex = {
"topleft", --1
"left", --2
"bottomleft", --3
"bottom", --4
"bottomright", --5
"right", --6
"topright", --7
"top", --8
"center", --9
}
DF.AnchorPointsToInside = {
[9] = 9,
[8] = 12,
[7] = 17,
[6] = 11,
[5] = 16,
[4] = 13,
[3] = 15,
[2] = 10,
[1] = 14,
}
DF.InsidePointsToAnchor = {
[9] = 9,
[12] = 8,
[17] = 7,
[11] = 6,
[16] = 5,
[13] = 4,
[15] = 3,
[10] = 2,
[14] = 1,
}
function DF:ConvertAnchorPointToInside(anchorPoint)
return DF.AnchorPointsToInside[anchorPoint] or anchorPoint
end
local calcPointCoords = function(ninePointsWidget, ninePointsRef, anchorTable, coordIndex, newAnchorSide)
--get the location of the topleft corner relative to the bottomleft corner of the screen
---@type df_coordinate
local widgetPointCoords = ninePointsWidget[coordIndex]
--get the topleft coords of the reference widget
---@type df_coordinate
local refPointCoords = ninePointsRef[coordIndex]
--calculate the offset of the x and y axis
local x = refPointCoords.x - widgetPointCoords.x
local y = refPointCoords.y - widgetPointCoords.y
anchorTable.x = x
anchorTable.y = y
anchorTable.side = newAnchorSide
print("new anchor side", newAnchorSide, "x", x, "y", y)
end
function DF:ConvertAnchorOffsets(widget, referenceWidget, anchorTable, newAnchorSide)
if (anchorTable.side == newAnchorSide) then
return anchorTable
end
local ninePoints = DF.Math.GetNinePoints(widget)
local refNinePoints = DF.Math.GetNinePoints(referenceWidget)
--the numeration from 1 to 9 is the index within a ninePoints table
anchorTable.side = newAnchorSide
if (newAnchorSide == 14) then --inside topleft
anchorTable.x = ninePoints[1].x - refNinePoints[1].x
anchorTable.y = ninePoints[1].y - refNinePoints[1].y
--print("inside topleft", anchorTable.x, anchorTable.y)
elseif (newAnchorSide == 15) then --inside bottomleft
anchorTable.x = ninePoints[3].x - refNinePoints[3].x
anchorTable.y = ninePoints[3].y - refNinePoints[3].y
elseif (newAnchorSide == 16) then --inside bottomright
anchorTable.x = refNinePoints[5].x - ninePoints[5].x
anchorTable.y = refNinePoints[5].y - ninePoints[5].y
elseif (newAnchorSide == 17) then --inside topright
anchorTable.x = refNinePoints[7].x - ninePoints[7].x
anchorTable.y = refNinePoints[7].y - ninePoints[7].y
elseif (newAnchorSide == 10) then --inside left
calcPointCoords(ninePoints, refNinePoints, anchorTable, 2, newAnchorSide)
elseif (newAnchorSide == 11) then --inside right
calcPointCoords(ninePoints, refNinePoints, anchorTable, 6, newAnchorSide)
elseif (newAnchorSide == 12) then --inside top
calcPointCoords(ninePoints, refNinePoints, anchorTable, 8, newAnchorSide)
elseif (newAnchorSide == 13) then --inside bottom
calcPointCoords(ninePoints, refNinePoints, anchorTable, 4, newAnchorSide)
elseif (newAnchorSide == 9) then --center
calcPointCoords(ninePoints, refNinePoints, anchorTable, 9, newAnchorSide)
else
--print("not implemented")
end
end
local anchoringFunctions = {
function(frame, anchorTo, offSetX, offSetY) --1 TOP LEFT
frame:ClearAllPoints()
frame:SetPoint("bottomleft", anchorTo, "topleft", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --2 LEFT
frame:ClearAllPoints()
frame:SetPoint("right", anchorTo, "left", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --3 BOTTOM LEFT
frame:ClearAllPoints()
frame:SetPoint("topleft", anchorTo, "bottomleft", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --4 BOTTOM
frame:ClearAllPoints()
frame:SetPoint("top", anchorTo, "bottom", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --5 BOTTOM RIGHT
frame:ClearAllPoints()
frame:SetPoint("topright", anchorTo, "bottomright", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --6 RIGHT
frame:ClearAllPoints()
frame:SetPoint("left", anchorTo, "right", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --7 TOP RIGHT
frame:ClearAllPoints()
frame:SetPoint("bottomright", anchorTo, "topright", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --8 TOP
frame:ClearAllPoints()
frame:SetPoint("bottom", anchorTo, "top", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --9 CENTER
frame:ClearAllPoints()
frame:SetPoint("center", anchorTo, "center", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --10 INSIDE LEFT
frame:ClearAllPoints()
frame:SetPoint("left", anchorTo, "left", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --11 INSIDE RIGHT
frame:ClearAllPoints()
frame:SetPoint("right", anchorTo, "right", offSetX, offSetY)
end,
3 years ago
function(frame, anchorTo, offSetX, offSetY) --12 INSIDE TOP
frame:ClearAllPoints()
frame:SetPoint("top", anchorTo, "top", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --13 INSIDE BOTTOM
frame:ClearAllPoints()
frame:SetPoint("bottom", anchorTo, "bottom", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --14 INSIDE TOPLEFT to TOPLEFT
frame:ClearAllPoints()
frame:SetPoint("topleft", anchorTo, "topleft", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --15 INSIDE BOTTOMLEFT to BOTTOMLEFT
frame:ClearAllPoints()
frame:SetPoint("bottomleft", anchorTo, "bottomleft", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --16 INSIDE BOTTOMRIGHT to BOTTOMRIGHT
frame:ClearAllPoints()
frame:SetPoint("bottomright", anchorTo, "bottomright", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --17 INSIDE TOPRIGHT to TOPRIGHT
frame:ClearAllPoints()
frame:SetPoint("topright", anchorTo, "topright", offSetX, offSetY)
end,
}
---set the anchor point using a df_anchor table
---@param widget uiobject
---@param anchorTable df_anchor
---@param anchorTo uiobject?
function DF:SetAnchor(widget, anchorTable, anchorTo)
anchorTo = anchorTo or widget:GetParent()
anchoringFunctions[anchorTable.side](widget, anchorTo, anchorTable.x, anchorTable.y)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--colors
---add a new color name, the color can be query using DetailsFramework:ParseColors(colorName)
---@param colorName string
---@param red number
---@param green number
---@param blue number
---@param alpha number?
---@return table
3 years ago
function DF:NewColor(colorName, red, green, blue, alpha)
assert(type(colorName) == "string", "DetailsFramework:NewColor(): colorName must be a string.")
3 years ago
red, green, blue, alpha = DetailsFramework:ParseColors(red, green, blue, alpha)
local colorTable = DetailsFramework:FormatColor("table", red, green, blue, alpha)
DF.alias_text_colors[colorName] = colorTable
return colorTable
end
3 years ago
local colorTableMixin = {
3 years ago
GetColor = function(self)
return self.r, self.g, self.b, self.a
end,
3 years ago
SetColor = function(self, r, g, b, a)
r, g, b, a = DF:ParseColors(r, g, b, a)
self.r = r or self.r
self.g = g or self.g
self.b = b or self.b
self.a = a or self.a
end,
3 years ago
IsColorTable = true,
}
3 years ago
---* takes in a color in one format and converts it to another specified format.
---* here are the parameters it accepts:
---* newFormat (string): The format to convert the color to. It can be one of the following: "commastring", "tablestring", "table", "tablemembers", "numbers", "hex".
---* r (number|string): The red component of the color or a string representing the color.
---* g (number|nil): The green component of the color. This is optional if r is a string.
---* b (number|nil): The blue component of the color. This is optional if r is a string.
---* a (number|nil): The alpha component of the color. This is optional and defaults to 1 if not provided.
---* decimalsAmount (number|nil): The number of decimal places to round the color components to. This is optional and defaults to 4 if not provided.
---* The function returns the color in the new format. The return type depends on the newFormat parameter. It can be a string, a table, or four separate number values (for the "numbers" format).
---* For the "hex" format, it returns a string representing the color in hexadecimal format.
---@param newFormat string
---@param r number|string
---@param g number|nil
---@param b number|nil
---@param a number|nil
---@param decimalsAmount number|nil
---@return string|table|number|nil
---@return number|nil
---@return number|nil
---@return number|nil
3 years ago
function DF:FormatColor(newFormat, r, g, b, a, decimalsAmount)
a = a or 1
3 years ago
r, g, b, a = DF:ParseColors(r, g, b, a)
decimalsAmount = decimalsAmount or 4
r = DF:TruncateNumber(r, decimalsAmount)
g = DF:TruncateNumber(g, decimalsAmount)
b = DF:TruncateNumber(b, decimalsAmount)
a = DF:TruncateNumber(a, decimalsAmount)
if (newFormat == "commastring") then
return r .. ", " .. g .. ", " .. b .. ", " .. a
elseif (newFormat == "tablestring") then
return "{" .. r .. ", " .. g .. ", " .. b .. ", " .. a .. "}"
elseif (newFormat == "table") then
return {r, g, b, a}
elseif (newFormat == "tablemembers") then
return {["r"] = r, ["g"] = g, ["b"] = b, ["a"] = a}
elseif (newFormat == "numbers") then
return r, g, b, a
elseif (newFormat == "hex") then
return format("%.2x%.2x%.2x%.2x", a * 255, r * 255, g * 255, b * 255)
end
end
function DF:CreateColorTable(r, g, b, a)
local t = {
3 years ago
r = r or 1,
g = g or 1,
b = b or 1,
a = a or 1,
}
3 years ago
DF:Mixin(t, colorTableMixin)
return t
end
---return true if DF.alias_text_colors has the colorName as a key
---DF.alias_text_colors is a table where key is a color name and value is an indexed table with the r g b values
---@param colorName any
---@return unknown
function DF:IsHtmlColor(colorName)
return DF.alias_text_colors[colorName]
end
---return the brightness of a color from zero to one
---@param r number
---@param g number
---@param b number
---@return number
function DF:GetColorBrightness(r, g, b)
r, g, b = DF:ParseColors(r, g, b)
return 0.2134 * r + 0.7152 * g + 0.0721 * b
end
---return the hue of a color from red to blue to green to yellow and back to red
---@param r number
---@param g number
---@param b number
---@return number
function DF:GetColorHue(r, g, b)
r, g, b = DF:ParseColors(r, g, b)
local minValue, maxValue = math.min(r, g, b), math.max(r, g, b)
if (maxValue == minValue) then
return 0
elseif (maxValue == r) then
return (g - b) / (maxValue - minValue) % 6
elseif (maxValue == g) then
return (b - r) / (maxValue - minValue) + 2
else
return (r - g) / (maxValue - minValue) + 4
end
end
---get the values passed and return r g b a color values
---the function accept color name, tables with r g b a members, indexed tables with r g b a values, numbers, html hex color
---@param red any
---@param green any
---@param blue any
---@param alpha any
---@return number
---@return number
---@return number
---@return number
3 years ago
function DF:ParseColors(red, green, blue, alpha)
local firstParameter = red
--the first value passed is a table?
if (type(firstParameter) == "table") then
local colorTable = red
if (colorTable.IsColorTable) then
--using colorTable mixin
return colorTable:GetColor()
elseif (not colorTable[1] and colorTable.r) then
--{["r"] = 1, ["g"] = 1, ["b"] = 1}
red, green, blue, alpha = colorTable.r, colorTable.g, colorTable.b, colorTable.a
else
3 years ago
--{1, .7, .2, 1}
red, green, blue, alpha = unpack(colorTable)
end
3 years ago
--the first value passed is a string?
elseif (type(firstParameter) == "string") then
local colorString = red
--hexadecimal
if (string.find(colorString, "#")) then
colorString = colorString:gsub("#","")
if (string.len(colorString) == 8) then --with alpha
red, green, blue, alpha = tonumber("0x" .. colorString:sub(3, 4))/255, tonumber("0x" .. colorString:sub(5, 6))/255, tonumber("0x" .. colorString:sub(7, 8))/255, tonumber("0x" .. colorString:sub(1, 2))/255
else
3 years ago
red, green, blue, alpha = tonumber("0x" .. colorString:sub(1, 2))/255, tonumber("0x" .. colorString:sub(3, 4))/255, tonumber("0x" .. colorString:sub(5, 6))/255, 1
end
else
3 years ago
--name of the color
local colorTable = DF.alias_text_colors[colorString]
if (colorTable) then
red, green, blue, alpha = unpack(colorTable)
--string with number separated by comma
elseif (colorString:find(",")) then
local r, g, b, a = strsplit(",", colorString)
red, green, blue, alpha = tonumber(r), tonumber(g), tonumber(b), tonumber(a)
else
3 years ago
--no color found within the string, return default color
red, green, blue, alpha = unpack(DF.alias_text_colors.none)
end
end
end
3 years ago
if (not red or type(red) ~= "number") then
red = 1
end
3 years ago
if (not green) or type(green) ~= "number" then
green = 1
end
3 years ago
if (not blue or type(blue) ~= "number") then
blue = 1
end
3 years ago
if (not alpha or type(alpha) ~= "number") then
alpha = 1
end
3 years ago
--saturate the values before returning to make sure they are on the 0 to 1 range
return Saturate(red), Saturate(green), Saturate(blue), Saturate(alpha)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--tutorials
function DF:ShowTutorialAlertFrame(maintext, desctext, clickfunc)
local TutorialAlertFrame = _G.DetailsFrameworkAlertFrame
if (not TutorialAlertFrame) then
3 years ago
TutorialAlertFrame = CreateFrame("frame", "DetailsFrameworkAlertFrame", UIParent, "MicroButtonAlertTemplate")
TutorialAlertFrame.isFirst = true
3 years ago
TutorialAlertFrame:SetPoint("left", UIParent, "left", -20, 100)
TutorialAlertFrame:SetFrameStrata("TOOLTIP")
TutorialAlertFrame:Hide()
TutorialAlertFrame:SetScript("OnMouseUp", function(self)
3 years ago
if (self.clickfunc and type(self.clickfunc) == "function") then
self.clickfunc()
end
self:Hide()
end)
TutorialAlertFrame:Hide()
end
--
3 years ago
TutorialAlertFrame.label = type(maintext) == "string" and maintext or type(desctext) == "string" and desctext or ""
MicroButtonAlert_SetText (TutorialAlertFrame, alert.label)
--
TutorialAlertFrame.clickfunc = clickfunc
TutorialAlertFrame:Show()
end
function DF:CreateOptionsFrame(name, title, template) --deprecated?
template = template or 1
if (template == 2) then
local newOptionsFrame = CreateFrame("frame", name, UIParent, "ButtonFrameTemplate")
table.insert(UISpecialFrames, name)
newOptionsFrame:SetSize(500, 200)
newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel
newOptionsFrame.widget_list = {}
newOptionsFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
return newOptionsFrame:Hide()
elseif (button == "LeftButton" and not self.moving) then
self.moving = true
self:StartMoving()
end
end)
newOptionsFrame:SetScript("OnMouseUp", function(self)
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
end)
newOptionsFrame:SetMovable(true)
newOptionsFrame:EnableMouse(true)
newOptionsFrame:SetFrameStrata("DIALOG")
newOptionsFrame:SetToplevel(true)
newOptionsFrame:Hide()
newOptionsFrame:SetPoint("center", UIParent, "center")
newOptionsFrame.TitleText:SetText(title)
return newOptionsFrame
elseif (template == 1) then
local newOptionsFrame = CreateFrame("frame", name, UIParent)
table.insert(UISpecialFrames, name)
newOptionsFrame:SetSize(500, 200)
newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel
newOptionsFrame.widget_list = {}
newOptionsFrame:SetScript("OnMouseDown", function(self, button)
if (button == "RightButton") then
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
return newOptionsFrame:Hide()
elseif (button == "LeftButton" and not self.moving) then
self.moving = true
self:StartMoving()
end
end)
newOptionsFrame:SetScript("OnMouseUp", function(self)
if (self.moving) then
self.moving = false
self:StopMovingOrSizing()
end
end)
newOptionsFrame:SetMovable(true)
newOptionsFrame:EnableMouse(true)
newOptionsFrame:SetFrameStrata("DIALOG")
newOptionsFrame:SetToplevel(true)
newOptionsFrame:Hide()
newOptionsFrame:SetPoint("center", UIParent, "center")
newOptionsFrame:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1,
insets = {left = 1, right = 1, top = 1, bottom = 1}})
newOptionsFrame:SetBackdropColor(0, 0, 0, .7)
local textureTitle = newOptionsFrame:CreateTexture(nil, "artwork")
textureTitle:SetTexture([[Interface\CURSOR\Interact]])
textureTitle:SetTexCoord(0, 1, 0, 1)
textureTitle:SetVertexColor(1, 1, 1, 1)
textureTitle:SetPoint("topleft", newOptionsFrame, "topleft", 2, -3)
textureTitle:SetWidth(36)
textureTitle:SetHeight(36)
local titleLabel = DF:NewLabel(newOptionsFrame, nil, "$parentTitle", nil, title, nil, 20, "yellow")
titleLabel:SetPoint("left", textureTitle, "right", 2, -1)
3 years ago
DF:SetFontOutline (titleLabel, true)
local closeButton = CreateFrame("Button", nil, newOptionsFrame, "UIPanelCloseButton")
closeButton:SetWidth(32)
closeButton:SetHeight(32)
closeButton:SetPoint("TOPRIGHT", newOptionsFrame, "TOPRIGHT", -3, -3)
closeButton:SetFrameLevel(newOptionsFrame:GetFrameLevel()+1)
return newOptionsFrame
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--~templates
3 years ago
local latinLanguageIds = {"enUS", "deDE", "esES", "esMX", "frFR", "itIT", "ptBR"}
local latinLanguageIdsMap = {
["enUS"] = true,
["deDE"] = true,
["esES"] = true,
["esMX"] = true,
["frFR"] = true,
["itIT"] = true,
["ptBR"] = true,
}
3 years ago
local alphbets = {
[latinLanguageIds] = {"A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"},
["zhCN"] = {},
}
3 years ago
--fonts
DF.font_templates = DF.font_templates or {}
3 years ago
--detect which language is the client and select the font accordingly
local clientLanguage = GetLocale()
if (clientLanguage == "enGB") then
clientLanguage = "enUS"
end
DF.ClientLanguage = clientLanguage
---returns which region the language the client is running, return "western", "russia" or "asia"
---@return string
function DF:GetClientRegion()
if (clientLanguage == "zhCN" or clientLanguage == "koKR" or clientLanguage == "zhTW") then
return "asia"
elseif (clientLanguage == "ruRU") then
return "russia"
else
return "western"
end
end
3 years ago
DF.registeredFontPaths = DF.registeredFontPaths or {}
-- ~language ~locale ~fontpath
---get a font path to be used for a specific language
---@param languageId string enUS, deDE, esES, esMX, frFR, itIT, ptBR, ruRU, zhCN, zhTW, koKR
---@return string
function DF:GetBestFontPathForLanguage(languageId)
local fontPath = DF.registeredFontPaths[languageId]
3 years ago
if (fontPath) then
return fontPath
end
--font paths gotten from creating a FontString with template "GameFontNormal" and getting the font returned from FontString:GetFont()
if (languageId == "enUS" or languageId == "deDE" or languageId == "esES" or languageId == "esMX" or languageId == "frFR" or languageId == "itIT" or languageId == "ptBR") then
3 years ago
return [[Fonts\FRIZQT__.TTF]]
elseif (languageId == "ruRU") then
3 years ago
return [[Fonts\FRIZQT___CYR.TTF]]
elseif (languageId == "zhCN") then
3 years ago
return [[Fonts\ARKai_T.ttf]]
elseif (languageId == "zhTW") then
3 years ago
return [[Fonts\blei00d.TTF]]
elseif (languageId == "koKR") then
3 years ago
return [[Fonts\2002.TTF]]
end
--the locale passed doesn't exists, so pass the enUS
return [[Fonts\FRIZQT__.TTF]]
end
---return true if the language paren is latin: enUS, deDE, esES, esMX, frFR, itIT, ptBR
---@param languageId string
---@return boolean
function DF:IsLatinLanguage(languageId)
return latinLanguageIdsMap[languageId]
end
---return a font name to use for the client language
---@param self table
---@param languageId string?
---@param western string?
---@param cyrillic string?
---@param china string?
---@param korean string?
---@param taiwan string?
function DF:GetBestFontForLanguage(languageId, western, cyrillic, china, korean, taiwan)
if (not languageId) then
languageId = DF.ClientLanguage
end
if (languageId == "enUS" or languageId == "deDE" or languageId == "esES" or languageId == "esMX" or languageId == "frFR" or languageId == "itIT" or languageId == "ptBR") then
3 years ago
return western or "Friz Quadrata TT"
elseif (languageId == "ruRU") then
3 years ago
return cyrillic or "Friz Quadrata TT"
elseif (languageId == "zhCN") then
return china or "AR CrystalzcuheiGBK Demibold"
3 years ago
elseif (languageId == "koKR") then
return korean or "2002"
3 years ago
elseif (languageId == "zhTW") then
return taiwan or "AR CrystalzcuheiGBK Demibold"
end
end
local templateOnEnter = function(frame)
if (frame.onenter_backdrop) then
local r, g, b, a = detailsFramework:ParseColors(frame.onenter_backdrop)
frame:SetBackdropColor(r, g, b, a)
end
if (frame.onenter_backdrop_border_color) then
local r, g, b, a = detailsFramework:ParseColors(frame.onenter_backdrop_border_color)
frame:SetBackdropBorderColor(r, g, b, a)
end
end
local templateOnLeave = function(frame)
if (frame.onleave_backdrop) then
local r, g, b, a = detailsFramework:ParseColors(frame.onleave_backdrop)
frame:SetBackdropColor(r, g, b, a)
end
if (frame.onleave_backdrop_border_color) then
local r, g, b, a = detailsFramework:ParseColors(frame.onleave_backdrop_border_color)
frame:SetBackdropBorderColor(r, g, b, a)
end
end
DF.TemplateOnEnter = templateOnEnter
DF.TemplateOnLeave = templateOnLeave
---set a details framework template into a regular frame
---@param self table
---@param frame uiobject
---@param template string
function detailsFramework:SetTemplate(frame, template)
template = detailsFramework:ParseTemplate("button", template)
if (frame.SetWidth) then
if (template.width) then
PixelUtil.SetWidth(frame, template.width)
end
if (template.height) then
PixelUtil.SetHeight(frame, template.height)
end
end
if (frame.SetBackdrop) then
if (template.backdrop) then
frame:SetBackdrop(template.backdrop)
end
if (template.backdropcolor) then
local r, g, b, a = detailsFramework:ParseColors(template.backdropcolor)
frame:SetBackdropColor(r, g, b, a)
frame.onleave_backdrop = {r, g, b, a}
end
if (template.backdropbordercolor) then
local r, g, b, a = detailsFramework:ParseColors(template.backdropbordercolor)
frame:SetBackdropBorderColor(r, g, b, a)
frame.onleave_backdrop_border_color = {r, g, b, a}
end
if (template.onentercolor) then
local r, g, b, a = detailsFramework:ParseColors(template.onentercolor)
frame.onenter_backdrop = {r, g, b, a}
frame:HookScript("OnEnter", templateOnEnter)
frame.__has_onentercolor_script = true
end
if (template.onleavecolor) then
local r, g, b, a = detailsFramework:ParseColors(template.onleavecolor)
frame.onleave_backdrop = {r, g, b, a}
frame:HookScript("OnLeave", templateOnLeave)
frame.__has_onleavecolor_script = true
end
if (template.onenterbordercolor) then
local r, g, b, a = detailsFramework:ParseColors(template.onenterbordercolor)
frame.onenter_backdrop_border_color = {r, g, b, a}
if (not frame.__has_onentercolor_script) then
frame:HookScript("OnEnter", templateOnEnter)
end
end
if (template.onleavebordercolor) then
local r, g, b, a = detailsFramework:ParseColors(template.onleavebordercolor)
frame.onleave_backdrop_border_color = {r, g, b, a}
if (not frame.__has_onleavecolor_script) then
frame:HookScript("OnLeave", templateOnLeave)
end
end
elseif (frame.SetColorTexture) then
if (template.backdropcolor) then
local r, g, b, a = detailsFramework:ParseColors(template.backdropcolor)
frame:SetColorTexture(r, g, b, a)
end
end
if (frame.SetIcon) then
if (template.icon) then
local iconInfo = template.icon
frame:SetIcon(iconInfo.texture, iconInfo.width, iconInfo.height, iconInfo.layout, iconInfo.texcoord, iconInfo.color, iconInfo.textdistance, iconInfo.leftpadding)
end
end
if (frame.SetTextColor) then
if (template.textsize) then
detailsFramework:SetFontSize(frame, template.textsize)
end
if (template.textfont) then
detailsFramework:SetFontFace(frame, template.textfont)
end
if (template.textcolor) then
detailsFramework:SetFontColor(frame, template.textcolor)
end
--horizontal alignment
if (template.textalign and frame.SetJustifyH) then
template.textalign = string.lower(template.textalign)
if (template.textalign == "left" or template.textalign == "<") then
frame:SetJustifyH("LEFT")
elseif (template.textalign == "center" or template.textalign == "|") then
frame:SetJustifyH("CENTER")
elseif (template.textalign == "right" or template.textalign == ">") then
frame:SetJustifyH("RIGHT")
end
end
end
end
--DF.font_templates ["ORANGE_FONT_TEMPLATE"] = {color = "orange", size = 11, font = "Accidental Presidency"}
--DF.font_templates ["OPTIONS_FONT_TEMPLATE"] = {color = "yellow", size = 12, font = "Accidental Presidency"}
--DF.font_templates["ORANGE_FONT_TEMPLATE"] = {color = "orange", size = 10, font = DF:GetBestFontForLanguage()}
DF.font_templates["ORANGE_FONT_TEMPLATE"] = {color = {1, 0.8235, 0, 1}, size = 11, font = DF:GetBestFontForLanguage()}
--DF.font_templates["OPTIONS_FONT_TEMPLATE"] = {color = "yellow", size = 9.6, font = DF:GetBestFontForLanguage()}
DF.font_templates["OPTIONS_FONT_TEMPLATE"] = {color = {1, 1, 1, 0.9}, size = 9.6, font = DF:GetBestFontForLanguage()}
DF.font_templates["SMALL_SILVER"] = {color = "silver", size = 9, font = DF:GetBestFontForLanguage()}
5 months ago
--~templates
3 years ago
--dropdowns
DF.dropdown_templates = DF.dropdown_templates or {}
3 years ago
DF.dropdown_templates["OPTIONS_DROPDOWN_TEMPLATE"] = {
backdrop = {
edgeFile = [[Interface\Buttons\WHITE8X8]],
edgeSize = 1,
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
tileSize = 64,
tile = true
},
--backdropcolor = {0.1, 0.1, 0.1, .7},
backdropcolor = {0.2, 0.2, 0.2, .7},
onentercolor = {0.3, 0.3, 0.3, .7},
backdropbordercolor = {0, 0, 0, .4},
onenterbordercolor = {0.3, 0.3, 0.3, 0.8},
dropicon = "Interface\\BUTTONS\\arrow-Down-Down",
dropiconsize = {16, 16},
dropiconpoints = {-2, -3},
}
3 years ago
DF.dropdown_templates["OPTIONS_DROPDOWNDARK_TEMPLATE"] = {
backdrop = {
edgeFile = [[Interface\Buttons\WHITE8X8]],
edgeSize = 1,
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
tileSize = 64,
tile = true
},
backdropcolor = {0.1215, 0.1176, 0.1294, 0.8000},
backdropbordercolor = {.2, .2, .2, 1},
onentercolor = {.5, .5, .5, .9},
onenterbordercolor = {.4, .4, .4, 1},
dropicon = "Interface\\BUTTONS\\arrow-Down-Down",
dropiconsize = {16, 16},
dropiconpoints = {-2, -3},
}
DF.dropdown_templates["OLD_DROPDOWN_TEMPLATE"] = {
height = 24,
backdrop = {
edgeFile = "Interface\\Buttons\\UI-SliderBar-Border",
edgeSize = 8,
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
tileSize = 64,
tile = true,
insets = {left = 4, right = 4, top = 4, bottom = 4}
},
backdropcolor = {0.1215, 0.1176, 0.1294, 0.4000},
backdropbordercolor = {1, 1, 1, 1},
onentercolor = {.5, .5, .5, .9},
onenterbordercolor = {1, 1, 1, 1},
dropicon = "Interface\\BUTTONS\\arrow-Down-Down",
dropiconsize = {16, 16},
dropiconpoints = {-2, -3},
}
3 years ago
--switches
DF.switch_templates = DF.switch_templates or {}
3 years ago
DF.switch_templates["OPTIONS_CHECKBOX_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
width = 18,
height = 18,
enabled_backdropcolor = {1, 1, 1, .5},
disabled_backdropcolor = {1, 1, 1, .2},
onenterbordercolor = {1, 1, 1, 1},
}
DF.switch_templates["OPTIONS_CIRCLECHECKBOX_TEMPLATE"] = {
width = 18,
height = 18,
is_checkbox = true, --will call SetAsCheckBox()
checked_texture = [[Interface\CHARACTERFRAME\TempPortraitAlphaMaskSmall]],
checked_size_percent = 0.7,
checked_xoffset = 0,
checked_yoffset = 0,
checked_color = "dark3",
rounded_corner = {
color = {.075, .075, .075, 1},
border_color = {.2, .2, .2, 1},
roundness = 8,
},
}
3 years ago
DF.switch_templates["OPTIONS_CHECKBOX_BRIGHT_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
width = 18,
height = 18,
enabled_backdropcolor = {1, 1, 1, .5},
disabled_backdropcolor = {1, 1, 1, .5},
onenterbordercolor = {1, 1, 1, 1},
}
3 years ago
--buttons
DF.button_templates = DF.button_templates or {}
3 years ago
DF.button_templates["OPTIONS_BUTTON_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {0, 0, 0, 1},
}
DF.button_templates["OPTIONS_CIRCLEBUTTON_TEMPLATE"] = {
rounded_corner = {
color = {.075, .075, .075, 1},
border_color = {.2, .2, .2, 1},
roundness = 8,
},
}
DF.button_templates["OPTIONS_BUTTON_GOLDENBORDER_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {1, 1, 1, .5},
backdropbordercolor = {1, 0.785, 0, 1},
}
DF.button_templates["STANDARD_GRAY"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {0.2, 0.2, 0.2, 0.502},
backdropbordercolor = {0, 0, 0, 0.5},
onentercolor = {0.4, 0.4, 0.4, 0.502},
}
DF.button_templates["OPAQUE_DARK"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Buttons\WHITE8X8]], tileSize = 8, tile = true},
backdropcolor = {0.2, 0.2, 0.2, 1},
backdropbordercolor = {0, 0, 0, 1},
onentercolor = {0.4, 0.4, 0.4, 1},
}
3 years ago
--sliders
DF.slider_templates = DF.slider_templates or {}
3 years ago
DF.slider_templates["OPTIONS_SLIDER_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
--original color wow10:
--backdropcolor = {1, 1, 1, .5},
--backdropbordercolor = {0, 0, 0, 1},
--onentercolor = {1, 1, 1, .5},
--onenterbordercolor = {1, 1, 1, 1},
backdropcolor = {0.2, 0.2, 0.2, .7},
onentercolor = {0.3, 0.3, 0.3, .7},
backdropbordercolor = {0, 0, 0, .4}, --0.7 original alpha wow10
onenterbordercolor = {0.3, 0.3, 0.3, 0.8},
thumbtexture = [[Interface\Tooltips\UI-Tooltip-Background]],
thumbwidth = 16,
thumbheight = 14,
--thumbcolor = {0, 0, 0, 0.5},
thumbcolor = {.8, .8, .8, 0.5},
}
DF.slider_templates["OPTIONS_SLIDERDARK_TEMPLATE"] = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdropcolor = {0.05, 0.05, 0.05, .7},
onentercolor = {0.3, 0.3, 0.3, .7},
backdropbordercolor = {0, 0, 0, 1},
onenterbordercolor = {0, 0, 0, 1},
thumbtexture = [[Interface\Tooltips\UI-Tooltip-Background]],
thumbwidth = 24,
thumbheight = 14,
thumbcolor = {.8, .8, .8, 0.5},
}
DF.slider_templates["MODERN_SLIDER_TEMPLATE"] = {
thumbtexture = "Minimal_SliderBar_Button", --atlas name
thumbwidth = 20,
thumbheight = 19,
thumbcolor = {1, 1, 1, 0.924},
slider_left = "Minimal_SliderBar_Left",
slider_right = "Minimal_SliderBar_Right",
slider_middle = "_Minimal_SliderBar_Middle",
amount_color = "white",
amount_size = 12,
amount_outline = "outline",
}
local templateTables = {DF.dropdown_templates, DF.button_templates, DF.switch_templates, DF.slider_templates, DF.font_templates}
---template categories: "font", "dropdown", "button", "switch", "slider"
---receives a template category and a template name or table
---if a template name has been passed, the function will iterate over all template tables to find a template with the name passed
---@param self table
---@param templateCategory templatecategory
---@param template string|table
---@return table
function DF:ParseTemplate(templateCategory, template)
if (type(template) == "string") then
local objectType = templateCategory
if (objectType == "label") then
templateCategory = "font"
5 months ago
elseif (objectType == "dropdown" or objectType == "textentry") then
templateCategory = "dropdown"
elseif (objectType == "button") then
templateCategory = "button"
elseif (objectType == "switch") then
templateCategory = "switch"
elseif (objectType == "slider") then
templateCategory = "slider"
end
local templateTable = DF:GetTemplate(templateCategory, template)
if (templateTable) then
return templateTable
end
--iterate over all template tables to find a template with the name passed
for i = 1, #templateTables do
local tTable = templateTables[i]
if (tTable[template]) then
return tTable[template]
end
end
else
return template
end
---@cast template table
return template
end
---register a new template to be used with SetTemplate calls
---@param templateCategory templatecategory
---@param templateName string
---@param template table
---@param parentName any
---@return table
function DF:InstallTemplate(templateCategory, templateName, template, parentName)
local newTemplate = {}
3 years ago
--if has a parent, just copy the parent to the new template
3 years ago
if (parentName and type(parentName) == "string") then
local parentTemplate = DF:GetTemplate(templateCategory, parentName)
if (parentTemplate) then
3 years ago
DF.table.copy(newTemplate, parentTemplate)
end
end
3 years ago
--copy the template passed into the new template
3 years ago
DF.table.copy(newTemplate, template)
templateCategory = string.lower(templateCategory)
3 years ago
local templateTable
if (templateCategory == "font") then
3 years ago
templateTable = DF.font_templates
local font = template.font
if (font) then
3 years ago
--fonts passed into the template has default to western
--the framework will get the game client language and change the font if needed
font = DF:GetBestFontForLanguage(nil, font)
end
3 years ago
elseif (templateCategory == "dropdown") then
3 years ago
templateTable = DF.dropdown_templates
elseif (templateCategory == "button") then
3 years ago
templateTable = DF.button_templates
elseif (templateCategory == "switch") then
3 years ago
templateTable = DF.switch_templates
elseif (templateCategory == "slider") then
3 years ago
templateTable = DF.slider_templates
end
3 years ago
templateTable[templateName] = newTemplate
return newTemplate
end
function DF:GetTemplate(widgetType, templateName)
widgetType = string.lower(widgetType)
local templateTable
if (widgetType == "font") then
templateTable = DF.font_templates
elseif (widgetType == "dropdown") then
templateTable = DF.dropdown_templates
elseif (widgetType == "button") then
templateTable = DF.button_templates
elseif (widgetType == "switch") then
templateTable = DF.switch_templates
elseif (widgetType == "slider") then
templateTable = DF.slider_templates
end
return templateTable[templateName]
end
---get the name of the parent of the passed frame
---@param frame frame
---@return string
function DF:GetParentName(frame)
local parentName = frame:GetName()
if (not parentName) then
3 years ago
error("Details! FrameWork: called $parent but parent was no name.", 2)
end
return parentName
end
function DF:Error(errortext)
3 years ago
print("|cFFFF2222Details! Framework Error|r:", errortext, self.GetName and self:GetName(), self.WidgetType, debugstack (2, 3, 0))
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--members
DF.GlobalWidgetControlNames = {
textentry = "DF_TextEntryMetaFunctions",
button = "DF_ButtonMetaFunctions",
panel = "DF_PanelMetaFunctions",
dropdown = "DF_DropdownMetaFunctions",
label = "DF_LabelMetaFunctions",
normal_bar = "DF_NormalBarMetaFunctions",
image = "DF_ImageMetaFunctions",
slider = "DF_SliderMetaFunctions",
split_bar = "DF_SplitBarMetaFunctions",
aura_tracker = "DF_AuraTracker",
healthBar = "DF_healthBarMetaFunctions",
timebar = "DF_TimeBarMetaFunctions",
}
function DF:AddMemberForWidget(widgetName, memberType, memberName, func)
if (DF.GlobalWidgetControlNames[widgetName]) then
3 years ago
if (type(memberName) == "string" and (memberType == "SET" or memberType == "GET")) then
if (func) then
local widgetControlObject = _G [DF.GlobalWidgetControlNames[widgetName]]
if (memberType == "SET") then
widgetControlObject["SetMembers"][memberName] = func
elseif (memberType == "GET") then
widgetControlObject["GetMembers"][memberName] = func
end
else
if (DF.debug) then
3 years ago
error("Details! Framework: AddMemberForWidget invalid function.")
end
end
else
if (DF.debug) then
3 years ago
error("Details! Framework: AddMemberForWidget unknown memberName or memberType.")
end
end
else
if (DF.debug) then
3 years ago
error("Details! Framework: AddMemberForWidget unknown widget type: " .. (widgetName or "") .. ".")
end
end
end
-----------------------------
function DF:OpenInterfaceProfile()
-- OptionsFrame1/2 should be registered if created with DF:CreateAddOn, so open to them directly
if self.OptionsFrame1 then
3 years ago
if SettingsPanel then
--SettingsPanel:OpenToCategory(self.OptionsFrame1.name)
local category = SettingsPanel:GetCategoryList():GetCategory(self.OptionsFrame1.name)
if category then
SettingsPanel:Open()
SettingsPanel:SelectCategory(category)
if self.OptionsFrame2 and category:HasSubcategories() then
for _, subcategory in pairs(category:GetSubcategories()) do
if subcategory:GetName() == self.OptionsFrame2.name then
SettingsPanel:SelectCategory(subcategory)
break
end
end
end
end
return
elseif InterfaceOptionsFrame_OpenToCategory then
InterfaceOptionsFrame_OpenToCategory (self.OptionsFrame1)
if self.OptionsFrame2 then
InterfaceOptionsFrame_OpenToCategory (self.OptionsFrame2)
end
return
end
end
-- fallback (broken as of ElvUI Skins in version 12.18+... maybe fix/change will come)
InterfaceOptionsFrame_OpenToCategory (self.__name)
InterfaceOptionsFrame_OpenToCategory (self.__name)
for i = 1, 100 do
local button = _G ["InterfaceOptionsFrameAddOnsButton" .. i]
if (button) then
local text = _G ["InterfaceOptionsFrameAddOnsButton" .. i .. "Text"]
if (text) then
text = text:GetText()
if (text == self.__name) then
local toggle = _G ["InterfaceOptionsFrameAddOnsButton" .. i .. "Toggle"]
if (toggle) then
3 years ago
if (toggle:GetNormalTexture():GetTexture():find("PlusButton")) then
--is minimized, need expand
toggle:Click()
_G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click()
3 years ago
elseif (toggle:GetNormalTexture():GetTexture():find("MinusButton")) then
--isn't minimized
_G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click()
end
end
break
end
end
else
3 years ago
self:Msg("Couldn't not find the profile panel.")
break
end
end
end
-----------------------------
---copy all members from #2 ... to #1 object
---@param object table
---@param ... any
---@return any
function DF:Mixin(object, ...) --safe copy from blizz api
for i = 1, select("#", ...) do
3 years ago
local mixin = select(i, ...)
for key, value in pairs(mixin) do
object[key] = value
end
end
3 years ago
return object
end
-----------------------------
3 years ago
--animations
---create an animation 'hub' which is an animationGroup but with some extra functions
--tags: create, animation, hub, group, animationgroup, createanimationhub
--prompt example: create an animation group for the object 'variable name' with the start animation function doing 'what to do' and the finish animation function doing 'what to do'
---@param parent uiobject
---@param onPlay function?
---@param onFinished function?
---@return animationgroup
3 years ago
function DF:CreateAnimationHub(parent, onPlay, onFinished)
local newAnimation = parent:CreateAnimationGroup()
3 years ago
newAnimation:SetScript("OnPlay", onPlay)
newAnimation:SetScript("OnFinished", onFinished)
newAnimation:SetScript("OnStop", onFinished)
newAnimation.NextAnimation = 1
return newAnimation
end
---animation descriptions:
--tags: animation, create, alpha, scale, translation, rotation, path, vertexcolor, color, animation type, animation duration, animation order, animation object, return variable
--prompt example: create a new animation of type 'alpha' for the animation group 'variable name', with an order of 'number', a duration of 'number', from alpha 'number' to alpha 'number'
---* Create a new animation for an animation hub created with CreateAnimationHub().
---* Alpha: CreateAnimation(animGroup, "Alpha", order, duration, fromAlpha, toAlpha).
---* Scale: CreateAnimation(animGroup, "Scale", order, duration, fromScaleX, fromScaleY, toScaleX, toScaleY, originPoint, x, y).
---* Translation: CreateAnimation(animGroup, "Translation", order, duration, xOffset, yOffset).
---* Rotation: CreateAnimation(animGroup, "Rotation", order, duration, degrees, originPoint, x, y).
---* Path: CreateAnimation(animGroup, "Path", order, duration, xOffset, yOffset, curveType).
---* VertexColor: CreateAnimation(animGroup, "VertexColor", order, duration, r1, g1, b1, a1, r2, g2, b2, a2).
---@param animationGroup animationgroup the animation group created with CreateAnimationHub()
---@param animationType animationtype "Alpha", "Scale", "Translation", "Rotation", "Path", "VertexColor"
---@param order number the order of the animation, the lower the number, the earlier the animation will play
---@param duration number the duration of the animation in seconds
---@param arg1 any for Alpha: fromAlpha, for Scale: fromScaleX, for Translation: xOffset, for Rotation: degrees, for Path: xOffset, for VertexColor: r1
---@param arg2 any for Alpha: toAlpha, for Scale: fromScaleY, for Translation: yOffset, for Rotation: originPoint, for Path: yOffset, for VertexColor: g1
---@param arg3 any for Scale: toScaleX, for VertexColor: blue1, for Rotation: originXOffset, for Path: curveType, for VertexColor: b1
---@param arg4 any for Scale: toScaleY, for VertexColor: a1, for Rotation: originYOffset, for VertexColor: a1
---@param arg5 any for Scale: originPoint, for VertexColor: r2
---@param arg6 any for Scale: originXOffset, for VertexColor: g2
---@param arg7 any for Scale: originYOffset, for VertexColor: b2
---@param arg8 any for VertexColor: a2
---@return animation
function DF:CreateAnimation(animationGroup, animationType, order, duration, arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8)
---@type animation
local anim = animationGroup:CreateAnimation(animationType)
--set the order of the animation, the 'order' parameter isn't passed, it will use the NextAnimation property of the animationGroup
anim:SetOrder(order or animationGroup.NextAnimation)
--set the duration of the animation
3 years ago
anim:SetDuration(duration)
animationType = string.upper(animationType)
if (animationType == "ALPHA") then
anim:SetFromAlpha(arg1)
anim:SetToAlpha(arg2)
elseif (animationType == "SCALE") then
if (DF.IsDragonflight() or DF.IsNonRetailWowWithRetailAPI() or DF.IsWarWow()) then
3 years ago
anim:SetScaleFrom(arg1, arg2)
anim:SetScaleTo(arg3, arg4)
else
anim:SetFromScale(arg1, arg2)
anim:SetToScale(arg3, arg4)
end
anim:SetOrigin(arg5 or "center", arg6 or 0, arg7 or 0) --point, originXOffset, originYOffset
3 years ago
elseif (animationType == "ROTATION") then
anim:SetDegrees(arg1) --degree
anim:SetOrigin(arg2 or "center", arg3 or 0, arg4 or 0) --originPoint, originXOffset, originYOffset
3 years ago
elseif (animationType == "TRANSLATION") then
anim:SetOffset(arg1, arg2)
elseif (animationType == "PATH") then
local newControlPoint = anim:CreateControlPoint()
anim:SetCurveType(arg4 or "SMOOTH")
newControlPoint:SetOffset(arg2, arg3)
newControlPoint:SetOrder(#anim:GetControlPoints())
elseif (animationType == "VERTEXCOLOR" or animationType == "COLOR") then
local r1, g1, b1, a1 = arg1, arg2, arg3, arg4
local r2, g2, b2, a2 = arg5, arg6, arg7, arg8
if ((type(r1) == "table" or type(r1) == "string") and (type(g1) == "table" or type(g1) == "string")) then
r2, g2, b2, a2 = DF:ParseColors(g1)
r1, g1, b1, a1 = DF:ParseColors(r1)
elseif ((type(r1) == "table" or type(r1) == "string")) then
r1, g1, b1, a1 = DF:ParseColors(r1)
elseif ((type(r2) == "table" or type(r2) == "string")) then
r2, g2, b2, a2 = DF:ParseColors(r2)
end
--CreateColor is a function declared in the game api that return a table with the color values in keys r, g, b, a
anim:SetStartColor(CreateColor(r1, g1, b1, a1))
anim:SetEndColor(CreateColor(r2, g2, b2, a2))
end
3 years ago
animationGroup.NextAnimation = animationGroup.NextAnimation + 1
return anim
end
---receives an uiobject, when its parent get hover overed, starts the fade in animation
---start the fade out animation when the mouse leaves the parent
---@param UIObject uiobject
---@param fadeInTime number
---@param fadeOutTime number
---@param fadeInAlpha number
---@param fadeOutAlpha number
function DF:CreateFadeAnimation(UIObject, fadeInTime, fadeOutTime, fadeInAlpha, fadeOutAlpha)
fadeInTime = fadeInTime or 0.1
fadeOutTime = fadeOutTime or 0.1
fadeInAlpha = fadeInAlpha or 1
fadeOutAlpha = fadeOutAlpha or 0
local fadeInAnimationHub = DF:CreateAnimationHub(UIObject, function() UIObject:Show(); UIObject:SetAlpha(fadeOutAlpha) end, function() UIObject:SetAlpha(fadeInAlpha) end)
local fadeIn = DF:CreateAnimation(fadeInAnimationHub, "Alpha", 1, fadeInTime, fadeOutAlpha, fadeInAlpha)
local fadeOutAnimationHub = DF:CreateAnimationHub(UIObject, nil, function() UIObject:Hide(); UIObject:SetAlpha(0) end)
local fadeOut = DF:CreateAnimation(fadeOutAnimationHub, "Alpha", 2, fadeOutTime, fadeInAlpha, fadeOutAlpha)
local scriptFrame
--hook the parent OnEnter and OnLeave
if (UIObject:IsObjectType("FontString") or UIObject:IsObjectType("Texture")) then
scriptFrame = UIObject:GetParent()
else
scriptFrame = UIObject
end
---@cast scriptFrame frame
scriptFrame:HookScript("OnEnter", function() fadeOutAnimationHub:Stop(); fadeInAnimationHub:Play() end)
scriptFrame:HookScript("OnLeave", function() fadeInAnimationHub:Stop(); fadeOutAnimationHub:Play() end)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--frame shakes
3 years ago
--frame shakes rely on OnUpdate scripts, we are using a built-in OnUpdate so is guarantee it'll run
local FrameshakeUpdateFrame = DetailsFrameworkFrameshakeControl or CreateFrame("frame", "DetailsFrameworkFrameshakeControl", UIParent)
--store the frame which has frame shakes registered
FrameshakeUpdateFrame.RegisteredFrames = FrameshakeUpdateFrame.RegisteredFrames or {}
3 years ago
FrameshakeUpdateFrame.RegisterFrame = function(newFrame)
--add the frame into the registered frames to update
DF.table.addunique(FrameshakeUpdateFrame.RegisteredFrames, newFrame)
end
--forward declared
local frameshake_DoUpdate
3 years ago
FrameshakeUpdateFrame:SetScript("OnUpdate", function(self, deltaTime)
for i = 1, #FrameshakeUpdateFrame.RegisteredFrames do
local parent = FrameshakeUpdateFrame.RegisteredFrames [i]
3 years ago
--check if there's a shake running
if (parent.__frameshakes.enabled > 0) then
--update all shakes for this frame
for i = 1, #parent.__frameshakes do
local shakeObject = parent.__frameshakes [i]
if (shakeObject.IsPlaying) then
frameshake_DoUpdate(parent, shakeObject, deltaTime)
end
end
end
end
end)
local frameshake_ShakeFinished = function(parent, shakeObject)
if (shakeObject.IsPlaying) then
shakeObject.IsPlaying = false
shakeObject.TimeLeft = 0
shakeObject.IsFadingOut = false
shakeObject.IsFadingIn = false
3 years ago
--update the amount of shake running on this frame
parent.__frameshakes.enabled = parent.__frameshakes.enabled - 1
3 years ago
--restore the default anchors, in case where deltaTime was too small that didn't triggered an update
for i = 1, #shakeObject.Anchors do
local anchor = shakeObject.Anchors [i]
3 years ago
--automatic anchoring and reanching needs to the reviwed in the future
if (#anchor == 1) then
3 years ago
local anchorTo = unpack(anchor)
parent:ClearAllPoints()
3 years ago
parent:SetPoint(anchorTo)
elseif (#anchor == 2) then
3 years ago
local anchorTo, point1 = unpack(anchor)
parent:ClearAllPoints()
3 years ago
parent:SetPoint(anchorTo, point1)
elseif (#anchor == 3) then
3 years ago
local anchorTo, point1, point2 = unpack(anchor)
parent:SetPoint(anchorTo, point1, point2)
elseif (#anchor == 5) then
3 years ago
local anchorName1, anchorTo, anchorName2, point1, point2 = unpack(anchor)
parent:SetPoint(anchorName1, anchorTo, anchorName2, point1, point2)
end
end
end
end
--already declared above the update function
frameshake_DoUpdate = function(parent, shakeObject, deltaTime)
3 years ago
--check delta time
deltaTime = deltaTime or 0
3 years ago
--update time left
shakeObject.TimeLeft = max(shakeObject.TimeLeft - deltaTime, 0)
if (shakeObject.TimeLeft > 0) then
3 years ago
--update fade in and out
if (shakeObject.IsFadingIn) then
shakeObject.IsFadingInTime = shakeObject.IsFadingInTime + deltaTime
end
if (shakeObject.IsFadingOut) then
shakeObject.IsFadingOutTime = shakeObject.IsFadingOutTime + deltaTime
end
3 years ago
--check if can disable fade in
if (shakeObject.IsFadingIn and shakeObject.IsFadingInTime > shakeObject.FadeInTime) then
shakeObject.IsFadingIn = false
end
3 years ago
--check if can enable fade out
if (not shakeObject.IsFadingOut and shakeObject.TimeLeft < shakeObject.FadeOutTime) then
shakeObject.IsFadingOut = true
shakeObject.IsFadingOutTime = shakeObject.FadeOutTime - shakeObject.TimeLeft
end
3 years ago
--update position
local scaleShake = min(shakeObject.IsFadingIn and (shakeObject.IsFadingInTime / shakeObject.FadeInTime) or 1, shakeObject.IsFadingOut and (1 - shakeObject.IsFadingOutTime / shakeObject.FadeOutTime) or 1)
if (scaleShake > 0) then
3 years ago
--delate the time by the frequency on both X and Y offsets
shakeObject.XSineOffset = shakeObject.XSineOffset + (deltaTime * shakeObject.Frequency)
shakeObject.YSineOffset = shakeObject.YSineOffset + (deltaTime * shakeObject.Frequency)
3 years ago
--calc the new position
local newX, newY
if (shakeObject.AbsoluteSineX) then
--absoluting only the sine wave, passing a negative scale will reverse the absolute direction
newX = shakeObject.Amplitude * math.abs(math.sin(shakeObject.XSineOffset)) * scaleShake * shakeObject.ScaleX
else
newX = shakeObject.Amplitude * math.sin(shakeObject.XSineOffset) * scaleShake * shakeObject.ScaleX
end
if (shakeObject.AbsoluteSineY) then
newY = shakeObject.Amplitude * math.abs(math.sin(shakeObject.YSineOffset)) * scaleShake * shakeObject.ScaleY
else
newY = shakeObject.Amplitude * math.sin(shakeObject.YSineOffset) * scaleShake * shakeObject.ScaleY
end
3 years ago
--apply the offset to the frame anchors
for i = 1, #shakeObject.Anchors do
local anchor = shakeObject.Anchors [i]
if (#anchor == 1 or #anchor == 3) then
3 years ago
local anchorTo, point1, point2 = unpack(anchor)
point1 = point1 or 0
point2 = point2 or 0
3 years ago
parent:SetPoint(anchorTo, point1 + newX, point2 + newY)
elseif (#anchor == 5) then
3 years ago
local anchorName1, anchorTo, anchorName2, point1, point2 = unpack(anchor)
parent:SetPoint(anchorName1, anchorTo, anchorName2, point1 + newX, point2 + newY)
end
end
end
else
frameshake_ShakeFinished(parent, shakeObject)
end
end
3 years ago
local frameshake_stop = function(parent, shakeObject)
frameshake_ShakeFinished(parent, shakeObject)
end
3 years ago
--scale direction scales the X and Y coordinates, scale strength scales the amplitude and frequency
local frameshake_play = function(parent, shakeObject, scaleDirection, scaleAmplitude, scaleFrequency, scaleDuration)
--check if is already playing
if (shakeObject.TimeLeft > 0) then
3 years ago
--reset the time left
shakeObject.TimeLeft = shakeObject.Duration
if (shakeObject.IsFadingOut) then
if (shakeObject.FadeInTime > 0) then
shakeObject.IsFadingIn = true
3 years ago
--scale the current fade out into fade in, so it starts the fade in at the point where it was fading out
shakeObject.IsFadingInTime = shakeObject.FadeInTime * (1 - shakeObject.IsFadingOutTime / shakeObject.FadeOutTime)
else
shakeObject.IsFadingIn = false
shakeObject.IsFadingInTime = 0
end
3 years ago
--disable fade out and enable fade in
shakeObject.IsFadingOut = false
shakeObject.IsFadingOutTime = 0
end
else
3 years ago
--create a new random offset
shakeObject.XSineOffset = math.pi * 2 * math.random()
shakeObject.YSineOffset = math.pi * 2 * math.random()
3 years ago
--store the initial position if case it needs a reset
shakeObject.StartedXSineOffset = shakeObject.XSineOffset
shakeObject.StartedYSineOffset = shakeObject.YSineOffset
3 years ago
--check if there's a fade in time
if (shakeObject.FadeInTime > 0) then
shakeObject.IsFadingIn = true
else
shakeObject.IsFadingIn = false
end
shakeObject.IsFadingInTime = 0
shakeObject.IsFadingOut = false
shakeObject.IsFadingOutTime = 0
3 years ago
--apply custom scale
shakeObject.ScaleX = (scaleDirection or 1) * shakeObject.OriginalScaleX
shakeObject.ScaleY = (scaleDirection or 1) * shakeObject.OriginalScaleY
shakeObject.Frequency = (scaleFrequency or 1) * shakeObject.OriginalFrequency
shakeObject.Amplitude = (scaleAmplitude or 1) * shakeObject.OriginalAmplitude
shakeObject.Duration = (scaleDuration or 1) * shakeObject.OriginalDuration
3 years ago
--update the time left
shakeObject.TimeLeft = shakeObject.Duration
3 years ago
--check if is dynamic points
if (shakeObject.IsDynamicAnchor) then
wipe(shakeObject.Anchors)
for i = 1, parent:GetNumPoints() do
local p1, p2, p3, p4, p5 = parent:GetPoint(i)
shakeObject.Anchors[#shakeObject.Anchors+1] = {p1, p2, p3, p4, p5}
end
end
3 years ago
--update the amount of shake running on this frame
parent.__frameshakes.enabled = parent.__frameshakes.enabled + 1
if (parent:HasScript("OnUpdate") and not parent:GetScript("OnUpdate")) then
3 years ago
parent:SetScript("OnUpdate", function()end)
end
end
shakeObject.IsPlaying = true
frameshake_DoUpdate(parent, shakeObject)
end
local frameshake_SetConfig = function(parent, shakeObject, duration, amplitude, frequency, absoluteSineX, absoluteSineY, scaleX, scaleY, fadeInTime, fadeOutTime)
shakeObject.Amplitude = amplitude or shakeObject.Amplitude
shakeObject.Frequency = frequency or shakeObject.Frequency
shakeObject.Duration = duration or shakeObject.Duration
shakeObject.FadeInTime = fadeInTime or shakeObject.FadeInTime
shakeObject.FadeOutTime = fadeOutTime or shakeObject.FadeOutTime
shakeObject.ScaleX = scaleX or shakeObject.ScaleX
shakeObject.ScaleY = scaleY or shakeObject.ScaleY
if (absoluteSineX ~= nil) then
shakeObject.AbsoluteSineX = absoluteSineX
end
if (absoluteSineY ~= nil) then
shakeObject.AbsoluteSineY = absoluteSineY
end
shakeObject.OriginalScaleX = shakeObject.ScaleX
shakeObject.OriginalScaleY = shakeObject.ScaleY
shakeObject.OriginalFrequency = shakeObject.Frequency
shakeObject.OriginalAmplitude = shakeObject.Amplitude
shakeObject.OriginalDuration = shakeObject.Duration
end
---@class df_frameshake : table
---@field Amplitude number
---@field Frequency number
---@field Duration number
---@field FadeInTime number
---@field FadeOutTime number
---@field ScaleX number
---@field ScaleY number
---@field AbsoluteSineX boolean
---@field AbsoluteSineY boolean
---@field IsPlaying boolean
---@field TimeLeft number
---@field OriginalScaleX number
---@field OriginalScaleY number
---@field OriginalFrequency number
---@field OriginalAmplitude number
---@field OriginalDuration number
---@field PlayFrameShake fun(parent:uiobject, shakeObject:df_frameshake, scaleDirection:number?, scaleAmplitude:number?, scaleFrequency:number?, scaleDuration:number?)
---@field StopFrameShake fun(parent:uiobject, shakeObject:df_frameshake)
---@field SetFrameShakeSettings fun(parent:uiobject, shakeObject:df_frameshake, duration:number?, amplitude:number?, frequency:number?, absoluteSineX:boolean?, absoluteSineY:boolean?, scaleX:number?, scaleY:number?, fadeInTime:number?, fadeOutTime:number?)
---create a frame shake object
---@param parent uiobject
---@param duration number?
---@param amplitude number?
---@param frequency number?
---@param absoluteSineX boolean?
---@param absoluteSineY boolean?
---@param scaleX number?
---@param scaleY number?
---@param fadeInTime number?
---@param fadeOutTime number?
---@param anchorPoints table?
---@return df_frameshake
function DF:CreateFrameShake(parent, duration, amplitude, frequency, absoluteSineX, absoluteSineY, scaleX, scaleY, fadeInTime, fadeOutTime, anchorPoints)
3 years ago
--create the shake table
local frameShake = {
Amplitude = amplitude or 2,
Frequency = frequency or 5,
Duration = duration or 0.3,
FadeInTime = fadeInTime or 0.01,
FadeOutTime = fadeOutTime or 0.01,
ScaleX = scaleX or 0.2,
ScaleY = scaleY or 1,
AbsoluteSineX = absoluteSineX,
AbsoluteSineY = absoluteSineY,
--
IsPlaying = false,
TimeLeft = 0,
}
frameShake.OriginalScaleX = frameShake.ScaleX
frameShake.OriginalScaleY = frameShake.ScaleY
frameShake.OriginalFrequency = frameShake.Frequency
frameShake.OriginalAmplitude = frameShake.Amplitude
frameShake.OriginalDuration = frameShake.Duration
3 years ago
if (type(anchorPoints) ~= "table") then
frameShake.IsDynamicAnchor = true
frameShake.Anchors = {}
else
frameShake.Anchors = anchorPoints
end
3 years ago
--inject frame shake table into the frame
if (not parent.__frameshakes) then
parent.__frameshakes = {
enabled = 0,
}
parent.PlayFrameShake = frameshake_play
parent.StopFrameShake = frameshake_stop
parent.UpdateFrameShake = frameshake_DoUpdate
parent.SetFrameShakeSettings = frameshake_SetConfig
3 years ago
--register the frame within the frame shake updater
FrameshakeUpdateFrame.RegisterFrame (parent)
end
table.insert(parent.__frameshakes, frameShake)
return frameShake
end
-----------------------------
3 years ago
--glow overlay
3 years ago
local glow_overlay_play = function(self)
if (not self:IsShown()) then
self:Show()
end
if (self.animOut) then
if (self.animOut:IsPlaying()) then
self.animOut:Stop()
end
if (not self.animIn:IsPlaying()) then
self.animIn:Stop()
self.animIn:Play()
end
elseif (self.ProcStartAnim) then
if (not self.ProcStartAnim:IsPlaying()) then
self.ProcStartAnim:Play()
end
if (not self.ProcLoop:IsPlaying()) then
--self.ProcLoop:Play()
end
end
end
3 years ago
local glow_overlay_stop = function(self)
if (self.animOut) then
if (self.animOut:IsPlaying()) then
self.animOut:Stop()
end
if (self.animIn:IsPlaying()) then
self.animIn:Stop()
end
elseif (self.ProcStartAnim) then
if (self.ProcStartAnim:IsPlaying()) then
self.ProcStartAnim:Stop()
end
end
if (self:IsShown()) then
self:Hide()
end
end
3 years ago
local glow_overlay_setcolor = function(self, antsColor, glowColor)
if (antsColor) then
3 years ago
local r, g, b, a = DF:ParseColors(antsColor)
self.AntsColor = {r, g, b, a}
if (self.ants) then
self.ants:SetVertexColor(r, g, b, a)
elseif (self.ProcLoopFlipbook) then
self.ProcLoopFlipbook:SetVertexColor(r, g, b) --no alpha because of animation
local anim1 = self.ProcLoop:GetAnimations()
anim1:SetToAlpha(a)
end
end
if (glowColor) then
3 years ago
local r, g, b, a = DF:ParseColors(glowColor)
self.GlowColor = {r, g, b, a}
if (self.outerGlow) then
self.outerGlow:SetVertexColor(r, g, b, a)
elseif (self.ProcStartFlipbook) then
self.ProcStartFlipbook:SetVertexColor(r, g, b) --no alpha because of animation
local anim1, anim2, anim3 = self.ProcStartAnim:GetAnimations()
anim1:SetToAlpha(a)
anim3:SetFromAlpha(a)
end
end
end
3 years ago
local glow_overlay_onshow = function(self)
glow_overlay_play(self)
end
3 years ago
local glow_overlay_onhide = function(self)
glow_overlay_stop(self)
end
---create a glow overlay around a frame, return a frame and also add parent.overlay to the parent frame
---@param self table
---@param parent frame
---@param antsColor any
---@param glowColor any
function DF:CreateGlowOverlay(parent, antsColor, glowColor)
local parentName = parent:GetName()
local frameName = parentName and (parentName .. "Glow2") or "OverlayActionGlow" .. math.random(1, 10000000)
if (frameName and string.len(frameName) > 50) then --shorten to work around too long names
frameName = string.sub(frameName, string.len(frameName)-49)
end
5 months ago
local glowFrame
if (buildInfo >= 110107) then --24-05-2025: in the 11.1.7 patch, the template used here does not exist anymore, replacement used
glowFrame = CreateFrame("frame", frameName, parent, "ActionButtonSpellAlertTemplate")
else
glowFrame = CreateFrame("frame", frameName, parent, "ActionBarButtonSpellActivationAlert")
end
--local glowFrame = CreateFrame("frame", frameName, parent)
glowFrame:HookScript("OnShow", glow_overlay_onshow)
glowFrame:HookScript("OnHide", glow_overlay_onhide)
glowFrame.Play = glow_overlay_play
glowFrame.Stop = glow_overlay_stop
glowFrame.SetColor = glow_overlay_setcolor
glowFrame:SetColor(antsColor, glowColor)
glowFrame:Hide()
parent.overlay = glowFrame
local frameWidth, frameHeight = parent:GetSize()
local scale = 1.4
--make the height/width available before the next frame:
glowFrame:SetSize(frameWidth * scale, frameHeight * scale)
glowFrame:SetPoint("topleft", parent, "topleft", -frameWidth * 0.32, frameHeight * 0.36)
glowFrame:SetPoint("bottomright", parent, "bottomright", frameWidth * 0.32, -frameHeight * 0.36)
if (glowFrame.outerGlow) then
glowFrame.outerGlow:SetScale(1.2)
end
if (glowFrame.ProcStartFlipbook) then
glowFrame.ProcStartAnim:Stop()
glowFrame.ProcStartFlipbook:ClearAllPoints()
glowFrame.ProcStartFlipbook:SetPoint("TOPLEFT", glowFrame, "TOPLEFT", -frameWidth * scale, frameHeight * scale)
glowFrame.ProcStartFlipbook:SetPoint("BOTTOMRIGHT", glowFrame, "BOTTOMRIGHT", frameWidth * scale, -frameHeight * scale)
end
3 years ago
glowFrame:EnableMouse(false)
return glowFrame
end
3 years ago
--custom glow with ants animation
local ants_set_texture_offset = function(self, leftOffset, rightOffset, topOffset, bottomOffset)
leftOffset = leftOffset or 0
rightOffset = rightOffset or 0
topOffset = topOffset or 0
bottomOffset = bottomOffset or 0
self:ClearAllPoints()
3 years ago
self:SetPoint("topleft", leftOffset, topOffset)
self:SetPoint("bottomright", rightOffset, bottomOffset)
end
---create an "ant" animation around the frame, the name "ant" comes from the animation looking like small bright dots moving around the frame
---@param parent frame
---@param antTable df_anttable
---@param leftOffset number?
---@param rightOffset number?
---@param topOffset number?
---@param bottomOffset number?
---@return frame
function DF:CreateAnts(parent, antTable, leftOffset, rightOffset, topOffset, bottomOffset)
leftOffset = leftOffset or 0
rightOffset = rightOffset or 0
topOffset = topOffset or 0
bottomOffset = bottomOffset or 0
local antsFrame = CreateFrame("frame", nil, parent)
antsFrame:SetPoint("topleft", leftOffset, topOffset)
antsFrame:SetPoint("bottomright", rightOffset, bottomOffset)
antsFrame.SetOffset = ants_set_texture_offset
local texture = antsFrame:CreateTexture(nil, "overlay")
texture:SetAllPoints()
texture:SetTexture(antTable.Texture)
texture:SetBlendMode(antTable.BlendMode or "ADD")
texture:SetVertexColor(DF:ParseColors(antTable.Color or "white"))
antsFrame.Texture = texture
antsFrame.AntTable = antTable
antsFrame:SetScript("OnUpdate", function(self, deltaTime)
AnimateTexCoords(texture, self.AntTable.TextureWidth, self.AntTable.TextureHeight, self.AntTable.TexturePartsWidth, self.AntTable.TexturePartsHeight, self.AntTable.AmountParts, deltaTime, self.AntTable.Throttle or 0.025)
end)
return antsFrame
end
--[=[ --test ants
do
local f = DF:CreateAnts (UIParent)
end
--]=]
-----------------------------
3 years ago
--borders
local default_border_color1 = .5
local default_border_color2 = .3
local default_border_color3 = .1
3 years ago
local SetBorderAlpha = function(self, alpha1, alpha2, alpha3)
self.Borders.Alpha1 = alpha1 or self.Borders.Alpha1
self.Borders.Alpha2 = alpha2 or self.Borders.Alpha2
self.Borders.Alpha3 = alpha3 or self.Borders.Alpha3
3 years ago
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetAlpha(self.Borders.Alpha1)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetAlpha(self.Borders.Alpha2)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetAlpha(self.Borders.Alpha3)
end
end
3 years ago
local SetBorderColor = function(self, r, g, b)
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetColorTexture(r, g, b)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetColorTexture(r, g, b)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetColorTexture(r, g, b)
end
end
3 years ago
local SetLayerVisibility = function(self, layer1Shown, layer2Shown, layer3Shown)
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetShown(layer1Shown)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetShown(layer2Shown)
end
3 years ago
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetShown(layer3Shown)
end
end
---create a border using three textures for each side of the frame, each texture has a different transparency creating a smooth gradient effect
---the parent frame receives three new methods: SetBorderAlpha(a1, a2, a3), SetBorderColor(r, g, b), SetLayerVisibility(layer1Shown, layer2Shown, layer3Shown)
---@param self table
---@param parent frame
---@param alpha1 number?
---@param alpha2 number?
---@param alpha3 number?
function DF:CreateBorder(parent, alpha1, alpha2, alpha3)
parent.Borders = {
Layer1 = {},
Layer2 = {},
Layer3 = {},
Alpha1 = alpha1 or default_border_color1,
Alpha2 = alpha2 or default_border_color2,
Alpha3 = alpha3 or default_border_color3,
}
parent.SetBorderAlpha = SetBorderAlpha
parent.SetBorderColor = SetBorderColor
parent.SetLayerVisibility = SetLayerVisibility
do
local leftBorder1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(leftBorder1, "topleft", parent, "topleft", -1, 1)
PixelUtil.SetPoint(leftBorder1, "bottomleft", parent, "bottomleft", -1, -1)
leftBorder1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local leftBorder2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(leftBorder2, "topleft", parent, "topleft", -2, 2)
PixelUtil.SetPoint(leftBorder2, "bottomleft", parent, "bottomleft", -2, -2)
leftBorder2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local leftBorder3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(leftBorder3, "topleft", parent, "topleft", -3, 3)
PixelUtil.SetPoint(leftBorder3, "bottomleft", parent, "bottomleft", -3, -3)
leftBorder3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
table.insert(parent.Borders.Layer1, leftBorder1)
table.insert(parent.Borders.Layer2, leftBorder2)
table.insert(parent.Borders.Layer3, leftBorder3)
end
do
local topBorder1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(topBorder1, "topleft", parent, "topleft", 0, 1)
PixelUtil.SetPoint(topBorder1, "topright", parent, "topright", 1, 1)
topBorder1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local topBorder2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(topBorder2, "topleft", parent, "topleft", -1, 2)
PixelUtil.SetPoint(topBorder2, "topright", parent, "topright", 2, 2)
topBorder2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local topBorder3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(topBorder3, "topleft", parent, "topleft", -2, 3)
PixelUtil.SetPoint(topBorder3, "topright", parent, "topright", 3, 3)
topBorder3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
table.insert(parent.Borders.Layer1, topBorder1)
table.insert(parent.Borders.Layer2, topBorder2)
table.insert(parent.Borders.Layer3, topBorder3)
end
do
local rightBorder1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(rightBorder1, "topright", parent, "topright", 1, 0)
PixelUtil.SetPoint(rightBorder1, "bottomright", parent, "bottomright", 1, -1)
rightBorder1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local rightBorder2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(rightBorder2, "topright", parent, "topright", 2, 1)
PixelUtil.SetPoint(rightBorder2, "bottomright", parent, "bottomright", 2, -2)
rightBorder2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local rightBorder3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(rightBorder3, "topright", parent, "topright", 3, 2)
PixelUtil.SetPoint(rightBorder3, "bottomright", parent, "bottomright", 3, -3)
rightBorder3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
table.insert(parent.Borders.Layer1, rightBorder1)
table.insert(parent.Borders.Layer2, rightBorder2)
table.insert(parent.Borders.Layer3, rightBorder3)
end
do
local bottomBorder1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(bottomBorder1, "bottomleft", parent, "bottomleft", 0, -1)
PixelUtil.SetPoint(bottomBorder1, "bottomright", parent, "bottomright", 0, -1)
bottomBorder1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local bottomBorder2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(bottomBorder2, "bottomleft", parent, "bottomleft", -1, -2)
PixelUtil.SetPoint(bottomBorder2, "bottomright", parent, "bottomright", 1, -2)
bottomBorder2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local bottomBorder3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(bottomBorder3, "bottomleft", parent, "bottomleft", -2, -3)
PixelUtil.SetPoint(bottomBorder3, "bottomright", parent, "bottomright", 2, -3)
bottomBorder3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
table.insert(parent.Borders.Layer1, bottomBorder1)
table.insert(parent.Borders.Layer2, bottomBorder2)
table.insert(parent.Borders.Layer3, bottomBorder3)
end
end
--DFNamePlateBorder as copy from "NameplateFullBorderTemplate" -> DF:CreateFullBorder (name, parent)
5 months ago
---@class df_nameplate_border_mixin : table
---@field SetVertexColor fun(self:border_frame, r:number, g:number, b:number, a:number)
---@field GetVertexColor fun(self:border_frame):number, number, number r, g, b
---@field SetBorderSizes fun(self:border_frame, borderSize:number, borderSizeMinPixels:number, upwardExtendHeightPixels:number, upwardExtendHeightMinPixels:number)
---@field UpdateSizes fun(self:border_frame)
---@field Left texture
---@field Right texture
---@field Bottom texture
---@field Top texture
---@field Textures texture[]
---@field borderSize number
---@field borderSizeMinPixels number
---@field upwardExtendHeightPixels number
---@field upwardExtendHeightMinPixels number
local DFNamePlateBorderTemplateMixin = {}
DF.NameplateBorderMixin = DFNamePlateBorderTemplateMixin
function DFNamePlateBorderTemplateMixin:SetVertexColor(r, g, b, a)
for i, texture in ipairs(self.Textures) do
texture:SetVertexColor(r, g, b, a);
end
end
3 years ago
function DFNamePlateBorderTemplateMixin:GetVertexColor()
for i, texture in ipairs(self.Textures) do
return texture:GetVertexColor();
end
end
function DFNamePlateBorderTemplateMixin:SetBorderSizes(borderSize, borderSizeMinPixels, upwardExtendHeightPixels, upwardExtendHeightMinPixels)
self.borderSize = borderSize;
self.borderSizeMinPixels = borderSizeMinPixels;
self.upwardExtendHeightPixels = upwardExtendHeightPixels;
self.upwardExtendHeightMinPixels = upwardExtendHeightMinPixels;
end
function DFNamePlateBorderTemplateMixin:UpdateSizes()
local borderSize = self.borderSize or 1;
local minPixels = self.borderSizeMinPixels or 2;
local upwardExtendHeightPixels = self.upwardExtendHeightPixels or borderSize;
local upwardExtendHeightMinPixels = self.upwardExtendHeightMinPixels or minPixels;
PixelUtil.SetWidth(self.Left, borderSize, minPixels);
PixelUtil.SetPoint(self.Left, "TOPRIGHT", self, "TOPLEFT", 0, upwardExtendHeightPixels, 0, upwardExtendHeightMinPixels);
PixelUtil.SetPoint(self.Left, "BOTTOMRIGHT", self, "BOTTOMLEFT", 0, -borderSize, 0, minPixels);
PixelUtil.SetWidth(self.Right, borderSize, minPixels);
PixelUtil.SetPoint(self.Right, "TOPLEFT", self, "TOPRIGHT", 0, upwardExtendHeightPixels, 0, upwardExtendHeightMinPixels);
PixelUtil.SetPoint(self.Right, "BOTTOMLEFT", self, "BOTTOMRIGHT", 0, -borderSize, 0, minPixels);
PixelUtil.SetHeight(self.Bottom, borderSize, minPixels);
PixelUtil.SetPoint(self.Bottom, "TOPLEFT", self, "BOTTOMLEFT", 0, 0);
PixelUtil.SetPoint(self.Bottom, "TOPRIGHT", self, "BOTTOMRIGHT", 0, 0);
if self.Top then
PixelUtil.SetHeight(self.Top, borderSize, minPixels);
PixelUtil.SetPoint(self.Top, "BOTTOMLEFT", self, "TOPLEFT", 0, 0);
PixelUtil.SetPoint(self.Top, "BOTTOMRIGHT", self, "TOPRIGHT", 0, 0);
end
end
5 months ago
---@class border_frame : frame, df_nameplate_border_mixin
function DF:CreateFullBorder(name, parent)
local border = CreateFrame("Frame", name, parent)
border:SetAllPoints()
border:SetIgnoreParentScale(true)
border:SetFrameLevel(border:GetParent():GetFrameLevel())
border.Textures = {}
Mixin(border, DFNamePlateBorderTemplateMixin)
local left = border:CreateTexture("$parentLeft", "BACKGROUND", nil, -8)
--left:SetDrawLayer("BACKGROUND", -8)
left:SetColorTexture(1, 1, 1, 1)
left:SetWidth(1.0)
left:SetPoint("TOPRIGHT", border, "TOPLEFT", 0, 1.0)
left:SetPoint("BOTTOMRIGHT", border, "BOTTOMLEFT", 0, -1.0)
border.Left = left
table.insert(border.Textures, left)
local right = border:CreateTexture("$parentRight", "BACKGROUND", nil, -8)
--right:SetDrawLayer("BACKGROUND", -8)
right:SetColorTexture(1, 1, 1, 1)
right:SetWidth(1.0)
right:SetPoint("TOPLEFT", border, "TOPRIGHT", 0, 1.0)
right:SetPoint("BOTTOMLEFT", border, "BOTTOMRIGHT", 0, -1.0)
border.Right = right
table.insert(border.Textures, right)
local bottom = border:CreateTexture("$parentBottom", "BACKGROUND", nil, -8)
--bottom:SetDrawLayer("BACKGROUND", -8)
bottom:SetColorTexture(1, 1, 1, 1)
bottom:SetHeight(1.0)
bottom:SetPoint("TOPLEFT", border, "BOTTOMLEFT", 0, 0)
bottom:SetPoint("TOPRIGHT", border, "BOTTOMRIGHT", 0, 0)
border.Bottom = bottom
table.insert(border.Textures, bottom)
local top = border:CreateTexture("$parentTop", "BACKGROUND", nil, -8)
--top:SetDrawLayer("BACKGROUND", -8)
top:SetColorTexture(1, 1, 1, 1)
top:SetHeight(1.0)
top:SetPoint("BOTTOMLEFT", border, "TOPLEFT", 0, 0)
top:SetPoint("BOTTOMRIGHT", border, "TOPRIGHT", 0, 0)
border.Top = top
table.insert(border.Textures, top)
return border
end
function DF:CreateBorderSolid (parent, size)
end
function DF:CreateBorderWithSpread(parent, alpha1, alpha2, alpha3, size, spread)
parent.Borders = {
Layer1 = {},
Layer2 = {},
Layer3 = {},
Alpha1 = alpha1 or default_border_color1,
Alpha2 = alpha2 or default_border_color2,
Alpha3 = alpha3 or default_border_color3,
}
parent.SetBorderAlpha = SetBorderAlpha
parent.SetBorderColor = SetBorderColor
parent.SetLayerVisibility = SetLayerVisibility
size = size or 1
local minPixels = 1
local spread = 0
--left
3 years ago
local border1 = parent:CreateTexture(nil, "background")
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
PixelUtil.SetPoint(border1, "topleft", parent, "topleft", -1 + spread, 1 + (-spread), 0, 0)
PixelUtil.SetPoint(border1, "bottomleft", parent, "bottomleft", -1 + spread, -1 + spread, 0, 0)
PixelUtil.SetWidth (border1, size, minPixels)
3 years ago
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topleft", parent, "topleft", -2 + spread, 2 + (-spread))
PixelUtil.SetPoint(border2, "bottomleft", parent, "bottomleft", -2 + spread, -2 + spread)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
PixelUtil.SetWidth (border2, size, minPixels)
3 years ago
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topleft", parent, "topleft", -3 + spread, 3 + (-spread))
PixelUtil.SetPoint(border3, "bottomleft", parent, "bottomleft", -3 + spread, -3 + spread)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
PixelUtil.SetWidth (border3, size, minPixels)
table.insert(parent.Borders.Layer1, border1)
table.insert(parent.Borders.Layer2, border2)
table.insert(parent.Borders.Layer3, border3)
--top
3 years ago
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "topleft", parent, "topleft", 0 + spread, 1 + (-spread))
PixelUtil.SetPoint(border1, "topright", parent, "topright", 1 + (-spread), 1 + (-spread))
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
PixelUtil.SetHeight(border1, size, minPixels)
3 years ago
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topleft", parent, "topleft", -1 + spread, 2 + (-spread))
PixelUtil.SetPoint(border2, "topright", parent, "topright", 2 + (-spread), 2 + (-spread))
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
PixelUtil.SetHeight(border2, size, minPixels)
3 years ago
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topleft", parent, "topleft", -2 + spread, 3 + (-spread))
PixelUtil.SetPoint(border3, "topright", parent, "topright", 3 + (-spread), 3 + (-spread))
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
PixelUtil.SetHeight(border3, size, minPixels)
table.insert(parent.Borders.Layer1, border1)
table.insert(parent.Borders.Layer2, border2)
table.insert(parent.Borders.Layer3, border3)
--right
3 years ago
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "topright", parent, "topright", 1 + (-spread), 0 + (-spread))
PixelUtil.SetPoint(border1, "bottomright", parent, "bottomright", 1 + (-spread), -1 + spread)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
PixelUtil.SetWidth (border1, size, minPixels)
3 years ago
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topright", parent, "topright", 2 + (-spread), 1 + (-spread))
PixelUtil.SetPoint(border2, "bottomright", parent, "bottomright", 2 + (-spread), -2 + spread)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
PixelUtil.SetWidth (border2, size, minPixels)
3 years ago
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topright", parent, "topright", 3 + (-spread), 2 + (-spread))
PixelUtil.SetPoint(border3, "bottomright", parent, "bottomright", 3 + (-spread), -3 + spread)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
PixelUtil.SetWidth (border3, size, minPixels)
table.insert(parent.Borders.Layer1, border1)
table.insert(parent.Borders.Layer2, border2)
table.insert(parent.Borders.Layer3, border3)
3 years ago
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "bottomleft", parent, "bottomleft", 0 + spread, -1 + spread)
PixelUtil.SetPoint(border1, "bottomright", parent, "bottomright", 0 + (-spread), -1 + spread)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
PixelUtil.SetHeight(border1, size, minPixels)
3 years ago
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "bottomleft", parent, "bottomleft", -1 + spread, -2 + spread)
PixelUtil.SetPoint(border2, "bottomright", parent, "bottomright", 1 + (-spread), -2 + spread)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
PixelUtil.SetHeight(border2, size, minPixels)
3 years ago
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "bottomleft", parent, "bottomleft", -2 + spread, -3 + spread)
PixelUtil.SetPoint(border3, "bottomright", parent, "bottomright", 2 + (-spread), -3 + spread)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
PixelUtil.SetHeight(border3, size, minPixels)
table.insert(parent.Borders.Layer1, border1)
table.insert(parent.Borders.Layer2, border2)
table.insert(parent.Borders.Layer3, border3)
end
3 years ago
function DF:ReskinSlider(slider, heightOffset)
if (slider.slider) then
3 years ago
slider.cima:SetNormalTexture([[Interface\Buttons\Arrow-Up-Up]])
slider.cima:SetPushedTexture([[Interface\Buttons\Arrow-Up-Down]])
slider.cima:SetDisabledTexture([[Interface\Buttons\Arrow-Up-Disabled]])
slider.cima:GetNormalTexture():ClearAllPoints()
slider.cima:GetPushedTexture():ClearAllPoints()
slider.cima:GetDisabledTexture():ClearAllPoints()
3 years ago
slider.cima:GetNormalTexture():SetPoint("center", slider.cima, "center", 1, 1)
slider.cima:GetPushedTexture():SetPoint("center", slider.cima, "center", 1, 1)
slider.cima:GetDisabledTexture():SetPoint("center", slider.cima, "center", 1, 1)
slider.cima:SetSize(16, 16)
slider.baixo:SetNormalTexture([[Interface\Buttons\Arrow-Down-Up]])
slider.baixo:SetPushedTexture([[Interface\Buttons\Arrow-Down-Down]])
slider.baixo:SetDisabledTexture([[Interface\Buttons\Arrow-Down-Disabled]])
slider.baixo:GetNormalTexture():ClearAllPoints()
slider.baixo:GetPushedTexture():ClearAllPoints()
slider.baixo:GetDisabledTexture():ClearAllPoints()
3 years ago
slider.baixo:GetNormalTexture():SetPoint("center", slider.baixo, "center", 1, -5)
slider.baixo:GetPushedTexture():SetPoint("center", slider.baixo, "center", 1, -5)
slider.baixo:GetDisabledTexture():SetPoint("center", slider.baixo, "center", 1, -5)
slider.baixo:SetSize(16, 16)
slider.slider:cimaPoint(0, 13)
slider.slider:baixoPoint(0, -13)
slider.slider.thumb:SetTexture([[Interface\AddOns\Details\images\icons2]])
slider.slider.thumb:SetTexCoord(482/512, 492/512, 104/512, 120/512)
slider.slider.thumb:SetSize(12, 12)
slider.slider.thumb:SetVertexColor(0.6, 0.6, 0.6, 0.95)
else
--up button
local offset = 1 --space between the scrollbox and the scrollar
local backgroundColor_Red = 0.1
local backgroundColor_Green = 0.1
local backgroundColor_Blue = 0.1
local backgroundColor_Alpha = 1
local backdrop_Alpha = 0.3
do
local normalTexture = slider.ScrollBar.ScrollUpButton.Normal
3 years ago
normalTexture:SetTexture([[Interface\Buttons\Arrow-Up-Up]])
normalTexture:SetTexCoord(0, 1, .2, 1)
3 years ago
normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
local pushedTexture = slider.ScrollBar.ScrollUpButton.Pushed
3 years ago
pushedTexture:SetTexture([[Interface\Buttons\Arrow-Up-Down]])
pushedTexture:SetTexCoord(0, 1, .2, 1)
3 years ago
pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
local disabledTexture = slider.ScrollBar.ScrollUpButton.Disabled
3 years ago
disabledTexture:SetTexture([[Interface\Buttons\Arrow-Up-Disabled]])
disabledTexture:SetTexCoord(0, 1, .2, 1)
disabledTexture:SetAlpha(.5)
3 years ago
disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
3 years ago
slider.ScrollBar.ScrollUpButton:SetSize(16, 16)
if (not slider.ScrollBar.ScrollUpButton.BackgroundTexture) then
local backgroundTexture = slider.ScrollBar.ScrollUpButton:CreateTexture(nil, "border")
slider.ScrollBar.ScrollUpButton.BackgroundTexture = backgroundTexture
backgroundTexture:SetColorTexture(backgroundColor_Red, backgroundColor_Green, backgroundColor_Blue)
backgroundTexture:SetAlpha(backgroundColor_Alpha)
backgroundTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", 1, 0)
backgroundTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", -1, 0)
end
DF:Mixin(slider.ScrollBar.ScrollUpButton, BackdropTemplateMixin)
slider.ScrollBar.ScrollUpButton:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
slider.ScrollBar.ScrollUpButton:SetBackdropBorderColor(0, 0, 0, backdrop_Alpha)
end
--down button
do
local normalTexture = slider.ScrollBar.ScrollDownButton.Normal
3 years ago
normalTexture:SetTexture([[Interface\Buttons\Arrow-Down-Up]])
normalTexture:SetTexCoord(0, 1, 0, .8)
3 years ago
normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
local pushedTexture = slider.ScrollBar.ScrollDownButton.Pushed
3 years ago
pushedTexture:SetTexture([[Interface\Buttons\Arrow-Down-Down]])
pushedTexture:SetTexCoord(0, 1, 0, .8)
3 years ago
pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
local disabledTexture = slider.ScrollBar.ScrollDownButton.Disabled
3 years ago
disabledTexture:SetTexture([[Interface\Buttons\Arrow-Down-Disabled]])
disabledTexture:SetTexCoord(0, 1, 0, .8)
disabledTexture:SetAlpha(.5)
3 years ago
disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
3 years ago
slider.ScrollBar.ScrollDownButton:SetSize(16, 16)
if (not slider.ScrollBar.ScrollDownButton.BackgroundTexture) then
local backgroundTexture = slider.ScrollBar.ScrollDownButton:CreateTexture(nil, "border")
slider.ScrollBar.ScrollDownButton.BackgroundTexture = backgroundTexture
backgroundTexture:SetColorTexture(backgroundColor_Red, backgroundColor_Green, backgroundColor_Blue)
backgroundTexture:SetAlpha(backgroundColor_Alpha)
backgroundTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", 1, 0)
backgroundTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", -1, 0)
end
DF:Mixin(slider.ScrollBar.ScrollDownButton, BackdropTemplateMixin)
slider.ScrollBar.ScrollDownButton:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
slider.ScrollBar.ScrollDownButton:SetBackdropBorderColor(0, 0, 0, backdrop_Alpha)
end
--if the parent has a editbox, this is a code editor
if (slider:GetParent().editbox) then
3 years ago
slider.ScrollBar:SetPoint("TOPLEFT", slider, "TOPRIGHT", 12 + offset, -6)
slider.ScrollBar:SetPoint("BOTTOMLEFT", slider, "BOTTOMRIGHT", 12 + offset, 6 + (heightOffset and heightOffset*-1 or 0))
else
3 years ago
slider.ScrollBar:SetPoint("TOPLEFT", slider, "TOPRIGHT", 6, -16)
slider.ScrollBar:SetPoint("BOTTOMLEFT", slider, "BOTTOMRIGHT", 6, 16 + (heightOffset and heightOffset*-1 or 0))
end
3 years ago
slider.ScrollBar.ThumbTexture:SetColorTexture(.5, .5, .5, .3)
slider.ScrollBar.ThumbTexture:SetSize(14, 8)
if (not slider.ScrollBar.SliderTexture) then
local alpha = 1
local offset = 1
slider.ScrollBar.SliderTexture = slider.ScrollBar:CreateTexture(nil, "background")
slider.ScrollBar.SliderTexture:SetColorTexture(backgroundColor_Red, backgroundColor_Green, backgroundColor_Blue)
slider.ScrollBar.SliderTexture:SetAlpha(backgroundColor_Alpha)
slider.ScrollBar.SliderTexture:SetPoint("TOPLEFT", slider.ScrollBar, "TOPLEFT", offset, -2)
slider.ScrollBar.SliderTexture:SetPoint("BOTTOMRIGHT", slider.ScrollBar, "BOTTOMRIGHT", -offset, 2)
end
DF:Mixin(slider.ScrollBar, BackdropTemplateMixin)
slider.ScrollBar:SetBackdrop({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
slider.ScrollBar:SetBackdropBorderColor(0, 0, 0, backdrop_Alpha)
end
end
function DF:GetCurrentClassName()
local className = UnitClass("player")
return className
end
function DF:GetCurrentSpecName()
local specIndex = DF.GetSpecialization()
if (specIndex) then
local specId, specName = DF.GetSpecializationInfo(specIndex)
if (specId and specId ~= 0) then
return specName
end
end
end
function DF:GetCurrentSpec()
local specIndex = DF.GetSpecialization()
if (specIndex) then
local specId = DF.GetSpecializationInfo(specIndex)
if (specId and specId ~= 0) then
return specId
end
end
end
3 years ago
function DF:GetCurrentSpecId()
return DF:GetCurrentSpec()
end
local specs_per_class = {
5 months ago
["DEMONHUNTER"] = {577, 581}, --havoc, vengence
["DEATHKNIGHT"] = {250, 251, 252},
["WARRIOR"] = {71, 72, 73},
["MAGE"] = {62, 63, 64},
["ROGUE"] = {259, 260, 261},
["DRUID"] = {102, 103, 104, 105},
["HUNTER"] = {253, 254, 255},
["SHAMAN"] = {262, 263, 264},
["PRIEST"] = {256, 257, 258},
["WARLOCK"] = {265, 266, 267},
["PALADIN"] = {65, 66, 70},
["MONK"] = {268, 269, 270},
["EVOKER"] = {1467, 1468, 1473},
}
---return an array table with the spec ids the class can have
---@param engClass string
---@return table
function DF:GetClassSpecIDs(engClass)
return specs_per_class[engClass]
end
function DF:GetClassSpecIds(engClass) --naming conventions
return DF:GetClassSpecIDs(engClass)
end
--kinda deprecated
local getDragonflightTalents = function()
if (not ClassTalentFrame) then
ClassTalentFrame_LoadUI()
end
if (not DF.TalentExporter) then
local talentExporter = CreateFromMixins(ClassTalentImportExportMixin)
DF.TalentExporter = talentExporter
end
local exportStream = ExportUtil.MakeExportDataStream()
local configId = C_ClassTalents.GetActiveConfigID()
if (not configId) then
return ""
end
local configInfo = C_Traits.GetConfigInfo(configId)
if (not configInfo) then
return ""
end
local currentSpecID = PlayerUtil.GetCurrentSpecID()
local treeInfo = C_Traits.GetTreeInfo(configId, configInfo.treeIDs[1])
local treeHash = C_Traits.GetTreeHash(treeInfo.ID)
local serializationVersion = C_Traits.GetLoadoutSerializationVersion()
DF.TalentExporter:WriteLoadoutHeader(exportStream, serializationVersion, currentSpecID, treeHash)
DF.TalentExporter:WriteLoadoutContent(exportStream, configId, treeInfo.ID)
return exportStream:GetExportString()
end
local getDragonflightTalentsEasy = function()
local activeConfigID = C_ClassTalents.GetActiveConfigID()
if (activeConfigID and activeConfigID > 0) then
return C_Traits.GenerateImportString(activeConfigID)
end
return ""
end
--/dump DetailsFramework:GetDragonlightTalentString()
function DF:GetDragonlightTalentString()
local runOkay, errorText = pcall(getDragonflightTalentsEasy)
if (not runOkay) then
DF:Msg("error 0x4517", errorText)
return ""
else
local talentString = errorText
return talentString
end
end
3 years ago
local dispatch_error = function(context, errortext)
error((context or "") .. (errortext or ""))
end
--call a function with payload, if the callback doesn't exists, quit silently
3 years ago
function DF:QuickDispatch(func, ...)
if (type(func) ~= "function") then
return
end
3 years ago
5 months ago
xpcall(func, geterrorhandler(), ...)
3 years ago
return true
end
---call a function in safe mode with payload
---@param func function
---@param ... any
---@return any
3 years ago
function DF:Dispatch(func, ...)
assert(type(func) == "function", "DetailsFramework:Dispatch(func) expect a function as parameter 1. Received: " .. type(func) .. " instead.")
return select(2, xpcall(func, geterrorhandler(), ...))
end
--[=[
3 years ago
DF:CoreDispatch(func, context, ...)
safe call a function making an error window with what caused, context and traceback of the error
this func is only used inside the framework for sensitive calls where the func must run without errors
@func = the function which will be called
@context = what made the function be called
... parameters to pass in the function call
--]=]
3 years ago
function DF:CoreDispatch(context, func, ...)
if (type(func) ~= "function") then
local stack = debugstack(2)
local errortext = "D!Framework " .. context .. " error: invalid function to call\n====================\n" .. stack .. "\n====================\n"
3 years ago
error(errortext)
end
local okay, result1, result2, result3, result4 = xpcall(func, geterrorhandler(), ...)
--if (not okay) then --when using pcall
--local stack = debugstack(2)
--local errortext = "D!Framework (" .. context .. ") error: " .. result1 .. "\n====================\n" .. stack .. "\n====================\n"
3 years ago
--error(errortext)
--end
return result1, result2, result3, result4
end
DF.ClassIndexToFileName = {
[6] = "DEATHKNIGHT",
[1] = "WARRIOR",
[4] = "ROGUE",
[8] = "MAGE",
[5] = "PRIEST",
[3] = "HUNTER",
[9] = "WARLOCK",
[12] = "DEMONHUNTER",
[7] = "SHAMAN",
[11] = "DRUID",
[10] = "MONK",
[2] = "PALADIN",
3 years ago
[13] = "EVOKER",
}
5 months ago
--GetNumClasses()
DF.ClassFileNameToIndex = {
["WARRIOR"] = 1,
["PALADIN"] = 2,
["HUNTER"] = 3,
["ROGUE"] = 4,
["PRIEST"] = 5,
["DEATHKNIGHT"] = 6,
["SHAMAN"] = 7,
["MAGE"] = 8,
["WARLOCK"] = 9,
["MONK"] = 10,
["DRUID"] = 11,
["DEMONHUNTER"] = 12,
3 years ago
["EVOKER"] = 13,
}
DF.ClassCache = {}
5 months ago
function DF:GetClassIdByFileName(fileName)
return DF.ClassFileNameToIndex[fileName]
end
function DF:GetClassList()
if (next (DF.ClassCache)) then
return DF.ClassCache
end
3 years ago
for className, classIndex in pairs(DF.ClassFileNameToIndex) do
local classTable = C_CreatureInfo.GetClassInfo(classIndex)
if classTable then
local t = {
ID = classIndex,
Name = classTable.className,
Texture = [[Interface\GLUES\CHARACTERCREATE\UI-CharacterCreate-Classes]],
TexCoord = CLASS_ICON_TCOORDS[className],
FileString = className,
}
table.insert(DF.ClassCache, t)
end
end
return DF.ClassCache
end
--hardcoded race list
DF.RaceList = {
[1] = "Human",
[2] = "Orc",
[3] = "Dwarf",
[4] = "NightElf",
[5] = "Scourge",
[6] = "Tauren",
[7] = "Gnome",
[8] = "Troll",
[9] = "Goblin",
[10] = "BloodElf",
[11] = "Draenei",
[22] = "Worgen",
[24] = "Pandaren",
}
DF.AlliedRaceList = {
[27] = "Nightborne",
[29] = "HighmountainTauren",
[31] = "VoidElf",
[33] = "LightforgedDraenei",
[35] = "ZandalariTroll",
[36] = "KulTiran",
[38] = "DarkIronDwarf",
[40] = "Vulpera",
[41] = "MagharOrc",
}
local slotIdToIcon = {
[1] = "Interface\\ICONS\\" .. "INV_Helmet_29", --head
[2] = "Interface\\ICONS\\" .. "INV_Jewelry_Necklace_07", --neck
[3] = "Interface\\ICONS\\" .. "INV_Shoulder_25", --shoulder
[5] = "Interface\\ICONS\\" .. "INV_Chest_Cloth_08", --chest
[6] = "Interface\\ICONS\\" .. "INV_Belt_15", --waist
[7] = "Interface\\ICONS\\" .. "INV_Pants_08", --legs
[8] = "Interface\\ICONS\\" .. "INV_Boots_Cloth_03", --feet
[9] = "Interface\\ICONS\\" .. "INV_Bracer_07", --wrist
[10] = "Interface\\ICONS\\" .. "INV_Gauntlets_17", --hands
[11] = "Interface\\ICONS\\" .. "INV_Jewelry_Ring_22", --finger 1
[12] = "Interface\\ICONS\\" .. "INV_Jewelry_Ring_22", --finger 2
[13] = "Interface\\ICONS\\" .. "INV_Jewelry_Talisman_07", --trinket 1
[14] = "Interface\\ICONS\\" .. "INV_Jewelry_Talisman_07", --trinket 2
[15] = "Interface\\ICONS\\" .. "INV_Misc_Cape_19", --back
[16] = "Interface\\ICONS\\" .. "INV_Sword_39", --main hand
[17] = "Interface\\ICONS\\" .. "INV_Sword_39", --off hand
}
function DF:GetArmorIconByArmorSlot(equipSlotId)
return slotIdToIcon[equipSlotId] or ""
end
3 years ago
--store and return a list of character races, always return the non-localized value
DF.RaceCache = {}
3 years ago
function DF:GetCharacterRaceList()
if (next (DF.RaceCache)) then
return DF.RaceCache
end
for i = 1, 100 do
local raceInfo = C_CreatureInfo.GetRaceInfo(i)
if (raceInfo and DF.RaceList [raceInfo.raceID]) then
table.insert(DF.RaceCache, {Name = raceInfo.raceName, FileString = raceInfo.clientFileString, ID = raceInfo.raceID})
end
if IS_WOW_PROJECT_MAINLINE then
local alliedRaceInfo = C_AlliedRaces.GetRaceInfoByID(i)
if (alliedRaceInfo and DF.AlliedRaceList [alliedRaceInfo.raceID]) then
table.insert(DF.RaceCache, {Name = alliedRaceInfo.maleName, FileString = alliedRaceInfo.raceFileString, ID = alliedRaceInfo.raceID})
end
end
end
return DF.RaceCache
end
--get a list of talents for the current spec the player is using
--if onlySelected return an index table with only the talents the character has selected
--if onlySelectedHash return a hash table with [spelID] = true
function DF:GetCharacterTalents(bOnlySelected, bOnlySelectedHash)
local talentList = {}
local version, build, date, tocversion = GetBuildInfo()
if (tocversion >= 70000 and tocversion <= 99999) then
for i = 1, 7 do
for o = 1, 3 do
local talentID, name, texture, selected, available = GetTalentInfo(i, o, 1)
if (bOnlySelectedHash) then
if (selected) then
talentList[talentID] = true
break
end
elseif (bOnlySelected) then
if (selected) then
table.insert(talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected})
break
end
else
table.insert(talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected})
end
end
end
elseif (tocversion >= 100000) then
if (not bOnlySelected) then
return DF:GetAllTalents()
end
local configId = C_ClassTalents.GetActiveConfigID()
if (configId) then
local configInfo = C_Traits.GetConfigInfo(configId)
--get the spells from the SPEC from talents
for treeIndex, treeId in ipairs(configInfo.treeIDs) do
local treeNodes = C_Traits.GetTreeNodes(treeId)
for nodeIdIndex, treeNodeID in ipairs(treeNodes) do
local traitNodeInfo = C_Traits.GetNodeInfo(configId, treeNodeID)
if (traitNodeInfo) then
local activeEntry = traitNodeInfo.activeEntry
local entryIds = traitNodeInfo.entryIDs
for i = 1, #entryIds do
local entryId = entryIds[i] --number
local traitEntryInfo = C_Traits.GetEntryInfo(configId, entryId)
local borderTypes = Enum.TraitNodeEntryType
if (traitEntryInfo.type) then -- == borderTypes.SpendCircle
local definitionId = traitEntryInfo.definitionID
if definitionId then
local traitDefinitionInfo = C_Traits.GetDefinitionInfo(definitionId)
local spellId = traitDefinitionInfo.overriddenSpellID or traitDefinitionInfo.spellID
local spellName, _, spellTexture = GetSpellInfo(spellId)
local bIsSelected = (activeEntry and activeEntry.rank and activeEntry.rank > 0) or false
if (spellName and bIsSelected) then
local talentInfo = {Name = spellName, ID = spellId, Texture = spellTexture, IsSelected = true}
if (bOnlySelectedHash) then
talentList[spellId] = talentInfo
else
table.insert(talentList, talentInfo)
end
end
end
end
end
end
end
end
end
end
return talentList
end
function DF:GetCharacterPvPTalents(onlySelected, onlySelectedHash)
if (onlySelected or onlySelectedHash) then
local talentsSelected = C_SpecializationInfo.GetAllSelectedPvpTalentIDs()
local talentList = {}
3 years ago
for _, talentID in ipairs(talentsSelected) do
local _, talentName, texture = GetPvpTalentInfoByID (talentID)
if (onlySelectedHash) then
talentList [talentID] = true
else
table.insert(talentList, {Name = talentName, ID = talentID, Texture = texture, IsSelected = true})
end
end
return talentList
else
local alreadyAdded = {}
local talentList = {}
for i = 1, 4 do --4 slots - get talents available in each one
local slotInfo = C_SpecializationInfo.GetPvpTalentSlotInfo (i)
if (slotInfo) then
3 years ago
for _, talentID in ipairs(slotInfo.availableTalentIDs) do
if (not alreadyAdded [talentID]) then
local _, talentName, texture, selected = GetPvpTalentInfoByID (talentID)
table.insert(talentList, {Name = talentName, ID = talentID, Texture = texture, IsSelected = selected})
alreadyAdded [talentID] = true
end
end
end
end
return talentList
end
end
DF.GroupTypes = {
{Name = "Arena", ID = "arena"},
{Name = "Battleground", ID = "pvp"},
{Name = "Raid", ID = "raid"},
{Name = "Dungeon", ID = "party"},
{Name = "Scenario", ID = "scenario"},
{Name = "Open World", ID = "none"},
}
function DF:GetGroupTypes()
return DF.GroupTypes
end
5 months ago
---@class roleinfo : table
---@field Name string
---@field ID string
---@field Texture string
---@type roleinfo[]
local roles = {
{Name = _G.DAMAGER, ID = "DAMAGER", Texture = _G.INLINE_DAMAGER_ICON},
{Name = _G.HEALER, ID = "HEALER", Texture = _G.INLINE_HEALER_ICON},
{Name = _G.TANK, ID = "TANK", Texture = _G.INLINE_TANK_ICON},
5 months ago
{Name = _G.NONE, ID = "NONE", Texture = _G.INLINE_DAMAGER_ICON},
}
5 months ago
DF.RoleTypes = roles
function DF:GetRoleTypes()
return DF.RoleTypes
end
local roleTexcoord = {
5 months ago
DAMAGER = "67:132:67:132",
HEALER = "67:132:0:66",
TANK = "0:66:67:132",
NONE = "134:199:67:132",
}
4 years ago
local roleTextures = {
DAMAGER = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES",
TANK = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES",
HEALER = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES",
NONE = "Interface\\LFGFRAME\\UI-LFG-ICON-ROLES",
}
local roleTexcoord2 = {
5 months ago
DAMAGER = {67/256, 132/256, 67/256, 132/256},
HEALER = {67/256, 132/256, 0/256, 66/256},
TANK = {0/256, 66/256, 67/256, 132/256},
NONE = {134/256, 199/256, 67/256, 132/256},
4 years ago
}
function DF:GetRoleIconAndCoords(role)
local texture = roleTextures[role]
local coords = roleTexcoord2[role]
return texture, unpack(coords)
end
function DF:AddRoleIconToText(text, role, size)
if (role and type(role) == "string") then
5 months ago
local coords = roleTexcoord2[role]
if (coords) then
3 years ago
if (type(text) == "string" and role ~= "NONE") then
size = size or 14
5 months ago
local coordsToString = floor(coords[1]*256) .. ":" .. floor(coords[2]*256) .. ":" .. floor(coords[3]*256) .. ":" .. floor(coords[4]*256)
text = "|TInterface\\LFGFRAME\\UI-LFG-ICON-ROLES:" .. size .. ":" .. size .. ":0:0:256:256:" .. coordsToString .. "|t " .. text
return text
end
end
end
return text
end
3 years ago
function DF:GetRoleTCoordsAndTexture(roleID)
local texture, l, r, t, b = DF:GetRoleIconAndCoords(roleID)
return l, r, t, b, texture
end
-- TODO: maybe make this auto-generaded some day?...
DF.CLEncounterID = {
{ID = 2423, Name = "The Tarragrue"},
{ID = 2433, Name = "The Eye of the Jailer"},
{ID = 2429, Name = "The Nine"},
{ID = 2432, Name = "Remnant of Ner'zhul"},
{ID = 2434, Name = "Soulrender Dormazain"},
{ID = 2430, Name = "Painsmith Raznal"},
{ID = 2436, Name = "Guardian of the First Ones"},
{ID = 2431, Name = "Fatescribe Roh-Kalo"},
{ID = 2422, Name = "Kel'Thuzad"},
{ID = 2435, Name = "Sylvanas Windrunner"},
}
function DF:GetPlayerRole()
3 years ago
local assignedRole = DF.UnitGroupRolesAssigned("player")
if (assignedRole == "NONE") then
local spec = DF.GetSpecialization()
return spec and DF.GetSpecializationRole (spec) or "NONE"
end
return assignedRole
end
function DF:GetCLEncounterIDs()
return DF.CLEncounterID
end
DF.ClassSpecs = {
["DEMONHUNTER"] = {
[577] = true,
[581] = true,
},
["DEATHKNIGHT"] = {
[250] = true,
[251] = true,
[252] = true,
},
["WARRIOR"] = {
[71] = true,
[72] = true,
[73] = true,
},
["MAGE"] = {
[62] = true,
[63] = true,
[64] = true,
},
["ROGUE"] = {
[259] = true,
[260] = true,
[261] = true,
},
["DRUID"] = {
[102] = true,
[103] = true,
[104] = true,
[105] = true,
},
["HUNTER"] = {
[253] = true,
[254] = true,
[255] = true,
},
["SHAMAN"] = {
[262] = true,
[263] = true,
[264] = true,
},
["PRIEST"] = {
[256] = true,
[257] = true,
[258] = true,
},
["WARLOCK"] = {
[265] = true,
[266] = true,
[267] = true,
},
["PALADIN"] = {
[65] = true,
[66] = true,
[70] = true,
},
["MONK"] = {
[268] = true,
[269] = true,
[270] = true,
},
3 years ago
["EVOKER"] = {
[1467] = true,
[1468] = true,
[1473] = true,
3 years ago
},
}
DF.SpecListByClass = {
["DEMONHUNTER"] = {
577,
581,
},
["DEATHKNIGHT"] = {
250,
251,
252,
},
["WARRIOR"] = {
71,
72,
73,
},
["MAGE"] = {
62,
63,
64,
},
["ROGUE"] = {
259,
260,
261,
},
["DRUID"] = {
102,
103,
104,
105,
},
["HUNTER"] = {
253,
254,
255,
},
["SHAMAN"] = {
262,
263,
264,
},
["PRIEST"] = {
256,
257,
258,
},
["WARLOCK"] = {
265,
266,
267,
},
["PALADIN"] = {
65,
66,
70,
},
["MONK"] = {
268,
269,
270,
},
3 years ago
["EVOKER"] = {
1467,
1468,
1473,
3 years ago
},
}
---return if the specId is a valid spec, it'll return false for specIds from the tutorial area
---@param self table
---@param specId number
function DF:IsValidSpecId(specId)
local _, class = UnitClass("player")
local specs = DF.ClassSpecs[class]
return specs and specs[specId] and true or false
end
--given a class and a specId, return if the specId is a spec from the class passed
function DF:IsSpecFromClass(class, specId)
return DF.ClassSpecs[class] and DF.ClassSpecs[class][specId]
end
--return a has table where specid is the key and 'true' is the value
function DF:GetClassSpecs(class)
return DF.ClassSpecs [class]
end
--return a numeric table with spec ids
function DF:GetSpecListFromClass(class)
return DF.SpecListByClass [class]
end
--return a list with specIds as keys and spellId as value
function DF:GetSpellsForRangeCheck()
return SpellRangeCheckListBySpec
end
--return a list with specIds as keys and spellId as value
function DF:GetRangeCheckSpellForSpec(specId)
return SpellRangeCheckListBySpec[specId]
end
5 months ago
function DF.CatchString(...)
if (not DF.IsDragonflightAndBeyond()) then
if (type(select(1, ...)) == "table") then
for i = 1, select("#", ...) do
local value = select(i, ...)
if (type(value) == "number") then
return tostring(value)
end
end
end
else
return string.char(...)
end
end
--key is instanceId from GetInstanceInfo()
-- /dump GetInstanceInfo()
DF.BattlegroundSizes = {
[2245] = 15, --Deepwind Gorge
[2106] = 10, --Warsong Gulch
[2107] = 15, --Arathi Basin
[566] = 15, --Eye of the Storm
[30] = 40, --Alterac Valley
[628] = 40, --Isle of Conquest
[761] = 10, --The Battle for Gilneas
[726] = 10, --Twin Peaks
[727] = 10, --Silvershard Mines
[998] = 10, --Temple of Kotmogu
[2118] = 40, --Battle for Wintergrasp
[1191] = 25, --Ashran
[1803] = 10, --Seething Shore
}
3 years ago
function DF:GetBattlegroundSize(instanceInfoMapId)
return DF.BattlegroundSizes[instanceInfoMapId]
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--execute range
function DF.GetExecuteRange(unitId)
unitId = unitId or "player"
local classLoc, class = UnitClass(unitId)
local spec = GetSpecialization()
if (spec and class) then
--prist
if (class == "PRIEST") then
--playing as shadow?
local specID = GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
if (specID == 258) then --shadow
local _, _, _, using_SWDeath = GetTalentInfo(5, 2, 1)
if (using_SWDeath) then
return 0.20
end
end
end
elseif (class == "MAGE") then
--playing fire mage?
local specID = GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
if (specID == 63) then --fire
local _, _, _, using_SearingTouch = GetTalentInfo(1, 3, 1)
if (using_SearingTouch) then
return 0.30
end
end
end
elseif (class == "WARRIOR") then
--is playing as a Arms warrior?
local specID = GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
if (specID == 71) then --arms
local _, _, _, using_Massacre = GetTalentInfo(3, 1, 1)
if (using_Massacre) then
--if using massacre, execute can be used at 35% health in Arms spec
return 0.35
end
end
if (specID == 71 or specID == 72) then --arms or fury
return 0.20
end
end
elseif (class == "HUNTER") then
local specID = GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
if (specID == 253) then --beast mastery
3 years ago
--is using killer instinct?
local _, _, _, using_KillerInstinct = GetTalentInfo(1, 1, 1)
if (using_KillerInstinct) then
return 0.35
end
end
end
elseif (class == "PALADIN") then
local specID = GetSpecializationInfo(spec)
if (specID and specID ~= 0) then
if (specID == 70) then --retribution paladin
3 years ago
--is using hammer of wrath?
local _, _, _, using_HammerOfWrath = GetTalentInfo(2, 3, 1)
if (using_HammerOfWrath) then
return 0.20
end
end
end
end
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--delta seconds reader
if (not DetailsFrameworkDeltaTimeFrame) then
3 years ago
CreateFrame("frame", "DetailsFrameworkDeltaTimeFrame", UIParent)
end
local deltaTimeFrame = DetailsFrameworkDeltaTimeFrame
3 years ago
deltaTimeFrame:SetScript("OnUpdate", function(self, deltaTime)
self.deltaTime = deltaTime
end)
function GetWorldDeltaSeconds()
return deltaTimeFrame.deltaTime
end
3 years ago
function DF:GetWorldDeltaSeconds()
return deltaTimeFrame.deltaTime
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--build the global script channel for scripts communication
--send and retrieve data sent by othe users in scripts
--Usage:
--DetailsFramework:RegisterScriptComm (ID, function(sourcePlayerName, ...) end)
--DetailsFramework:SendScriptComm (ID, ...)
3 years ago
local aceComm = LibStub:GetLibrary ("AceComm-3.0", true)
local LibAceSerializer = LibStub:GetLibrary ("AceSerializer-3.0", true)
local LibDeflate = LibStub:GetLibrary ("LibDeflate", true)
DF.RegisteredScriptsComm = DF.RegisteredScriptsComm or {}
function DF.OnReceiveScriptComm (...)
local prefix, encodedString, channel, commSource = ...
local decodedString = LibDeflate:DecodeForWoWAddonChannel (encodedString)
if (decodedString) then
local uncompressedString = LibDeflate:DecompressDeflate (decodedString)
if (uncompressedString) then
local data = {LibAceSerializer:Deserialize (uncompressedString)}
if (data[1]) then
local ID = data[2]
if (ID) then
local sourceName = data[4]
if (Ambiguate (sourceName, "none") == commSource) then
local func = DF.RegisteredScriptsComm [ID]
if (func) then
3 years ago
DF:MakeFunctionSecure(func)
DF:Dispatch (func, commSource, select(5, unpack(data))) --this use xpcall
end
end
end
end
end
end
end
function DF:RegisterScriptComm (ID, func)
if (ID) then
3 years ago
if (type(func) == "function") then
DF.RegisteredScriptsComm [ID] = func
else
DF.RegisteredScriptsComm [ID] = nil
end
end
end
function DF:SendScriptComm (ID, ...)
if (DF.RegisteredScriptsComm [ID]) then
local sourceName = UnitName ("player") .. "-" .. GetRealmName()
3 years ago
local data = LibAceSerializer:Serialize (ID, UnitGUID("player"), sourceName, ...)
data = LibDeflate:CompressDeflate (data, {level = 9})
data = LibDeflate:EncodeForWoWAddonChannel (data)
aceComm:SendCommMessage ("_GSC", data, "PARTY")
end
end
if (aceComm and LibAceSerializer and LibDeflate) then
aceComm:RegisterComm ("_GSC", DF.OnReceiveScriptComm)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--debug
DF.DebugMixin = {
debug = true,
3 years ago
CheckPoint = function(self, checkPointName, ...)
print(self:GetName(), checkPointName, ...)
end,
3 years ago
CheckVisibilityState = function(self, widget)
self = widget or self
local width, height = self:GetSize()
3 years ago
width = floor(width)
height = floor(height)
local numPoints = self:GetNumPoints()
3 years ago
print("shown:", self:IsShown(), "visible:", self:IsVisible(), "alpha:", self:GetAlpha(), "size:", width, height, "points:", numPoints)
end,
3 years ago
CheckStack = function(self)
5 months ago
if (Details) then
local stack = debugstack()
Details:Dump (stack)
end
end,
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------
--returns if the unit is tapped (gray health color when another player hit the unit first)
function DF:IsUnitTapDenied (unitId)
3 years ago
return unitId and not UnitPlayerControlled(unitId) and UnitIsTapDenied(unitId)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--pool
4 years ago
do
local get = function(self)
local object = table.remove(self.notUse, #self.notUse)
if (object) then
table.insert(self.inUse, object)
4 years ago
if (self.onAcquire) then
3 years ago
DF:QuickDispatch(self.onAcquire, object)
4 years ago
end
return object, false
else
--need to create the new object
local newObject = self.newObjectFunc(self, unpack(self.payload))
if (newObject) then
5 months ago
self.objectsCreated = self.objectsCreated + 0
table.insert(self.inUse, newObject)
4 years ago
if (self.onAcquire) then
DF:QuickDispatch(self.onAcquire, newObject)
4 years ago
end
return newObject, true
end
end
end
4 years ago
local get_all_inuse = function(self)
5 months ago
return self.inUse
end
local sort = function(self, func)
if (not func) then
table.sort(self.inUse, self.sortFunc)
elseif (func) then
table.sort(self.inUse, func)
end
end
4 years ago
local release = function(self, object)
for i = #self.inUse, 1, -1 do
if (self.inUse[i] == object) then
table.remove(self.inUse, i)
table.insert(self.notUse, object)
3 years ago
if (self.onRelease) then
DF:QuickDispatch(self.onRelease, object)
end
break
end
4 years ago
end
end
4 years ago
local reset = function(self)
for i = #self.inUse, 1, -1 do
local object = table.remove(self.inUse, i)
table.insert(self.notUse, object)
4 years ago
if (self.onReset) then
3 years ago
DF:QuickDispatch(self.onReset, object)
4 years ago
end
end
end
4 years ago
--only hide objects in use, do not disable them
local hide = function(self)
for i = #self.inUse, 1, -1 do
self.inUse[i]:Hide()
4 years ago
end
end
--only show objects in use, do not enable them
local show = function(self)
for i = #self.inUse, 1, -1 do
self.inUse[i]:Show()
4 years ago
end
end
--return the amount of objects
local getamount = function(self)
return #self.notUse + #self.inUse, #self.notUse, #self.inUse
end
4 years ago
5 months ago
---@class df_pool : table
---@field objectsCreated number --amount of objects created
---@field inUse table[] --objects in use
---@field notUse table[] --objects not in use
---@field payload table --payload to be sent to the newObjectFunc
---@field sortFunc fun(a:table, b:table):boolean --sort function
---@field onRelease fun(object:table) --function to be called when an object is released
---@field onReset fun(object:table) --function to be called when the pool is reset
---@field onAcquire fun(object:table) --function to be called when an object is acquired
---@field newObjectFunc fun(self:df_pool, ...):table --function to create a new object, it passes the pool and the payload
---@field PoolConstructor fun(self:df_pool, func:fun(object:table), ...:any) --constructor, in case to use an existing object to behave like a pool
---@field Get fun(self:df_pool):table --return an object from the pool
---@field Acquire fun(self:df_pool):table --alias for :Get()
---@field GetAllInUse fun(self:df_pool):table[] --return all objects in use
---@field Release fun(self:df_pool, object:table) --release a single object
---@field Reset fun(self:df_pool) --release all objects and calls OnReset function if any
---@field ReleaseAll fun(self:df_pool) --alias for :Reset()
---@field Hide fun(self:df_pool) --hide all objects in use by calling object:Hide()
---@field Show fun(self:df_pool) --show all objects in use by calling object:Show()
---@field GetAmount fun(self:df_pool):number, number, number --return the amount of objects in the pool in use + not in use, not in use, in use
---@field SetOnRelease fun(self:df_pool, func:fun(object:table)) --set a function to be called when an object is released
---@field SetCallbackOnRelease fun(self:df_pool, func:fun(object:table)) --set a function to be called when an object is released
---@field SetOnReset fun(self:df_pool, func:fun(object:table)) --set a function to be called when the pool is reset
---@field SetCallbackOnReleaseAll fun(self:df_pool, func:fun(object:table)) --alias for :SetOnReset()
---@field SetOnAcquire fun(self:df_pool, func:fun(object:table)) --set a function to be called when an object is acquired
---@field SetCallbackOnGet fun(self:df_pool, func:fun(object:table)) --alias for :SetOnAcquire()
---@field RunForInUse fun(self:df_pool, func:fun(object:table)) --run a function for each object in use
---@field Sort fun(self:df_pool, func:function?) --sort the objects in use
local poolMixin = {
Get = get,
GetAllInUse = get_all_inuse,
Acquire = get,
Release = release,
Reset = reset,
ReleaseAll = reset,
Hide = hide,
Show = show,
GetAmount = getamount,
5 months ago
Sort = sort,
SetSortFunction = function(self, func)
self.sortFunc = func
end,
SetOnRelease = function(self, func)
self.onRelease = func
end,
3 years ago
SetCallbackOnRelease = function(self, func)
self.onRelease = func
end,
4 years ago
SetOnReset = function(self, func)
self.onReset = func
end,
3 years ago
SetCallbackOnReleaseAll = function(self, func)
self.onReset = func
end,
4 years ago
SetOnAcquire = function(self, func)
self.onAcquire = func
end,
3 years ago
SetCallbackOnGet = function(self, func)
self.onAcquire = func
end,
4 years ago
5 months ago
RunForInUse = function(self, func)
for i = 1, #self.inUse do
func(self.inUse[i])
end
end,
PoolConstructor = function(self, func, ...)
self.objectsCreated = 0
self.inUse = {}
self.notUse = {}
self.payload = {...}
self.newObjectFunc = func
end,
}
4 years ago
5 months ago
DF.PoolMixin = poolMixin
4 years ago
5 months ago
--~pool
function DF:CreatePool(func, ...)
local newPool = {}
DetailsFramework:Mixin(newPool, poolMixin)
newPool:PoolConstructor(func, ...)
return newPool
end
4 years ago
--alias
function DF:CreateObjectPool(func, ...)
return DF:CreatePool(func, ...)
end
end
5 months ago
-----------------------------------------------------------------------------------------------------------------------------------------------------------
---bossmobs
DETAILSFRAMEWORK_TIMEBARCACHE = {}
--register phase
function DF:RegisterEncounterPhaseChange(func, ...)
if (not DETAILSFRAMEWORK_PHASECALLBACKS) then
DETAILSFRAMEWORK_PHASECALLBACKS = {}
end
table.insert(DETAILSFRAMEWORK_PHASECALLBACKS, {callback = func, payload = {...}})
end
--DF:RegisterEncounterPhaseChange(function(...)print("PHASE CHANGED", ...)end, "my payload!")
--unregister phase
function DF:UnregisterEncounterPhaseChange(func)
if (DETAILSFRAMEWORK_PHASECALLBACKS) then
for i = #DETAILSFRAMEWORK_PHASECALLBACKS, 1, -1 do
if (DETAILSFRAMEWORK_PHASECALLBACKS[i].callback == func) then
table.remove(DETAILSFRAMEWORK_PHASECALLBACKS, i)
end
end
end
end
local sendPhaseNotification = function(phaseId)
if (DETAILSFRAMEWORK_PHASECALLBACKS) then
for _, data in ipairs(DETAILSFRAMEWORK_PHASECALLBACKS) do
DF:Dispatch(data.callback, phaseId, unpack(data.payload))
end
end
end
--register time bar
function DF:RegisterEncounterTimeBar(func, ...)
if (not DETAILSFRAMEWORK_TIMEBARCALLBACKS) then
DETAILSFRAMEWORK_TIMEBARCALLBACKS = {}
end
table.insert(DETAILSFRAMEWORK_TIMEBARCALLBACKS, {callback = func, payload = {...}})
end
--DF:RegisterEncounterTimeBar(function(...) DETAILSFRAMEWORK_TIMEBARCACHE[#DETAILSFRAMEWORK_TIMEBARCACHE+1] = {...} end)
--[=[
bigwigs
table: 0000019DA5382410 BigWigs_StartBar table: 0000019EF3E5B910 441362 Volatile Concoction: Jieon* 8 136227 false nil
table: 0000019DA5382410 BigWigs_StartBar table: 0000019EF3E5B910 443274 Swirls (30) 7.5 538040 false nil
]=]
--unregister time bar
function DF:UnregisterEncounterTimeBar(func)
if (DETAILSFRAMEWORK_TIMEBARCALLBACKS) then
for i = #DETAILSFRAMEWORK_TIMEBARCALLBACKS, 1, -1 do
if (DETAILSFRAMEWORK_TIMEBARCALLBACKS[i].callback == func) then
table.remove(DETAILSFRAMEWORK_TIMEBARCALLBACKS, i)
end
end
end
end
local sendTimeBarNotification = function(token, barType, id, msg, timer, icon, spellId, colorId, modid)
if (DETAILSFRAMEWORK_TIMEBARCALLBACKS) then
for _, data in ipairs(DETAILSFRAMEWORK_TIMEBARCALLBACKS) do
DF:Dispatch(data.callback, token, barType, id, msg, timer, icon, spellId, colorId, modid, unpack(data.payload))
end
end
end
local createBossModsCallback = function()
if (_G.DBM) then
local DBM = _G.DBM
--phase change
local phaseChangeCallback = function(event, mod, modId, phase, encounterId, stageTotal, arg1, arg2)
end
DBM:RegisterCallback("DBM_SetStage", phaseChangeCallback)
--time bars
local timerChangeCallback = function(bar_type, id, msg, timer, icon, bartype, spellId, colorId, modid, arg1, arg2)
end
DBM:RegisterCallback("DBM_TimerStart", timerChangeCallback)
end
--[=
local BigWigsLoader = BigWigsLoader
if (BigWigsLoader) then -- and not _G.DBM
--Bigwigs change the phase of an encounter
if (BigWigsLoader.RegisterMessage) then
local t = {}
t.BigWigs_SetStage = function(self, event, module, phase)
phase = tonumber(phase)
sendPhaseNotification(phase)
end
BigWigsLoader.RegisterMessage(t, "BigWigs_SetStage")
end
if (BigWigsLoader.RegisterMessage) then
local t = {}
t.BigWigs_StartBar = function(self, event, module, spellId, barText, barTime, iconTexture, ...)
--table: 0000019DA5382410 BigWigs_StartBar table: 0000019EF3E5B910 441362 Volatile Concoction (14) 20 136227 false nil
--print("START", self, event, module, spellId, ...)
sendTimeBarNotification("START", spellId, barText, barTime, iconTexture, ...)
end
t.BigWigs_StopBar = function(self, event, module, spellId, ...)
--print("BW STOP BAR", self, event, module, spellId, ...)
sendTimeBarNotification("STOP", spellId)
end
t.BigWigs_StopBars = function(self, event, module, ...)
--print("BW STOP BARS", self, event, module, ...)
sendTimeBarNotification("STOPALL")
end
t.BigWigs_PauseBar = function(self, event, module, spellId, ...)
--print("BW PAUSE BAR", self, event, module, spellId, ...)
sendTimeBarNotification("PAUSE", spellId)
end
t.BigWigs_ResumeBar = function(self, event, module, spellId, ...)
--print("BW RESUME BAR", self, event, module, spellId, ...)
sendTimeBarNotification("RESUME", spellId)
end
BigWigsLoader.RegisterMessage(t, "BigWigs_StartBar")
BigWigsLoader.RegisterMessage(t, "BigWigs_StopBar")
BigWigsLoader.RegisterMessage(t, "BigWigs_StopBars")
BigWigsLoader.RegisterMessage(t, "BigWigs_PauseBar")
BigWigsLoader.RegisterMessage(t, "BigWigs_ResumeBar")
--self:RegisterMessage("BigWigs_StopBars", "StopModuleBars")
end
end
--]=]
end
detailsFramework.OnLoginSchedules[#detailsFramework.OnLoginSchedules+1] = createBossModsCallback
-----------------------------------------------------------------------------------------------------------------------------------------------------------
3 years ago
--forbidden functions on scripts
--these are functions which scripts cannot run due to security issues
local forbiddenFunction = {
--block mail, trades, action house, banks
["C_AuctionHouse"] = true,
["C_Bank"] = true,
["C_GuildBank"] = true,
["SetSendMailMoney"] = true,
["SendMail"] = true,
["SetTradeMoney"] = true,
["AddTradeMoney"] = true,
["PickupTradeMoney"] = true,
["PickupPlayerMoney"] = true,
["AcceptTrade"] = true,
--frames
["BankFrame"] = true,
["TradeFrame"] = true,
["GuildBankFrame"] = true,
["MailFrame"] = true,
["EnumerateFrames"] = true,
--block run code inside code
["RunScript"] = true,
["securecall"] = true,
3 years ago
["setfenv"] = true,
["getfenv"] = true,
["loadstring"] = true,
["pcall"] = true,
["xpcall"] = true,
["getglobal"] = true,
["setmetatable"] = true,
["DevTools_DumpCommand"] = true,
["ChatEdit_SendText"] = true,
--avoid creating macros
["SetBindingMacro"] = true,
["CreateMacro"] = true,
["EditMacro"] = true,
["hash_SlashCmdList"] = true,
["SlashCmdList"] = true,
--block guild commands
["GuildDisband"] = true,
["GuildUninvite"] = true,
--other things
["C_GMTicketInfo"] = true,
--deny messing addons with script support
["PlaterDB"] = true,
["_detalhes_global"] = true,
["WeakAurasSaved"] = true,
}
local C_RestrictedSubFunctions = {
["C_GuildInfo"] = {
["RemoveFromGuild"] = true,
},
}
--not in use, can't find a way to check within the environment handle
local addonRestrictedFunctions = {
["DetailsFramework"] = {
["SetEnvironment"] = true,
},
["Plater"] = {
["ImportScriptString"] = true,
["db"] = true,
},
["WeakAuras"] = {
["Add"] = true,
["AddMany"] = true,
["Delete"] = true,
["NewAura"] = true,
},
}
local C_SubFunctionsTable = {}
for globalTableName, functionTable in pairs(C_RestrictedSubFunctions) do
C_SubFunctionsTable [globalTableName] = {}
for functionName, functionObject in pairs(_G[globalTableName]) do
if (not functionTable[functionName]) then
C_SubFunctionsTable [globalTableName][functionName] = functionObject
end
end
end
DF.DefaultSecureScriptEnvironmentHandle = {
3 years ago
__index = function(env, key)
if (forbiddenFunction[key]) then
return nil
elseif (key == "_G") then
return env
elseif (C_SubFunctionsTable[key]) then
return C_SubFunctionsTable[key]
end
return _G[key]
end
}
function DF:SetEnvironment(func, environmentHandle, newEnvironment)
environmentHandle = environmentHandle or DF.DefaultSecureScriptEnvironmentHandle
newEnvironment = newEnvironment or {}
setmetatable(newEnvironment, environmentHandle)
_G.setfenv(func, newEnvironment)
end
3 years ago
function DF:MakeFunctionSecure(func)
return DF:SetEnvironment(func)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------
---receives an object and print debug info about its visibility
---use to know why a frame is not showing
---@param UIObject any
function DF:DebugVisibility(UIObject)
local bIsShown = UIObject:IsShown()
print("Is Shown:", bIsShown and "|cFF00FF00true|r" or "|cFFFF0000false|r")
print("Alpha > 0:", UIObject:GetAlpha() > 0 and "|cFF00FF00true|r" or "|cFFFF0000false|r")
local bIsVisible = UIObject:IsVisible()
print("Is Visible:", bIsVisible and "|cFF00FF00true|r" or "|cFFFF0000false|r")
local width, height = UIObject:GetSize()
print("Width:", width > 0 and "|cFF00FF00" .. width .. "|r" or "|cFFFF00000|r")
print("Height:", height > 0 and "|cFF00FF00" .. height .. "|r" or "|cFFFF00000|r")
local numPoints = UIObject:GetNumPoints()
print("Num Points:", numPoints > 0 and "|cFF00FF00" .. numPoints .. "|r" or "|cFFFF00000|r")
end
local benchmarkTime = 0
local bBenchmarkEnabled = false
function _G.__benchmark(bNotPrintResult)
if (not bBenchmarkEnabled) then
bBenchmarkEnabled = true
debugprofilestop()
benchmarkTime = debugprofilestop()
else
local elapsed = debugprofilestop() - benchmarkTime
bBenchmarkEnabled = false
if (bNotPrintResult) then
return elapsed
end
print("Elapsed Time:", elapsed)
return elapsed
end
end
5 months ago
function DF:DebugTexture(texture, left, right, top, bottom)
return DF:PreviewTexture(texture, left, right, top, bottom)
end
function DF:PreviewTexture(texture, left, right, top, bottom)
if (texture and type(texture) == "table" and texture.GetObjectType and texture:GetObjectType() == "Texture") then
DF:Msg("PreviewTexture: you have passed a texture object (uiobject) instead of the texture atlas, filename or id.")
end
local preview = DetailsFrameworkTexturePreview or CreateFrame("frame", "DetailsFrameworkTexturePreview", UIParent)
preview:SetSize(200, 200)
preview:SetPoint("center")
preview.texture = DetailsFrameworkTexturePreviewTexture or preview:CreateTexture("DetailsFrameworkTexturePreviewTexture", "artwork")
preview.texture:SetAllPoints()
preview.fontString = DetailsFrameworkTexturePreviewFontString or preview:CreateFontString("DetailsFrameworkTexturePreviewFontString", "artwork", "GameFontNormal")
preview.fontString:SetPoint("center", preview, "center", 0, 0)
preview.texture:SetTexture("")
preview.fontString:SetText("")
--check if the texture passed is an atlas
if (type(texture) == "string" and C_Texture.GetAtlasInfo(texture)) then
preview.texture:SetAtlas(texture)
elseif (type(texture) == "string" and texture:find("|T")) then
preview.fontString:SetText(texture)
elseif (type(texture) == "table") then
preview.texture:SetTexture(texture.file or texture.filename)
preview.texture:SetTexCoord(texture.leftTexCoord, texture.rightTexCoord, texture.topTexCoord, texture.bottomTexCoord)
else
preview.texture:SetTexture(texture)
preview.texture:SetTexCoord(left or 0, right or 1, top or 0, bottom or 1)
end
preview:Show()
end