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.

4921 lines
154 KiB

local dversion = 390
local major, minor = "DetailsFramework-1.0", dversion
local DF, oldminor = LibStub:NewLibrary(major, minor)
if (not DF) then
DetailsFrameworkCanLoad = false
return
end
_G["DetailsFramework"] = DF
DetailsFrameworkCanLoad = true
local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0")
local _
local type = type
local unpack = unpack
local upper = string.upper
local string_match = string.match
local tinsert = _G.tinsert
local abs = _G.abs
local tremove = _G.tremove
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
SMALL_NUMBER = 0.000001
ALPHA_BLEND_AMOUNT = 0.8400251
DF.dversion = dversion
DF.AuthorInfo = {
Name = "Terciob",
Discord = "https://discord.gg/AGSzAZX",
}
function DF:Msg(msg, ...)
print("|cFFFFFFAA" .. (self.__name or "FW Msg:") .. "|r ", msg, ...)
end
local PixelUtil = PixelUtil or DFPixelUtil
if (not PixelUtil) then
--check if is in classic, TBC, or WotLK wow, if it is, build a replacement for PixelUtil
local gameVersion = GetBuildInfo()
if (gameVersion:match("%d") == "1" or gameVersion:match("%d") == "2" or gameVersion:match("%d") == "3") then
PixelUtil = {
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
function DF:GetDefaultBackdropColor()
return 0.1215, 0.1176, 0.1294, 0.8
end
function DF.IsDragonflightAndBeyond()
return select(4, GetBuildInfo()) >= 100000
end
function DF.IsDragonflight()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 110000 and buildInfo >= 100000) then
return true
end
end
function DF.IsTimewalkWoW()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 40000) then
return true
end
end
function DF.IsClassicWow()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 20000) then
return true
end
end
function DF.IsTBCWow()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 30000 and buildInfo >= 20000) then
return true
end
end
function DF.IsWotLKWow()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 40000 and buildInfo >= 30000) then
return true
end
end
function DF.IsShadowlandsWow()
local _, _, _, buildInfo = GetBuildInfo()
if (buildInfo < 100000 and buildInfo >= 90000) then
return true
end
end
local roleBySpecTextureName = {
DruidBalance = "DAMAGER",
DruidFeralCombat = "DAMAGER",
DruidRestoration = "HEALER",
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",
DeathKnightBlood = "TANK",
DeathKnightFrost = "DAMAGER",
DeathKnightUnholy = "DAMAGER",
}
--classic, tbc and wotlk role guesser based on the weights of each talent tree
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 name, iconTexture, pointsSpent, fileName = GetTalentTabInfo(i)
if (name) then
tinsert(pointsPerSpec, {name, pointsSpent, fileName})
end
end
end
local MIN_SPECS = 4
--put the spec with more talent point to the top
table.sort(pointsPerSpec, function(t1, t2) return t1[2] > t2[2] end)
--get the spec with more points spent
local spec = pointsPerSpec[1]
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
return "DAMAGER"
end
function DF.UnitGroupRolesAssigned(unitId)
if (not DF.IsTimewalkWoW()) then --Was function exist check. TBC has function, returns NONE. -Flamanis 5/16/2022
local role = UnitGroupRolesAssigned(unitId)
if (role == "NONE" and UnitIsUnit(unitId, "player")) then
local specializationIndex = GetSpecialization() or 0
local id, name, description, icon, role, primaryStat = GetSpecializationInfo(specializationIndex)
return id and role or "NONE"
end
return role
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
if (Details) then
--attempt to get the role from Details! Damage Meter
local guid = UnitGUID(unitId)
if (guid) then
local role = Details.cached_roles[guid]
if (role) then
return role
end
end
end
local role = DF:GetRoleByClassicTalentTree()
return role
end
end
--return the specialization of the player it self
function DF.GetSpecialization()
if (GetSpecialization) then
return GetSpecialization()
end
return nil
end
function DF.GetSpecializationInfoByID(...)
if (GetSpecializationInfoByID) then
return GetSpecializationInfoByID(...)
end
return nil
end
function DF.GetSpecializationInfo(...)
if (GetSpecializationInfo) then
return GetSpecializationInfo(...)
end
return nil
end
function DF.GetSpecializationRole(...)
if (GetSpecializationRole) then
return GetSpecializationRole(...)
end
return nil
end
--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_
DF.EncounterJournal = {
EJ_GetCurrentInstance = EJ_GetCurrentInstance or function() return nil end,
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,
EJ_GetLootInfoByIndex = EJ_GetLootInfoByIndex or function() return nil end,
}
--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
DF.FrameWorkVersion = tostring(dversion)
function DF:PrintVersion()
print("Details! Framework Version:", DF.FrameWorkVersion)
end
--get the working folder
do
local path = string.match(debugstack(1, 1, 0), "AddOns\\(.+)fw.lua")
if (path) then
DF.folder = "Interface\\AddOns\\" .. path
else
--if not found, try to use the last valid one
DF.folder = DF.folder or ""
end
end
DF.debug = false
function DF:GetFrameworkFolder()
return DF.folder
end
function DF:SetFrameworkDebugState(state)
DF.debug = state
end
DF.embeds = DF.embeds or {}
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",
"CreateDropDown",
"CreateButton",
"CreateColorPickButton",
"CreateLabel",
"CreateBar",
"CreatePanel",
"CreateFillPanel",
"ColorPick",
"IconPick",
"CreateSimplePanel",
"CreateChartPanel",
"CreateImage",
"CreateScrollBar",
"CreateSwitch",
"CreateSlider",
"CreateSplitBar",
"CreateTextEntry",
"Create1PxPanel",
"CreateOptionsFrame",
"NewSpecialLuaEditorEntry",
"ShowPromptPanel",
"ShowTextPromptPanel",
"www_icons",
"GetTemplate",
"InstallTemplate",
"GetFrameworkFolder",
"ShowPanicWarning",
"SetFrameworkDebugState",
"FindHighestParent",
"OpenInterfaceProfile",
"CreateInCombatTexture",
"CreateAnimationHub",
"CreateAnimation",
"CreateScrollBox",
"CreateBorder",
"FormatNumber",
"IntegerToTimer",
"QuickDispatch",
"Dispatch",
"CommaValue",
"RemoveRealmName",
"Trim",
"CreateGlowOverlay",
"CreateAnts",
"CreateFrameShake",
"RegisterScriptComm",
"SendScriptComm",
}
function DF:Embed(target)
for k, v in pairs(embedFunctions) do
target[v] = self[v]
end
self.embeds[target] = true
return target
end
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()
frame:SetAlpha(1)
elseif (t == 1) then
frame.hidden = true
frame.faded = true
frame.fading_out = false
frame.fading_in = false
frame:SetAlpha(0)
frame:Hide()
end
end
------------------------------------------------------------------------------------------------------------
--table
DF.table = {}
function DF.table.find(t, value)
for i = 1, #t do
if (t[i] == value) then
return i
end
end
end
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
tinsert(t, index, value)
return true
end
function DF.table.reverse(t)
local new = {}
local index = 1
for i = #t, 1, -1 do
new[index] = t[i]
index = index + 1
end
return new
end
function DF.table.duplicate(t1, t2)
for key, value in pairs(t2) do
if (key ~= "__index" and key ~= "__newindex") then
--preserve a wowObject passing it to the new table with copying it
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 from table2 to table1 overwriting values
function DF.table.copy(t1, t2)
for key, value in pairs(t2) do
if (key ~= "__index" and key ~= "__newindex") then
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
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
--add the indexes of table2 into table1
function DF.table.append(t1, t2)
for i = 1, #t2 do
t1[#t1+1] = t2[i]
end
return t1
end
--copy values that does exist on table2 but not on table1
function DF.table.deploy(t1, t2)
for key, value in pairs(t2) do
if (type(value) == "table") then
t1 [key] = t1 [key] or {}
DF.table.deploy(t1 [key], t2 [key])
elseif (t1 [key] == nil) then
t1 [key] = value
end
end
return t1
end
function DF.table.dump(t, resultString, deep)
resultString = resultString or ""
deep = deep or 0
local space = ""
for i = 1, deep do
space = space .. " "
end
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
if (type(key) == "number") then
resultString = resultString .. space .. "[" .. key .. "] = |cFFa9ffa9 {|r\n"
else
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFa9ffa9 {|r\n"
end
resultString = resultString .. DF.table.dump (value, nil, deep+1)
resultString = resultString .. space .. "|cFFa9ffa9},|r\n"
elseif (valueType == "string") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = \"|cFFfff1c1" .. value .. "|r\",\n"
elseif (valueType == "number") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFffc1f4" .. value .. "|r,\n"
elseif (valueType == "function") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = function()end,\n"
elseif (valueType == "boolean") then
resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF99d0ff" .. (value and "true" or "false") .. "|r,\n"
end
end
return resultString
end
--grab a text and split it into lines adding each line to a indexed table
function DF:SplitTextInLines(text)
local lines = {}
local position = 1
local startScope, endScope = text:find("\n", position, true)
while (startScope) do
if (startScope ~= 1) then
tinsert(lines, text:sub(position, startScope-1))
end
position = endScope + 1
startScope, endScope = text:find("\n", position, true)
end
if (position <= #text) then
tinsert(lines, text:sub(position))
end
return lines
end
DF.www_icons = {
texture = "feedback_sites",
wowi = {0, 0.7890625, 0, 37/128},
curse = {0, 0.7890625, 38/123, 79/128},
mmoc = {0, 0.7890625, 80/123, 123/128},
}
local symbol_1K, symbol_10K, symbol_1B
if (GetLocale() == "koKR") then
symbol_1K, symbol_10K, symbol_1B = "", "", ""
elseif (GetLocale() == "zhCN") then
symbol_1K, symbol_10K, symbol_1B = "", "", "亿"
elseif (GetLocale() == "zhTW") then
symbol_1K, symbol_10K, symbol_1B = "", "", ""
end
function DF:GetAsianNumberSymbols()
if (GetLocale() == "koKR") then
return "", "", ""
elseif (GetLocale() == "zhCN") then
return "", "", "亿"
elseif (GetLocale() == "zhTW") then
return "", "", ""
else
--return korean as default (if the language is western)
return "", "", ""
end
end
if (symbol_1K) then
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
return format("%.1f", number)
end
else
function DF.FormatNumber (number)
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
return floor(number)
end
end
function DF:CommaValue(value)
if (not value) then
return "0"
end
value = floor(value)
if (value == 0) then
return "0"
end
--source http://richard.warburton.it
local left, num, right = string_match (value, '^([^%d]*%d)(%d*)(.-)$')
return left .. (num:reverse():gsub('(%d%d%d)','%1,'):reverse()) .. right
end
function DF:GroupIterator(callback, ...)
if (IsInRaid()) then
for i = 1, GetNumGroupMembers() do
DF:QuickDispatch(callback, "raid" .. i, ...)
end
elseif (IsInGroup()) then
for i = 1, GetNumGroupMembers() - 1 do
DF:QuickDispatch(callback, "party" .. i, ...)
end
DF:QuickDispatch(callback, "player", ...)
else
DF:QuickDispatch(callback, "player", ...)
end
end
function DF:IntegerToTimer(value)
return "" .. floor(value/60) .. ":" .. format("%02.f", value%60)
end
function DF:RemoveRealmName(name)
return name:gsub(("%-.*"), "")
end
function DF:RemoveRealName(name)
return name:gsub(("%-.*"), "")
end
function DF:SetFontSize(fontString, ...)
local font, _, flags = fontString:GetFont()
fontString:SetFont(font, max(...), flags)
end
function DF:SetFontFace(fontString, fontface)
local font = SharedMedia:Fetch("font", fontface, true)
if (font) then
fontface = font
end
local _, size, flags = fontString:GetFont()
fontString:SetFont(fontface, size, flags)
end
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
function DF:SetFontShadow(fontString, r, g, b, a, x, y)
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
function DF:SetFontRotation(fontString, degrees)
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
end
function DF:AddClassColorToText(text, className)
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
text = "|c" .. color.colorStr .. DF:RemoveRealName(text) .. "|r"
else
return DF:RemoveRealName(text)
end
return text
end
function DF:GetClassTCoordsAndTexture(class)
local l, r, t, b = unpack(CLASS_ICON_TCOORDS[class])
return l, r, t, b, [[Interface\WORLDSTATEFRAME\Icons-Classes]]
end
function DF:AddClassIconToText(text, playerName, class, useSpec, iconSize)
local size = iconSize or 16
local spec
if (useSpec) then
if (Details) then
local GUID = UnitGUID(playerName)
if (GUID) then
spec = Details.cached_specs[GUID]
if (spec) then
spec = spec
end
end
end
end
if (spec) then --if spec is valid, the user has Details! installed
local specString = ""
local L, R, T, B = unpack(Details.class_specs_coords[spec])
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 (class) then
local classString = ""
local L, R, T, B = unpack(Details.class_coords[class])
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:GetFontSize(fontString)
local _, size = fontString:GetFont()
return size
end
function DF:GetFontFace(fontString)
local fontface = fontString:GetFont()
return fontface
end
local ValidOutlines = {
["NONE"] = true,
["MONOCHROME"] = true,
["OUTLINE"] = true,
["THICKOUTLINE"] = true,
}
function DF:SetFontOutline(fontString, outline)
local font, fontSize = fontString:GetFont()
if (outline) then
if (type(outline) == "string") then
outline = outline:upper()
end
if (ValidOutlines[outline]) then
outline = outline
elseif (type(outline) == "boolean" and outline) then
outline = "OUTLINE"
elseif (type(outline) == "boolean" and not outline) then
outline = "NONE"
elseif (outline == 1) then
outline = "OUTLINE"
elseif (outline == 2) then
outline = "THICKOUTLINE"
end
end
fontString:SetFont(font, fontSize, outline)
end
function DF:Trim(string)
return DF:trim(string)
end
function DF:trim(string)
local from = string:match"^%s*()"
return from > #string and "" or string:match(".*%S", from)
end
--truncated revoming at a maximum of 10 character from the string
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
function DF:TruncateText(fontString, maxWidth)
local text = fontString:GetText()
while (fontString:GetStringWidth() > maxWidth) do
text = strsub(text, 1, #text - 1)
fontString:SetText(text)
if (string.len(text) <= 1) then
break
end
end
text = DF:CleanTruncateUTF8String(text)
fontString:SetText(text)
end
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
if b1 and b1 >= 194 and b1 <= 244 then
text = strsub (text, 1, #text - 1)
elseif b2 and b2 >= 224 and b2 <= 244 then
text = strsub (text, 1, #text - 2)
elseif b3 and b3 >= 240 and b3 <= 244 then
text = strsub (text, 1, #text - 3)
end
end
return text
end
--DF:TruncateNumber(number, fractionDigits): truncate the amount of numbers used to show fraction.
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
function DF:GetNpcIdFromGuid(GUID)
local npcId = select(6, strsplit("-", GUID ))
if (npcId) then
return tonumber(npcId)
end
return 0
end
function DF.SortOrder1(t1, t2)
return t1[1] > t2[1]
end
function DF.SortOrder2(t1, t2)
return t1[2] > t2[2]
end
function DF.SortOrder3(t1, t2)
return t1[3] > t2[3]
end
function DF.SortOrder1R(t1, t2)
return t1[1] < t2[1]
end
function DF.SortOrder2R(t1, t2)
return t1[2] < t2[2]
end
function DF.SortOrder3R(t1, t2)
return t1[3] < t2[3]
end
--return a list of spells from the player spellbook
function DF:GetSpellBookSpells()
local spellNamesInSpellBook = {}
local spellIdsInSpellBook = {}
for i = 1, GetNumSpellTabs() do
local tabName, tabTexture, offset, numSpells, isGuild, offspecId = GetSpellTabInfo(i)
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, "player")
if (spellId) then
if (spellType ~= "FLYOUT") then
local spellName = GetSpellInfo(spellId)
if (spellName) then
spellNamesInSpellBook[spellName] = true
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
spellIdsInSpellBook[#spellIdsInSpellBook+1] = spellID
end
end
end
end
end
end
end
end
return spellNamesInSpellBook, spellIdsInSpellBook
end
------------------------------------------------------------------------------------------------------------------------
--flash animation
local onFinishFlashAnimation = function(self)
if (self.showWhenDone) then
self.frame:SetAlpha(1)
else
self.frame:SetAlpha(0)
self.frame:Hide()
end
if (self.onFinishFunc) then
self:onFinishFunc(self.frame)
end
end
local stopAnimation_Method = function(self)
local FlashAnimation = self.FlashAnimation
FlashAnimation:Stop()
end
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()
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()
self:SetAlpha(0)
flashAnimation:Play()
end
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
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--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
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
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
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
elseif (type(point1) == "number") then --setpoint(x, y)
point1, point2, point3, point4, point5 = "topleft", object.widget:GetParent(), "topleft", point1, point2
elseif (type(point1) == "table") then --setpoint(frame, x, y)
point1, point2, point3, point4, point5 = "topleft", point1, "topleft", point2, point3
end
if (not point2) then
point2 = object.widget:GetParent()
elseif (point2.dframework) then
point2 = point2.widget
end
return point1 or "topleft", point2, point3 or "topleft", point4 or 0, point5 or 0
end
local anchoringFunctions = {
function(frame, anchorTo, offSetX, offSetY) --1 TOP LEFT
frame:ClearAllPoints()
frame:SetPoint("bottomleft", anchorTo, "topleft", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --2 LEFT
frame:ClearAllPoints()
frame:SetPoint("right", anchorTo, "left", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --3 BOTTOM LEFT
frame:ClearAllPoints()
frame:SetPoint("topleft", anchorTo, "bottomleft", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --4 BOTTOM
frame:ClearAllPoints()
frame:SetPoint("top", anchorTo, "bottom", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --5 BOTTOM RIGHT
frame:ClearAllPoints()
frame:SetPoint("topright", anchorTo, "bottomright", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --6 RIGHT
frame:ClearAllPoints()
frame:SetPoint("left", anchorTo, "right", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --7 TOP RIGHT
frame:ClearAllPoints()
frame:SetPoint("bottomright", anchorTo, "topright", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --8 TOP
frame:ClearAllPoints()
frame:SetPoint("bottom", anchorTo, "top", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --9 CENTER
frame:ClearAllPoints()
frame:SetPoint("center", anchorTo, "center", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --10
frame:ClearAllPoints()
frame:SetPoint("left", anchorTo, "left", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --11
frame:ClearAllPoints()
frame:SetPoint("right", anchorTo, "right", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --12
frame:ClearAllPoints()
frame:SetPoint("top", anchorTo, "top", offSetX, offSetY)
end,
function(frame, anchorTo, offSetX, offSetY) --13
frame:ClearAllPoints()
frame:SetPoint("bottom", anchorTo, "bottom", offSetX, offSetY)
end
}
function DF:SetAnchor(widget, config, anchorTo)
anchorTo = anchorTo or widget:GetParent()
anchoringFunctions[config.side](widget, anchorTo, config.x, config.y)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--colors
--add a new color name, the color can be query using DetailsFramework:ParseColors(colorName)
function DF:NewColor(colorName, red, green, blue, alpha)
assert(type(colorName) == "string", "DetailsFramework:NewColor(): colorName must be a string.")
assert(not DF.alias_text_colors[colorName], "DetailsFramework:NewColor(): colorName already exists.")
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
local colorTableMixin = {
GetColor = function(self)
return self.r, self.g, self.b, self.a
end,
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,
IsColorTable = true,
}
--convert a any format of color to any other format of color
function DF:FormatColor(newFormat, r, g, b, a, decimalsAmount)
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 = {
r = r or 1,
g = g or 1,
b = b or 1,
a = a or 1,
}
DF:Mixin(t, colorTableMixin)
return t
end
function DF:IsHtmlColor(color)
return DF.alias_text_colors[color]
end
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
--{1, .7, .2, 1}
red, green, blue, alpha = unpack(colorTable)
end
--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
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
--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
--no color found within the string, return default color
red, green, blue, alpha = unpack(DF.alias_text_colors.none)
end
end
end
if (not red or type(red) ~= "number") then
red = 1
end
if (not green) or type(green) ~= "number" then
green = 1
end
if (not blue or type(blue) ~= "number") then
blue = 1
end
if (not alpha or type(alpha) ~= "number") then
alpha = 1
end
return Saturate(red), Saturate(green), Saturate(blue), Saturate(alpha)
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--menus
local formatOptionNameWithColon = function(text, useColon)
if (text) then
if (useColon) then
text = text .. ":"
return text
else
return text
end
end
end
local widgetsToDisableOnCombat = {}
local getMenuWidgetVolative = function(parent, widgetType, indexTable)
local widgetObject
if (widgetType == "label") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType], "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "dropdown") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateDropDown(parent, function() return {} end, nil, 140, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType])
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
widgetObject.hasLabel.text = ""
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "switch") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateSwitch(parent, nil, true, 20, 20, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType])
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "slider") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateSlider(parent, 140, 20, 1, 2, 1, 1, false, nil, "$parentWidget" .. widgetType .. indexTable[widgetType])
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "color") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateColorPickButton(parent, "$parentWidget" .. widgetType .. indexTable[widgetType], nil, function()end, 1)
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "button") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateButton(parent, function()end, 120, 18, "", nil, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType])
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
end
indexTable[widgetType] = indexTable[widgetType] + 1
elseif (widgetType == "textentry") then
widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]]
if (not widgetObject) then
widgetObject = DF:CreateTextEntry(parent, function()end, 120, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType])
widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay")
tinsert(parent.widget_list, widgetObject)
tinsert(parent.widget_list_by_type[widgetType], widgetObject)
else
widgetObject:ClearHooks()
end
indexTable[widgetType] = indexTable[widgetType] + 1
end
--if the widget is inside the no combat table, remove it
for i = 1, #widgetsToDisableOnCombat do
if (widgetsToDisableOnCombat[i] == widgetObject) then
tremove(widgetsToDisableOnCombat, i)
break
end
end
return widgetObject
end
--volatile menu can be called several times, each time all settings are reset and a new menu is built using the same widgets
function DF:BuildMenuVolatile(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook)
if (not parent.widget_list) then
DF:SetAsOptionsPanel(parent)
end
DF:ClearOptionsPanel(parent)
local currentXOffset = xOffset
local currentYOffset = yOffset
local maxColumnWidth = 0
local latestInlineWidget
local widgetIndexes = {
label = 1,
dropdown = 1,
switch = 1,
slider = 1,
color = 1,
button = 1,
textentry = 1,
}
height = abs((height or parent:GetHeight()) - abs(yOffset) + 20)
height = height * -1
--normalize format types
for index, widgetTable in ipairs(menuOptions) do
if (widgetTable.type == "space") then
widgetTable.type = "blank"
elseif (widgetTable.type == "dropdown") then
widgetTable.type = "select"
elseif (widgetTable.type == "switch") then
widgetTable.type = "toggle"
elseif (widgetTable.type == "slider") then
widgetTable.type = "range"
elseif (widgetTable.type == "button") then
widgetTable.type = "execute"
end
end
--catch some options added in the hash part of the menu table
local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst
local languageAddonId = menuOptions.language_addonId
local languageTable
if (languageAddonId) then
languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId)
end
for index, widgetTable in ipairs(menuOptions) do
if (not widgetTable.hidden) then
local widgetCreated
if (latestInlineWidget) then
if (not widgetTable.inline) then
latestInlineWidget = nil
currentYOffset = currentYOffset - 20
end
end
local extraPaddingY = 0
if (not widgetTable.novolatile) then
--step a line
if (widgetTable.type == "blank" or widgetTable.type == "space") then
--do nothing
elseif (widgetTable.type == "label" or widgetTable.type == "text") then
local label = getMenuWidgetVolative(parent, "label", widgetIndexes)
widgetCreated = label
label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.get and widgetTable.get() or widgetTable.text) or (widgetTable.namePhraseId) or ""
label.color = widgetTable.color
if (widgetTable.font) then
label.fontface = widgetTable.font
end
if (widgetTable.text_template or textTemplate) then
label:SetTemplate(widgetTable.text_template or textTemplate)
else
label.fontsize = widgetTable.size or 10
end
label._get = widgetTable.get
label.widget_type = "label"
label:ClearAllPoints()
label:SetPoint(currentXOffset, currentYOffset)
if (widgetTable.id) then
parent.widgetids [widgetTable.id] = label
end
--dropdowns
elseif (widgetTable.type == "select" or widgetTable.type == "dropdown") then
assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'")
local dropdown = getMenuWidgetVolative(parent, "dropdown", widgetIndexes)
widgetCreated = dropdown
dropdown:SetFunction(widgetTable.values)
dropdown:Refresh()
dropdown:Select(widgetTable.get())
dropdown:SetTemplate(dropdownTemplate)
dropdown:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
dropdown._get = widgetTable.get
dropdown.widget_type = "select"
dropdown.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or ""
dropdown.hasLabel:SetTemplate(widgetTable.text_template or textTemplate)
dropdown:ClearAllPoints()
dropdown:SetPoint("left", dropdown.hasLabel, "right", 2)
dropdown.hasLabel:ClearAllPoints()
dropdown.hasLabel:SetPoint(currentXOffset, currentYOffset)
--global callback
if (valueChangeHook) then
dropdown:SetHook("OnOptionSelected", valueChangeHook)
end
--hook list (hook list is wiped when getting the widget)
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
dropdown:SetHook(hookName, hookFunc)
end
end
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = dropdown
end
local widgetTotalSize = dropdown.hasLabel.widget:GetStringWidth() + 140 + 4
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--switchs
elseif (widgetTable.type == "toggle" or widgetTable.type == "switch") then
local switch = getMenuWidgetVolative(parent, "switch", widgetIndexes)
widgetCreated = switch
switch:SetValue(widgetTable.get())
switch:SetTemplate(switchTemplate)
switch:SetAsCheckBox() --it's always a checkbox on volatile menu
switch:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
switch._get = widgetTable.get
switch.widget_type = "toggle"
switch.OnSwitch = widgetTable.set
if (valueChangeHook) then
switch:SetHook("OnSwitch", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
switch:SetHook(hookName, hookFunc)
end
end
if (widgetTable.width) then
switch:SetWidth(widgetTable.width)
end
if (widgetTable.height) then
switch:SetHeight(widgetTable.height)
end
switch.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or ""
switch.hasLabel:SetTemplate(widgetTable.text_template or textTemplate)
switch:ClearAllPoints()
switch.hasLabel:ClearAllPoints()
if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then
switch:SetPoint(currentXOffset, currentYOffset)
switch.hasLabel:SetPoint("left", switch, "right", 2)
local nextWidgetTable = menuOptions[index+1]
if (nextWidgetTable) then
if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then
extraPaddingY = 4
end
end
else
switch.hasLabel:SetPoint(currentXOffset, currentYOffset)
switch:SetPoint("left", switch.hasLabel, "right", 2)
end
if (widgetTable.id) then
parent.widgetids [widgetTable.id] = switch
end
local widgetTotalSize = switch.hasLabel:GetStringWidth() + 32
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--slider
elseif (widgetTable.type == "range" or widgetTable.type == "slider") then
local slider = getMenuWidgetVolative(parent, "slider", widgetIndexes)
widgetCreated = slider
if (widgetTable.usedecimals) then
slider.slider:SetValueStep(0.01)
else
slider.slider:SetValueStep(widgetTable.step)
end
slider.useDecimals = widgetTable.usedecimals
slider.slider:SetMinMaxValues(widgetTable.min, widgetTable.max)
slider.slider:SetValue(widgetTable.get())
slider.ivalue = slider.slider:GetValue()
slider:SetTemplate(sliderTemplate)
slider:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
slider._get = widgetTable.get
slider.widget_type = "range"
slider:SetHook("OnValueChange", widgetTable.set)
if (valueChangeHook) then
slider:SetHook("OnValueChange", valueChangeHook)
end
if (widgetTable.thumbscale) then
slider:SetThumbSize (slider.thumb.originalWidth * widgetTable.thumbscale, nil)
else
slider:SetThumbSize (slider.thumb.originalWidth * 1.3, nil)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
slider:SetHook(hookName, hookFunc)
end
end
slider.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or ""
slider.hasLabel:SetTemplate(widgetTable.text_template or textTemplate)
slider:SetPoint("left", slider.hasLabel, "right", 2)
slider.hasLabel:SetPoint(currentXOffset, currentYOffset)
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = slider
end
local widgetTotalSize = slider.hasLabel:GetStringWidth() + 146
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--color
elseif (widgetTable.type == "color" or widgetTable.type == "color") then
local colorpick = getMenuWidgetVolative(parent, "color", widgetIndexes)
widgetCreated = colorpick
colorpick.color_callback = widgetTable.set --callback
colorpick:SetTemplate(buttonTemplate)
colorpick:SetSize(18, 18)
colorpick:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
colorpick._get = widgetTable.get
colorpick.widget_type = "color"
local default_value, g, b, a = widgetTable.get()
if (type(default_value) == "table") then
colorpick:SetColor(unpack(default_value))
else
colorpick:SetColor(default_value, g, b, a)
end
if (valueChangeHook) then
colorpick:SetHook("OnColorChanged", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
colorpick:SetHook(hookName, hookFunc)
end
end
local label = colorpick.hasLabel
label.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or ""
label:SetTemplate(widgetTable.text_template or textTemplate)
label:ClearAllPoints()
colorpick:ClearAllPoints()
if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then
label:SetPoint("left", colorpick, "right", 2)
colorpick:SetPoint(currentXOffset, currentYOffset)
extraPaddingY = 1
else
colorpick:SetPoint("left", label, "right", 2)
label:SetPoint(currentXOffset, currentYOffset)
end
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = colorpick
end
local widgetTotalSize = label:GetStringWidth() + 32
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--button
elseif (widgetTable.type == "execute" or widgetTable.type == "button") then
local button = getMenuWidgetVolative(parent, "button", widgetIndexes)
widgetCreated = button
button:SetTemplate(buttonTemplate)
button:SetSize(widgetTable.width or 120, widgetTable.height or 18)
button:SetClickFunction(widgetTable.func, widgetTable.param1, widgetTable.param2)
local textTemplate = widgetTable.text_template or textTemplate or DF.font_templates["ORANGE_FONT_TEMPLATE"]
button.textcolor = textTemplate.color
button.textfont = textTemplate.font
button.textsize = textTemplate.size
button.text = (languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.name) or (widgetTable.namePhraseId) or ""
if (widgetTable.inline) then
if (latestInlineWidget) then
button:SetPoint("left", latestInlineWidget, "right", 2, 0)
latestInlineWidget = button
else
button:SetPoint(currentXOffset, currentYOffset)
latestInlineWidget = button
end
else
button:SetPoint(currentXOffset, currentYOffset)
end
button:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
button.widget_type = "execute"
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
button:SetHook(hookName, hookFunc)
end
end
if (widgetTable.width) then
button:SetWidth(widgetTable.width)
end
if (widgetTable.height) then
button:SetHeight(widgetTable.height)
end
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = button
end
local widgetTotalSize = button:GetWidth() + 4
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--textentry
elseif (widgetTable.type == "textentry") then
local textentry = getMenuWidgetVolative(parent, "textentry", widgetIndexes)
widgetCreated = textentry
textentry:SetCommitFunction(widgetTable.func or widgetTable.set)
textentry:SetTemplate(widgetTable.template or widgetTable.button_template or buttonTemplate)
textentry:SetSize(widgetTable.width or 120, widgetTable.height or 18)
textentry:SetTooltip((languageTable and languageTable[widgetTable.namePhraseId]) or (widgetTable.desc) or (widgetTable.namePhraseId))
textentry.text = widgetTable.get()
textentry._get = widgetTable.get
textentry.widget_type = "textentry"
textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set)
textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set)
textentry.hasLabel.text = (languageTable and languageTable[widgetTable.namePhraseId]) or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or ""
textentry.hasLabel:SetTemplate(widgetTable.text_template or textTemplate)
textentry:SetPoint("left", textentry.hasLabel, "right", 2)
textentry.hasLabel:SetPoint(currentXOffset, currentYOffset)
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
textentry:SetHook(hookName, hookFunc)
end
end
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = textentry
end
local widgetTotalSize = textentry.hasLabel:GetStringWidth() + 64
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
end --end loop
if (widgetTable.nocombat) then
tinsert(widgetsToDisableOnCombat, widgetCreated)
end
if (not widgetTable.inline) then
if (widgetTable.spacement) then
currentYOffset = currentYOffset - 30
else
currentYOffset = currentYOffset - 20
end
end
if (extraPaddingY > 0) then
currentYOffset = currentYOffset - extraPaddingY
end
if (widgetTable.type == "breakline" or currentYOffset < height) then
currentYOffset = yOffset
currentXOffset = currentXOffset + maxColumnWidth + 20
maxColumnWidth = 0
end
if widgetCreated then
widgetCreated:Show()
end
end
end
end
DF.RefreshUnsafeOptionsWidgets()
end
function DF:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook)
if (not parent.widget_list) then
DF:SetAsOptionsPanel(parent)
end
local currentXOffset = xOffset
local currentYOffset = yOffset
local maxColumnWidth = 0
--how many widgets has been created on this line loop pass
local amountLineWidgetCreated = 0
local latestInlineWidget
height = abs((height or parent:GetHeight()) - abs(yOffset) + 20)
height = height * -1
--normalize format types
for index, widgetTable in ipairs(menuOptions) do
if (widgetTable.type == "space") then
widgetTable.type = "blank"
elseif (widgetTable.type == "dropdown") then
widgetTable.type = "select"
elseif (widgetTable.type == "switch") then
widgetTable.type = "toggle"
elseif (widgetTable.type == "slider") then
widgetTable.type = "range"
elseif (widgetTable.type == "button") then
widgetTable.type = "execute"
end
end
--catch some options added in the hash part of the menu table
local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst
local languageAddonId = menuOptions.language_addonId
local languageTable
if (languageAddonId) then
languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId)
end
for index, widgetTable in ipairs(menuOptions) do
if (not widgetTable.hidden) then
local widgetCreated
if (latestInlineWidget) then
if (not widgetTable.inline) then
latestInlineWidget = nil
currentYOffset = currentYOffset - 28
end
end
local extraPaddingY = 0
if (widgetTable.type == "blank") then
--do nothing
elseif (widgetTable.type == "label" or widgetTable.type == "text") then
local label = DF:CreateLabel(parent, "", widgetTable.text_template or textTemplate or widgetTable.size, widgetTable.color, widgetTable.font, nil, "$parentWidget" .. index, "overlay")
label._get = widgetTable.get
label.widget_type = "label"
label:SetPoint(currentXOffset, currentYOffset)
if (widgetTable.namePhraseId) then
DetailsFramework.Language.RegisterFontString(languageAddonId, label.widget, widgetTable.namePhraseId)
else
local textToSet = (widgetTable.get and widgetTable.get()) or widgetTable.text or ""
label:SetText(textToSet)
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, label)
tinsert(parent.widget_list_by_type.label, label)
amountLineWidgetCreated = amountLineWidgetCreated + 1
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = label
end
elseif (widgetTable.type == "select") then
assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'")
local dropdown = DF:NewDropDown(parent, nil, "$parentWidget" .. index, nil, 140, 18, widgetTable.values, widgetTable.get(), dropdownTemplate)
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
dropdown._get = widgetTable.get
dropdown.widget_type = "select"
local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon))
dropdown:SetPoint("left", label, "right", 2)
label:SetPoint(currentXOffset, currentYOffset)
dropdown.hasLabel = label
--global callback
if (valueChangeHook) then
dropdown:SetHook("OnOptionSelected", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
dropdown:SetHook(hookName, hookFunc)
end
end
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = dropdown
end
local widgetTotalSize = label.widget:GetStringWidth() + 144
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, dropdown)
tinsert(parent.widget_list_by_type.dropdown, dropdown)
widgetCreated = dropdown
amountLineWidgetCreated = amountLineWidgetCreated + 1
elseif (widgetTable.type == "toggle") then
local switch = DF:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate)
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
switch._get = widgetTable.get
switch.widget_type = "toggle"
switch.OnSwitch = widgetTable.set
if (switchIsCheckbox) then
switch:SetAsCheckBox()
end
if (valueChangeHook) then
switch:SetHook("OnSwitch", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
switch:SetHook(hookName, hookFunc)
end
end
if (widgetTable.width) then
switch:SetWidth(widgetTable.width)
end
if (widgetTable.height) then
switch:SetHeight(widgetTable.height)
end
local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon))
if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then
switch:SetPoint(currentXOffset, currentYOffset)
label:SetPoint("left", switch, "right", 2)
local nextWidgetTable = menuOptions[index+1]
if (nextWidgetTable) then
if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then
extraPaddingY = 4
end
end
else
label:SetPoint(currentXOffset, currentYOffset)
switch:SetPoint("left", label, "right", 2, 0)
end
switch.hasLabel = label
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = switch
end
local widgetTotalSize = label.widget:GetStringWidth() + 32
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, switch)
tinsert(parent.widget_list_by_type.switch, switch)
widgetCreated = switch
amountLineWidgetCreated = amountLineWidgetCreated + 1
elseif (widgetTable.type == "range") then
assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'range'")
local isDecimanls = widgetTable.usedecimals
local slider = DF:NewSlider(parent, nil, "$parentWidget" .. index, nil, 140, 20, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), isDecimanls, nil, nil, sliderTemplate)
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
slider._get = widgetTable.get
slider.widget_type = "range"
slider:SetHook("OnValueChange", widgetTable.set)
if (widgetTable.thumbscale) then
slider:SetThumbSize(slider.thumb:GetWidth() * widgetTable.thumbscale, nil)
else
slider:SetThumbSize(slider.thumb:GetWidth() * 1.3, nil)
end
if (valueChangeHook) then
slider:SetHook("OnValueChange", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
slider:SetHook(hookName, hookFunc)
end
end
local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon))
slider:SetPoint("left", label, "right", 2)
label:SetPoint(currentXOffset, currentYOffset)
slider.hasLabel = label
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = slider
end
local widgetTotalSize = label.widget:GetStringWidth() + 146
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, slider)
tinsert(parent.widget_list_by_type.slider, slider)
widgetCreated = slider
amountLineWidgetCreated = amountLineWidgetCreated + 1
elseif (widgetTable.type == "color") then
assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'")
local colorpick = DF:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate)
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
colorpick._get = widgetTable.get
colorpick.widget_type = "color"
colorpick:SetSize(18, 18)
local r, g, b, a = DF:ParseColors(widgetTable.get())
colorpick:SetColor(r, g, b, a)
if (valueChangeHook) then
colorpick:SetHook("OnColorChanged", valueChangeHook)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
colorpick:SetHook(hookName, hookFunc)
end
end
local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon))
if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then
label:SetPoint("left", colorpick, "right", 2)
colorpick:SetPoint(currentXOffset, currentYOffset)
extraPaddingY = 1
else
colorpick:SetPoint("left", label, "right", 2)
label:SetPoint(currentXOffset, currentYOffset)
end
colorpick.hasLabel = label
if (widgetTable.id) then
parent.widgetids[widgetTable.id] = colorpick
end
local widgetTotalSize = label.widget:GetStringWidth() + 32
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, colorpick)
tinsert(parent.widget_list_by_type.color, colorpick)
widgetCreated = colorpick
amountLineWidgetCreated = amountLineWidgetCreated + 1
elseif (widgetTable.type == "execute") then
local button = DF:NewButton(parent, nil, "$parentWidget" .. index, nil, 120, 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, widgetTable.namePhraseId, widgetTable.name)
if (not buttonTemplate) then
button:InstallCustomTexture()
end
if (widgetTable.inline) then
if (latestInlineWidget) then
button:SetPoint("left", latestInlineWidget, "right", 2, 0)
latestInlineWidget = button
else
button:SetPoint(currentXOffset, currentYOffset)
latestInlineWidget = button
end
else
button:SetPoint(currentXOffset, currentYOffset)
end
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
button.widget_type = "execute"
--button icon
if (widgetTable.icontexture) then
button:SetIcon(widgetTable.icontexture, nil, nil, nil, widgetTable.icontexcoords, nil, nil, 2)
end
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
button:SetHook(hookName, hookFunc)
end
end
if (widgetTable.id) then
parent.widgetids [widgetTable.id] = button
end
if (widgetTable.width) then
button:SetWidth(widgetTable.width)
end
if (widgetTable.height) then
button:SetHeight(widgetTable.height)
end
local widgetTotalSize = button:GetWidth() + 4
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, button)
tinsert(parent.widget_list_by_type.button, button)
widgetCreated = button
amountLineWidgetCreated = amountLineWidgetCreated + 1
elseif (widgetTable.type == "textentry") then
local textentry = DF:CreateTextEntry(parent, widgetTable.func or widgetTable.set, 120, 18, nil, "$parentWidget" .. index, nil, buttonTemplate)
DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", widgetTable.descPhraseId, widgetTable.desc)
textentry.text = widgetTable.get()
textentry._get = widgetTable.get
textentry.widget_type = "textentry"
textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set)
textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set)
local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12)
DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, widgetTable.namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon))
textentry:SetPoint("left", label, "right", 2)
label:SetPoint(currentXOffset, currentYOffset)
textentry.hasLabel = label
--hook list
if (widgetTable.hooks) then
for hookName, hookFunc in pairs(widgetTable.hooks) do
textentry:SetHook(hookName, hookFunc)
end
end
if (widgetTable.id) then
parent.widgetids [widgetTable.id] = textentry
end
local widgetTotalSize = label.widget:GetStringWidth() + 64
if (widgetTotalSize > maxColumnWidth) then
maxColumnWidth = widgetTotalSize
end
--store the widget created into the overall table and the widget by type
tinsert(parent.widget_list, textentry)
tinsert(parent.widget_list_by_type.textentry, textentry)
widgetCreated = textentry
amountLineWidgetCreated = amountLineWidgetCreated + 1
end
if (widgetTable.nocombat) then
tinsert(widgetsToDisableOnCombat, widgetCreated)
end
if (not widgetTable.inline) then
if (widgetTable.spacement) then
currentYOffset = currentYOffset - 30
else
currentYOffset = currentYOffset - 20
end
end
if (extraPaddingY > 0) then
currentYOffset = currentYOffset - extraPaddingY
end
if (widgetTable.type == "breakline" or currentYOffset < height) then
currentYOffset = yOffset
currentXOffset = currentXOffset + maxColumnWidth + 20
amountLineWidgetCreated = 0
maxColumnWidth = 0
end
end
end
DF.RefreshUnsafeOptionsWidgets()
end
local lockNotSafeWidgetsForCombat = function()
for _, widget in ipairs(widgetsToDisableOnCombat) do
widget:Disable()
end
end
local unlockNotSafeWidgetsForCombat = function()
for _, widget in ipairs(widgetsToDisableOnCombat) do
widget:Enable()
end
end
function DF.RefreshUnsafeOptionsWidgets()
if (DF.PlayerHasCombatFlag) then
lockNotSafeWidgetsForCombat()
else
unlockNotSafeWidgetsForCombat()
end
end
DF.PlayerHasCombatFlag = false
local ProtectCombatFrame = CreateFrame("frame")
ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_ENABLED")
ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_DISABLED")
ProtectCombatFrame:RegisterEvent("PLAYER_ENTERING_WORLD")
ProtectCombatFrame:SetScript("OnEvent", function(self, event)
if (event == "PLAYER_ENTERING_WORLD") then
if (InCombatLockdown()) then
DF.PlayerHasCombatFlag = true
else
DF.PlayerHasCombatFlag = false
end
DF.RefreshUnsafeOptionsWidgets()
elseif (event == "PLAYER_REGEN_ENABLED") then
DF.PlayerHasCombatFlag = false
DF.RefreshUnsafeOptionsWidgets()
elseif (event == "PLAYER_REGEN_DISABLED") then
DF.PlayerHasCombatFlag = true
DF.RefreshUnsafeOptionsWidgets()
end
end)
function DF:CreateInCombatTexture(frame)
if (DF.debug and not frame) then
error("Details! Framework: CreateInCombatTexture invalid frame on parameter 1.")
end
local inCombatBackgroundTexture = DF:CreateImage(frame)
inCombatBackgroundTexture:SetColorTexture(.6, 0, 0, .1)
inCombatBackgroundTexture:Hide()
local inCombatLabel = Plater:CreateLabel(frame, "you are in combat", 24, "silver")
inCombatLabel:SetPoint("right", inCombatBackgroundTexture, "right", -10, 0)
inCombatLabel:Hide()
frame:RegisterEvent("PLAYER_REGEN_DISABLED")
frame:RegisterEvent("PLAYER_REGEN_ENABLED")
frame:SetScript("OnEvent", function(self, event)
if (event == "PLAYER_REGEN_DISABLED") then
inCombatBackgroundTexture:Show()
inCombatLabel:Show()
elseif (event == "PLAYER_REGEN_ENABLED") then
inCombatBackgroundTexture:Hide()
inCombatLabel:Hide()
end
end)
return inCombatBackgroundTexture
end
----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--tutorials
function DF:ShowTutorialAlertFrame(maintext, desctext, clickfunc)
local TutorialAlertFrame = _G.DetailsFrameworkAlertFrame
if (not TutorialAlertFrame) then
TutorialAlertFrame = CreateFrame("frame", "DetailsFrameworkAlertFrame", UIParent, "MicroButtonAlertTemplate")
TutorialAlertFrame.isFirst = true
TutorialAlertFrame:SetPoint("left", UIParent, "left", -20, 100)
TutorialAlertFrame:SetFrameStrata("TOOLTIP")
TutorialAlertFrame:Hide()
TutorialAlertFrame:SetScript("OnMouseUp", function(self)
if (self.clickfunc and type(self.clickfunc) == "function") then
self.clickfunc()
end
self:Hide()
end)
TutorialAlertFrame:Hide()
end
--
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
local refresh_options = function(self)
for _, widget in ipairs(self.widget_list) do
if (widget._get) then
if (widget.widget_type == "label") then
if (widget._get()) then
widget:SetText(widget._get())
end
elseif (widget.widget_type == "select") then
widget:Select(widget._get())
elseif (widget.widget_type == "toggle" or widget.widget_type == "range") then
widget:SetValue(widget._get())
elseif (widget.widget_type == "textentry") then
widget:SetText(widget._get())
elseif (widget.widget_type == "color") then
local default_value, g, b, a = widget._get()
if (type(default_value) == "table") then
widget:SetColor (unpack(default_value))
else
widget:SetColor (default_value, g, b, a)
end
end
end
end
end
local get_frame_by_id = function(self, id)
return self.widgetids [id]
end
function DF:ClearOptionsPanel(frame)
for i = 1, #frame.widget_list do
frame.widget_list[i]:Hide()
if (frame.widget_list[i].hasLabel) then
frame.widget_list[i].hasLabel:SetText("")
end
end
table.wipe(frame.widgetids)
end
function DF:SetAsOptionsPanel(frame)
frame.RefreshOptions = refresh_options
frame.widget_list = {}
frame.widget_list_by_type = {
["dropdown"] = {}, -- "select"
["switch"] = {}, -- "toggle"
["slider"] = {}, -- "range"
["color"] = {}, --
["button"] = {}, -- "execute"
["textentry"] = {}, --
["label"] = {}, --"text"
}
frame.widgetids = {}
frame.GetWidgetById = get_frame_by_id
end
function DF:CreateOptionsFrame(name, title, template)
template = template or 1
if (template == 2) then
local newOptionsFrame = CreateFrame("frame", name, UIParent, "ButtonFrameTemplate")
tinsert(UISpecialFrames, name)
newOptionsFrame:SetSize(500, 200)
newOptionsFrame.RefreshOptions = refresh_options
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)
tinsert(UISpecialFrames, name)
newOptionsFrame:SetSize(500, 200)
newOptionsFrame.RefreshOptions = refresh_options
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)
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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--~templates
local latinLanguageIds = {"enUS", "deDE", "esES", "esMX", "frFR", "itIT", "ptBR"}
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"] = {},
}
--fonts
DF.font_templates = DF.font_templates or {}
--detect which language is the client and select the font accordingly
local clientLanguage = GetLocale()
if (clientLanguage == "enGB") then
clientLanguage = "enUS"
end
DF.ClientLanguage = clientLanguage
function DF:DetectTextLanguage(text)
for i = 1, #text do
--or not
end
end
--returns which region the language the client is running, return "western", "russia" or "asia"
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
DF.registeredFontPaths = DF.registeredFontPaths or {}
function DF:GetBestFontPathForLanguage(locale)
local fontPath = DF.registeredFontPaths[locale]
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 (locale == "enUS" or locale == "deDE" or locale == "esES" or locale == "esMX" or locale == "frFR" or locale == "itIT" or locale == "ptBR") then
return [[Fonts\FRIZQT__.TTF]]
elseif (locale == "ruRU") then
return [[Fonts\FRIZQT___CYR.TTF]]
elseif (locale == "zhCN") then
return [[Fonts\ARKai_T.ttf]]
elseif (locale == "zhTW") then
return [[Fonts\blei00d.TTF]]
elseif (locale == "koKR") then
return [[Fonts\2002.TTF]]
end
--the locale passed doesn't exists, so pass the enUS
return [[Fonts\FRIZQT__.TTF]]
end
--return the best font to use for the client language
function DF:GetBestFontForLanguage(language, western, cyrillic, china, korean, taiwan)
if (not language) then
language = DF.ClientLanguage
end
if (language == "enUS" or language == "deDE" or language == "esES" or language == "esMX" or language == "frFR" or language == "itIT" or language == "ptBR") then
return western or "Friz Quadrata TT"
elseif (language == "ruRU") then
return cyrillic or "Friz Quadrata TT"
elseif (language == "zhCN") then
return china or "AR CrystalzcuheiGBK Demibold"
elseif (language == "koKR") then
return korean or "2002"
elseif (language == "zhTW") then
return taiwan or "AR CrystalzcuheiGBK Demibold"
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["OPTIONS_FONT_TEMPLATE"] = {color = "yellow", size = 9.6, font = DF:GetBestFontForLanguage()}
--dropdowns
DF.dropdown_templates = DF.dropdown_templates or {}
DF.dropdown_templates["OPTIONS_DROPDOWN_TEMPLATE"] = {
backdrop = {
edgeFile = [[Interface\Buttons\WHITE8X8]],
edgeSize = 1,
bgFile = [[Interface\Tooltips\UI-Tooltip-Background]],
tileSize = 64,
tile = true
},
backdropcolor = {1, 1, 1, .7},
backdropbordercolor = {0, 0, 0, 1},
onentercolor = {1, 1, 1, .9},
onenterbordercolor = {1, 1, 1, 1},
dropicon = "Interface\\BUTTONS\\arrow-Down-Down",
dropiconsize = {16, 16},
dropiconpoints = {-2, -3},
}
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},
}
--switches
DF.switch_templates = DF.switch_templates or {}
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_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},
}
--buttons
DF.button_templates = DF.button_templates or {}
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},
}
--sliders
DF.slider_templates = DF.slider_templates or {}
DF.slider_templates["OPTIONS_SLIDER_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},
onentercolor = {1, 1, 1, .5},
onenterbordercolor = {1, 1, 1, 1},
thumbtexture = [[Interface\Tooltips\UI-Tooltip-Background]],
thumbwidth = 16,
thumbheight = 14,
thumbcolor = {0, 0, 0, 0.5},
}
function DF:InstallTemplate(widgetType, templateName, template, parentName)
local newTemplate = {}
--if has a parent, just copy the parent to the new template
if (parentName and type(parentName) == "string") then
local parentTemplate = DF:GetTemplate(widgetType, parentName)
if (parentTemplate) then
DF.table.copy(newTemplate, parentTemplate)
end
end
--copy the template passed into the new template
DF.table.copy(newTemplate, template)
widgetType = string.lower(widgetType)
local templateTable
if (widgetType == "font") then
templateTable = DF.font_templates
local font = template.font
if (font) then
--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
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
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
function DF.GetParentName(frame)
local parentName = frame:GetName()
if (not parentName) then
error("Details! FrameWork: called $parent but parent was no name.", 2)
end
return parentName
end
function DF:Error (errortext)
print("|cFFFF2222Details! Framework Error|r:", errortext, self.GetName and self:GetName(), self.WidgetType, debugstack (2, 3, 0))
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--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
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
error("Details! Framework: AddMemberForWidget invalid function.")
end
end
else
if (DF.debug) then
error("Details! Framework: AddMemberForWidget unknown memberName or memberType.")
end
end
else
if (DF.debug) then
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
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
if (toggle:GetNormalTexture():GetTexture():find("PlusButton")) then
--is minimized, need expand
toggle:Click()
_G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click()
elseif (toggle:GetNormalTexture():GetTexture():find("MinusButton")) then
--isn't minimized
_G ["InterfaceOptionsFrameAddOnsButton" .. i+1]:Click()
end
end
break
end
end
else
self:Msg("Couldn't not find the profile panel.")
break
end
end
end
-----------------------------
--safe copy from blizz api
function DF:Mixin(object, ...)
for i = 1, select("#", ...) do
local mixin = select(i, ...)
for key, value in pairs(mixin) do
object[key] = value
end
end
return object
end
-----------------------------
--animations
function DF:CreateAnimationHub(parent, onPlay, onFinished)
local newAnimation = parent:CreateAnimationGroup()
newAnimation:SetScript("OnPlay", onPlay)
newAnimation:SetScript("OnFinished", onFinished)
newAnimation:SetScript("OnStop", onFinished)
newAnimation.NextAnimation = 1
return newAnimation
end
function DF:CreateAnimation(animation, animationType, order, duration, arg1, arg2, arg3, arg4, arg5, arg6, arg7)
local anim = animation:CreateAnimation(animationType)
anim:SetOrder(order or animation.NextAnimation)
anim:SetDuration(duration)
animationType = string.upper(animationType)
if (animationType == "ALPHA") then
anim:SetFromAlpha(arg1)
anim:SetToAlpha(arg2)
elseif (animationType == "SCALE") then
if (DF.IsDragonflight()) then
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, x, y
elseif (animationType == "ROTATION") then
anim:SetDegrees(arg1) --degree
anim:SetOrigin(arg2 or "center", arg3 or 0, arg4 or 0) --point, x, y
elseif (animationType == "TRANSLATION") then
anim:SetOffset(arg1, arg2)
end
animation.NextAnimation = animation.NextAnimation + 1
return anim
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--frame shakes
--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 {}
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
FrameshakeUpdateFrame:SetScript("OnUpdate", function(self, deltaTime)
for i = 1, #FrameshakeUpdateFrame.RegisteredFrames do
local parent = FrameshakeUpdateFrame.RegisteredFrames [i]
--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
--update the amount of shake running on this frame
parent.__frameshakes.enabled = parent.__frameshakes.enabled - 1
--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]
--automatic anchoring and reanching needs to the reviwed in the future
if (#anchor == 1) then
local anchorTo = unpack(anchor)
parent:ClearAllPoints()
parent:SetPoint(anchorTo)
elseif (#anchor == 2) then
local anchorTo, point1 = unpack(anchor)
parent:ClearAllPoints()
parent:SetPoint(anchorTo, point1)
elseif (#anchor == 3) then
local anchorTo, point1, point2 = unpack(anchor)
parent:SetPoint(anchorTo, point1, point2)
elseif (#anchor == 5) then
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)
--check delta time
deltaTime = deltaTime or 0
--update time left
shakeObject.TimeLeft = max(shakeObject.TimeLeft - deltaTime, 0)
if (shakeObject.TimeLeft > 0) then
--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
--check if can disable fade in
if (shakeObject.IsFadingIn and shakeObject.IsFadingInTime > shakeObject.FadeInTime) then
shakeObject.IsFadingIn = false
end
--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
--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
--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)
--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 * 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 * abs(math.sin(shakeObject.YSineOffset)) * scaleShake * shakeObject.ScaleY
else
newY = shakeObject.Amplitude * math.sin(shakeObject.YSineOffset) * scaleShake * shakeObject.ScaleY
end
--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
local anchorTo, point1, point2 = unpack(anchor)
point1 = point1 or 0
point2 = point2 or 0
parent:SetPoint(anchorTo, point1 + newX, point2 + newY)
elseif (#anchor == 5) then
local anchorName1, anchorTo, anchorName2, point1, point2 = unpack(anchor)
--parent:ClearAllPoints()
parent:SetPoint(anchorName1, anchorTo, anchorName2, point1 + newX, point2 + newY)
end
end
end
else
frameshake_ShakeFinished(parent, shakeObject)
end
end
local frameshake_stop = function(parent, shakeObject)
frameshake_ShakeFinished(parent, shakeObject)
end
--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
--reset the time left
shakeObject.TimeLeft = shakeObject.Duration
if (shakeObject.IsFadingOut) then
if (shakeObject.FadeInTime > 0) then
shakeObject.IsFadingIn = true
--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
--disable fade out and enable fade in
shakeObject.IsFadingOut = false
shakeObject.IsFadingOutTime = 0
end
else
--create a new random offset
shakeObject.XSineOffset = math.pi * 2 * math.random()
shakeObject.YSineOffset = math.pi * 2 * math.random()
--store the initial position if case it needs a reset
shakeObject.StartedXSineOffset = shakeObject.XSineOffset
shakeObject.StartedYSineOffset = shakeObject.YSineOffset
--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
--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
--update the time left
shakeObject.TimeLeft = shakeObject.Duration
--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
--update the amount of shake running on this frame
parent.__frameshakes.enabled = parent.__frameshakes.enabled + 1
if (not parent:GetScript("OnUpdate")) then
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, anchorPoints)
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
function DF:CreateFrameShake(parent, duration, amplitude, frequency, absoluteSineX, absoluteSineY, scaleX, scaleY, fadeInTime, fadeOutTime, anchorPoints)
--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
if (type(anchorPoints) ~= "table") then
frameShake.IsDynamicAnchor = true
frameShake.Anchors = {}
else
frameShake.Anchors = anchorPoints
end
--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
--register the frame within the frame shake updater
FrameshakeUpdateFrame.RegisterFrame (parent)
end
tinsert(parent.__frameshakes, frameShake)
return frameShake
end
-----------------------------
--glow overlay
local glow_overlay_play = function(self)
if (not self:IsShown()) then
self:Show()
end
if (self.animOut:IsPlaying()) then
self.animOut:Stop()
end
if (not self.animIn:IsPlaying()) then
self.animIn:Stop()
self.animIn:Play()
end
end
local glow_overlay_stop = function(self)
if (self.animOut:IsPlaying()) then
self.animOut:Stop()
end
if (self.animIn:IsPlaying()) then
self.animIn:Stop()
end
if (self:IsShown()) then
self:Hide()
end
end
local glow_overlay_setcolor = function(self, antsColor, glowColor)
if (antsColor) then
local r, g, b, a = DF:ParseColors(antsColor)
self.ants:SetVertexColor(r, g, b, a)
self.AntsColor.r = r
self.AntsColor.g = g
self.AntsColor.b = b
self.AntsColor.a = a
end
if (glowColor) then
local r, g, b, a = DF:ParseColors(glowColor)
self.outerGlow:SetVertexColor(r, g, b, a)
self.GlowColor.r = r
self.GlowColor.g = g
self.GlowColor.b = b
self.GlowColor.a = a
end
end
local glow_overlay_onshow = function(self)
glow_overlay_play (self)
end
local glow_overlay_onhide = function(self)
glow_overlay_stop (self)
end
--this is most copied from the wow client code, few changes applied to customize it
function DF:CreateGlowOverlay (parent, antsColor, glowColor)
local pName = parent:GetName()
local fName = pName and (pName.."Glow2") or "OverlayActionGlow" .. math.random(1, 10000000)
if fName and string.len(fName) > 50 then -- shorten to work around too long names
fName = strsub(fName, string.len(fName)-49)
end
local glowFrame = CreateFrame("frame", fName, parent, "ActionBarButtonSpellActivationAlert")
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:Hide()
parent.overlay = glowFrame
local frameWidth, frameHeight = parent:GetSize()
local scale = 1.4
--Make the height/width available before the next frame:
parent.overlay:SetSize(frameWidth * scale, frameHeight * scale)
parent.overlay:SetPoint("TOPLEFT", parent, "TOPLEFT", -frameWidth * 0.32, frameHeight * 0.36)
parent.overlay:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", frameWidth * 0.32, -frameHeight * 0.36)
local r, g, b, a = DF:ParseColors(antsColor)
glowFrame.ants:SetVertexColor(r, g, b, a)
glowFrame.AntsColor = {r, g, b, a}
local r, g, b, a = DF:ParseColors(glowColor)
glowFrame.outerGlow:SetVertexColor(r, g, b, a)
glowFrame.GlowColor = {r, g, b, a}
glowFrame.outerGlow:SetScale(1.2)
glowFrame:EnableMouse(false)
return glowFrame
end
--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()
self:SetPoint("topleft", leftOffset, topOffset)
self:SetPoint("bottomright", rightOffset, bottomOffset)
end
function DF:CreateAnts (parent, antTable, leftOffset, rightOffset, topOffset, bottomOffset, antTexture)
leftOffset = leftOffset or 0
rightOffset = rightOffset or 0
topOffset = topOffset or 0
bottomOffset = bottomOffset or 0
local f = CreateFrame("frame", nil, parent)
f:SetPoint("topleft", leftOffset, topOffset)
f:SetPoint("bottomright", rightOffset, bottomOffset)
f.SetOffset = ants_set_texture_offset
local t = f:CreateTexture(nil, "overlay")
t:SetAllPoints()
t:SetTexture(antTable.Texture)
t:SetBlendMode(antTable.BlendMode or "ADD")
t:SetVertexColor(DF:ParseColors(antTable.Color or "white"))
f.Texture = t
f.AntTable = antTable
f:SetScript("OnUpdate", function(self, deltaTime)
AnimateTexCoords (t, self.AntTable.TextureWidth, self.AntTable.TextureHeight, self.AntTable.TexturePartsWidth, self.AntTable.TexturePartsHeight, self.AntTable.AmountParts, deltaTime, self.AntTable.Throttle or 0.025)
end)
return f
end
--[=[ --test ants
do
local f = DF:CreateAnts (UIParent)
end
--]=]
-----------------------------
--borders
local default_border_color1 = .5
local default_border_color2 = .3
local default_border_color3 = .1
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
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetAlpha(self.Borders.Alpha1)
end
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetAlpha(self.Borders.Alpha2)
end
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetAlpha(self.Borders.Alpha3)
end
end
local SetBorderColor = function(self, r, g, b)
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetColorTexture(r, g, b)
end
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetColorTexture(r, g, b)
end
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetColorTexture(r, g, b)
end
end
local SetLayerVisibility = function(self, layer1Shown, layer2Shown, layer3Shown)
for _, texture in ipairs(self.Borders.Layer1) do
texture:SetShown (layer1Shown)
end
for _, texture in ipairs(self.Borders.Layer2) do
texture:SetShown (layer2Shown)
end
for _, texture in ipairs(self.Borders.Layer3) do
texture:SetShown (layer3Shown)
end
end
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
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "topleft", parent, "topleft", -1, 1)
PixelUtil.SetPoint(border1, "bottomleft", parent, "bottomleft", -1, -1)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topleft", parent, "topleft", -2, 2)
PixelUtil.SetPoint(border2, "bottomleft", parent, "bottomleft", -2, -2)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topleft", parent, "topleft", -3, 3)
PixelUtil.SetPoint(border3, "bottomleft", parent, "bottomleft", -3, -3)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "topleft", parent, "topleft", 0, 1)
PixelUtil.SetPoint(border1, "topright", parent, "topright", 1, 1)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topleft", parent, "topleft", -1, 2)
PixelUtil.SetPoint(border2, "topright", parent, "topright", 2, 2)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topleft", parent, "topleft", -2, 3)
PixelUtil.SetPoint(border3, "topright", parent, "topright", 3, 3)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "topright", parent, "topright", 1, 0)
PixelUtil.SetPoint(border1, "bottomright", parent, "bottomright", 1, -1)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "topright", parent, "topright", 2, 1)
PixelUtil.SetPoint(border2, "bottomright", parent, "bottomright", 2, -2)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "topright", parent, "topright", 3, 2)
PixelUtil.SetPoint(border3, "bottomright", parent, "bottomright", 3, -3)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
local border1 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border1, "bottomleft", parent, "bottomleft", 0, -1)
PixelUtil.SetPoint(border1, "bottomright", parent, "bottomright", 0, -1)
border1:SetColorTexture(0, 0, 0, alpha1 or default_border_color1)
local border2 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border2, "bottomleft", parent, "bottomleft", -1, -2)
PixelUtil.SetPoint(border2, "bottomright", parent, "bottomright", 1, -2)
border2:SetColorTexture(0, 0, 0, alpha2 or default_border_color2)
local border3 = parent:CreateTexture(nil, "background")
PixelUtil.SetPoint(border3, "bottomleft", parent, "bottomleft", -2, -3)
PixelUtil.SetPoint(border3, "bottomright", parent, "bottomright", 2, -3)
border3:SetColorTexture(0, 0, 0, alpha3 or default_border_color3)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
end
--DFNamePlateBorder as copy from "NameplateFullBorderTemplate" -> DF:CreateFullBorder (name, parent)
local DFNamePlateBorderTemplateMixin = {};
function DFNamePlateBorderTemplateMixin:SetVertexColor(r, g, b, a)
for i, texture in ipairs(self.Textures) do
texture:SetVertexColor(r, g, b, a);
end
end
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
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
tinsert(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
tinsert(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
tinsert(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
tinsert(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
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)
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)
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)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
--top
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)
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)
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)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
--right
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)
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)
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)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
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)
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)
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)
tinsert(parent.Borders.Layer1, border1)
tinsert(parent.Borders.Layer2, border2)
tinsert(parent.Borders.Layer3, border3)
end
function DF:ReskinSlider(slider, heightOffset)
if (slider.slider) then
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()
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()
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
do
local normalTexture = slider.ScrollBar.ScrollUpButton.Normal
normalTexture:SetTexture([[Interface\Buttons\Arrow-Up-Up]])
normalTexture:SetTexCoord(0, 1, .2, 1)
normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
local pushedTexture = slider.ScrollBar.ScrollUpButton.Pushed
pushedTexture:SetTexture([[Interface\Buttons\Arrow-Up-Down]])
pushedTexture:SetTexCoord(0, 1, .2, 1)
pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
local disabledTexture = slider.ScrollBar.ScrollUpButton.Disabled
disabledTexture:SetTexture([[Interface\Buttons\Arrow-Up-Disabled]])
disabledTexture:SetTexCoord(0, 1, .2, 1)
disabledTexture:SetAlpha(.5)
disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollUpButton, "topleft", offset, 0)
disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollUpButton, "bottomright", offset, 0)
slider.ScrollBar.ScrollUpButton:SetSize(16, 16)
end
--down button
do
local normalTexture = slider.ScrollBar.ScrollDownButton.Normal
normalTexture:SetTexture([[Interface\Buttons\Arrow-Down-Up]])
normalTexture:SetTexCoord(0, 1, 0, .8)
normalTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
normalTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
local pushedTexture = slider.ScrollBar.ScrollDownButton.Pushed
pushedTexture:SetTexture([[Interface\Buttons\Arrow-Down-Down]])
pushedTexture:SetTexCoord(0, 1, 0, .8)
pushedTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
pushedTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
local disabledTexture = slider.ScrollBar.ScrollDownButton.Disabled
disabledTexture:SetTexture([[Interface\Buttons\Arrow-Down-Disabled]])
disabledTexture:SetTexCoord(0, 1, 0, .8)
disabledTexture:SetAlpha(.5)
disabledTexture:SetPoint("topleft", slider.ScrollBar.ScrollDownButton, "topleft", offset, -4)
disabledTexture:SetPoint("bottomright", slider.ScrollBar.ScrollDownButton, "bottomright", offset, -4)
slider.ScrollBar.ScrollDownButton:SetSize(16, 16)
end
--if the parent has a editbox, this is a code editor
if (slider:GetParent().editbox) then
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
slider.ScrollBar:SetPoint("TOPLEFT", slider, "TOPRIGHT", 6, -16)
slider.ScrollBar:SetPoint("BOTTOMLEFT", slider, "BOTTOMRIGHT", 6, 16 + (heightOffset and heightOffset*-1 or 0))
end
slider.ScrollBar.ThumbTexture:SetColorTexture(.5, .5, .5, .3)
slider.ScrollBar.ThumbTexture:SetSize(12, 8)
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
function DF:GetCurrentSpecId()
return DF:GetCurrentSpec()
end
local specs_per_class = {
["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, 254},
["PRIEST"] = {256, 257, 258},
["WARLOCK"] = {265, 266, 267},
["PALADIN"] = {65, 66, 70},
["MONK"] = {268, 269, 270},
["EVOKER"] = {1467, 1468},
}
function DF:GetClassSpecIDs(class)
return specs_per_class [class]
end
local dispatch_error = function(context, errortext)
DF:Msg( (context or "<no context>") .. " |cFFFF9900error|r: " .. (errortext or "<no error given>"))
end
--safe call an external func with payload and without telling who is calling
function DF:QuickDispatch(func, ...)
if (type(func) ~= "function") then
return
end
local okay, errortext = xpcall(func, geterrorhandler(), ...)
if (not okay) then
--trigger an error msg
dispatch_error(_, errortext)
return
end
return true
end
function DF:Dispatch(func, ...)
if (type(func) ~= "function") then
return dispatch_error (_, "DF:Dispatch expect a function as parameter 1.")
end
local dispatchResult = {xpcall(func, geterrorhandler(), ...)}
local okay = dispatchResult[1]
if (not okay) then
return nil
end
tremove(dispatchResult, 1)
return unpack(dispatchResult)
end
--[=[
DF:CoreDispatch(func, context, ...)
safe call a function making a error window with what caused, the 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
--]=]
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"
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"
--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",
[13] = "EVOKER",
}
DF.ClassFileNameToIndex = {
["DEATHKNIGHT"] = 6,
["WARRIOR"] = 1,
["ROGUE"] = 4,
["MAGE"] = 8,
["PRIEST"] = 5,
["HUNTER"] = 3,
["WARLOCK"] = 9,
["DEMONHUNTER"] = 12,
["SHAMAN"] = 7,
["DRUID"] = 11,
["MONK"] = 10,
["PALADIN"] = 2,
["EVOKER"] = 13,
}
DF.ClassCache = {}
function DF:GetClassList()
if (next (DF.ClassCache)) then
return DF.ClassCache
end
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,
}
tinsert(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
--store and return a list of character races, always return the non-localized value
DF.RaceCache = {}
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
tinsert(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
tinsert(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 (onlySelected, onlySelectedHash)
local talentList = {}
for i = 1, 7 do
for o = 1, 3 do
local talentID, name, texture, selected, available = GetTalentInfo (i, o, 1)
if (onlySelectedHash) then
if (selected) then
talentList [talentID] = true
break
end
elseif (onlySelected) then
if (selected) then
tinsert(talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected})
break
end
else
tinsert(talentList, {Name = name, ID = talentID, Texture = texture, IsSelected = selected})
end
end
end
return talentList
end
function DF:GetCharacterPvPTalents (onlySelected, onlySelectedHash)
if (onlySelected or onlySelectedHash) then
local talentsSelected = C_SpecializationInfo.GetAllSelectedPvpTalentIDs()
local talentList = {}
for _, talentID in ipairs(talentsSelected) do
local _, talentName, texture = GetPvpTalentInfoByID (talentID)
if (onlySelectedHash) then
talentList [talentID] = true
else
tinsert(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
for _, talentID in ipairs(slotInfo.availableTalentIDs) do
if (not alreadyAdded [talentID]) then
local _, talentName, texture, selected = GetPvpTalentInfoByID (talentID)
tinsert(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
DF.RoleTypes = {
{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},
}
function DF:GetRoleTypes()
return DF.RoleTypes
end
local roleTexcoord = {
DAMAGER = "72:130:69:127",
HEALER = "72:130:2:60",
TANK = "5:63:69:127",
NONE = "139:196:69:127",
}
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 = {
DAMAGER = {72/256, 130/256, 69/256, 127/256},
HEALER = {72/256, 130/256, 2/256, 60/256},
TANK = {5/256, 63/256, 69/256, 127/256},
NONE = {139/256, 196/256, 69/256, 127/256},
}
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
local coords = GetTexCoordsForRole(role)
if (coords) then
if (type(text) == "string" and role ~= "NONE") then
size = size or 14
text = "|TInterface\\LFGFRAME\\UI-LFG-ICON-ROLES:" .. size .. ":" .. size .. ":0:0:256:256:" .. roleTexcoord[role] .. "|t " .. text
return text
end
end
end
return text
end
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()
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,
},
["EVOKER"] = {
[1467] = true,
[1468] = true,
},
}
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,
},
["EVOKER"] = {
1467,
1468,
},
}
--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
--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
}
function DF:GetBattlegroundSize(instanceInfoMapId)
return DF.BattlegroundSizes[instanceInfoMapId]
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--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
--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
--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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--delta seconds reader
if (not DetailsFrameworkDeltaTimeFrame) then
CreateFrame("frame", "DetailsFrameworkDeltaTimeFrame", UIParent)
end
local deltaTimeFrame = DetailsFrameworkDeltaTimeFrame
deltaTimeFrame:SetScript("OnUpdate", function(self, deltaTime)
self.deltaTime = deltaTime
end)
function GetWorldDeltaSeconds()
return deltaTimeFrame.deltaTime
end
function DF:GetWorldDeltaSeconds()
return deltaTimeFrame.deltaTime
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--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, ...)
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
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
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()
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
------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--debug
DF.DebugMixin = {
debug = true,
CheckPoint = function(self, checkPointName, ...)
print(self:GetName(), checkPointName, ...)
end,
CheckVisibilityState = function(self, widget)
self = widget or self
local width, height = self:GetSize()
width = floor(width)
height = floor(height)
local numPoints = self:GetNumPoints()
print("shown:", self:IsShown(), "visible:", self:IsVisible(), "alpha:", self:GetAlpha(), "size:", width, height, "points:", numPoints)
end,
CheckStack = function(self)
local stack = debugstack()
Details:Dump (stack)
end,
}
-----------------------------------------------------------------------------------------------------------------------------------------------------------
--returns if the unit is tapped (gray health color when another player hit the unit first)
function DF:IsUnitTapDenied (unitId)
return unitId and not UnitPlayerControlled(unitId) and UnitIsTapDenied(unitId)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------
--pool
do
local get = function(self)
local object = tremove(self.notUse, #self.notUse)
if (object) then
tinsert(self.inUse, object)
if (self.onAcquire) then
DF:QuickDispatch(self.onAcquire, object)
end
return object, false
else
--need to create the new object
local newObject = self.newObjectFunc(self, unpack(self.payload))
if (newObject) then
tinsert(self.inUse, newObject)
if (self.onAcquire) then
DF:QuickDispatch(self.onAcquire, object)
end
return newObject, true
end
end
end
local get_all_inuse = function(self)
return self.inUse;
end
local release = function(self, object)
for i = #self.inUse, 1, -1 do
if (self.inUse[i] == object) then
tremove(self.inUse, i)
tinsert(self.notUse, object)
if (self.onRelease) then
DF:QuickDispatch(self.onRelease, object)
end
break
end
end
end
local reset = function(self)
for i = #self.inUse, 1, -1 do
local object = tremove(self.inUse, i)
tinsert(self.notUse, object)
if (self.onReset) then
DF:QuickDispatch(self.onReset, object)
end
end
end
--only hide objects in use, do not disable them
local hide = function(self)
for i = #self.inUse, 1, -1 do
self.inUse[i]:Hide()
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()
end
end
--return the amount of objects
local getamount = function(self)
return #self.notUse + #self.inUse, #self.notUse, #self.inUse
end
local poolMixin = {
Get = get,
GetAllInUse = get_all_inuse,
Acquire = get,
Release = release,
Reset = reset,
ReleaseAll = reset,
Hide = hide,
Show = show,
GetAmount = getamount,
SetCallbackOnRelease = function(self, func)
self.onRelease = func
end,
SetOnReset = function(self, func)
self.onReset = func
end,
SetCallbackOnReleaseAll = function(self, func)
self.onReset = func
end,
SetOnAcquire = function(self, func)
self.onAcquire = func
end,
SetCallbackOnGet = function(self, func)
self.onAcquire = func
end,
}
function DF:CreatePool(func, ...)
local t = {}
DetailsFramework:Mixin(t, poolMixin)
t.inUse = {}
t.notUse = {}
t.newObjectFunc = func
t.payload = {...}
return t
end
--alias
function DF:CreateObjectPool(func, ...)
return DF:CreatePool(func, ...)
end
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------
--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,
["setfenv"] = true,
["getfenv"] = true,
["loadstring"] = true,
["pcall"] = true,
["xpcall"] = true,
["getglobal"] = true,
["setmetatable"] = true,
["DevTools_DumpCommand"] = 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 = {
__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
function DF:MakeFunctionSecure(func)
return DF:SetEnvironment(func)
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------