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.

10312 lines
322 KiB

local DF = _G ["DetailsFramework"]
if (not DF or not DetailsFrameworkCanLoad) then
return
end
local _
--> lua locals
local _rawset = rawset --> lua local
local _rawget = rawget --> lua local
local _setmetatable = setmetatable --> lua local
local _unpack = unpack --> lua local
local _type = type --> lua local
local _math_floor = math.floor --> lua local
local loadstring = loadstring --> lua local
local IS_WOW_PROJECT_MAINLINE = WOW_PROJECT_ID == WOW_PROJECT_MAINLINE
local IS_WOW_PROJECT_NOT_MAINLINE = WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE
local PixelUtil = PixelUtil or DFPixelUtil
local UnitGroupRolesAssigned = DetailsFramework.UnitGroupRolesAssigned
local cleanfunction = function() end
local APIFrameFunctions
do
local metaPrototype = {
WidgetType = "panel",
SetHook = DF.SetHook,
RunHooksForWidget = DF.RunHooksForWidget,
dversion = DF.dversion,
}
--check if there's a metaPrototype already existing
if (_G[DF.GlobalWidgetControlNames["panel"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[DF.GlobalWidgetControlNames ["panel"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < DF.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(metaPrototype) do
oldMetaPrototype[funcName] = metaPrototype[funcName]
end
end
else
--first time loading the framework
_G[DF.GlobalWidgetControlNames ["panel"]] = metaPrototype
end
end
local PanelMetaFunctions = _G[DF.GlobalWidgetControlNames ["panel"]]
--> mixin for options functions
DF.OptionsFunctions = {
SetOption = function (self, optionName, optionValue)
if (self.options) then
self.options [optionName] = optionValue
else
self.options = {}
self.options [optionName] = optionValue
end
if (self.OnOptionChanged) then
DF:Dispatch (self.OnOptionChanged, self, optionName, optionValue)
end
end,
GetOption = function (self, optionName)
return self.options and self.options [optionName]
end,
GetAllOptions = function (self)
if (self.options) then
local optionsTable = {}
for key, _ in pairs (self.options) do
optionsTable [#optionsTable + 1] = key
end
return optionsTable
else
return {}
end
end,
BuildOptionsTable = function (self, defaultOptions, userOptions)
self.options = self.options or {}
DF.table.deploy (self.options, userOptions or {})
DF.table.deploy (self.options, defaultOptions or {})
end
}
--> default options for the frame layout
local default_framelayout_options = {
amount_per_line = 4,
start_x = 2,
start_y = -2,
is_vertical = false,
grow_right = true, --on vertical (if not grow next line left)
grow_down = true, --on horizontal (if not grow next line up)
anchor_to_child = false, --if true set the point to the previous frame instead of coordinate
anchor_point = "topleft",
anchor_relative = "topleft",
offset_x = 100,
offset_y = 20,
width = 0, --if bigger than 0, it will set the value
height = 0,
break_if_hidden = true, --stop if encounters a hidden frame
}
--> mixin for frame layout
DF.LayoutFrame = {
AnchorTo = function (self, anchor, point, x, y)
if (point == "top") then
self:ClearAllPoints()
self:SetPoint ("bottom", anchor, "top", x or 0, y or 0)
elseif (point == "bottom") then
self:ClearAllPoints()
self:SetPoint ("top", anchor, "bottom", x or 0, y or 0)
elseif (point == "left") then
self:ClearAllPoints()
self:SetPoint ("right", anchor, "left", x or 0, y or 0)
elseif (point == "right") then
self:ClearAllPoints()
self:SetPoint ("left", anchor, "right", x or 0, y or 0)
end
end,
ArrangeFrames = function (self, frameList, options)
if (not frameList) then
frameList = {self:GetChildren()}
end
options = options or {}
DF.table.deploy (options, default_framelayout_options)
local breakLine = options.amount_per_line + 1
local currentX, currentY = options.start_x, options.start_y
local offsetX, offsetY = options.offset_x, options.offset_y
local anchorPoint = options.anchor_point
local anchorAt = options.anchor_relative
local latestFrame = self
local firstRowFrame = frameList [1]
if (options.is_vertical) then
for i = 1, #frameList do
local thisFrame = frameList [i]
if (options.break_if_hidden and not thisFrame:IsShown()) then
break
end
thisFrame:ClearAllPoints()
if (options.anchor_to_child) then
if (i == breakLine) then
if (options.grow_right) then
thisFrame:SetPoint ("topleft", firstRowFrame, "topright", offsetX, 0)
else
thisFrame:SetPoint ("topright", firstRowFrame, "topleft", -offsetX, 0)
end
firstRowFrame = thisFrame
latestFrame = thisFrame
breakLine = breakLine + options.amount_per_line
else
thisFrame:SetPoint (anchorPoint, latestFrame, i == 1 and "topleft" or anchorAt, offsetX, i == 1 and 0 or offsetY)
latestFrame = thisFrame
end
else
if (i == breakLine) then
if (options.grow_right) then
currentX = currentX + offsetX
else
currentX = currentX - offsetX
end
currentY = options.start_y
firstRowFrame = thisFrame
breakLine = breakLine + options.amount_per_line
end
thisFrame:SetPoint (anchorPoint, self, anchorAt, currentX, currentY)
currentY = currentY - offsetY
end
end
else
for i = 1, #frameList do
local thisFrame = frameList [i]
if (options.break_if_hidden and not thisFrame:IsShown()) then
break
end
thisFrame:ClearAllPoints()
if (options.anchor_to_child) then
if (i == breakLine) then
if (options.grow_down) then
thisFrame:SetPoint ("topleft", firstRowFrame, "bottomleft", 0, -offsetY)
else
thisFrame:SetPoint ("bottomleft", firstRowFrame, "topleft", 0, offsetY)
end
firstRowFrame = thisFrame
latestFrame = thisFrame
breakLine = breakLine + options.amount_per_line
else
thisFrame:SetPoint (anchorPoint, latestFrame, i == 1 and "topleft" or anchorAt, i == 1 and 0 or offsetX, offsetY)
latestFrame = thisFrame
end
else
if (i == breakLine) then
if (options.grow_down) then
currentY = currentY - offsetY
else
currentY = currentY + offsetY
end
currentX = options.start_x
firstRowFrame = thisFrame
breakLine = breakLine + options.amount_per_line
end
thisFrame:SetPoint (anchorPoint, self, anchorAt, currentX, currentY)
currentX = currentX + offsetX
end
end
end
end
}
------------------------------------------------------------------------------------------------------------
--> metatables
PanelMetaFunctions.__call = function (_table, value)
--> nothing to do
return true
end
------------------------------------------------------------------------------------------------------------
--> members
--> tooltip
local gmember_tooltip = function (_object)
return _object:GetTooltip()
end
--> shown
local gmember_shown = function (_object)
return _object:IsShown()
end
--> backdrop color
local gmember_color = function (_object)
return _object.frame:GetBackdropColor()
end
--> backdrop table
local gmember_backdrop = function (_object)
return _object.frame:GetBackdrop()
end
--> frame width
local gmember_width = function (_object)
return _object.frame:GetWidth()
end
--> frame height
local gmember_height = function (_object)
return _object.frame:GetHeight()
end
--> locked
local gmember_locked = function (_object)
return _rawget (_object, "is_locked")
end
PanelMetaFunctions.GetMembers = PanelMetaFunctions.GetMembers or {}
PanelMetaFunctions.GetMembers ["tooltip"] = gmember_tooltip
PanelMetaFunctions.GetMembers ["shown"] = gmember_shown
PanelMetaFunctions.GetMembers ["color"] = gmember_color
PanelMetaFunctions.GetMembers ["backdrop"] = gmember_backdrop
PanelMetaFunctions.GetMembers ["width"] = gmember_width
PanelMetaFunctions.GetMembers ["height"] = gmember_height
PanelMetaFunctions.GetMembers ["locked"] = gmember_locked
PanelMetaFunctions.__index = function (_table, _member_requested)
local func = PanelMetaFunctions.GetMembers [_member_requested]
if (func) then
return func (_table, _member_requested)
end
local fromMe = _rawget (_table, _member_requested)
if (fromMe) then
return fromMe
end
return PanelMetaFunctions [_member_requested]
end
--> tooltip
local smember_tooltip = function (_object, _value)
return _object:SetTooltip (_value)
end
--> show
local smember_show = function (_object, _value)
if (_value) then
return _object:Show()
else
return _object:Hide()
end
end
--> hide
local smember_hide = function (_object, _value)
if (not _value) then
return _object:Show()
else
return _object:Hide()
end
end
--> backdrop color
local smember_color = function (_object, _value)
local _value1, _value2, _value3, _value4 = DF:ParseColors (_value)
return _object:SetBackdropColor (_value1, _value2, _value3, _value4)
end
--> frame width
local smember_width = function (_object, _value)
return _object.frame:SetWidth (_value)
end
--> frame height
local smember_height = function (_object, _value)
return _object.frame:SetHeight (_value)
end
--> locked
local smember_locked = function (_object, _value)
if (_value) then
_object.frame:SetMovable (false)
return _rawset (_object, "is_locked", true)
else
_object.frame:SetMovable (true)
_rawset (_object, "is_locked", false)
return
end
end
--> backdrop
local smember_backdrop = function (_object, _value)
return _object.frame:SetBackdrop (_value)
end
--> close with right button
local smember_right_close = function (_object, _value)
return _rawset (_object, "rightButtonClose", _value)
end
PanelMetaFunctions.SetMembers = PanelMetaFunctions.SetMembers or {}
PanelMetaFunctions.SetMembers["tooltip"] = smember_tooltip
PanelMetaFunctions.SetMembers["show"] = smember_show
PanelMetaFunctions.SetMembers["hide"] = smember_hide
PanelMetaFunctions.SetMembers["color"] = smember_color
PanelMetaFunctions.SetMembers["backdrop"] = smember_backdrop
PanelMetaFunctions.SetMembers["width"] = smember_width
PanelMetaFunctions.SetMembers["height"] = smember_height
PanelMetaFunctions.SetMembers["locked"] = smember_locked
PanelMetaFunctions.SetMembers["close_with_right"] = smember_right_close
PanelMetaFunctions.__newindex = function (_table, _key, _value)
local func = PanelMetaFunctions.SetMembers [_key]
if (func) then
return func (_table, _value)
else
return _rawset (_table, _key, _value)
end
end
------------------------------------------------------------------------------------------------------------
--> methods
--> right click to close
function PanelMetaFunctions:CreateRightClickLabel (textType, w, h, close_text)
local text
w = w or 20
h = h or 20
if (close_text) then
text = close_text
else
if (textType) then
textType = string.lower (textType)
if (textType == "short") then
text = "close window"
elseif (textType == "medium") then
text = "close window"
elseif (textType == "large") then
text = "close window"
end
else
text = "close window"
end
end
return DF:NewLabel (self, _, "$parentRightMouseToClose", nil, "|TInterface\\TUTORIALFRAME\\UI-TUTORIAL-FRAME:"..w..":"..h..":0:1:512:512:8:70:328:409|t " .. text)
end
--> show & hide
function PanelMetaFunctions:Show()
self.frame:Show()
end
function PanelMetaFunctions:Hide()
self.frame:Hide()
end
-- setpoint
function PanelMetaFunctions:SetPoint (v1, v2, v3, v4, v5)
v1, v2, v3, v4, v5 = DF:CheckPoints (v1, v2, v3, v4, v5, self)
if (not v1) then
print ("Invalid parameter for SetPoint")
return
end
return self.widget:SetPoint (v1, v2, v3, v4, v5)
end
-- sizes
function PanelMetaFunctions:SetSize (w, h)
if (w) then
self.frame:SetWidth (w)
end
if (h) then
self.frame:SetHeight (h)
end
end
-- clear
function PanelMetaFunctions:HideWidgets()
for widgetName, widgetSelf in pairs (self) do
if (type (widgetSelf) == "table" and widgetSelf.dframework) then
widgetSelf:Hide()
end
end
end
-- backdrop
function PanelMetaFunctions:SetBackdrop (background, edge, tilesize, edgesize, tile, left, right, top, bottom)
if (_type (background) == "boolean" and not background) then
return self.frame:SetBackdrop (nil)
elseif (_type (background) == "table") then
self.frame:SetBackdrop (background)
else
local currentBackdrop = self.frame:GetBackdrop() or {edgeFile="Interface\\DialogFrame\\UI-DialogBox-Border", bgFile="Interface\\DialogFrame\\UI-DialogBox-Background", tile=true, tileSize=16, edgeSize=16, insets={left=1, right=0, top=0, bottom=0}}
currentBackdrop.bgFile = background or currentBackdrop.bgFile
currentBackdrop.edgeFile = edgeFile or currentBackdrop.edgeFile
currentBackdrop.tileSize = tilesize or currentBackdrop.tileSize
currentBackdrop.edgeSize = edgesize or currentBackdrop.edgeSize
currentBackdrop.tile = tile or currentBackdrop.tile
currentBackdrop.insets.left = left or currentBackdrop.insets.left
currentBackdrop.insets.right = left or currentBackdrop.insets.right
currentBackdrop.insets.top = left or currentBackdrop.insets.top
currentBackdrop.insets.bottom = left or currentBackdrop.insets.bottom
self.frame:SetBackdrop (currentBackdrop)
end
end
-- backdropcolor
function PanelMetaFunctions:SetBackdropColor (color, arg2, arg3, arg4)
if (arg2) then
self.frame:SetBackdropColor (color, arg2, arg3, arg4 or 1)
else
local _value1, _value2, _value3, _value4 = DF:ParseColors (color)
self.frame:SetBackdropColor (_value1, _value2, _value3, _value4)
end
end
-- border color
function PanelMetaFunctions:SetBackdropBorderColor (color, arg2, arg3, arg4)
if (arg2) then
return self.frame:SetBackdropBorderColor (color, arg2, arg3, arg4)
end
local _value1, _value2, _value3, _value4 = DF:ParseColors (color)
self.frame:SetBackdropBorderColor (_value1, _value2, _value3, _value4)
end
-- tooltip
function PanelMetaFunctions:SetTooltip (tooltip)
if (tooltip) then
return _rawset (self, "have_tooltip", tooltip)
else
return _rawset (self, "have_tooltip", nil)
end
end
function PanelMetaFunctions:GetTooltip()
return _rawget (self, "have_tooltip")
end
-- frame levels
function PanelMetaFunctions:GetFrameLevel()
return self.widget:GetFrameLevel()
end
function PanelMetaFunctions:SetFrameLevel (level, frame)
if (not frame) then
return self.widget:SetFrameLevel (level)
else
local framelevel = frame:GetFrameLevel (frame) + level
return self.widget:SetFrameLevel (framelevel)
end
end
-- frame stratas
function PanelMetaFunctions:SetFrameStrata()
return self.widget:GetFrameStrata()
end
function PanelMetaFunctions:SetFrameStrata (strata)
if (_type (strata) == "table") then
self.widget:SetFrameStrata (strata:GetFrameStrata())
else
self.widget:SetFrameStrata (strata)
end
end
------------------------------------------------------------------------------------------------------------
--> scripts
local OnEnter = function (frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnEnter", frame, capsule)
if (kill) then
return
end
if (frame.MyObject.have_tooltip) then
GameCooltip2:Reset()
GameCooltip2:SetType ("tooltip")
GameCooltip2:SetColor ("main", "transparent")
GameCooltip2:AddLine (frame.MyObject.have_tooltip)
GameCooltip2:SetOwner (frame)
GameCooltip2:ShowCooltip()
end
end
local OnLeave = function (frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnLeave", frame, capsule)
if (kill) then
return
end
if (frame.MyObject.have_tooltip) then
GameCooltip2:ShowMe (false)
end
end
local OnHide = function (frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnHide", frame, capsule)
if (kill) then
return
end
end
local OnShow = function (frame)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnShow", frame, capsule)
if (kill) then
return
end
end
local OnMouseDown = function (frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnMouseDown", frame, button, capsule)
if (kill) then
return
end
if (frame.MyObject.container == UIParent) then
if (not frame.isLocked and frame:IsMovable()) then
frame.isMoving = true
frame:StartMoving()
end
elseif (not frame.MyObject.container.isLocked and frame.MyObject.container:IsMovable()) then
if (not frame.isLocked and frame:IsMovable()) then
frame.MyObject.container.isMoving = true
frame.MyObject.container:StartMoving()
end
end
end
local OnMouseUp = function (frame, button)
local capsule = frame.MyObject
local kill = capsule:RunHooksForWidget ("OnMouseUp", frame, button, capsule)
if (kill) then
return
end
if (button == "RightButton" and frame.MyObject.rightButtonClose) then
frame.MyObject:Hide()
end
if (frame.MyObject.container == UIParent) then
if (frame.isMoving) then
frame:StopMovingOrSizing()
frame.isMoving = false
end
else
if (frame.MyObject.container.isMoving) then
frame.MyObject.container:StopMovingOrSizing()
frame.MyObject.container.isMoving = false
end
end
end
------------------------------------------------------------------------------------------------------------
--> object constructor
function DF:CreatePanel (parent, w, h, backdrop, backdropcolor, bordercolor, member, name)
return DF:NewPanel (parent, parent, name, member, w, h, backdrop, backdropcolor, bordercolor)
end
function DF:NewPanel (parent, container, name, member, w, h, backdrop, backdropcolor, bordercolor)
if (not name) then
name = "DetailsFrameworkPanelNumber" .. DF.PanelCounter
DF.PanelCounter = DF.PanelCounter + 1
elseif (not parent) then
parent = UIParent
end
if (not container) then
container = parent
end
if (name:find ("$parent")) then
name = name:gsub ("$parent", parent:GetName())
end
local PanelObject = {type = "panel", dframework = true}
if (member) then
parent [member] = PanelObject
end
if (parent.dframework) then
parent = parent.widget
end
if (container.dframework) then
container = container.widget
end
--> default members:
--> misc
PanelObject.is_locked = true
PanelObject.container = container
PanelObject.rightButtonClose = false
PanelObject.frame = CreateFrame ("frame", name, parent,"BackdropTemplate")
PanelObject.frame:SetSize (100, 100)
PanelObject.frame.Gradient = {
["OnEnter"] = {0.3, 0.3, 0.3, 0.5},
["OnLeave"] = {0.9, 0.7, 0.7, 1}
}
PanelObject.frame:SetBackdrop ({bgFile = [[Interface\DialogFrame\UI-DialogBox-Background]], edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border", edgeSize = 10, tileSize = 64, tile = true})
PanelObject.widget = PanelObject.frame
if (not APIFrameFunctions) then
APIFrameFunctions = {}
local idx = getmetatable (PanelObject.frame).__index
for funcName, funcAddress in pairs (idx) do
if (not PanelMetaFunctions [funcName]) then
PanelMetaFunctions [funcName] = function (object, ...)
local x = loadstring ( "return _G['"..object.frame:GetName().."']:"..funcName.."(...)")
return x (...)
end
end
end
end
PanelObject.frame:SetWidth (w or 100)
PanelObject.frame:SetHeight (h or 100)
PanelObject.frame.MyObject = PanelObject
PanelObject.HookList = {
OnEnter = {},
OnLeave = {},
OnHide = {},
OnShow = {},
OnMouseDown = {},
OnMouseUp = {},
}
--> hooks
PanelObject.frame:SetScript ("OnEnter", OnEnter)
PanelObject.frame:SetScript ("OnLeave", OnLeave)
PanelObject.frame:SetScript ("OnHide", OnHide)
PanelObject.frame:SetScript ("OnShow", OnShow)
PanelObject.frame:SetScript ("OnMouseDown", OnMouseDown)
PanelObject.frame:SetScript ("OnMouseUp", OnMouseUp)
_setmetatable (PanelObject, PanelMetaFunctions)
if (backdrop) then
PanelObject:SetBackdrop (backdrop)
elseif (_type (backdrop) == "boolean") then
PanelObject.frame:SetBackdrop (nil)
end
if (backdropcolor) then
PanelObject:SetBackdropColor (backdropcolor)
end
if (bordercolor) then
PanelObject:SetBackdropBorderColor (bordercolor)
end
return PanelObject
end
------------fill panel
local button_on_enter = function (self)
self.MyObject._icon:SetBlendMode ("ADD")
if (self.MyObject.onenter_func) then
pcall (self.MyObject.onenter_func, self.MyObject)
end
end
local button_on_leave = function (self)
self.MyObject._icon:SetBlendMode ("BLEND")
if (self.MyObject.onleave_func) then
pcall (self.MyObject.onleave_func, self.MyObject)
end
end
local add_row = function (self, t, need_update)
local index = #self.rows+1
local thisrow = DF:NewPanel (self, self, "$parentHeader_" .. self._name .. index, nil, 1, 20)
thisrow.backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]]}
thisrow.color = {.3, .3, .3, .9}
thisrow.type = t.type
thisrow.func = t.func
thisrow.name = t.name
thisrow.notext = t.notext
thisrow.icon = t.icon
thisrow.iconalign = t.iconalign
thisrow.hidden = t.hidden or false
thisrow.onenter = t.onenter
thisrow.onleave = t.onleave
local text = DF:NewLabel (thisrow, nil, self._name .. "$parentLabel" .. index, "text")
text:SetPoint ("left", thisrow, "left", 2, 0)
text:SetText (t.name)
tinsert (self._raw_rows, t)
tinsert (self.rows, thisrow)
if (need_update) then
self:AlignRows()
end
end
local align_rows = function (self)
local rows_shown = 0
for index, row in ipairs (self.rows) do
if (not row.hidden) then
rows_shown = rows_shown + 1
end
end
local cur_width = 1
local row_width = self._width / max (rows_shown, 0.0001)
local sindex = 1
wipe (self._anchors)
for index, row in ipairs (self.rows) do
if (not row.hidden) then
if (self._autowidth) then
if (self._raw_rows [index].width) then
row.width = self._raw_rows [index].width
else
row.width = row_width
end
row:SetPoint ("topleft", self, "topleft", cur_width, -1)
tinsert (self._anchors, cur_width)
cur_width = cur_width + row_width + 1
else
row:SetPoint ("topleft", self, "topleft", cur_width, -1)
row.width = self._raw_rows [index].width
tinsert (self._anchors, cur_width)
cur_width = cur_width + self._raw_rows [index].width + 1
end
row:Show()
local type = row.type
if (type == "text") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local text = tremove (line.text_available)
if (not text) then
self:CreateRowText (line)
text = tremove (line.text_available)
end
tinsert (line.text_inuse, text)
text:SetPoint ("left", line, "left", self._anchors [#self._anchors], 0)
text:SetWidth (row.width)
DF:SetFontSize (text, row.textsize or 10)
text:SetJustifyH (row.textalign or "left")
end
elseif (type == "entry") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local entry = tremove (line.entry_available)
if (not entry) then
self:CreateRowEntry (line)
entry = tremove (line.entry_available)
end
tinsert (line.entry_inuse, entry)
entry:SetPoint ("left", line, "left", self._anchors [#self._anchors], 0)
if (sindex == rows_shown) then
entry:SetWidth (row.width - 25)
else
entry:SetWidth (row.width)
end
entry.func = row.func
entry.onenter_func = nil
entry.onleave_func = nil
if (row.onenter) then
entry.onenter_func = row.onenter
end
if (row.onleave) then
entry.onleave_func = row.onleave
end
end
elseif (type == "checkbox") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local checkbox = tremove (line.checkbox_available)
if (not checkbox) then
self:CreateCheckbox (line)
checkbox = tremove (line.checkbox_available)
end
tinsert (line.checkbox_inuse, checkbox)
checkbox:SetPoint ("left", line, "left", self._anchors [#self._anchors] + ((row.width - 20) / 2), 0)
if (sindex == rows_shown) then
checkbox:SetWidth (20)
--checkbox:SetWidth (row.width - 25)
else
checkbox:SetWidth (20)
end
checkbox.onenter_func = nil
checkbox.onleave_func = nil
end
elseif (type == "button") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local button = tremove (line.button_available)
if (not button) then
self:CreateRowButton (line)
button = tremove (line.button_available)
end
tinsert (line.button_inuse, button)
button:SetPoint ("left", line, "left", self._anchors [#self._anchors], 0)
if (sindex == rows_shown) then
button:SetWidth (row.width - 25)
else
button:SetWidth (row.width)
end
if (row.icon) then
button._icon.texture = row.icon
button._icon:ClearAllPoints()
if (row.iconalign) then
if (row.iconalign == "center") then
button._icon:SetPoint ("center", button, "center")
elseif (row.iconalign == "right") then
button._icon:SetPoint ("right", button, "right")
end
else
button._icon:SetPoint ("left", button, "left")
end
end
if (row.name and not row.notext) then
button._text:SetPoint ("left", button._icon, "right", 2, 0)
button._text.text = row.name
end
button.onenter_func = nil
button.onleave_func = nil
if (row.onenter) then
button.onenter_func = row.onenter
end
if (row.onleave) then
button.onleave_func = row.onleave
end
end
elseif (type == "icon") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local icon = tremove (line.icon_available)
if (not icon) then
self:CreateRowIcon (line)
icon = tremove (line.icon_available)
end
tinsert (line.icon_inuse, icon)
icon:SetPoint ("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0)
icon.func = row.func
end
elseif (type == "texture") then
for i = 1, #self.scrollframe.lines do
local line = self.scrollframe.lines [i]
local texture = tremove (line.texture_available)
if (not texture) then
self:CreateRowTexture (line)
texture = tremove (line.texture_available)
end
tinsert (line.texture_inuse, texture)
texture:SetPoint ("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0)
end
end
sindex = sindex + 1
else
row:Hide()
end
end
if (#self.rows > 0) then
if (self._autowidth) then
self.rows [#self.rows]:SetWidth (row_width - rows_shown + 1)
else
self.rows [#self.rows]:SetWidth (self._raw_rows [rows_shown].width - rows_shown + 1)
end
end
self.showing_amt = rows_shown
end
local update_rows = function (self, updated_rows)
for i = 1, #updated_rows do
local t = updated_rows [i]
local raw = self._raw_rows [i]
if (not raw) then
self:AddRow (t)
else
raw.name = t.name
raw.hidden = t.hidden or false
raw.textsize = t.textsize
raw.textalign = t.textalign
local widget = self.rows [i]
widget.name = t.name
widget.textsize = t.textsize
widget.textalign = t.textalign
widget.hidden = t.hidden or false
--
widget.onenter = t.onenter
widget.onleave = t.onleave
--
widget.text:SetText (t.name)
DF:SetFontSize (widget.text, raw.textsize or 10)
widget.text:SetJustifyH (raw.textalign or "left")
end
end
for i = #updated_rows+1, #self._raw_rows do
local raw = self._raw_rows [i]
local widget = self.rows [i]
raw.hidden = true
widget.hidden = true
end
for index, row in ipairs (self.scrollframe.lines) do
for i = #row.text_inuse, 1, -1 do
tinsert (row.text_available, tremove (row.text_inuse, i))
end
for i = 1, #row.text_available do
row.text_available[i]:Hide()
end
for i = #row.entry_inuse, 1, -1 do
tinsert (row.entry_available, tremove (row.entry_inuse, i))
end
for i = 1, #row.entry_available do
row.entry_available[i]:Hide()
end
for i = #row.button_inuse, 1, -1 do
tinsert (row.button_available, tremove (row.button_inuse, i))
end
for i = 1, #row.button_available do
row.button_available[i]:Hide()
end
for i = #row.checkbox_inuse, 1, -1 do
tinsert (row.checkbox_available, tremove (row.checkbox_inuse, i))
end
for i = 1, #row.checkbox_available do
row.checkbox_available[i]:Hide()
end
for i = #row.icon_inuse, 1, -1 do
tinsert (row.icon_available, tremove (row.icon_inuse, i))
end
for i = 1, #row.icon_available do
row.icon_available[i]:Hide()
end
for i = #row.texture_inuse, 1, -1 do
tinsert (row.texture_available, tremove (row.texture_inuse, i))
end
for i = 1, #row.texture_available do
row.texture_available[i]:Hide()
end
end
self.current_header = updated_rows
self:AlignRows()
end
local create_panel_text = function (self, row)
row.text_total = row.text_total + 1
local text = DF:NewLabel (row, nil, self._name .. "$parentLabel" .. row.text_total, "text" .. row.text_total)
tinsert (row.text_available, text)
end
local create_panel_entry = function (self, row)
row.entry_total = row.entry_total + 1
local editbox = DF:NewTextEntry (row, nil, "$parentEntry" .. row.entry_total, "entry", 120, 20)
editbox.align = "left"
editbox:SetHook ("OnEnterPressed", function()
editbox.widget.focuslost = true
editbox:ClearFocus()
editbox.func (editbox.index, editbox.text)
return true
end)
editbox:SetHook ("OnEnter", function()
if (editbox.onenter_func) then
pcall (editbox.onenter_func, editbox)
end
end)
editbox:SetHook ("OnLeave", function()
if (editbox.onleave_func) then
pcall (editbox.onleave_func, editbox)
end
end)
editbox.editbox.current_bordercolor = {1, 1, 1, 0.1}
editbox:SetTemplate (DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
editbox:SetBackdropColor (.2, .2, .2, 0.7)
tinsert (row.entry_available, editbox)
end
local create_panel_checkbox = function (self, row)
--row.checkbox_available
row.checkbox_total = row.checkbox_total + 1
local switch = DF:NewSwitch (row, nil, "$parentCheckBox" .. row.checkbox_total, nil, 20, 20, nil, nil, false)
switch:SetAsCheckBox()
switch:SetTemplate(DF:GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE"))
tinsert (row.checkbox_available, switch)
end
local create_panel_button = function (self, row)
row.button_total = row.button_total + 1
local button = DF:NewButton (row, nil, "$parentButton" .. row.button_total, "button" .. row.button_total, 120, 20)
--> create icon and the text
local icon = DF:NewImage (button, nil, 20, 20)
local text = DF:NewLabel (button)
button._icon = icon
button._text = text
button:SetHook ("OnEnter", button_on_enter)
button:SetHook ("OnLeave", button_on_leave)
tinsert (row.button_available, button)
end
local icon_onclick = function (texture, iconbutton)
iconbutton._icon.texture = texture
iconbutton.func (iconbutton.index, texture)
end
local create_panel_icon = function (self, row)
row.icon_total = row.icon_total + 1
local iconbutton = DF:NewButton (row, nil, "$parentIconButton" .. row.icon_total, "iconbutton", 22, 20)
iconbutton:SetHook ("OnEnter", button_on_enter)
iconbutton:SetHook ("OnLeave", button_on_leave)
iconbutton:SetHook ("OnMouseUp", function()
DF:IconPick (icon_onclick, true, iconbutton)
return true
end)
local icon = DF:NewImage (iconbutton, nil, 20, 20, "artwork", nil, "_icon", "$parentIcon" .. row.icon_total)
iconbutton._icon = icon
icon:SetPoint ("center", iconbutton, "center", 0, 0)
tinsert (row.icon_available, iconbutton)
end
local create_panel_texture = function (self, row)
row.texture_total = row.texture_total + 1
local texture = DF:NewImage (row, nil, 20, 20, "artwork", nil, "_icon" .. row.texture_total, "$parentIcon" .. row.texture_total)
tinsert (row.texture_available, texture)
end
local set_fill_function = function (self, func)
self._fillfunc = func
end
local set_total_function = function (self, func)
self._totalfunc = func
end
local drop_header_function = function (self)
wipe (self.rows)
end
local fillpanel_update_size = function (self, elapsed)
local panel = self.MyObject
panel._width = panel:GetWidth()
panel._height = panel:GetHeight()
panel:UpdateRowAmount()
if (panel.current_header) then
update_rows (panel, panel.current_header)
end
panel:Refresh()
self:SetScript ("OnUpdate", nil)
end
-- ~fillpanel
--alias
function DF:CreateFillPanel (parent, rows, w, h, total_lines, fill_row, autowidth, options, member, name)
return DF:NewFillPanel (parent, rows, name, member, w, h, total_lines, fill_row, autowidth, options)
end
function DF:NewFillPanel (parent, rows, name, member, w, h, total_lines, fill_row, autowidth, options)
local panel = DF:NewPanel (parent, parent, name, member, w, h)
panel.backdrop = nil
options = options or {rowheight = 20}
panel.rows = {}
panel.AddRow = add_row
panel.AlignRows = align_rows
panel.UpdateRows = update_rows
panel.CreateRowText = create_panel_text
panel.CreateRowEntry = create_panel_entry
panel.CreateRowButton = create_panel_button
panel.CreateCheckbox = create_panel_checkbox
panel.CreateRowIcon = create_panel_icon
panel.CreateRowTexture = create_panel_texture
panel.SetFillFunction = set_fill_function
panel.SetTotalFunction = set_total_function
panel.DropHeader = drop_header_function
panel._name = name
panel._width = w
panel._height = h
panel._raw_rows = {}
panel._anchors = {}
panel._fillfunc = fill_row
panel._totalfunc = total_lines
panel._autowidth = autowidth
panel:SetScript ("OnSizeChanged", function()
panel:SetScript ("OnUpdate", fillpanel_update_size)
end)
for index, t in ipairs (rows) do
panel.AddRow (panel, t)
end
local refresh_fillbox = function (self)
local offset = FauxScrollFrame_GetOffset (self)
local filled_lines = panel._totalfunc (panel)
for index = 1, #self.lines do
local row = self.lines [index]
if (index <= filled_lines) then
local real_index = index + offset
local results = panel._fillfunc (real_index, panel)
if (results and results [1]) then
row:Show()
local text, entry, button, icon, texture, checkbox = 1, 1, 1, 1, 1, 1
for index, t in ipairs (panel.rows) do
if (not t.hidden) then
if (t.type == "text") then
local fontstring = row.text_inuse [text]
text = text + 1
fontstring:SetText (results [index])
fontstring.index = real_index
fontstring:Show()
elseif (t.type == "entry") then
local entrywidget = row.entry_inuse [entry]
entry = entry + 1
entrywidget.index = real_index
if (type (results [index]) == "table") then
entrywidget:SetText (results [index].text)
entrywidget.id = results [index].id
entrywidget.data1 = results [index].data1
entrywidget.data2 = results [index].data2
else
entrywidget:SetText (results [index])
end
entrywidget:SetCursorPosition(0)
entrywidget:Show()
elseif (t.type == "checkbox") then
local checkboxwidget = row.checkbox_inuse [button]
checkbox = checkbox + 1
checkboxwidget.index = real_index
checkboxwidget:SetValue(results [index])
local func = function()
t.func (real_index, index)
panel:Refresh()
end
checkboxwidget.OnSwitch = func
elseif (t.type == "button") then
local buttonwidget = row.button_inuse [button]
button = button + 1
buttonwidget.index = real_index
if (type (results [index]) == "table") then
if (results [index].text) then
buttonwidget:SetText (results [index].text)
end
if (results [index].icon) then
buttonwidget._icon:SetTexture (results [index].icon)
end
if (results [index].func) then
local func = function()
t.func (real_index, results [index].value)
panel:Refresh()
end
buttonwidget:SetClickFunction (func)
else
local func = function()
t.func (real_index, index)
panel:Refresh()
end
buttonwidget:SetClickFunction (func)
end
buttonwidget.id = results [index].id
buttonwidget.data1 = results [index].data1
buttonwidget.data2 = results [index].data2
else
local func = function()
t.func (real_index, index)
panel:Refresh()
end
buttonwidget:SetClickFunction (func)
buttonwidget:SetText (results [index])
end
buttonwidget:Show()
elseif (t.type == "icon") then
local iconwidget = row.icon_inuse [icon]
icon = icon + 1
iconwidget.line = index
iconwidget.index = real_index
if (type (results [index]) == "string") then
local result = results [index]:gsub (".-%\\", "")
iconwidget._icon.texture = results [index]
iconwidget._icon:SetTexCoord (0.1, .9, 0.1, .9)
elseif (type (results [index]) == "table") then
iconwidget._icon:SetTexture (results [index].texture)
local textCoord = results [index].texcoord
if (textCoord) then
iconwidget._icon:SetTexCoord (unpack(textCoord))
else
iconwidget._icon:SetTexCoord (0.1, .9, 0.1, .9)
end
local color = results [index].color
if (color) then
local r, g, b, a = DF:ParseColors(color)
iconwidget._icon:SetVertexColor(r, g, b, a)
else
iconwidget._icon:SetVertexColor(1, 1, 1, 1)
end
else
iconwidget._icon:SetTexture (results [index])
iconwidget._icon:SetTexCoord (0.1, .9, 0.1, .9)
end
iconwidget:Show()
elseif (t.type == "texture") then
local texturewidget = row.texture_inuse [texture]
texture = texture + 1
texturewidget.line = index
texturewidget.index = real_index
if (type (results [index]) == "string") then
local result = results [index]:gsub (".-%\\", "")
texturewidget.texture = results [index]
elseif (type (results [index]) == "table") then
texturewidget:SetTexture (results [index].texture)
local textCoord = results [index].texcoord
if (textCoord) then
texturewidget:SetTexCoord (unpack(textCoord))
else
texturewidget:SetTexCoord (0, 1, 0, 1)
end
local color = results [index].color
if (color) then
local r, g, b, a = DF:ParseColors(color)
texturewidget:SetVertexColor(r, g, b, a)
else
texturewidget:SetVertexColor(1, 1, 1, 1)
end
else
texturewidget:SetTexture (results [index])
end
texturewidget:Show()
end
end
end
else
row:Hide()
end
else
row:Hide()
end
end
end
function panel:Refresh()
if (type (panel._totalfunc) == "boolean") then
--> not yet initialized
return
end
local filled_lines = panel._totalfunc (panel)
local scroll_total_lines = #panel.scrollframe.lines
local line_height = options.rowheight
refresh_fillbox (panel.scrollframe)
FauxScrollFrame_Update (panel.scrollframe, filled_lines, scroll_total_lines, line_height)
panel.scrollframe:Show()
end
local scrollframe = CreateFrame ("scrollframe", name .. "Scroll", panel.widget, "FauxScrollFrameTemplate", "BackdropTemplate")
scrollframe:SetScript ("OnVerticalScroll", function (self, offset) FauxScrollFrame_OnVerticalScroll (self, offset, 20, panel.Refresh) end)
scrollframe:SetPoint ("topleft", panel.widget, "topleft", 0, -21)
scrollframe:SetPoint ("topright", panel.widget, "topright", -23, -21)
scrollframe:SetPoint ("bottomleft", panel.widget, "bottomleft")
scrollframe:SetPoint ("bottomright", panel.widget, "bottomright", -23, 0)
scrollframe:SetSize (w, h)
panel.scrollframe = scrollframe
scrollframe.lines = {}
DF:ReskinSlider (scrollframe)
--create lines
function panel:UpdateRowAmount()
local size = options.rowheight
local amount = math.floor (((panel._height-21) / size))
for i = #scrollframe.lines+1, amount do
local row = CreateFrame ("frame", panel:GetName() .. "Row_" .. i, panel.widget,"BackdropTemplate")
row:SetSize (1, size)
row.color = {1, 1, 1, .2}
row:SetBackdrop ({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]]})
if (i%2 == 0) then
row:SetBackdropColor (.5, .5, .5, 0.2)
else
row:SetBackdropColor (1, 1, 1, 0.00)
end
row:SetPoint ("topleft", scrollframe, "topleft", 0, (i-1) * size * -1)
row:SetPoint ("topright", scrollframe, "topright", 0, (i-1) * size * -1)
tinsert (scrollframe.lines, row)
row.text_available = {}
row.text_inuse = {}
row.text_total = 0
row.entry_available = {}
row.entry_inuse = {}
row.entry_total = 0
row.button_available = {}
row.button_inuse = {}
row.button_total = 0
row.checkbox_available = {}
row.checkbox_inuse = {}
row.checkbox_total = 0
row.icon_available = {}
row.icon_inuse = {}
row.icon_total = 0
row.texture_available = {}
row.texture_inuse = {}
row.texture_total = 0
end
end
panel:UpdateRowAmount()
panel.AlignRows (panel)
return panel
end
------------color pick
local color_pick_func = function()
local r, g, b = ColorPickerFrame:GetColorRGB()
local a = OpacitySliderFrame:GetValue()
ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
end
local color_pick_func_cancel = function()
ColorPickerFrame:SetColorRGB (unpack (ColorPickerFrame.previousValues))
local r, g, b = ColorPickerFrame:GetColorRGB()
local a = OpacitySliderFrame:GetValue()
ColorPickerFrame:dcallback (r, g, b, a, ColorPickerFrame.dframe)
end
function DF:ColorPick (frame, r, g, b, alpha, callback)
ColorPickerFrame:ClearAllPoints()
ColorPickerFrame:SetPoint ("bottomleft", frame, "topright", 0, 0)
ColorPickerFrame.dcallback = callback
ColorPickerFrame.dframe = frame
ColorPickerFrame.func = color_pick_func
ColorPickerFrame.opacityFunc = color_pick_func
ColorPickerFrame.cancelFunc = color_pick_func_cancel
ColorPickerFrame.opacity = alpha
ColorPickerFrame.hasOpacity = alpha and true
ColorPickerFrame.previousValues = {r, g, b}
ColorPickerFrame:SetParent (UIParent)
ColorPickerFrame:SetFrameStrata ("tooltip")
ColorPickerFrame:SetColorRGB (r, g, b)
ColorPickerFrame:Show()
end
------------icon pick
function DF:IconPick (callback, close_when_select, param1, param2)
if (not DF.IconPickFrame) then
local string_lower = string.lower
DF.IconPickFrame = CreateFrame ("frame", "DetailsFrameworkIconPickFrame", UIParent, "BackdropTemplate")
tinsert (UISpecialFrames, "DetailsFrameworkIconPickFrame")
DF.IconPickFrame:SetFrameStrata ("TOOLTIP")
DF.IconPickFrame:SetPoint ("center", UIParent, "center")
DF.IconPickFrame:SetWidth (350)
DF.IconPickFrame:SetHeight (277)
DF.IconPickFrame:EnableMouse (true)
DF.IconPickFrame:SetMovable (true)
DF:CreateTitleBar (DF.IconPickFrame, "Icon Picker")
DF.IconPickFrame:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
DF.IconPickFrame:SetBackdropBorderColor (0, 0, 0)
DF.IconPickFrame:SetBackdropColor (24/255, 24/255, 24/255, .8)
DF.IconPickFrame:SetFrameLevel (5000)
DF.IconPickFrame:SetScript ("OnMouseDown", function (self)
if (not self.isMoving) then
DF.IconPickFrame:StartMoving()
self.isMoving = true
end
end)
DF.IconPickFrame:SetScript ("OnMouseUp", function (self)
if (self.isMoving) then
DF.IconPickFrame:StopMovingOrSizing()
self.isMoving = nil
end
end)
DF.IconPickFrame.emptyFunction = function() end
DF.IconPickFrame.callback = DF.IconPickFrame.emptyFunction
DF.IconPickFrame.preview = CreateFrame ("frame", nil, UIParent, "BackdropTemplate")
DF.IconPickFrame.preview:SetFrameStrata ("tooltip")
DF.IconPickFrame.preview:SetFrameLevel (6001)
DF.IconPickFrame.preview:SetSize (76, 76)
local preview_image_bg = DF:NewImage (DF.IconPickFrame.preview, nil, 76, 76)
preview_image_bg:SetDrawLayer ("background", 0)
preview_image_bg:SetAllPoints (DF.IconPickFrame.preview)
preview_image_bg:SetColorTexture (0, 0, 0)
local preview_image = DF:NewImage (DF.IconPickFrame.preview, nil, 76, 76)
preview_image:SetAllPoints (DF.IconPickFrame.preview)
DF.IconPickFrame.preview.icon = preview_image
DF.IconPickFrame.preview:Hide()
--serach
DF.IconPickFrame.searchLabel = DF:NewLabel (DF.IconPickFrame, nil, "$parentSearchBoxLabel", nil, "search:", font, size, color)
DF.IconPickFrame.searchLabel:SetPoint ("topleft", DF.IconPickFrame, "topleft", 12, -36)
DF.IconPickFrame.searchLabel:SetTemplate (DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
DF.IconPickFrame.search = DF:NewTextEntry (DF.IconPickFrame, nil, "$parentSearchBox", nil, 140, 20)
DF.IconPickFrame.search:SetPoint ("left", DF.IconPickFrame.searchLabel, "right", 2, 0)
DF.IconPickFrame.search:SetTemplate (DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
DF.IconPickFrame.search:SetHook ("OnTextChanged", function()
DF.IconPickFrame.searching = DF.IconPickFrame.search:GetText()
if (DF.IconPickFrame.searching == "") then
DF.IconPickFrameScroll:Show()
DF.IconPickFrame.searching = nil
DF.IconPickFrame.updateFunc()
else
DF.IconPickFrameScroll:Hide()
FauxScrollFrame_SetOffset (DF.IconPickFrame, 1)
DF.IconPickFrame.last_filter_index = 1
DF.IconPickFrame.updateFunc()
end
end)
--manually enter the icon path
DF.IconPickFrame.customIcon = DF:CreateLabel (DF.IconPickFrame, "Icon Path:", DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
DF.IconPickFrame.customIcon:SetPoint ("bottomleft", DF.IconPickFrame, "bottomleft", 12, 16)
DF.IconPickFrame.customIconEntry = DF:CreateTextEntry (DF.IconPickFrame, function()end, 200, 20, "CustomIconEntry", _, _, DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
DF.IconPickFrame.customIconEntry:SetPoint ("left", DF.IconPickFrame.customIcon, "right", 2, 0)
DF.IconPickFrame.customIconEntry:SetHook ("OnTextChanged", function()
DF.IconPickFrame.preview:SetPoint ("bottom", DF.IconPickFrame.customIconEntry.widget, "top", 0, 2)
DF.IconPickFrame.preview.icon:SetTexture (DF.IconPickFrame.customIconEntry:GetText())
DF.IconPickFrame.preview:Show()
end)
DF.IconPickFrame.customIconEntry:SetHook ("OnEnter", function()
DF.IconPickFrame.preview:SetPoint ("bottom", DF.IconPickFrame.customIconEntry.widget, "top", 0, 2)
DF.IconPickFrame.preview.icon:SetTexture (DF.IconPickFrame.customIconEntry:GetText())
DF.IconPickFrame.preview:Show()
end)
--> close button
local close_button = CreateFrame ("button", nil, DF.IconPickFrame, "UIPanelCloseButton", "BackdropTemplate")
close_button:SetWidth (32)
close_button:SetHeight (32)
close_button:SetPoint ("TOPRIGHT", DF.IconPickFrame, "TOPRIGHT", -8, -7)
close_button:SetFrameLevel (close_button:GetFrameLevel()+2)
close_button:SetAlpha (0) --just hide, it is used below
--> accept custom icon button
local accept_custom_icon = function()
local path = DF.IconPickFrame.customIconEntry:GetText()
DF:QuickDispatch (DF.IconPickFrame.callback, path, DF.IconPickFrame.param1, DF.IconPickFrame.param2)
if (DF.IconPickFrame.click_close) then
close_button:Click()
end
end
DF.IconPickFrame.customIconAccept = DF:CreateButton (DF.IconPickFrame, accept_custom_icon, 82, 20, "Accept", nil, nil, nil, nil, nil, nil, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"), DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
DF.IconPickFrame.customIconAccept:SetPoint ("left", DF.IconPickFrame.customIconEntry, "right", 2, 0)
--fill with icons
local MACRO_ICON_FILENAMES = {}
local SPELLNAMES_CACHE = {}
DF.IconPickFrame:SetScript ("OnShow", function()
MACRO_ICON_FILENAMES [1] = "INV_MISC_QUESTIONMARK"
local index = 2
for i = 1, GetNumSpellTabs() do
local tab, tabTex, offset, numSpells, _ = GetSpellTabInfo (i)
offset = offset + 1
local tabEnd = offset + numSpells
for j = offset, tabEnd - 1 do
--to get spell info by slot, you have to pass in a pet argument
local spellType, ID = GetSpellBookItemInfo (j, "player")
if (spellType ~= "FLYOUT") then
MACRO_ICON_FILENAMES [index] = GetSpellBookItemTexture (j, "player") or 0
SPELLNAMES_CACHE [index] = GetSpellInfo (ID)
index = index + 1
elseif (spellType == "FLYOUT") then
local _, _, numSlots, isKnown = GetFlyoutInfo (ID)
if (isKnown and numSlots > 0) then
for k = 1, numSlots do
local spellID, overrideSpellID, isKnown = GetFlyoutSlotInfo (ID, k)
if (isKnown) then
MACRO_ICON_FILENAMES [index] = GetSpellTexture (spellID) or 0
SPELLNAMES_CACHE [index] = GetSpellInfo (spellID)
index = index + 1
end
end
end
end
end
end
GetLooseMacroItemIcons (MACRO_ICON_FILENAMES)
GetLooseMacroIcons (MACRO_ICON_FILENAMES)
GetMacroIcons (MACRO_ICON_FILENAMES)
GetMacroItemIcons (MACRO_ICON_FILENAMES)
--reset the custom icon text entry
DF.IconPickFrame.customIconEntry:SetText ("")
--reset the search text entry
DF.IconPickFrame.search:SetText ("")
end)
DF.IconPickFrame:SetScript ("OnHide", function()
wipe (MACRO_ICON_FILENAMES)
wipe (SPELLNAMES_CACHE)
DF.IconPickFrame.preview:Hide()
collectgarbage()
end)
DF.IconPickFrame.buttons = {}
local OnClickFunction = function (self)
DF:QuickDispatch (DF.IconPickFrame.callback, self.icon:GetTexture(), DF.IconPickFrame.param1, DF.IconPickFrame.param2)
if (DF.IconPickFrame.click_close) then
close_button:Click()
end
end
local onenter = function (self)
DF.IconPickFrame.preview:SetPoint ("bottom", self, "top", 0, 2)
DF.IconPickFrame.preview.icon:SetTexture (self.icon:GetTexture())
DF.IconPickFrame.preview:Show()
self.icon:SetBlendMode ("ADD")
end
local onleave = function (self)
DF.IconPickFrame.preview:Hide()
self.icon:SetBlendMode ("BLEND")
end
local backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tile = true, tileSize = 16,
insets = {left = 0, right = 0, top = 0, bottom = 0}, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1}
for i = 0, 9 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..(i+1), DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..(i+1).."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i+1
newcheck:SetPoint ("topleft", DF.IconPickFrame, "topleft", 12 + (i*30), -60)
newcheck:SetID (i+1)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
for i = 11, 20 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..i, DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..i.."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i
newcheck:SetPoint ("topleft", "DetailsFrameworkIconPickFrameButton"..(i-10), "bottomleft", 0, -1)
newcheck:SetID (i)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
for i = 21, 30 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..i, DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..i.."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i
newcheck:SetPoint ("topleft", "DetailsFrameworkIconPickFrameButton"..(i-10), "bottomleft", 0, -1)
newcheck:SetID (i)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
for i = 31, 40 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..i, DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..i.."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i
newcheck:SetPoint ("topleft", "DetailsFrameworkIconPickFrameButton"..(i-10), "bottomleft", 0, -1)
newcheck:SetID (i)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
for i = 41, 50 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..i, DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..i.."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i
newcheck:SetPoint ("topleft", "DetailsFrameworkIconPickFrameButton"..(i-10), "bottomleft", 0, -1)
newcheck:SetID (i)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
for i = 51, 60 do
local newcheck = CreateFrame ("Button", "DetailsFrameworkIconPickFrameButton"..i, DF.IconPickFrame, "BackdropTemplate")
local image = newcheck:CreateTexture ("DetailsFrameworkIconPickFrameButton"..i.."Icon", "overlay")
newcheck.icon = image
image:SetPoint ("topleft", newcheck, "topleft", 2, -2) image:SetPoint ("bottomright", newcheck, "bottomright", -2, 2)
newcheck:SetSize (30, 28)
newcheck:SetBackdrop (backdrop)
newcheck:SetScript ("OnClick", OnClickFunction)
newcheck.param1 = i
newcheck:SetPoint ("topleft", "DetailsFrameworkIconPickFrameButton"..(i-10), "bottomleft", 0, -1)
newcheck:SetID (i)
DF.IconPickFrame.buttons [#DF.IconPickFrame.buttons+1] = newcheck
newcheck:SetScript ("OnEnter", onenter)
newcheck:SetScript ("OnLeave", onleave)
end
local scroll = CreateFrame ("ScrollFrame", "DetailsFrameworkIconPickFrameScroll", DF.IconPickFrame, "ListScrollFrameTemplate", "BackdropTemplate")
DF:ReskinSlider (scroll)
local ChecksFrame_Update = function (self)
local numMacroIcons = #MACRO_ICON_FILENAMES
local macroPopupIcon, macroPopupButton
local macroPopupOffset = FauxScrollFrame_GetOffset (scroll)
local index
local texture
local filter
if (DF.IconPickFrame.searching) then
filter = string_lower (DF.IconPickFrame.searching)
end
local pool
local shown = 0
if (filter and filter ~= "") then
--do the filter
pool = {}
for i = 1, #SPELLNAMES_CACHE do
if (SPELLNAMES_CACHE [i] and SPELLNAMES_CACHE [i]:lower():find (filter)) then
pool [#pool+1] = MACRO_ICON_FILENAMES [i]
shown = shown + 1
end
end
else
shown = nil
end
if (not pool) then
pool = MACRO_ICON_FILENAMES
end
for i = 1, 60 do
macroPopupIcon = _G ["DetailsFrameworkIconPickFrameButton"..i.."Icon"]
macroPopupButton = _G ["DetailsFrameworkIconPickFrameButton"..i]
index = (macroPopupOffset * 10) + i
texture = pool [index]
if ( index <= numMacroIcons and texture ) then
if (type (texture) == "number") then
macroPopupIcon:SetTexture (texture)
else
macroPopupIcon:SetTexture ("INTERFACE\\ICONS\\" .. texture)
end
macroPopupIcon:SetTexCoord (4/64, 60/64, 4/64, 60/64)
macroPopupButton.IconID = index
macroPopupButton:Show()
else
macroPopupButton:Hide()
end
end
pool = nil
-- Scrollbar stuff
FauxScrollFrame_Update (scroll, ceil ((shown or numMacroIcons) / 10) , 5, 20 )
end
DF.IconPickFrame.updateFunc = ChecksFrame_Update
scroll:SetPoint ("topleft", DF.IconPickFrame, "topleft", -18, -58)
scroll:SetWidth (330)
scroll:SetHeight (178)
scroll:SetScript ("OnVerticalScroll", function (self, offset) FauxScrollFrame_OnVerticalScroll (scroll, offset, 20, ChecksFrame_Update) end)
scroll.update = ChecksFrame_Update
DF.IconPickFrameScroll = scroll
DF.IconPickFrame:Hide()
end
DF.IconPickFrame.param1, DF.IconPickFrame.param2 = param1, param2
DF.IconPickFrame:Show()
DF.IconPickFrameScroll.update (DF.IconPickFrameScroll)
DF.IconPickFrame.callback = callback or DF.IconPickFrame.emptyFunction
DF.IconPickFrame.click_close = close_when_select
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
function DF:ShowPanicWarning (text)
if (not DF.PanicWarningWindow) then
DF.PanicWarningWindow = CreateFrame ("frame", "DetailsFrameworkPanicWarningWindow", UIParent, "BackdropTemplate")
DF.PanicWarningWindow:SetHeight (80)
DF.PanicWarningWindow:SetBackdrop ({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
DF.PanicWarningWindow:SetBackdropColor (1, 0, 0, 0.2)
DF.PanicWarningWindow:SetPoint ("topleft", UIParent, "topleft", 0, -250)
DF.PanicWarningWindow:SetPoint ("topright", UIParent, "topright", 0, -250)
DF.PanicWarningWindow.text = DF.PanicWarningWindow:CreateFontString (nil, "overlay", "GameFontNormal")
DF.PanicWarningWindow.text:SetPoint ("center", DF.PanicWarningWindow, "center")
DF.PanicWarningWindow.text:SetTextColor (1, 0.6, 0)
end
DF.PanicWarningWindow.text:SetText (text)
DF.PanicWarningWindow:Show()
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
local simple_panel_mouse_down = function (self, button)
if (button == "RightButton") then
if (self.IsMoving) then
self.IsMoving = false
self:StopMovingOrSizing()
if (self.db and self.db.position) then
DF:SavePositionOnScreen (self)
end
end
if (not self.DontRightClickClose) then
self:Hide()
end
return
end
if (not self.IsMoving and not self.IsLocked) then
self.IsMoving = true
self:StartMoving()
end
end
local simple_panel_mouse_up = function (self, button)
if (self.IsMoving) then
self.IsMoving = false
self:StopMovingOrSizing()
if (self.db and self.db.position) then
DF:SavePositionOnScreen (self)
end
end
end
local simple_panel_settitle = function (self, title)
self.Title:SetText (title)
end
local simple_panel_close_click = function (self)
self:GetParent():GetParent():Hide()
end
local SimplePanel_frame_backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}
local SimplePanel_frame_backdrop_color = {0, 0, 0, 0.9}
local SimplePanel_frame_backdrop_border_color = {0, 0, 0, 1}
--with_label was making the frame stay in place while its parent moves
--the slider was anchoring to with_label and here here were anchoring the slider again
function DF:CreateScaleBar (frame, config)
local scaleBar, text = DF:CreateSlider (frame, 120, 14, 0.6, 1.6, 0.1, config.scale, true, "ScaleBar", nil, "Scale:", DF:GetTemplate ("slider", "OPTIONS_SLIDER_TEMPLATE"), DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
--scaleBar:SetPoint ("right", frame.Close, "left", -26, 0)
text:SetPoint ("topleft", frame, "topleft", 12, -7)
scaleBar:SetFrameLevel (DF.FRAMELEVEL_OVERLAY)
scaleBar.OnValueChanged = function (_, _, value)
config.scale = value
if (not scaleBar.IsValueChanging) then
frame:SetScale (config.scale)
end
end
scaleBar:SetHook ("OnMouseUp", function()
frame:SetScale (config.scale)
end)
scaleBar:SetAlpha (0.5)
return scaleBar
end
local no_options = {}
function DF:CreateSimplePanel (parent, w, h, title, name, panel_options, db)
if (db and name and not db [name]) then
db [name] = {scale = 1}
end
if (not name) then
name = "DetailsFrameworkSimplePanel" .. DF.SimplePanelCounter
DF.SimplePanelCounter = DF.SimplePanelCounter + 1
end
if (not parent) then
parent = UIParent
end
panel_options = panel_options or no_options
local f = CreateFrame ("frame", name, UIParent,"BackdropTemplate")
f:SetSize (w or 400, h or 250)
f:SetPoint ("center", UIParent, "center", 0, 0)
f:SetFrameStrata ("FULLSCREEN")
f:EnableMouse()
f:SetMovable (true)
f:SetBackdrop (SimplePanel_frame_backdrop)
f:SetBackdropColor (unpack (SimplePanel_frame_backdrop_color))
f:SetBackdropBorderColor (unpack (SimplePanel_frame_backdrop_border_color))
f.DontRightClickClose = panel_options.DontRightClickClose
if (not panel_options.NoTUISpecialFrame) then
tinsert (UISpecialFrames, name)
end
local title_bar = CreateFrame ("frame", name .. "TitleBar", f,"BackdropTemplate")
title_bar:SetPoint ("topleft", f, "topleft", 2, -3)
title_bar:SetPoint ("topright", f, "topright", -2, -3)
title_bar:SetHeight (20)
title_bar:SetBackdrop (SimplePanel_frame_backdrop)
title_bar:SetBackdropColor (.2, .2, .2, 1)
title_bar:SetBackdropBorderColor (0, 0, 0, 1)
f.TitleBar = title_bar
local close = CreateFrame ("button", name and name .. "CloseButton", title_bar)
close:SetFrameLevel (DF.FRAMELEVEL_OVERLAY)
close:SetSize (16, 16)
close:SetNormalTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:SetHighlightTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:SetPushedTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:GetNormalTexture():SetDesaturated(true)
close:GetHighlightTexture():SetDesaturated(true)
close:GetPushedTexture():SetDesaturated(true)
close:SetAlpha (0.7)
close:SetScript ("OnClick", simple_panel_close_click)
f.Close = close
local title_string = title_bar:CreateFontString (name and name .. "Title", "overlay", "GameFontNormal")
title_string:SetTextColor (.8, .8, .8, 1)
title_string:SetText (title or "")
f.Title = title_string
if (panel_options.UseScaleBar and db [name]) then
DF:CreateScaleBar (f, db [name])
f:SetScale (db [name].scale)
end
f.Title:SetPoint ("center", title_bar, "center")
f.Close:SetPoint ("right", title_bar, "right", -2, 0)
f:SetScript ("OnMouseDown", simple_panel_mouse_down)
f:SetScript ("OnMouseUp", simple_panel_mouse_up)
f.SetTitle = simple_panel_settitle
return f
end
local Panel1PxBackdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 64,
edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, insets = {left = 2, right = 2, top = 3, bottom = 3}}
local Panel1PxOnClickClose = function (self)
self:GetParent():Hide()
end
local Panel1PxOnToggleLock = function (self)
if (self.IsLocked) then
self.IsLocked = false
self:SetMovable (true)
self:EnableMouse (true)
self.Lock:GetNormalTexture():SetTexCoord (16/64, 32/64, 0, 1)
self.Lock:GetHighlightTexture():SetTexCoord (16/32, 32/64, 0, 1)
self.Lock:GetPushedTexture():SetTexCoord (16/64, 32/64, 0, 1)
if (self.OnUnlock) then
self:OnUnlock()
end
if (self.db) then
self.db.IsLocked = self.IsLocked
end
else
self.IsLocked = true
self:SetMovable (false)
self:EnableMouse (false)
self.Lock:GetNormalTexture():SetTexCoord (0/64, 16/64, 0, 1)
self.Lock:GetHighlightTexture():SetTexCoord (0/64, 16/64, 0, 1)
self.Lock:GetPushedTexture():SetTexCoord (0/64, 16/64, 0, 1)
if (self.OnLock) then
self:OnLock()
end
if (self.db) then
self.db.IsLocked = self.IsLocked
end
end
end
local Panel1PxOnClickLock = function (self)
local f = self:GetParent()
Panel1PxOnToggleLock (f)
end
local Panel1PxSetTitle = function (self, text)
self.Title:SetText (text or "")
end
local Panel1PxSetLocked= function (self, lock_state)
if (type (lock_state) ~= "boolean") then
return
end
if (lock_state) then
-- lock it
self.IsLocked = false
Panel1PxOnClickLock (self.Lock)
else
-- unlockit
self.IsLocked = true
Panel1PxOnClickLock (self.Lock)
end
end
local Panel1PxReadConfig = function (self)
local db = self.db
if (db) then
db.IsLocked = db.IsLocked or false
self.IsLocked = db.IsLocked
db.position = db.position or {x = 0, y = 0}
db.position.x = db.position.x or 0
db.position.y = db.position.y or 0
DF:RestoreFramePosition (self)
end
end
function DF:SavePositionOnScreen (frame)
if (frame.db and frame.db.position) then
local x, y = DF:GetPositionOnScreen (frame)
--print ("saving...", x, y, frame:GetName())
if (x and y) then
frame.db.position.x, frame.db.position.y = x, y
end
end
end
function DF:GetPositionOnScreen (frame)
local xOfs, yOfs = frame:GetCenter()
if (not xOfs) then
return
end
local scale = frame:GetEffectiveScale()
local UIscale = UIParent:GetScale()
xOfs = xOfs*scale - GetScreenWidth()*UIscale/2
yOfs = yOfs*scale - GetScreenHeight()*UIscale/2
return xOfs/UIscale, yOfs/UIscale
end
function DF:RestoreFramePosition (frame)
if (frame.db and frame.db.position) then
local scale, UIscale = frame:GetEffectiveScale(), UIParent:GetScale()
frame:ClearAllPoints()
frame.db.position.x = frame.db.position.x or 0
frame.db.position.y = frame.db.position.y or 0
frame:SetPoint ("center", UIParent, "center", frame.db.position.x * UIscale / scale, frame.db.position.y * UIscale / scale)
end
end
local Panel1PxSavePosition= function (self)
DF:SavePositionOnScreen (self)
end
local Panel1PxHasPosition = function (self)
local db = self.db
if (db) then
if (db.position and db.position.x and (db.position.x ~= 0 or db.position.y ~= 0)) then
return true
end
end
end
function DF:Create1PxPanel (parent, w, h, title, name, config, title_anchor, no_special_frame)
local f = CreateFrame ("frame", name, parent or UIParent, "BackdropTemplate")
f:SetSize (w or 100, h or 75)
f:SetPoint ("center", UIParent, "center")
if (name and not no_special_frame) then
tinsert (UISpecialFrames, name)
end
f:SetScript ("OnMouseDown", simple_panel_mouse_down)
f:SetScript ("OnMouseUp", simple_panel_mouse_up)
f:SetBackdrop (Panel1PxBackdrop)
f:SetBackdropColor (0, 0, 0, 0.5)
f.IsLocked = (config and config.IsLocked ~= nil and config.IsLocked) or false
f:SetMovable (true)
f:EnableMouse (true)
f:SetUserPlaced (true)
f.db = config
--print (config.position.x, config.position.x)
Panel1PxReadConfig (f)
local close = CreateFrame ("button", name and name .. "CloseButton", f, "BackdropTemplate")
close:SetSize (16, 16)
close:SetNormalTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:SetHighlightTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:SetPushedTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
close:GetNormalTexture():SetDesaturated(true)
close:GetHighlightTexture():SetDesaturated(true)
close:GetPushedTexture():SetDesaturated(true)
close:SetAlpha (0.7)
local lock = CreateFrame ("button", name and name .. "LockButton", f, "BackdropTemplate")
lock:SetSize (16, 16)
lock:SetNormalTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
lock:SetHighlightTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
lock:SetPushedTexture ([[Interface\GLUES\CharacterSelect\Glues-AddOn-Icons]])
lock:GetNormalTexture():SetDesaturated(true)
lock:GetHighlightTexture():SetDesaturated(true)
lock:GetPushedTexture():SetDesaturated(true)
--lock:GetNormalTexture():SetBlendMode("ADD")
--lock:GetHighlightTexture():SetBlendMode("ADD")
--lock:GetPushedTexture():SetBlendMode("ADD")
--lock:GetNormalTexture():SetTexCoord(73/256, 105/256, 64/128, 110/)
--lock:GetHighlightTexture():SetTexCoord(73/256, 105/256, 64/128, 110/)
--lock:GetPushedTexture():SetTexCoord(73/256, 105/256, 64/128, 110/)
lock:SetAlpha (0.7)
close:SetPoint ("topright", f, "topright", -3, -3)
lock:SetPoint ("right", close, "left", 3, 0)
close:SetScript ("OnClick", Panel1PxOnClickClose)
lock:SetScript ("OnClick", Panel1PxOnClickLock)
local title_string = f:CreateFontString (name and name .. "Title", "overlay", "GameFontNormal")
title_string:SetPoint ("topleft", f, "topleft", 5, -5)
title_string:SetText (title or "")
if (title_anchor) then
if (title_anchor == "top") then
title_string:ClearAllPoints()
title_string:SetPoint ("bottomleft", f, "topleft", 0, 0)
close:ClearAllPoints()
close:SetPoint ("bottomright", f, "topright", 0, 0)
end
f.title_anchor = title_anchor
end
f.SetTitle = Panel1PxSetTitle
f.Title = title_string
f.Lock = lock
f.Close = close
f.HasPosition = Panel1PxHasPosition
f.SavePosition = Panel1PxSavePosition
f.IsLocked = not f.IsLocked
f.SetLocked = Panel1PxSetLocked
Panel1PxOnToggleLock (f)
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------
-- ~prompt
function DF:ShowPromptPanel (message, func_true, func_false, no_repeated, width)
if (not DetailsFrameworkPromptSimple) then
local f = CreateFrame ("frame", "DetailsFrameworkPromptSimple", UIParent, "BackdropTemplate")
f:SetSize (400, 80)
f:SetFrameStrata ("DIALOG")
f:SetPoint ("center", UIParent, "center", 0, 300)
f:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
f:SetBackdropColor (0, 0, 0, 0.8)
f:SetBackdropBorderColor (0, 0, 0, 1)
tinsert (UISpecialFrames, "DetailsFrameworkPromptSimple")
DF:CreateTitleBar (f, "Prompt!")
DF:ApplyStandardBackdrop (f)
local prompt = f:CreateFontString (nil, "overlay", "GameFontNormal")
prompt:SetPoint ("top", f, "top", 0, -28)
prompt:SetJustifyH ("center")
f.prompt = prompt
local button_text_template = DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local button_true = DF:CreateButton (f, nil, 60, 20, "Yes", nil, nil, nil, nil, nil, nil, options_dropdown_template)
button_true:SetPoint ("bottomright", f, "bottomright", -5, 5)
f.button_true = button_true
local button_false = DF:CreateButton (f, nil, 60, 20, "No", nil, nil, nil, nil, nil, nil, options_dropdown_template)
button_false:SetPoint ("bottomleft", f, "bottomleft", 5, 5)
f.button_false = button_false
button_true:SetClickFunction (function()
local my_func = button_true.true_function
if (my_func) then
local okey, errormessage = pcall (my_func, true)
if (not okey) then
print ("error:", errormessage)
end
f:Hide()
end
end)
button_false:SetClickFunction (function()
local my_func = button_false.false_function
if (my_func) then
local okey, errormessage = pcall (my_func, true)
if (not okey) then
print ("error:", errormessage)
end
f:Hide()
end
end)
f.ShowAnimation = DF:CreateAnimationHub (f, function()
f:SetBackdropBorderColor (0, 0, 0, 0)
f.TitleBar:SetBackdropBorderColor (0, 0, 0, 0)
end, function()
f:SetBackdropBorderColor (0, 0, 0, 1)
f.TitleBar:SetBackdropBorderColor (0, 0, 0, 1)
end)
DF:CreateAnimation (f.ShowAnimation, "scale", 1, .075, .2, .2, 1.1, 1.1, "center", 0, 0)
DF:CreateAnimation (f.ShowAnimation, "scale", 2, .075, 1, 1, .90, .90, "center", 0, 0)
f.FlashTexture = f:CreateTexture (nil, "overlay")
f.FlashTexture:SetColorTexture (1, 1, 1, 1)
f.FlashTexture:SetAllPoints()
f.FlashAnimation = DF:CreateAnimationHub (f.FlashTexture, function() f.FlashTexture:Show() end, function() f.FlashTexture:Hide() end)
DF:CreateAnimation (f.FlashAnimation, "alpha", 1, .075, 0, .25)
DF:CreateAnimation (f.FlashAnimation, "alpha", 2, .075, .35, 0)
f:Hide()
DF.promtp_panel = f
end
assert (type (func_true) == "function" and type (func_false) == "function", "ShowPromptPanel expects two functions.")
if (no_repeated) then
if (DF.promtp_panel:IsShown()) then
return
end
end
if (width) then
DF.promtp_panel:SetWidth (width)
else
DF.promtp_panel:SetWidth (400)
end
DF.promtp_panel.prompt:SetText (message)
DF.promtp_panel.button_true.true_function = func_true
DF.promtp_panel.button_false.false_function = func_false
DF.promtp_panel:Show()
DF.promtp_panel.ShowAnimation:Play()
DF.promtp_panel.FlashAnimation:Play()
end
function DF:ShowTextPromptPanel (message, callback)
if (not DF.text_prompt_panel) then
local f = CreateFrame ("frame", "DetailsFrameworkPrompt", UIParent, "BackdropTemplate")
f:SetSize (400, 120)
f:SetFrameStrata ("FULLSCREEN")
f:SetPoint ("center", UIParent, "center", 0, 100)
f:EnableMouse (true)
f:SetMovable (true)
f:RegisterForDrag ("LeftButton")
f:SetScript ("OnDragStart", function() f:StartMoving() end)
f:SetScript ("OnDragStop", function() f:StopMovingOrSizing() end)
f:SetScript ("OnMouseDown", function (self, button) if (button == "RightButton") then f.EntryBox:ClearFocus() f:Hide() end end)
tinsert (UISpecialFrames, "DetailsFrameworkPrompt")
DF:CreateTitleBar (f, "Prompt!")
DF:ApplyStandardBackdrop (f)
local prompt = f:CreateFontString (nil, "overlay", "GameFontNormal")
prompt:SetPoint ("top", f, "top", 0, -25)
prompt:SetJustifyH ("center")
prompt:SetSize (360, 36)
f.prompt = prompt
local button_text_template = DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local textbox = DF:CreateTextEntry (f, function()end, 380, 20, "textbox", nil, nil, options_dropdown_template)
textbox:SetPoint ("topleft", f, "topleft", 10, -60)
f.EntryBox = textbox
local button_true = DF:CreateButton (f, nil, 60, 20, "Okey", nil, nil, nil, nil, nil, nil, options_dropdown_template)
button_true:SetPoint ("bottomright", f, "bottomright", -10, 5)
f.button_true = button_true
local button_false = DF:CreateButton (f, function() f.textbox:ClearFocus() f:Hide() end, 60, 20, "Cancel", nil, nil, nil, nil, nil, nil, options_dropdown_template)
button_false:SetPoint ("bottomleft", f, "bottomleft", 10, 5)
f.button_false = button_false
local executeCallback = function()
local my_func = button_true.true_function
if (my_func) then
local okey, errormessage = pcall (my_func, textbox:GetText())
textbox:ClearFocus()
if (not okey) then
print ("error:", errormessage)
end
f:Hide()
end
end
button_true:SetClickFunction (function()
executeCallback()
end)
textbox:SetHook ("OnEnterPressed", function()
executeCallback()
end)
f:Hide()
DF.text_prompt_panel = f
end
DF.text_prompt_panel:Show()
DetailsFrameworkPrompt.EntryBox:SetText ("")
DF.text_prompt_panel.prompt:SetText (message)
DF.text_prompt_panel.button_true.true_function = callback
DF.text_prompt_panel.textbox:SetFocus (true)
end
------------------------------------------------------------------------------------------------------------------------------------------------
--> options button -- ~options
function DF:CreateOptionsButton (parent, callback, name)
local b = CreateFrame ("button", name, parent, "BackdropTemplate")
b:SetSize (14, 14)
b:SetNormalTexture ([[Interface\GossipFrame\BinderGossipIcon]])
b:SetHighlightTexture ([[Interface\GossipFrame\BinderGossipIcon]])
b:SetPushedTexture ([[Interface\GossipFrame\BinderGossipIcon]])
b:GetNormalTexture():SetDesaturated(true)
b:GetHighlightTexture():SetDesaturated(true)
b:GetPushedTexture():SetDesaturated(true)
b:SetAlpha (0.7)
b:SetScript ("OnClick", callback)
b:SetScript ("OnEnter", function (self)
GameCooltip2:Reset()
GameCooltip2:AddLine ("Options")
GameCooltip2:ShowCooltip (self, "tooltip")
end)
b:SetScript ("OnLeave", function (self)
GameCooltip2:Hide()
end)
return b
end
------------------------------------------------------------------------------------------------------------------------------------------------
--> feedback panel -- ~feedback
function DF:CreateFeedbackButton (parent, callback, name)
local b = CreateFrame ("button", name, parent, "BackdropTemplate")
b:SetSize (12, 13)
b:SetScript ("OnClick", callback)
b:SetScript ("OnEnter", function (self)
GameCooltip2:Reset()
GameCooltip2:AddLine ("Send Feedback")
GameCooltip2:ShowCooltip (self, "tooltip")
end)
b:SetScript ("OnLeave", function (self)
GameCooltip2:Hide()
end)
print("Framework:CreateFeedbackButton() is deprecated.")
return b
end
local backdrop_fb_line = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1,
tile = true, tileSize = 64, insets = {left = 2, right = 2, top = 2, bottom = 2}}
local on_enter_feedback = function (self)
self:SetBackdropColor (1, 1, 0, 0.5)
end
local on_leave_feedback = function (self)
self:SetBackdropColor (0, 0, 0, 0.3)
end
local on_click_feedback = function (self)
local feedback_link_textbox = DF.feedback_link_textbox
if (not feedback_link_textbox) then
local editbox = DF:CreateTextEntry (AddonFeedbackPanel, _, 275, 34)
editbox:SetAutoFocus (false)
editbox:SetHook ("OnEditFocusGained", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox:SetHook ("OnEditFocusLost", function()
editbox:Hide()
end)
editbox:SetHook ("OnChar", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox.text = ""
DF.feedback_link_textbox = editbox
feedback_link_textbox = editbox
end
feedback_link_textbox.link = self.link
feedback_link_textbox.text = self.link
feedback_link_textbox:Show()
feedback_link_textbox:SetPoint ("topleft", self.icon, "topright", 3, 0)
feedback_link_textbox:HighlightText()
feedback_link_textbox:SetFocus()
feedback_link_textbox:SetFrameLevel (self:GetFrameLevel()+2)
end
local feedback_get_fb_line = function (self)
local line = self.feedback_lines [self.next_feedback]
if (not line) then
line = CreateFrame ("frame", "AddonFeedbackPanelFB" .. self.next_feedback, self, "BackdropTemplate")
line:SetBackdrop (backdrop_fb_line)
line:SetBackdropColor (0, 0, 0, 0.3)
line:SetSize (390, 42)
line:SetPoint ("topleft", self.feedback_anchor, "bottomleft", 0, -5 + ((self.next_feedback-1) * 46 * -1))
line:SetScript ("OnEnter", on_enter_feedback)
line:SetScript ("OnLeave", on_leave_feedback)
line:SetScript ("OnMouseUp", on_click_feedback)
line.icon = line:CreateTexture (nil, "overlay")
line.icon:SetSize (90, 36)
line.desc = line:CreateFontString (nil, "overlay", "GameFontHighlightSmall")
line.icon:SetPoint ("left", line, "left", 5, 0)
line.desc:SetPoint ("left", line.icon, "right", 5, 0)
local arrow = line:CreateTexture (nil, "overlay")
arrow:SetTexture ([[Interface\Buttons\JumpUpArrow]])
arrow:SetRotation (-1.55)
arrow:SetPoint ("right", line, "right", -5, 0)
self.feedback_lines [self.next_feedback] = line
end
self.next_feedback = self.next_feedback + 1
return line
end
local on_click_feedback = function (self)
local feedback_link_textbox = DF.feedback_link_textbox
if (not feedback_link_textbox) then
local editbox = DF:CreateTextEntry (AddonFeedbackPanel, _, 275, 34)
editbox:SetAutoFocus (false)
editbox:SetHook ("OnEditFocusGained", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox:SetHook ("OnEditFocusLost", function()
editbox:Hide()
end)
editbox:SetHook ("OnChar", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox.text = ""
DF.feedback_link_textbox = editbox
feedback_link_textbox = editbox
end
feedback_link_textbox.link = self.link
feedback_link_textbox.text = self.link
feedback_link_textbox:Show()
feedback_link_textbox:SetPoint ("topleft", self.icon, "topright", 3, 0)
feedback_link_textbox:HighlightText()
feedback_link_textbox:SetFocus()
feedback_link_textbox:SetFrameLevel (self:GetFrameLevel()+2)
end
local on_enter_addon = function (self)
if (self.tooltip) then
GameCooltip2:Preset (2)
GameCooltip2:AddLine ("|cFFFFFF00" .. self.name .. "|r")
GameCooltip2:AddLine ("")
GameCooltip2:AddLine (self.tooltip)
GameCooltip2:ShowCooltip (self, "tooltip")
end
self.icon:SetBlendMode ("ADD")
end
local on_leave_addon = function (self)
if (self.tooltip) then
GameCooltip2:Hide()
end
self.icon:SetBlendMode ("BLEND")
end
local on_click_addon = function (self)
local addon_link_textbox = DF.addon_link_textbox
if (not addon_link_textbox) then
local editbox = DF:CreateTextEntry (AddonFeedbackPanel, _, 128, 64)
editbox:SetAutoFocus (false)
editbox:SetHook ("OnEditFocusGained", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox:SetHook ("OnEditFocusLost", function()
editbox:Hide()
end)
editbox:SetHook ("OnChar", function()
editbox.text = editbox.link
editbox:HighlightText()
end)
editbox.text = ""
DF.addon_link_textbox = editbox
addon_link_textbox = editbox
end
addon_link_textbox.link = self.link
addon_link_textbox.text = self.link
addon_link_textbox:Show()
addon_link_textbox:SetPoint ("topleft", self.icon, "topleft", 0, 0)
addon_link_textbox:HighlightText()
addon_link_textbox:SetFocus()
addon_link_textbox:SetFrameLevel (self:GetFrameLevel()+2)
end
local feedback_get_addons_line = function (self)
local line = self.addons_lines [self.next_addons]
if (not line) then
line = CreateFrame ("frame", "AddonFeedbackPanelSA" .. self.next_addons, self, "BackdropTemplate")
line:SetSize (128, 64)
if (self.next_addons == 1) then
line:SetPoint ("topleft", self.addons_anchor, "bottomleft", 0, -5)
elseif (self.next_addons_line_break == self.next_addons) then
line:SetPoint ("topleft", self.addons_anchor, "bottomleft", 0, -5 + floor (self.next_addons_line_break/3) * 66 * -1)
self.next_addons_line_break = self.next_addons_line_break + 3
else
local previous = self.addons_lines [self.next_addons - 1]
line:SetPoint ("topleft", previous, "topright", 2, 0)
end
line:SetScript ("OnEnter", on_enter_addon)
line:SetScript ("OnLeave", on_leave_addon)
line:SetScript ("OnMouseUp", on_click_addon)
line.icon = line:CreateTexture (nil, "overlay")
line.icon:SetSize (128, 64)
line.icon:SetPoint ("topleft", line, "topleft", 0, 0)
self.addons_lines [self.next_addons] = line
end
self.next_addons = self.next_addons + 1
return line
end
local default_coords = {0, 1, 0, 1}
local feedback_add_fb = function (self, table)
local line = self:GetFeedbackLine()
line.icon:SetTexture (table.icon)
line.icon:SetTexCoord (unpack (table.coords or default_coords))
line.desc:SetText (table.desc)
line.link = table.link
line:Show()
end
local feedback_add_addon = function (self, table)
local block = self:GetAddonsLine()
block.icon:SetTexture (table.icon)
block.icon:SetTexCoord (unpack (table.coords or default_coords))
block.link = table.link
block.tooltip = table.desc
block.name = table.name
block:Show()
end
local feedback_hide_all = function (self)
self.next_feedback = 1
self.next_addons = 1
for index, line in ipairs (self.feedback_lines) do
line:Hide()
end
for index, line in ipairs (self.addons_lines) do
line:Hide()
end
end
-- feedback_methods = { { icon = icon path, desc = description, link = url}}
function DF:ShowFeedbackPanel (addon_name, version, feedback_methods, more_addons)
local f = _G.AddonFeedbackPanel
if (not f) then
f = DF:Create1PxPanel (UIParent, 400, 100, addon_name .. " Feedback", "AddonFeedbackPanel", nil)
f:SetFrameStrata ("FULLSCREEN")
f:SetPoint ("center", UIParent, "center")
f:SetBackdropColor (0, 0, 0, 0.8)
f.feedback_lines = {}
f.addons_lines = {}
f.next_feedback = 1
f.next_addons = 1
f.next_addons_line_break = 4
local feedback_anchor = f:CreateFontString (nil, "overlay", "GameFontNormal")
feedback_anchor:SetText ("Feedback:")
feedback_anchor:SetPoint ("topleft", f, "topleft", 5, -30)
f.feedback_anchor = feedback_anchor
local excla_text = f:CreateFontString (nil, "overlay", "GameFontNormal")
excla_text:SetText ("click and copy the link")
excla_text:SetPoint ("topright", f, "topright", -5, -30)
excla_text:SetTextColor (1, 0.8, 0.2, 0.6)
local addons_anchor = f:CreateFontString (nil, "overlay", "GameFontNormal")
addons_anchor:SetText ("AddOns From the Same Author:")
f.addons_anchor = addons_anchor
local excla_text2 = f:CreateFontString (nil, "overlay", "GameFontNormal")
excla_text2:SetText ("click and copy the link")
excla_text2:SetTextColor (1, 0.8, 0.2, 0.6)
f.excla_text2 = excla_text2
f.GetFeedbackLine = feedback_get_fb_line
f.GetAddonsLine = feedback_get_addons_line
f.AddFeedbackMethod = feedback_add_fb
f.AddOtherAddon = feedback_add_addon
f.HideAll = feedback_hide_all
DF:SetFontSize (f.Title, 14)
end
f:HideAll()
f:SetTitle (addon_name)
for index, feedback in ipairs (feedback_methods) do
f:AddFeedbackMethod (feedback)
end
f.addons_anchor:SetPoint ("topleft", f, "topleft", 5, f.next_feedback * 50 * -1)
f.excla_text2:SetPoint ("topright", f, "topright", -5, f.next_feedback * 50 * -1)
for index, addon in ipairs (more_addons) do
f:AddOtherAddon (addon)
end
f:SetHeight (80 + ((f.next_feedback-1) * 50) + (ceil ((f.next_addons-1)/3) * 66))
f:Show()
return true
end
------------------------------------------------------------------------------------------------------------------------------------------------
--> chart panel -- ~chart
local chart_panel_backdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16,
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border", edgeSize = 32, insets = {left = 5, right = 5, top = 5, bottom = 5}}
local chart_panel_align_timelabels = function (self, elapsed_time)
self.TimeScale = elapsed_time
local linha = self.TimeLabels [17]
local minutos, segundos = math.floor (elapsed_time / 60), math.floor (elapsed_time % 60)
if (segundos < 10) then
segundos = "0" .. segundos
end
if (minutos > 0) then
if (minutos < 10) then
minutos = "0" .. minutos
end
linha:SetText (minutos .. ":" .. segundos)
else
linha:SetText ("00:" .. segundos)
end
local time_div = elapsed_time / 16 --786 -- 49.125
for i = 2, 16 do
local linha = self.TimeLabels [i]
local this_time = time_div * (i-1)
local minutos, segundos = math.floor (this_time / 60), math.floor (this_time % 60)
if (segundos < 10) then
segundos = "0" .. segundos
end
if (minutos > 0) then
if (minutos < 10) then
minutos = "0" .. minutos
end
linha:SetText (minutos .. ":" .. segundos)
else
linha:SetText ("00:" .. segundos)
end
end
end
local chart_panel_set_scale = function (self, amt, func, text)
if (type (amt) ~= "number") then
return
end
--each line amount, then multiply the line index by this number
local piece = amt / 8
for i = 1, 8 do
if (func) then
self ["dpsamt" .. math.abs (i-9)]:SetText (func (piece*i))
else
if (piece*i > 1) then
self ["dpsamt" .. math.abs (i-9)]:SetText (DF.FormatNumber (piece*i))
else
self ["dpsamt" .. math.abs (i-9)]:SetText (format ("%.3f", piece*i))
end
end
end
end
local chart_panel_can_move = function (self, can)
self.can_move = can
end
local chart_panel_overlay_reset = function (self)
self.OverlaysAmount = 1
for index, pack in ipairs (self.Overlays) do
for index2, texture in ipairs (pack) do
texture:Hide()
end
end
end
local chart_panel_reset = function (self)
self.Graphic:ResetData()
self.Graphic.max_value = 0
self.TimeScale = nil
self.BoxLabelsAmount = 1
table.wipe (self.GData)
table.wipe (self.OData)
for index, box in ipairs (self.BoxLabels) do
box.check:Hide()
box.button:Hide()
box.box:Hide()
box.text:Hide()
box.border:Hide()
box.showing = false
end
chart_panel_overlay_reset (self)
end
local chart_panel_enable_line = function (f, thisbox)
local index = thisbox.index
local type = thisbox.type
if (thisbox.enabled) then
--disable
thisbox.check:Hide()
thisbox.enabled = false
else
--enable
thisbox.check:Show()
thisbox.enabled = true
end
if (type == "graphic") then
f.Graphic:ResetData()
f.Graphic.max_value = 0
local max = 0
local max_time = 0
for index, box in ipairs (f.BoxLabels) do
if (box.type == type and box.showing and box.enabled) then
local data = f.GData [index]
f.Graphic:AddDataSeries (data[1], data[2], nil, data[3])
if (data[4] > max) then
max = data[4]
end
if (data [5] > max_time) then
max_time = data [5]
end
end
end
f:SetScale (max)
f:SetTime (max_time)
elseif (type == "overlay") then
chart_panel_overlay_reset (f)
for index, box in ipairs (f.BoxLabels) do
if (box.type == type and box.showing and box.enabled) then
f:AddOverlay (box.index)
end
end
end
end
local create_box = function (self, next_box)
local thisbox = {}
self.BoxLabels [next_box] = thisbox
local box = DF:NewImage (self.Graphic, nil, 16, 16, "border")
local text = DF:NewLabel (self.Graphic)
local border = DF:NewImage (self.Graphic, [[Interface\DialogFrame\UI-DialogBox-Gold-Corner]], 30, 30, "artwork")
border:SetPoint ("center", box, "center", -3, -4)
border:SetTexture ([[Interface\DialogFrame\UI-DialogBox-Gold-Corner]])
local checktexture = DF:NewImage (self.Graphic, [[Interface\Buttons\UI-CheckBox-Check]], 18, 18, "overlay")
checktexture:SetPoint ("center", box, "center", 0, -1)
checktexture:SetTexture ([[Interface\Buttons\UI-CheckBox-Check]])
thisbox.box = box
thisbox.text = text
thisbox.border = border
thisbox.check = checktexture
thisbox.enabled = true
local button = CreateFrame ("button", nil, self.Graphic, "BackdropTemplate")
button:SetSize (20, 20)
button:SetScript ("OnClick", function()
chart_panel_enable_line (self, thisbox)
end)
button:SetPoint ("topleft", box.widget or box, "topleft", 0, 0)
button:SetPoint ("bottomright", box.widget or box, "bottomright", 0, 0)
button:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
button:SetBackdropColor (0, 0, 0, 0.0)
button:SetBackdropBorderColor (0, 0, 0, 1)
thisbox.button = button
thisbox.box:SetPoint ("right", text, "left", -4, 0)
if (next_box == 1) then
thisbox.text:SetPoint ("topright", self, "topright", -35, -16)
else
thisbox.text:SetPoint ("right", self.BoxLabels [next_box-1].box, "left", -17, 0)
end
return thisbox
end
local realign_labels = function (self)
if (not self.ShowHeader) then
for _, box in ipairs (self.BoxLabels) do
box.check:Hide()
box.button:Hide()
box.border:Hide()
box.box:Hide()
box.text:Hide()
end
return
end
local width = self:GetWidth() - 108
local first_box = self.BoxLabels [1]
first_box.text:SetPoint ("topright", self, "topright", -35, -16)
local line_width = first_box.text:GetStringWidth() + 26
for i = 2, #self.BoxLabels do
local box = self.BoxLabels [i]
if (box.box:IsShown()) then
line_width = line_width + box.text:GetStringWidth() + 26
if (line_width > width) then
line_width = box.text:GetStringWidth() + 26
box.text:SetPoint ("topright", self, "topright", -35, -40)
else
box.text:SetPoint ("right", self.BoxLabels [i-1].box, "left", -27, 0)
end
else
break
end
end
if (self.HeaderOnlyIndicator) then
for _, box in ipairs (self.BoxLabels) do
box.check:Hide()
box.button:Hide()
end
return
end
end
local chart_panel_add_label = function (self, color, name, type, number)
local next_box = self.BoxLabelsAmount
local thisbox = self.BoxLabels [next_box]
if (not thisbox) then
thisbox = create_box (self, next_box)
end
self.BoxLabelsAmount = self.BoxLabelsAmount + 1
thisbox.type = type
thisbox.index = number
thisbox.box:SetColorTexture (unpack (color))
thisbox.text:SetText (name)
thisbox.check:Show()
thisbox.button:Show()
thisbox.border:Hide()
thisbox.box:Show()
thisbox.text:Show()
thisbox.showing = true
thisbox.enabled = true
realign_labels (self)
end
local line_default_color = {1, 1, 1}
local draw_overlay = function (self, this_overlay, overlayData, color)
local pixel = self.Graphic:GetWidth() / self.TimeScale
local index = 1
local r, g, b, a = unpack (color or line_default_color)
for i = 1, #overlayData, 2 do
local aura_start = overlayData [i]
local aura_end = overlayData [i+1]
local this_block = this_overlay [index]
if (not this_block) then
this_block = self.Graphic:CreateTexture (nil, "border")
tinsert (this_overlay, this_block)
end
this_block:SetHeight (self.Graphic:GetHeight())
this_block:SetPoint ("left", self.Graphic, "left", pixel * aura_start, 0)
if (aura_end) then
this_block:SetWidth ((aura_end-aura_start)*pixel)
else
--malformed table
this_block:SetWidth (pixel*5)
end
this_block:SetColorTexture (r, g, b, a or 0.25)
this_block:Show()
index = index + 1
end
end
local chart_panel_add_overlay = function (self, overlayData, color, name, icon)
if (not self.TimeScale) then
error ("Use SetTime (time) before adding an overlay.")
end
if (type (overlayData) == "number") then
local overlay_index = overlayData
draw_overlay (self, self.Overlays [self.OverlaysAmount], self.OData [overlay_index][1], self.OData [overlay_index][2])
else
local this_overlay = self.Overlays [self.OverlaysAmount]
if (not this_overlay) then
this_overlay = {}
tinsert (self.Overlays, this_overlay)
end
draw_overlay (self, this_overlay, overlayData, color)
tinsert (self.OData, {overlayData, color or line_default_color})
if (name and self.HeaderShowOverlays) then
self:AddLabel (color or line_default_color, name, "overlay", #self.OData)
end
end
self.OverlaysAmount = self.OverlaysAmount + 1
end
-- Define the tricube weight function
function calc_cubeweight (i, j, d)
local w = ( 1 - math.abs ((j-i)/d)^3)^3
if w < 0 then
w = 0
end
return w
end
local calc_lowess_smoothing = function (self, data, bandwidth)
local length = #data
local newData = {}
for i = 1, length do
local A = 0
local B = 0
local C = 0
local D = 0
local E = 0
-- Calculate span of values to be included in the regression
local jmin = floor (i-bandwidth/2)
local jmax = ceil (i+bandwidth/2)
if jmin < 1 then
jmin = 1
end
if jmax > length then
jmax = length
end
-- For all the values in the span, compute the weight and then the linear fit
for j = jmin, jmax do
w = calc_cubeweight (i, j, bandwidth/2)
x = j
y = data [j]
A = A + w*x
B = B + w*y
C = C + w*x^2
D = D + w*x*y
E = E + w
end
-- Calculate a (slope) and b (offset) for the linear fit
local a = (A*B-D*E)/(A^2 - C*E)
local b = (A*D-B*C)/(A^2 - C*E)
-- Calculate the smoothed value by the formula y=a*x+b (x <- i)
newData [i] = a*i+b
end
return newData
end
local calc_stddev = function (self, data)
local total = 0
for i = 1, #data do
total = total + data[i]
end
local mean = total / #data
local totalDistance = 0
for i = 1, #data do
totalDistance = totalDistance + ((data[i] - mean) ^ 2)
end
local deviation = math.sqrt (totalDistance / #data)
return deviation
end
local SMA_table = {}
local SMA_max = 0
local reset_SMA = function()
table.wipe (SMA_table)
SMA_max = 0
end
local calc_SMA
calc_SMA = function (a, b, ...)
if (b) then
return calc_SMA (a + b, ...)
else
return a
end
end
local do_SMA = function (value, max_value)
if (#SMA_table == 10) then
tremove (SMA_table, 1)
end
SMA_table [#SMA_table + 1] = value
local new_value = calc_SMA (unpack (SMA_table)) / #SMA_table
if (new_value > SMA_max) then
SMA_max = new_value
return new_value, SMA_max
else
return new_value
end
end
local chart_panel_onresize = function (self)
local width, height = self:GetSize()
local spacement = width - 78 - 60
spacement = spacement / 16
for i = 1, 17 do
local label = self.TimeLabels [i]
label:SetPoint ("bottomleft", self, "bottomleft", 78 + ((i-1)*spacement), self.TimeLabelsHeight)
label.line:SetHeight (height - 45)
end
local spacement = (self.Graphic:GetHeight()) / 8
for i = 1, 8 do
self ["dpsamt"..i]:SetPoint ("TOPLEFT", self, "TOPLEFT", 27, -25 + (-(spacement* (i-1))) )
self ["dpsamt"..i].line:SetWidth (width-20)
end
self.Graphic:SetSize (width - 135, height - 67)
self.Graphic:SetPoint ("topleft", self, "topleft", 108, -35)
end
local chart_panel_add_data = function (self, graphicData, color, name, elapsed_time, lineTexture, smoothLevel, firstIndex)
local f = self
self = self.Graphic
local _data = {}
local max_value = graphicData.max_value
local amount = #graphicData
local scaleW = 1/self:GetWidth()
local content = graphicData
tinsert (content, 1, 0)
tinsert (content, 1, 0)
tinsert (content, #content+1, 0)
tinsert (content, #content+1, 0)
local _i = 3
local graphMaxDps = math.max (self.max_value, max_value)
if (not smoothLevel) then
while (_i <= #content-2) do
local v = (content[_i-2]+content[_i-1]+content[_i]+content[_i+1]+content[_i+2])/5 --> normalize
_data [#_data+1] = {scaleW*(_i-2), v/graphMaxDps} --> x and y coords
_i = _i + 1
end
elseif (smoothLevel == "SHORT") then
while (_i <= #content-2) do
local value = (content[_i] + content[_i+1]) / 2
_data [#_data+1] = {scaleW*(_i-2), value}
_data [#_data+1] = {scaleW*(_i-2), value}
_i = _i + 2
end
elseif (smoothLevel == "SMA") then
reset_SMA()
while (_i <= #content-2) do
local value, is_new_max_value = do_SMA (content[_i], max_value)
if (is_new_max_value) then
max_value = is_new_max_value
end
_data [#_data+1] = {scaleW*(_i-2), value} --> x and y coords
_i = _i + 1
end
elseif (smoothLevel == -1) then
while (_i <= #content-2) do
local current = content[_i]
local minus_2 = content[_i-2] * 0.6
local minus_1 = content[_i-1] * 0.8
local plus_1 = content[_i+1] * 0.8
local plus_2 = content[_i+2] * 0.6
local v = (current + minus_2 + minus_1 + plus_1 + plus_2)/5 --> normalize
_data [#_data+1] = {scaleW*(_i-2), v/graphMaxDps} --> x and y coords
_i = _i + 1
end
elseif (smoothLevel == 1) then
_i = 2
while (_i <= #content-1) do
local v = (content[_i-1]+content[_i]+content[_i+1])/3 --> normalize
_data [#_data+1] = {scaleW*(_i-1), v/graphMaxDps} --> x and y coords
_i = _i + 1
end
elseif (smoothLevel == 2) then
_i = 1
while (_i <= #content) do
local v = content[_i] --> do not normalize
_data [#_data+1] = {scaleW*(_i), v/graphMaxDps} --> x and y coords
_i = _i + 1
end
end
tremove (content, 1)
tremove (content, 1)
tremove (content, #graphicData)
tremove (content, #graphicData)
if (max_value > self.max_value) then
--> normalize previous data
if (self.max_value > 0) then
local normalizePercent = self.max_value / max_value
for dataIndex, Data in ipairs (self.Data) do
local Points = Data.Points
for i = 1, #Points do
Points[i][2] = Points[i][2]*normalizePercent
end
end
end
self.max_value = max_value
f:SetScale (max_value)
end
tinsert (f.GData, {_data, color or line_default_color, lineTexture, max_value, elapsed_time})
if (name) then
f:AddLabel (color or line_default_color, name, "graphic", #f.GData)
end
if (firstIndex) then
if (lineTexture) then
if (not lineTexture:find ("\\") and not lineTexture:find ("//")) then
local path = string.match (debugstack (1, 1, 0), "AddOns\\(.+)LibGraph%-2%.0%.lua")
if path then
lineTexture = "Interface\\AddOns\\" .. path .. lineTexture
else
lineTexture = nil
end
end
end
table.insert (self.Data, 1, {Points = _data, Color = color or line_default_color, lineTexture = lineTexture, ElapsedTime = elapsed_time})
self.NeedsUpdate = true
else
self:AddDataSeries (_data, color or line_default_color, nil, lineTexture)
self.Data [#self.Data].ElapsedTime = elapsed_time
end
local max_time = 0
for _, data in ipairs (self.Data) do
if (data.ElapsedTime > max_time) then
max_time = data.ElapsedTime
end
end
f:SetTime (max_time)
chart_panel_onresize (f)
end
local chart_panel_vlines_on = function (self)
for i = 1, 17 do
local label = self.TimeLabels [i]
label.line:Show()
end
end
local chart_panel_vlines_off = function (self)
for i = 1, 17 do
local label = self.TimeLabels [i]
label.line:Hide()
end
end
local chart_panel_set_title = function (self, title)
self.chart_title.text = title
end
local chart_panel_mousedown = function (self, button)
if (button == "LeftButton" and self.can_move) then
if (not self.isMoving) then
self:StartMoving()
self.isMoving = true
end
elseif (button == "RightButton" and not self.no_right_click_close) then
if (not self.isMoving) then
self:Hide()
end
end
end
local chart_panel_mouseup = function (self, button)
if (button == "LeftButton" and self.isMoving) then
self:StopMovingOrSizing()
self.isMoving = nil
end
end
local chart_panel_hide_close_button = function (self)
self.CloseButton:Hide()
end
local chart_panel_right_click_close = function (self, value)
if (type (value) == "boolean") then
if (value) then
self.no_right_click_close = nil
else
self.no_right_click_close = true
end
end
end
function DF:CreateChartPanel (parent, w, h, name)
if (not name) then
name = "DFPanel" .. DF.PanelCounter
DF.PanelCounter = DF.PanelCounter + 1
end
parent = parent or UIParent
w = w or 800
h = h or 500
local f = CreateFrame ("frame", name, parent, "BackdropTemplate")
f:SetSize (w or 500, h or 400)
f:EnableMouse (true)
f:SetMovable (true)
f:SetScript ("OnMouseDown", chart_panel_mousedown)
f:SetScript ("OnMouseUp", chart_panel_mouseup)
f:SetBackdrop (chart_panel_backdrop)
f:SetBackdropColor (.3, .3, .3, .3)
local c = CreateFrame ("Button", nil, f, "UIPanelCloseButton", "BackdropTemplate")
c:SetWidth (32)
c:SetHeight (32)
c:SetPoint ("TOPRIGHT", f, "TOPRIGHT", -3, -7)
c:SetFrameLevel (f:GetFrameLevel()+1)
c:SetAlpha (0.9)
f.CloseButton = c
local title = DF:NewLabel (f, nil, "$parentTitle", "chart_title", "Chart!", nil, 20, {1, 1, 0})
title:SetPoint ("topleft", f, "topleft", 110, -13)
f.Overlays = {}
f.OverlaysAmount = 1
f.BoxLabels = {}
f.BoxLabelsAmount = 1
f.ShowHeader = true
f.HeaderOnlyIndicator = false
f.HeaderShowOverlays = true
--graphic
local g = LibStub:GetLibrary("LibGraph-2.0"):CreateGraphLine (name .. "Graphic", f, "topleft","topleft", 108, -35, w - 120, h - 67)
g:SetXAxis (-1,1)
g:SetYAxis (-1,1)
g:SetGridSpacing (false, false)
g:SetGridColor ({0.5,0.5,0.5,0.3})
g:SetAxisDrawing (false,false)
g:SetAxisColor({1.0,1.0,1.0,1.0})
g:SetAutoScale (true)
g:SetLineTexture ("smallline")
g:SetBorderSize ("right", 0.001)
g:SetBorderSize ("left", 0.000)
g:SetBorderSize ("top", 0.002)
g:SetBorderSize ("bottom", 0.001)
g.VerticalLines = {}
g.max_value = 0
g:SetLineTexture ("line")
f.Graphic = g
f.GData = {}
f.OData = {}
f.ChartFrames = {}
--div lines
for i = 1, 8, 1 do
local line = g:CreateTexture (nil, "overlay")
line:SetColorTexture (1, 1, 1, .05)
line:SetWidth (670)
line:SetHeight (1.1)
local s = f:CreateFontString (nil, "overlay", "GameFontHighlightSmall")
f ["dpsamt"..i] = s
s:SetText ("100k")
s:SetPoint ("topleft", f, "topleft", 27, -61 + (-(24.6*i)))
line:SetPoint ("topleft", s, "bottom", -27, 0)
line:SetPoint ("topright", g, "right", 0, 0)
s.line = line
end
--create time labels and the bottom texture to use as a background to these labels
f.TimeLabels = {}
f.TimeLabelsHeight = 16
for i = 1, 17 do
local time = f:CreateFontString (nil, "overlay", "GameFontHighlightSmall")
time:SetText ("00:00")
time:SetPoint ("bottomleft", f, "bottomleft", 78 + ((i-1)*36), f.TimeLabelsHeight)
f.TimeLabels [i] = time
local line = f:CreateTexture (nil, "border")
line:SetSize (1, h-45)
line:SetColorTexture (1, 1, 1, .1)
line:SetPoint ("bottomleft", time, "topright", 0, -10)
line:Hide()
time.line = line
end
local bottom_texture = DF:NewImage (f, nil, 702, 25, "background", nil, nil, "$parentBottomTexture")
bottom_texture:SetColorTexture (.1, .1, .1, .7)
bottom_texture:SetPoint ("topright", g, "bottomright", 0, 0)
bottom_texture:SetPoint ("bottomleft", f, "bottomleft", 8, 12)
f.SetTime = chart_panel_align_timelabels
f.EnableVerticalLines = chart_panel_vlines_on
f.DisableVerticalLines = chart_panel_vlines_off
f.SetTitle = chart_panel_set_title
f.SetScale = chart_panel_set_scale
f.Reset = chart_panel_reset
f.AddLine = chart_panel_add_data
f.CanMove = chart_panel_can_move
f.AddLabel = chart_panel_add_label
f.AddOverlay = chart_panel_add_overlay
f.HideCloseButton = chart_panel_hide_close_button
f.RightClickClose = chart_panel_right_click_close
f.CalcStdDev = calc_stddev
f.CalcLowessSmoothing = calc_lowess_smoothing
f:SetScript ("OnSizeChanged", chart_panel_onresize)
chart_panel_onresize (f)
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~gframe
local gframe_on_enter_line = function (self)
self:SetBackdropColor (0, 0, 0, 0)
local parent = self:GetParent()
local ball = self.ball
ball:SetBlendMode ("ADD")
local on_enter = parent._onenter_line
if (on_enter) then
return on_enter (self, parent)
end
end
local gframe_on_leave_line = function (self)
self:SetBackdropColor (0, 0, 0, .6)
local parent = self:GetParent()
local ball = self.ball
ball:SetBlendMode ("BLEND")
local on_leave = parent._onleave_line
if (on_leave) then
return on_leave (self, parent)
end
end
local gframe_create_line = function (self)
local index = #self._lines+1
local f = CreateFrame ("frame", nil, self, "BackdropTemplate")
self._lines [index] = f
f.id = index
f:SetScript ("OnEnter", gframe_on_enter_line)
f:SetScript ("OnLeave", gframe_on_leave_line)
f:SetWidth (self._linewidth)
if (index == 1) then
f:SetPoint ("topleft", self, "topleft")
f:SetPoint ("bottomleft", self, "bottomleft")
else
local previous_line = self._lines [index-1]
f:SetPoint ("topleft", previous_line, "topright")
f:SetPoint ("bottomleft", previous_line, "bottomright")
end
local t = f:CreateTexture (nil, "background")
t:SetWidth (1)
t:SetPoint ("topright", f, "topright")
t:SetPoint ("bottomright", f, "bottomright")
t:SetColorTexture (1, 1, 1, .1)
f.grid = t
local b = f:CreateTexture (nil, "overlay")
b:SetTexture ([[Interface\COMMON\Indicator-Yellow]])
b:SetSize (16, 16)
f.ball = b
local anchor = CreateFrame ("frame", nil, f, "BackdropTemplate")
anchor:SetAllPoints (b)
b.tooltip_anchor = anchor
local spellicon = f:CreateTexture (nil, "artwork")
spellicon:SetPoint ("bottom", b, "bottom", 0, 10)
spellicon:SetSize (16, 16)
f.spellicon = spellicon
local text = f:CreateFontString (nil, "overlay", "GameFontNormal")
local textBackground = f:CreateTexture (nil, "artwork")
textBackground:SetSize (30, 16)
textBackground:SetColorTexture (0, 0, 0, 0.5)
textBackground:SetPoint ("bottom", f.ball, "top", 0, -6)
text:SetPoint ("center", textBackground, "center")
DF:SetFontSize (text, 10)
f.text = text
f.textBackground = textBackground
local timeline = f:CreateFontString (nil, "overlay", "GameFontNormal")
timeline:SetPoint ("bottomright", f, "bottomright", -2, 0)
DF:SetFontSize (timeline, 8)
f.timeline = timeline
return f
end
local gframe_getline = function (self, index)
local line = self._lines [index]
if (not line) then
line = gframe_create_line (self)
end
return line
end
local gframe_reset = function (self)
for i, line in ipairs (self._lines) do
line:Hide()
end
if (self.GraphLib_Lines_Used) then
for i = #self.GraphLib_Lines_Used, 1, -1 do
local line = tremove (self.GraphLib_Lines_Used)
tinsert (self.GraphLib_Lines, line)
line:Hide()
end
end
end
local gframe_update = function (self, lines)
local g = LibStub:GetLibrary ("LibGraph-2.0")
local h = self:GetHeight()/100
local amtlines = #lines
local linewidth = self._linewidth
local max_value = 0
for i = 1, amtlines do
if (lines [i].value > max_value) then
max_value = lines [i].value
end
end
self.MaxValue = max_value
local o = 1
local lastvalue = self:GetHeight()/2
max_value = math.max (max_value, 0.0000001)
for i = 1, min (amtlines, self._maxlines) do
local data = lines [i]
local pvalue = data.value / max_value * 100
if (pvalue > 98) then
pvalue = 98
end
pvalue = pvalue * h
g:DrawLine (self, (o-1)*linewidth, lastvalue, o*linewidth, pvalue, linewidth, {1, 1, 1, 1}, "overlay")
lastvalue = pvalue
local line = self:GetLine (i)
line:Show()
line.ball:Show()
line.ball:SetPoint ("bottomleft", self, "bottomleft", (o*linewidth)-8, pvalue-8)
line.spellicon:SetTexture (nil)
line.timeline:SetText (data.text)
line.timeline:Show()
if (data.utext) then
line.text:Show()
line.textBackground:Show()
line.text:SetText (data.utext)
else
line.text:Hide()
line.textBackground:Hide()
end
line.data = data
o = o + 1
end
end
function DF:CreateGFrame (parent, w, h, linewidth, onenter, onleave, member, name)
local f = CreateFrame ("frame", name, parent, "BackdropTemplate")
f:SetSize (w or 450, h or 150)
--f.CustomLine = [[Interface\AddOns\Details\Libs\LibGraph-2.0\line]]
if (member) then
parent [member] = f
end
f.CreateLine = gframe_create_line
f.GetLine = gframe_getline
f.Reset = gframe_reset
f.UpdateLines = gframe_update
f.MaxValue = 0
f._lines = {}
f._onenter_line = onenter
f._onleave_line = onleave
f._linewidth = linewidth or 50
f._maxlines = floor (f:GetWidth() / f._linewidth)
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~buttoncontainer
function DF:CreateButtonContainer (parent, name)
local f = CreateFrame ("frame", name, parent, "BackdropTemplate")
-- f.
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> options tabs and buttons -dot
function DF:FindHighestParent (self)
local f
if (self:GetParent() == UIParent) then
f = self
end
if (not f) then
f = self
for i = 1, 6 do
local parent = f:GetParent()
if (parent == UIParent) then
break
else
f = parent
end
end
end
return f
end
DF.TabContainerFunctions = {}
local button_tab_template = DF.table.copy ({}, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"))
button_tab_template.backdropbordercolor = nil
DF.TabContainerFunctions.CreateUnderlineGlow = function (button)
local selectedGlow = button:CreateTexture (nil, "background", -4)
selectedGlow:SetPoint ("topleft", button.widget, "bottomleft", -7, 0)
selectedGlow:SetPoint ("topright", button.widget, "bottomright", 7, 0)
selectedGlow:SetTexture ([[Interface\BUTTONS\UI-Panel-Button-Glow]])
selectedGlow:SetTexCoord (0, 95/128, 30/64, 38/64)
selectedGlow:SetBlendMode ("ADD")
selectedGlow:SetHeight (8)
selectedGlow:SetAlpha (.75)
selectedGlow:Hide()
button.selectedUnderlineGlow = selectedGlow
end
DF.TabContainerFunctions.OnMouseDown = function (self, button)
--> search for UIParent
local f = DF:FindHighestParent (self)
local container = self:GetParent()
if (button == "LeftButton") then
if (not f.IsMoving and f:IsMovable()) then
f:StartMoving()
f.IsMoving = true
end
elseif (button == "RightButton") then
if (not f.IsMoving and container.IsContainer) then
if (self.IsFrontPage) then
if (container.CanCloseWithRightClick) then
if (f.CloseFunction) then
f:CloseFunction()
else
f:Hide()
end
end
else
--goes back to front page
DF.TabContainerFunctions.SelectIndex (self, _, 1)
end
end
end
end
DF.TabContainerFunctions.OnMouseUp = function (self, button)
local f = DF:FindHighestParent (self)
if (f.IsMoving) then
f:StopMovingOrSizing()
f.IsMoving = false
end
end
DF.TabContainerFunctions.SelectIndex = function (self, fixedParam, menuIndex)
local mainFrame = self.AllFrames and self or self.mainFrame or self:GetParent()
for i = 1, #mainFrame.AllFrames do
mainFrame.AllFrames[i]:Hide()
if (mainFrame.ButtonNotSelectedBorderColor) then
mainFrame.AllButtons[i]:SetBackdropBorderColor (unpack (mainFrame.ButtonNotSelectedBorderColor))
end
if (mainFrame.AllButtons[i].selectedUnderlineGlow) then
mainFrame.AllButtons[i].selectedUnderlineGlow:Hide()
end
end
mainFrame.AllFrames[menuIndex]:Show()
if mainFrame.AllFrames[menuIndex].RefreshOptions then
mainFrame.AllFrames[menuIndex]:RefreshOptions()
end
if (mainFrame.ButtonSelectedBorderColor) then
mainFrame.AllButtons[menuIndex]:SetBackdropBorderColor (unpack (mainFrame.ButtonSelectedBorderColor))
end
if (mainFrame.AllButtons[menuIndex].selectedUnderlineGlow) then
mainFrame.AllButtons[menuIndex].selectedUnderlineGlow:Show()
end
mainFrame.CurrentIndex = menuIndex
end
DF.TabContainerFunctions.SetIndex = function (self, index)
self.CurrentIndex = index
end
local tab_container_on_show = function (self)
local index = self.CurrentIndex
self.SelectIndex (self.AllButtons[index], nil, index)
end
function DF:CreateTabContainer (parent, title, frame_name, frame_list, options_table)
local options_text_template = DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local options_switch_template = DF:GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE")
local options_slider_template = DF:GetTemplate ("slider", "OPTIONS_SLIDER_TEMPLATE")
local options_button_template = DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE")
options_table = options_table or {}
local frame_width = parent:GetWidth()
local frame_height = parent:GetHeight()
local y_offset = options_table.y_offset or 0
local button_width = options_table.button_width or 160
local button_height = options_table.button_height or 20
local button_anchor_x = options_table.button_x or 230
local button_anchor_y = options_table.button_y or -32
local button_text_size = options_table.button_text_size or 10
local mainFrame = CreateFrame ("frame", frame_name, parent.widget or parent, "BackdropTemplate")
mainFrame:SetAllPoints()
DF:Mixin (mainFrame, DF.TabContainerFunctions)
local mainTitle = DF:CreateLabel (mainFrame, title, 24, "white")
mainTitle:SetPoint ("topleft", mainFrame, "topleft", 10, -30 + y_offset)
mainFrame:SetFrameLevel (200)
mainFrame.AllFrames = {}
mainFrame.AllButtons = {}
mainFrame.CurrentIndex = 1
mainFrame.IsContainer = true
mainFrame.ButtonSelectedBorderColor = options_table.button_selected_border_color or {1, 1, 0, 1}
mainFrame.ButtonNotSelectedBorderColor = options_table.button_border_color or {0, 0, 0, 0}
if (options_table.right_click_interact ~= nil) then
mainFrame.CanCloseWithRightClick = options_table.right_click_interact
else
mainFrame.CanCloseWithRightClick = true
end
for i, frame in ipairs (frame_list) do
local f = CreateFrame ("frame", "$parent" .. frame.name, mainFrame, "BackdropTemplate")
f:SetAllPoints()
f:SetFrameLevel (210)
f:Hide()
local title = DF:CreateLabel (f, frame.title, 16, "silver")
title:SetPoint ("topleft", mainTitle, "bottomleft", 0, 0)
local tabButton = DF:CreateButton (mainFrame, DF.TabContainerFunctions.SelectIndex, button_width, button_height, frame.title, i, nil, nil, nil, nil, false, button_tab_template)
PixelUtil.SetSize (tabButton, button_width, button_height)
tabButton:SetFrameLevel (220)
tabButton.textsize = button_text_size
tabButton.mainFrame = mainFrame
DF.TabContainerFunctions.CreateUnderlineGlow (tabButton)
local right_click_to_back
if (i == 1 or options_table.rightbutton_always_close) then
right_click_to_back = DF:CreateLabel (f, "right click to close", 10, "gray")
right_click_to_back:SetPoint ("bottomright", f, "bottomright", -1, options_table.right_click_y or 0)
if (options_table.close_text_alpha) then
right_click_to_back:SetAlpha (options_table.close_text_alpha)
end
f.IsFrontPage = true
else
right_click_to_back = DF:CreateLabel (f, "right click to go back to main menu", 10, "gray")
right_click_to_back:SetPoint ("bottomright", f, "bottomright", -1, options_table.right_click_y or 0)
if (options_table.close_text_alpha) then
right_click_to_back:SetAlpha (options_table.close_text_alpha)
end
end
if (options_table.hide_click_label) then
right_click_to_back:Hide()
end
f:SetScript ("OnMouseDown", DF.TabContainerFunctions.OnMouseDown)
f:SetScript ("OnMouseUp", DF.TabContainerFunctions.OnMouseUp)
tinsert (mainFrame.AllFrames, f)
tinsert (mainFrame.AllButtons, tabButton)
end
--order buttons
local x = button_anchor_x
local y = button_anchor_y
local space_for_buttons = frame_width - (#frame_list*3) - button_anchor_x
local amount_buttons_per_row = floor (space_for_buttons / button_width)
local last_button = mainFrame.AllButtons[1]
mainFrame.AllButtons[1]:SetPoint ("topleft", mainTitle, "topleft", x, y)
x = x + button_width + 2
for i = 2, #mainFrame.AllButtons do
local button = mainFrame.AllButtons [i]
PixelUtil.SetPoint (button, "topleft", mainTitle, "topleft", x, y)
x = x + button_width + 2
if (i % amount_buttons_per_row == 0) then
x = button_anchor_x
y = y - button_height - 1
end
end
--> when show the frame, reset to the current internal index
mainFrame:SetScript ("OnShow", tab_container_on_show)
--> select the first frame
mainFrame.SelectIndex (mainFrame.AllButtons[1], nil, 1)
return mainFrame
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~listbox
local simple_list_box_ResetWidgets = function (self)
for _, widget in ipairs (self.widgets) do
widget:Hide()
end
self.nextWidget = 1
end
local simple_list_box_onenter = function (self, capsule)
self:GetParent().options.onenter (self, capsule, capsule.value)
end
local simple_list_box_onleave = function (self, capsule)
self:GetParent().options.onleave (self, capsule, capsule.value)
GameTooltip:Hide()
end
local simple_list_box_GetOrCreateWidget = function (self)
local index = self.nextWidget
local widget = self.widgets [index]
if (not widget) then
widget = DF:CreateButton (self, function()end, self.options.width, self.options.row_height, "", nil, nil, nil, nil, nil, nil, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"))
widget:SetHook ("OnEnter", simple_list_box_onenter)
widget:SetHook ("OnLeave", simple_list_box_onleave)
widget.textcolor = self.options.textcolor
widget.textsize = self.options.text_size
widget.onleave_backdrop = self.options.backdrop_color
widget.XButton = DF:CreateButton (widget, function()end, 16, 16)
widget.XButton:SetPoint ("topright", widget.widget, "topright")
widget.XButton:SetIcon ([[Interface\BUTTONS\UI-Panel-MinimizeButton-Up]], 16, 16, "overlay", nil, nil, 0, -4, 0, false)
widget.XButton.icon:SetDesaturated (true)
if (not self.options.show_x_button) then
widget.XButton:Hide()
end
tinsert (self.widgets, widget)
end
self.nextWidget = self.nextWidget + 1
return widget
end
local simple_list_box_RefreshWidgets = function (self)
self:ResetWidgets()
local amt = 0
for value, _ in pairs (self.list_table) do
local widget = self:GetOrCreateWidget()
widget:SetPoint ("topleft", self, "topleft", 1, -self.options.row_height * (self.nextWidget-2) - 4)
widget:SetPoint ("topright", self, "topright", -1, -self.options.row_height * (self.nextWidget-2) - 4)
widget:SetClickFunction (self.func, value)
if (self.options.show_x_button) then
widget.XButton:SetClickFunction (self.options.x_button_func, value)
widget.XButton.value = value
widget.XButton:Show()
else
widget.XButton:Hide()
end
widget.value = value
if (self.options.icon) then
if (type (self.options.icon) == "string" or type (self.options.icon) == "number") then
local coords = type (self.options.iconcoords) == "table" and self.options.iconcoords or {0, 1, 0, 1}
widget:SetIcon (self.options.icon, self.options.row_height - 2, self.options.row_height - 2, "overlay", coords)
elseif (type (self.options.icon) == "function") then
local icon = self.options.icon (value)
if (icon) then
local coords = type (self.options.iconcoords) == "table" and self.options.iconcoords or {0, 1, 0, 1}
widget:SetIcon (icon, self.options.row_height - 2, self.options.row_height - 2, "overlay", coords)
end
end
else
widget:SetIcon ("", self.options.row_height, self.options.row_height)
end
if (self.options.text) then
if (type (self.options.text) == "function") then
local text = self.options.text (value)
if (text) then
widget:SetText (text)
else
widget:SetText ("")
end
else
widget:SetText (self.options.text or "")
end
else
widget:SetText ("")
end
widget.value = value
local r, g, b, a = DF:ParseColors (self.options.backdrop_color)
widget:SetBackdropColor (r, g, b, a)
widget:Show()
amt = amt + 1
end
if (amt == 0) then
self.EmptyLabel:Show()
else
self.EmptyLabel:Hide()
end
end
local backdrop = {bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1}
local default_options = {
height = 400,
row_height = 16,
width = 230,
icon = false,
text = "",
text_size = 10,
textcolor = "wheat",
backdrop_color = {1, 1, 1, .5},
panel_border_color = {0, 0, 0, 0.5},
onenter = function (self, capsule)
if (capsule) then
capsule.textcolor = "white"
end
end,
onleave = function (self, capsule)
if (capsule) then
capsule.textcolor = self:GetParent().options.textcolor
end
GameTooltip:Hide()
end,
}
local simple_list_box_SetData = function (self, t)
self.list_table = t
end
function DF:CreateSimpleListBox (parent, name, title, empty_text, list_table, onclick, options)
local f = CreateFrame ("frame", name, parent, "BackdropTemplate")
f.ResetWidgets = simple_list_box_ResetWidgets
f.GetOrCreateWidget = simple_list_box_GetOrCreateWidget
f.Refresh = simple_list_box_RefreshWidgets
f.SetData = simple_list_box_SetData
f.nextWidget = 1
f.list_table = list_table
f.func = function (self, button, value)
--onclick (value)
DF:QuickDispatch (onclick, value)
f:Refresh()
end
f.widgets = {}
DF:ApplyStandardBackdrop (f)
f.options = options or {}
self.table.deploy (f.options, default_options)
if (f.options.x_button_func) then
local original_X_function = f.options.x_button_func
f.options.x_button_func = function (self, button, value)
DF:QuickDispatch (original_X_function, value)
f:Refresh()
end
end
f:SetBackdropBorderColor (unpack (f.options.panel_border_color))
f:SetSize (f.options.width + 2, f.options.height)
local name = DF:CreateLabel (f, title, 12, "silver")
name:SetTemplate (DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
name:SetPoint ("bottomleft", f, "topleft", 0, 2)
f.Title = name
local emptyLabel = DF:CreateLabel (f, empty_text, 12, "gray")
emptyLabel:SetAlpha (.6)
emptyLabel:SetSize (f.options.width-10, f.options.height)
emptyLabel:SetPoint ("center", 0, 0)
emptyLabel:Hide()
emptyLabel.align = "center"
f.EmptyLabel = emptyLabel
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~scrollbox
DF.SortFunctions = {}
local SortMember = ""
local SortByMember = function (t1, t2)
return t1[SortMember] > t2[SortMember]
end
local SortByMemberReverse = function (t1, t2)
return t1[SortMember] < t2[SortMember]
end
DF.SortFunctions.Sort = function (self, t, by, is_reverse)
SortMember = by
if (not is_reverse) then
table.sort (t, SortByMember)
else
table.sort (t, SortByMemberReverse)
end
end
DF.ScrollBoxFunctions = {}
DF.ScrollBoxFunctions.Refresh = function (self)
for _, frame in ipairs (self.Frames) do
frame:Hide()
frame._InUse = nil
end
local offset = 0
if (self.IsFauxScroll) then
FauxScrollFrame_Update (self, #self.data, self.LineAmount, self.LineHeight)
offset = FauxScrollFrame_GetOffset (self)
end
DF:CoreDispatch ((self:GetName() or "ScrollBox") .. ":Refresh()", self.refresh_func, self, self.data, offset, self.LineAmount)
for _, frame in ipairs (self.Frames) do
if (not frame._InUse) then
frame:Hide()
else
frame:Show()
end
end
self:Show()
if (self.HideScrollBar) then
local frameName = self:GetName()
if (frameName) then
local scrollBar = _G [frameName .. "ScrollBar"]
if (scrollBar) then
scrollBar:Hide()
end
else
end
end
return self.Frames
end
DF.ScrollBoxFunctions.OnVerticalScroll = function (self, offset)
FauxScrollFrame_OnVerticalScroll (self, offset, self.LineHeight, self.Refresh)
return true
end
DF.ScrollBoxFunctions.CreateLine = function (self, func)
if (not func) then
func = self.CreateLineFunc
end
local okay, newLine = pcall (func, self, #self.Frames+1)
if (okay) then
tinsert (self.Frames, newLine)
newLine.Index = #self.Frames
return newLine
else
error ("Details! FrameWork: CreateLine(): " .. newLine)
end
end
DF.ScrollBoxFunctions.GetLine = function (self, line_index)
local line = self.Frames [line_index]
if (line) then
line._InUse = true
end
return line
end
DF.ScrollBoxFunctions.SetData = function (self, data)
self.data = data
end
DF.ScrollBoxFunctions.GetData = function (self)
return self.data
end
DF.ScrollBoxFunctions.GetFrames = function (self)
return self.Frames
end
DF.ScrollBoxFunctions.GetNumFramesCreated = function (self)
return #self.Frames
end
DF.ScrollBoxFunctions.GetNumFramesShown = function (self)
return self.LineAmount
end
DF.ScrollBoxFunctions.SetNumFramesShown = function (self, new_amount)
--> hide frames which won't be used
if (new_amount < #self.Frames) then
for i = new_amount+1, #self.Frames do
self.Frames [i]:Hide()
end
end
--> set the new amount
self.LineAmount = new_amount
end
DF.ScrollBoxFunctions.SetFramesHeight = function (self, new_height)
self.LineHeight = new_height
self:OnSizeChanged()
self:Refresh()
end
DF.ScrollBoxFunctions.OnSizeChanged = function (self)
if (self.ReajustNumFrames) then
--> how many lines the scroll can show
local amountOfFramesToShow = floor (self:GetHeight() / self.LineHeight)
--> how many lines the scroll already have
local totalFramesCreated = self:GetNumFramesCreated()
--> how many lines are current shown
local totalFramesShown = self:GetNumFramesShown()
--> the amount of frames increased
if (amountOfFramesToShow > totalFramesShown) then
for i = totalFramesShown+1, amountOfFramesToShow do
--> check if need to create a new line
if (i > totalFramesCreated) then
self:CreateLine (self.CreateLineFunc)
end
end
--> the amount of frames decreased
elseif (amountOfFramesToShow < totalFramesShown) then
--> hide all frames above the new amount to show
for i = totalFramesCreated, amountOfFramesToShow, -1 do
if (self.Frames [i]) then
self.Frames [i]:Hide()
end
end
end
--> set the new amount of frames
self:SetNumFramesShown (amountOfFramesToShow)
--> refresh lines
self:Refresh()
end
end
function DF:CreateScrollBox (parent, name, refresh_func, data, width, height, line_amount, line_height, create_line_func, auto_amount, no_scroll)
local scroll = CreateFrame ("scrollframe", name, parent, "FauxScrollFrameTemplate,BackdropTemplate")
DF:ApplyStandardBackdrop (scroll)
scroll:SetSize (width, height)
scroll.LineAmount = line_amount
scroll.LineHeight = line_height
scroll.IsFauxScroll = true
scroll.HideScrollBar = no_scroll
scroll.Frames = {}
scroll.ReajustNumFrames = auto_amount
scroll.CreateLineFunc = create_line_func
DF:Mixin (scroll, DF.SortFunctions)
DF:Mixin (scroll, DF.ScrollBoxFunctions)
scroll.refresh_func = refresh_func
scroll.data = data
scroll:SetScript ("OnVerticalScroll", scroll.OnVerticalScroll)
scroll:SetScript ("OnSizeChanged", DF.ScrollBoxFunctions.OnSizeChanged)
return scroll
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~resizers
function DF:CreateResizeGrips (parent)
if (parent) then
local parentName = parent:GetName()
local leftResizer = CreateFrame ("button", parentName and parentName .. "LeftResizer" or nil, parent, "BackdropTemplate")
local rightResizer = CreateFrame ("button", parentName and parentName .. "RightResizer" or nil, parent, "BackdropTemplate")
leftResizer:SetPoint ("bottomleft", parent, "bottomleft")
rightResizer:SetPoint ("bottomright", parent, "bottomright")
leftResizer:SetSize (16, 16)
rightResizer:SetSize (16, 16)
rightResizer:SetNormalTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Up]])
rightResizer:SetHighlightTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Highlight]])
rightResizer:SetPushedTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Down]])
leftResizer:SetNormalTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Up]])
leftResizer:SetHighlightTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Highlight]])
leftResizer:SetPushedTexture ([[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Down]])
leftResizer:GetNormalTexture():SetTexCoord (1, 0, 0, 1)
leftResizer:GetHighlightTexture():SetTexCoord (1, 0, 0, 1)
leftResizer:GetPushedTexture():SetTexCoord (1, 0, 0, 1)
return leftResizer, rightResizer
end
end
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~keybind
--------------------------------
--> keybind frame ~key
local ignoredKeys = {
["LSHIFT"] = true,
["RSHIFT"] = true,
["LCTRL"] = true,
["RCTRL"] = true,
["LALT"] = true,
["RALT"] = true,
["UNKNOWN"] = true,
}
local mouseKeys = {
["LeftButton"] = "type1",
["RightButton"] = "type2",
["MiddleButton"] = "type3",
["Button4"] = "type4",
["Button5"] = "type5",
["Button6"] = "type6",
["Button7"] = "type7",
["Button8"] = "type8",
["Button9"] = "type9",
["Button10"] = "type10",
["Button11"] = "type11",
["Button12"] = "type12",
["Button13"] = "type13",
["Button14"] = "type14",
["Button15"] = "type15",
["Button16"] = "type16",
}
local keysToMouse = {
["type1"] = "LeftButton",
["type2"] = "RightButton",
["type3"] = "MiddleButton",
["type4"] = "Button4",
["type5"] = "Button5",
["type6"] = "Button6",
["type7"] = "Button7",
["type8"] = "Button8",
["type9"] = "Button9",
["type10"] = "Button10",
["type11"] = "Button11",
["type12"] = "Button12",
["type13"] = "Button13",
["type14"] = "Button14",
["type15"] = "Button15",
["type16"] = "Button16",
}
local keybind_set_data = function (self, new_data_table)
self.Data = new_data_table
self.keybindScroll:UpdateScroll()
end
function DF:CreateKeybindBox (parent, name, data, callback, width, height, line_amount, line_height)
local options_text_template = DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local options_switch_template = DF:GetTemplate ("switch", "OPTIONS_CHECKBOX_TEMPLATE")
local options_slider_template = DF:GetTemplate ("slider", "OPTIONS_SLIDER_TEMPLATE")
local options_button_template = DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE")
local SCROLL_ROLL_AMOUNT = line_amount
--keybind set frame
local new_keybind_frame = CreateFrame ("frame", name, parent, "BackdropTemplate")
new_keybind_frame:SetSize (width, height)
-- keybind scrollframe
local keybindScroll = CreateFrame ("scrollframe", "$parentScrollFrame", new_keybind_frame, "FauxScrollFrameTemplate, BackdropTemplate")
keybindScroll:SetSize (1019, 348)
keybindScroll.Frames = {}
new_keybind_frame.keybindScroll = keybindScroll
--waiting the player to press a key
new_keybind_frame.IsListening = false
--check for valid data table
if (type (data) ~= "table") then
print ("error: data must be a table. DF > CreateKeybindBox()")
return
end
if (not next (data)) then
--> build data table for the character class
local _, unitClass = UnitClass ("player")
if (unitClass) then
local specIds = DF:GetClassSpecIDs (unitClass)
if (specIds) then
for _, specId in ipairs (specIds) do
data [specId] = {}
end
end
end
end
new_keybind_frame.Data = data
new_keybind_frame.SetData = keybind_set_data
new_keybind_frame.EditingSpec = DF:GetCurrentSpec()
new_keybind_frame.CurrentKeybindEditingSet = new_keybind_frame.Data [new_keybind_frame.EditingSpec]
local allSpecButtons = {}
local switch_spec = function (self, button, specID)
new_keybind_frame.EditingSpec = specID
new_keybind_frame.CurrentKeybindEditingSet = new_keybind_frame.Data [specID]
for _, button in ipairs (allSpecButtons) do
button.selectedTexture:Hide()
end
self.MyObject.selectedTexture:Show()
--feedback ao jogador uma vez que as keybinds podem ter o mesmo valor
C_Timer.After (.04, function() new_keybind_frame:Hide() end)
C_Timer.After (.06, function() new_keybind_frame:Show() end)
--atualiza a scroll
keybindScroll:UpdateScroll()
end
--choose which spec to use
local spec1 = DF:CreateButton (new_keybind_frame, switch_spec, 160, 20, "Spec1 Placeholder Text", 1, _, _, "SpecButton1", _, 0, options_button_template, options_text_template)
local spec2 = DF:CreateButton (new_keybind_frame, switch_spec, 160, 20, "Spec2 Placeholder Text", 1, _, _, "SpecButton2", _, 0, options_button_template, options_text_template)
local spec3 = DF:CreateButton (new_keybind_frame, switch_spec, 160, 20, "Spec3 Placeholder Text", 1, _, _, "SpecButton3", _, 0, options_button_template, options_text_template)
local spec4 = DF:CreateButton (new_keybind_frame, switch_spec, 160, 20, "Spec4 Placeholder Text", 1, _, _, "SpecButton4", _, 0, options_button_template, options_text_template)
--format the button label and icon with the spec information
local className, class = UnitClass ("player")
local i = 1
local specIds = DF:GetClassSpecIDs (class)
for index, specId in ipairs (specIds) do
local button = new_keybind_frame ["SpecButton" .. index]
local spec_id, spec_name, spec_description, spec_icon, spec_background, spec_role, spec_class = DetailsFramework.GetSpecializationInfoByID (specId)
button.text = spec_name
button:SetClickFunction (switch_spec, specId)
button:SetIcon (spec_icon)
button.specID = specId
local selectedTexture = button:CreateTexture (nil, "background")
selectedTexture:SetAllPoints()
selectedTexture:SetColorTexture (1, 1, 1, 0.5)
if (specId ~= new_keybind_frame.EditingSpec) then
selectedTexture:Hide()
end
button.selectedTexture = selectedTexture
tinsert (allSpecButtons, button)
i = i + 1
end
local specsTitle = DF:CreateLabel (new_keybind_frame, "Config keys for spec:", 12, "silver")
specsTitle:SetPoint ("topleft", new_keybind_frame, "topleft", 10, mainStartY)
keybindScroll:SetPoint ("topleft", specsTitle.widget, "bottomleft", 0, -120)
spec1:SetPoint ("topleft", specsTitle, "bottomleft", 0, -10)
spec2:SetPoint ("topleft", specsTitle, "bottomleft", 0, -30)
spec3:SetPoint ("topleft", specsTitle, "bottomleft", 0, -50)
if (class == "DRUID") then
spec4:SetPoint ("topleft", specsTitle, "bottomleft", 0, -70)
end
local enter_the_key = CreateFrame ("frame", nil, new_keybind_frame, "BackdropTemplate")
enter_the_key:SetFrameStrata ("tooltip")
enter_the_key:SetSize (200, 60)
enter_the_key:SetBackdrop ({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
enter_the_key:SetBackdropColor (0, 0, 0, 1)
enter_the_key:SetBackdropBorderColor (1, 1, 1, 1)
enter_the_key.text = DF:CreateLabel (enter_the_key, "- Press a keyboard key to bind.\n- Click to bind a mouse button.\n- Press escape to cancel.", 11, "orange")
enter_the_key.text:SetPoint ("center", enter_the_key, "center")
enter_the_key:Hide()
local registerKeybind = function (self, key)
if (ignoredKeys [key]) then
return
end
if (key == "ESCAPE") then
enter_the_key:Hide()
new_keybind_frame.IsListening = false
new_keybind_frame:SetScript ("OnKeyDown", nil)
return
end
local bind = (IsShiftKeyDown() and "SHIFT-" or "") .. (IsControlKeyDown() and "CTRL-" or "") .. (IsAltKeyDown() and "ALT-" or "")
bind = bind .. key
--adiciona para a tabela de keybinds
local keybind = new_keybind_frame.CurrentKeybindEditingSet [self.keybindIndex]
keybind.key = bind
new_keybind_frame.IsListening = false
new_keybind_frame:SetScript ("OnKeyDown", nil)
enter_the_key:Hide()
new_keybind_frame.keybindScroll:UpdateScroll()
DF:QuickDispatch (callback)
end
local set_keybind_key = function (self, button, keybindIndex)
if (new_keybind_frame.IsListening) then
key = mouseKeys [button] or button
return registerKeybind (new_keybind_frame, key)
end
new_keybind_frame.IsListening = true
new_keybind_frame.keybindIndex = keybindIndex
new_keybind_frame:SetScript ("OnKeyDown", registerKeybind)
enter_the_key:Show()
enter_the_key:SetPoint ("bottom", self, "top")
end
local new_key_bind = function (self, button, specID)
tinsert (new_keybind_frame.CurrentKeybindEditingSet, {key = "-none-", action = "_target", actiontext = ""})
FauxScrollFrame_SetOffset (new_keybind_frame.keybindScroll, max (#new_keybind_frame.CurrentKeybindEditingSet-SCROLL_ROLL_AMOUNT, 0))
new_keybind_frame.keybindScroll:UpdateScroll()
end
local set_action_text = function (keybindIndex, _, text)
local keybind = new_keybind_frame.CurrentKeybindEditingSet [keybindIndex]
keybind.actiontext = text
DF:QuickDispatch (callback)
end
local set_action_on_espace_press = function (textentry, capsule)
capsule = capsule or textentry.MyObject
local keybind = new_keybind_frame.CurrentKeybindEditingSet [capsule.CurIndex]
textentry:SetText (keybind.actiontext)
DF:QuickDispatch (callback)
end
local lock_textentry = {
["_target"] = true,
["_taunt"] = true,
["_interrupt"] = true,
["_dispel"] = true,
["_spell"] = false,
["_macro"] = false,
}
local change_key_action = function (self, keybindIndex, value)
local keybind = new_keybind_frame.CurrentKeybindEditingSet [keybindIndex]
keybind.action = value
new_keybind_frame.keybindScroll:UpdateScroll()
DF:QuickDispatch (callback)
end
local fill_action_dropdown = function()
local locClass, class = UnitClass ("player")
local taunt = ""
local interrupt = ""
local dispel = ""
if (type (dispel) == "table") then
local dispelString = "\n"
for specID, spellid in pairs (dispel) do
local specid, specName = DetailsFramework.GetSpecializationInfoByID (specID)
local spellName = GetSpellInfo (spellid)
dispelString = dispelString .. "|cFFE5E5E5" .. (specName or "") .. "|r: |cFFFFFFFF" .. spellName .. "\n"
end
dispel = dispelString
else
dispel = ""
end
return {
--{value = "_target", label = "Target", onclick = change_key_action, desc = "Target the unit"},
--{value = "_taunt", label = "Taunt", onclick = change_key_action, desc = "Cast the taunt spell for your class\n\n|cFFFFFFFFSpell: " .. taunt},
--{value = "_interrupt", label = "Interrupt", onclick = change_key_action, desc = "Cast the interrupt spell for your class\n\n|cFFFFFFFFSpell: " .. interrupt},
--{value = "_dispel", label = "Dispel", onclick = change_key_action, desc = "Cast the interrupt spell for your class\n\n|cFFFFFFFFSpell: " .. dispel},
{value = "_spell", label = "Cast Spell", onclick = change_key_action, desc = "Type the spell name in the text box"},
{value = "_macro", label = "Run Macro", onclick = change_key_action, desc = "Type your macro in the text box"},
}
end
local copy_keybind = function (self, button, keybindIndex)
local keybind = new_keybind_frame.CurrentKeybindEditingSet [keybindIndex]
for specID, t in pairs (new_keybind_frame.Data) do
if (specID ~= new_keybind_frame.EditingSpec) then
local key = CopyTable (keybind)
local specid, specName = DetailsFramework.GetSpecializationInfoByID (specID)
tinsert (new_keybind_frame.Data [specID], key)
DF:Msg ("Keybind copied to " .. (specName or ""))
end
end
DF:QuickDispatch (callback)
end
local delete_keybind = function (self, button, keybindIndex)
tremove (new_keybind_frame.CurrentKeybindEditingSet, keybindIndex)
new_keybind_frame.keybindScroll:UpdateScroll()
DF:QuickDispatch (callback)
end
local newTitle = DF:CreateLabel (new_keybind_frame, "Create a new Keybind:", 12, "silver")
newTitle:SetPoint ("topleft", new_keybind_frame, "topleft", 200, mainStartY)
local createNewKeybind = DF:CreateButton (new_keybind_frame, new_key_bind, 160, 20, "New Key Bind", 1, _, _, "NewKeybindButton", _, 0, options_button_template, options_text_template)
createNewKeybind:SetPoint ("topleft", newTitle, "bottomleft", 0, -10)
--createNewKeybind:SetIcon ([[Interface\Buttons\UI-GuildButton-PublicNote-Up]])
local update_keybind_list = function (self)
local keybinds = new_keybind_frame.CurrentKeybindEditingSet
FauxScrollFrame_Update (self, #keybinds, SCROLL_ROLL_AMOUNT, 21)
local offset = FauxScrollFrame_GetOffset (self)
for i = 1, SCROLL_ROLL_AMOUNT do
local index = i + offset
local f = self.Frames [i]
local data = keybinds [index]
if (data) then
--index
f.Index.text = index
--keybind
local keyBindText = keysToMouse [data.key] or data.key
keyBindText = keyBindText:gsub ("type1", "LeftButton")
keyBindText = keyBindText:gsub ("type2", "RightButton")
keyBindText = keyBindText:gsub ("type3", "MiddleButton")
f.KeyBind.text = keyBindText
f.KeyBind:SetClickFunction (set_keybind_key, index, nil, "left")
f.KeyBind:SetClickFunction (set_keybind_key, index, nil, "right")
--action
f.ActionDrop:SetFixedParameter (index)
f.ActionDrop:Select (data.action)
--action text
f.ActionText.text = data.actiontext
f.ActionText:SetEnterFunction (set_action_text, index)
f.ActionText.CurIndex = index
if (lock_textentry [data.action]) then
f.ActionText:Disable()
else
f.ActionText:Enable()
end
--copy
f.Copy:SetClickFunction (copy_keybind, index)
--delete
f.Delete:SetClickFunction (delete_keybind, index)
f:Show()
else
f:Hide()
end
end
self:Show()
end
keybindScroll:SetScript ("OnVerticalScroll", function (self, offset)
FauxScrollFrame_OnVerticalScroll (self, offset, 21, update_keybind_list)
end)
keybindScroll.UpdateScroll = update_keybind_list
local backdropColor = {.3, .3, .3, .3}
local backdropColorOnEnter = {.6, .6, .6, .6}
local on_enter = function (self)
self:SetBackdropColor (unpack (backdropColorOnEnter))
end
local on_leave = function (self)
self:SetBackdropColor (unpack (backdropColor))
end
local font = "GameFontHighlightSmall"
for i = 1, SCROLL_ROLL_AMOUNT do
local f = CreateFrame ("frame", "$KeyBindFrame" .. i, keybindScroll, "BackdropTemplate")
f:SetSize (1009, 20)
f:SetPoint ("topleft", keybindScroll, "topleft", 0, -(i-1)*29)
f:SetBackdrop ({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
f:SetBackdropColor (unpack (backdropColor))
f:SetScript ("OnEnter", on_enter)
f:SetScript ("OnLeave", on_leave)
tinsert (keybindScroll.Frames, f)
f.Index = DF:CreateLabel (f, "1")
f.KeyBind = DF:CreateButton (f, set_key_bind, 100, 20, "", _, _, _, "SetNewKeybindButton", _, 0, options_button_template, options_text_template)
f.ActionDrop = DF:CreateDropDown (f, fill_action_dropdown, 0, 120, 20, "ActionDropdown", _, options_dropdown_template)
f.ActionText = DF:CreateTextEntry (f, function()end, 660, 20, "TextBox", _, _, options_dropdown_template)
f.Copy = DF:CreateButton (f, copy_keybind, 20, 20, "", _, _, _, "CopyKeybindButton", _, 0, options_button_template, options_text_template)
f.Delete = DF:CreateButton (f, delete_keybind, 16, 20, "", _, _, _, "DeleteKeybindButton", _, 2, options_button_template, options_text_template)
f.Index:SetPoint ("left", f, "left", 10, 0)
f.KeyBind:SetPoint ("left", f, "left", 43, 0)
f.ActionDrop:SetPoint ("left", f, "left", 150, 0)
f.ActionText:SetPoint ("left", f, "left", 276, 0)
f.Copy:SetPoint ("left", f, "left", 950, 0)
f.Delete:SetPoint ("left", f, "left", 990, 0)
f.Copy:SetIcon ([[Interface\Buttons\UI-GuildButton-PublicNote-Up]], nil, nil, nil, nil, nil, nil, 4)
f.Delete:SetIcon ([[Interface\Buttons\UI-StopButton]], nil, nil, nil, nil, nil, nil, 4)
f.Copy.tooltip = "copy this keybind to other specs"
f.Delete.tooltip = "erase this keybind"
--editbox
f.ActionText:SetJustifyH ("left")
f.ActionText:SetHook ("OnEscapePressed", set_action_on_espace_press)
f.ActionText:SetHook ("OnEditFocusGained", function()
local playerSpells = {}
local tab, tabTex, offset, numSpells = GetSpellTabInfo (2)
for i = 1, numSpells do
local index = offset + i
local spellType, spellId = GetSpellBookItemInfo (index, "player")
if (spellType == "SPELL") then
local spellName = GetSpellInfo (spellId)
tinsert (playerSpells, spellName)
end
end
f.ActionText.WordList = playerSpells
end)
f.ActionText:SetAsAutoComplete ("WordList")
end
local header = CreateFrame ("frame", "$parentOptionsPanelFrameHeader", keybindScroll, "BackdropTemplate")
header:SetPoint ("bottomleft", keybindScroll, "topleft", 0, 2)
header:SetPoint ("bottomright", keybindScroll, "topright", 0, 2)
header:SetHeight (16)
header.Index = DF:CreateLabel (header, "Index", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Key = DF:CreateLabel (header, "Key", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Action = DF:CreateLabel (header, "Action", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Macro = DF:CreateLabel (header, "Spell Name / Macro", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Copy = DF:CreateLabel (header, "Copy", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Delete = DF:CreateLabel (header, "Delete", DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE"))
header.Index:SetPoint ("left", header, "left", 10, 0)
header.Key:SetPoint ("left", header, "left", 43, 0)
header.Action:SetPoint ("left", header, "left", 150, 0)
header.Macro:SetPoint ("left", header, "left", 276, 0)
header.Copy:SetPoint ("left", header, "left", 950, 0)
header.Delete:SetPoint ("left", header, "left", 990, 0)
new_keybind_frame:SetScript ("OnShow", function()
--new_keybind_frame.EditingSpec = EnemyGrid.CurrentSpec
--new_keybind_frame.CurrentKeybindEditingSet = EnemyGrid.CurrentKeybindSet
for _, button in ipairs (allSpecButtons) do
if (new_keybind_frame.EditingSpec ~= button.specID) then
button.selectedTexture:Hide()
else
button.selectedTexture:Show()
end
end
keybindScroll:UpdateScroll()
end)
new_keybind_frame:SetScript ("OnHide", function()
if (new_keybind_frame.IsListening) then
new_keybind_frame.IsListening = false
new_keybind_frame:SetScript ("OnKeyDown", nil)
end
end)
return new_keybind_frame
end
function DF:BuildKeybindFunctions (data, prefix)
--~keybind
local classLoc, class = UnitClass ("player")
local bindingList = data
local bindString = "self:ClearBindings()"
local bindKeyBindTypeFunc = [[local unitFrame = ...]]
local bindMacroTextFunc = [[local unitFrame = ...]]
local isMouseBinding
for i = 1, #bindingList do
local bind = bindingList [i]
local bindType
--which button to press
if (bind.key:find ("type")) then
local keyNumber = tonumber (bind.key:match ("%d"))
bindType = keyNumber
isMouseBinding = true
else
bindType = prefix .. "" .. i
bindString = bindString .. "self:SetBindingClick (0, '" .. bind.key .. "', self:GetName(), '" .. bindType .. "')"
bindType = "-" .. prefix .. "" .. i
isMouseBinding = nil
end
--keybind type
local shift, alt, ctrl = bind.key:match ("SHIFT"), bind.key:match ("ALT"), bind.key:match ("CTRL")
local CommandKeys = alt and alt .. "-" or ""
CommandKeys = ctrl and CommandKeys .. ctrl .. "-" or CommandKeys
CommandKeys = shift and CommandKeys .. shift .. "-" or CommandKeys
local keyBindType
if (isMouseBinding) then
keyBindType = [[unitFrame:SetAttribute ("@COMMANDtype@BINDTYPE", "macro")]]
else
keyBindType = [[unitFrame:SetAttribute ("type@BINDTYPE", "macro")]]
end
keyBindType = keyBindType:gsub ("@BINDTYPE", bindType)
keyBindType = keyBindType:gsub ("@COMMAND", CommandKeys)
bindKeyBindTypeFunc = bindKeyBindTypeFunc .. keyBindType
--spell or macro
if (bind.action == "_spell") then
local macroTextLine
if (isMouseBinding) then
macroTextLine = [[unitFrame:SetAttribute ("@COMMANDmacrotext@BINDTYPE", "/cast [@mouseover] @SPELL")]]
else
macroTextLine = [[unitFrame:SetAttribute ("macrotext@BINDTYPE", "/cast [@mouseover] @SPELL")]]
end
macroTextLine = macroTextLine:gsub ("@BINDTYPE", bindType)
macroTextLine = macroTextLine:gsub ("@SPELL", bind.actiontext)
macroTextLine = macroTextLine:gsub ("@COMMAND", CommandKeys)
bindMacroTextFunc = bindMacroTextFunc .. macroTextLine
elseif (bind.action == "_macro") then
local macroTextLine
if (isMouseBinding) then
macroTextLine = [[unitFrame:SetAttribute ("@COMMANDmacrotext@BINDTYPE", "@MACRO")]]
else
macroTextLine = [[unitFrame:SetAttribute ("macrotext@BINDTYPE", "@MACRO")]]
end
macroTextLine = macroTextLine:gsub ("@BINDTYPE", bindType)
macroTextLine = macroTextLine:gsub ("@MACRO", bind.actiontext)
macroTextLine = macroTextLine:gsub ("@COMMAND", CommandKeys)
bindMacroTextFunc = bindMacroTextFunc .. macroTextLine
end
end
--~key
local bindTypeFuncLoaded = loadstring (bindKeyBindTypeFunc)
local bindMacroFuncLoaded = loadstring (bindMacroTextFunc)
if (not bindMacroFuncLoaded or not bindTypeFuncLoaded) then
return
end
return bindString, bindTypeFuncLoaded, bindMacroFuncLoaded
end
function DF:SetKeybindsOnProtectedFrame (frame, bind_string, bind_type_func, bind_macro_func)
bind_type_func (frame)
bind_macro_func (frame)
frame:SetAttribute ("_onenter", bind_string)
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~standard backdrop
function DF:ApplyStandardBackdrop (f, darkTheme, alphaScale)
alphaScale = alphaScale or 1.0
if(not f.SetBackdrop)then
print(debugstack(1,2,1))
end
if (darkTheme) then
f:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Cooldown\cooldown2]], tileSize = 32, tile = true})
f:SetBackdropBorderColor (0, 0, 0, 1)
f:SetBackdropColor (.54, .54, .54, .54 * alphaScale)
else
f:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true})
f:SetBackdropBorderColor (0, 0, 0, 1)
f:SetBackdropColor (0, 0, 0, 0.2 * alphaScale)
end
if (not f.__background) then
f.__background = f:CreateTexture (nil, "background")
end
f.__background:SetColorTexture (0.2317647, 0.2317647, 0.2317647)
f.__background:SetVertexColor (0.27, 0.27, 0.27)
f.__background:SetAlpha (0.8 * alphaScale)
f.__background:SetVertTile (true)
f.__background:SetHorizTile (true)
f.__background:SetAllPoints()
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~title bar
DF.TitleFunctions = {
SetTitle = function (self, titleText, titleColor, font, size)
self.TitleLabel:SetText (titleText or self.TitleLabel:GetText())
if (titleColor) then
local r, g, b, a = DF:ParseColors (titleColor)
self.TitleLabel:SetTextColor (r, g, b, a)
end
if (font) then
DF:SetFontFace (self.TitleLabel, font)
end
if (size) then
DF:SetFontSize (self.TitleLabel, size)
end
end
}
function DF:CreateTitleBar (f, titleText)
local titleBar = CreateFrame ("frame", f:GetName() and f:GetName() .. "TitleBar" or nil, f,"BackdropTemplate")
titleBar:SetPoint ("topleft", f, "topleft", 2, -3)
titleBar:SetPoint ("topright", f, "topright", -2, -3)
titleBar:SetHeight (20)
titleBar:SetBackdrop (SimplePanel_frame_backdrop) --it's an upload from this file
titleBar:SetBackdropColor (.2, .2, .2, 1)
titleBar:SetBackdropBorderColor (0, 0, 0, 1)
local closeButton = CreateFrame ("button", titleBar:GetName() and titleBar:GetName() .. "CloseButton" or nil, titleBar, "BackdropTemplate")
closeButton:SetSize (16, 16)
closeButton:SetNormalTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
closeButton:SetHighlightTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
closeButton:SetPushedTexture ([[Interface\GLUES\LOGIN\Glues-CheckBox-Check]])
closeButton:GetNormalTexture():SetDesaturated(true)
closeButton:GetHighlightTexture():SetDesaturated(true)
closeButton:GetPushedTexture():SetDesaturated(true)
closeButton:SetAlpha (0.7)
closeButton:SetScript ("OnClick", simple_panel_close_click) --upvalue from this file
local titleLabel = titleBar:CreateFontString (titleBar:GetName() and titleBar:GetName() .. "TitleText" or nil, "overlay", "GameFontNormal")
titleLabel:SetTextColor (.8, .8, .8, 1)
titleLabel:SetText (titleText or "")
--anchors
closeButton:SetPoint ("right", titleBar, "right", -2, 0)
titleLabel:SetPoint ("center", titleBar, "center")
--members
f.TitleBar = titleBar
f.CloseButton = closeButton
f.TitleLabel = titleLabel
titleBar.CloseButton = closeButton
titleBar.Text = titleLabel
DF:Mixin (f, DF.TitleFunctions)
return titleBar
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
-- ~icon row
DF.IconRowFunctions = {
GetIcon = function (self)
local iconFrame = self.IconPool [self.NextIcon]
if (not iconFrame) then
local newIconFrame = CreateFrame ("frame", "$parentIcon" .. self.NextIcon, self, "BackdropTemplate")
newIconFrame.Texture = newIconFrame:CreateTexture (nil, "artwork")
PixelUtil.SetPoint (newIconFrame.Texture, "topleft", newIconFrame, "topleft", 1, -1)
PixelUtil.SetPoint (newIconFrame.Texture, "bottomright", newIconFrame, "bottomright", -1, 1)
newIconFrame.Border = newIconFrame:CreateTexture (nil, "background")
newIconFrame.Border:SetAllPoints()
newIconFrame.Border:SetColorTexture (0, 0, 0)
newIconFrame:SetBackdrop ({edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1})
newIconFrame:SetBackdropBorderColor (0, 0, 0, 0)
newIconFrame:EnableMouse (false)
local cooldownFrame = CreateFrame ("cooldown", "$parentIconCooldown" .. self.NextIcon, newIconFrame, "CooldownFrameTemplate, BackdropTemplate")
cooldownFrame:SetAllPoints()
cooldownFrame:EnableMouse (false)
cooldownFrame:SetFrameLevel (newIconFrame:GetFrameLevel()+1)
cooldownFrame:SetHideCountdownNumbers (self.options.surpress_blizzard_cd_timer)
cooldownFrame.noCooldownCount = self.options.surpress_tulla_omni_cc
newIconFrame.CountdownText = cooldownFrame:CreateFontString (nil, "overlay", "GameFontNormal")
--newIconFrame.CountdownText:SetPoint ("center")
newIconFrame.CountdownText:SetPoint (self.options.text_anchor or "center", newIconFrame, self.options.text_rel_anchor or "center", self.options.text_x_offset or 0, self.options.text_y_offset or 0)
newIconFrame.CountdownText:Hide()
newIconFrame.StackText = newIconFrame:CreateFontString (nil, "overlay", "GameFontNormal")
--newIconFrame.StackText:SetPoint ("bottomright")
newIconFrame.StackText:SetPoint (self.options.stack_text_anchor or "center", newIconFrame, self.options.stack_text_rel_anchor or "bottomright", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
newIconFrame.StackText:Hide()
newIconFrame.Desc = newIconFrame:CreateFontString (nil, "overlay", "GameFontNormal")
--newIconFrame.Desc:SetPoint ("bottom", newIconFrame, "top", 0, 2)
newIconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", newIconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)
newIconFrame.Desc:Hide()
newIconFrame.Cooldown = cooldownFrame
self.IconPool [self.NextIcon] = newIconFrame
iconFrame = newIconFrame
end
iconFrame:ClearAllPoints()
local anchor = self.options.anchor
local anchorTo = self.NextIcon == 1 and self or self.IconPool [self.NextIcon - 1]
local xPadding = self.NextIcon == 1 and self.options.left_padding or self.options.icon_padding
local growDirection = self.options.grow_direction
if (growDirection == 1) then --grow to right
if (self.NextIcon == 1) then
PixelUtil.SetPoint (iconFrame, "left", anchorTo, "left", xPadding, 0)
else
PixelUtil.SetPoint (iconFrame, "left", anchorTo, "right", xPadding, 0)
end
elseif (growDirection == 2) then --grow to left
if (self.NextIcon == 1) then
PixelUtil.SetPoint (iconFrame, "right", anchorTo, "right", xPadding, 0)
else
PixelUtil.SetPoint (iconFrame, "right", anchorTo, "left", xPadding, 0)
end
end
DF:SetFontColor (iconFrame.CountdownText, self.options.text_color)
self.NextIcon = self.NextIcon + 1
return iconFrame
end,
SetIcon = function (self, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge)
local spellName, _, spellIcon
if (not forceTexture) then
spellName, _, spellIcon = GetSpellInfo (spellId)
else
spellIcon = forceTexture
end
if (spellIcon) then
local iconFrame = self:GetIcon()
iconFrame.Texture:SetTexture (spellIcon)
iconFrame.Texture:SetTexCoord (unpack (self.options.texcoord))
if (borderColor) then
iconFrame:SetBackdropBorderColor (Plater:ParseColors (borderColor))
else
iconFrame:SetBackdropBorderColor (0, 0, 0 ,0)
end
if (startTime) then
CooldownFrame_Set (iconFrame.Cooldown, startTime, duration, true, true)
if (self.options.show_text) then
iconFrame.CountdownText:Show()
local formattedTime = floor (startTime + duration - GetTime())
if (formattedTime >= 3600) then
formattedTime = floor (formattedTime / 3600) .. "h"
elseif (formattedTime >= 60) then
formattedTime = floor (formattedTime / 60) .. "m"
else
formattedTime = floor (formattedTime)
end
iconFrame.CountdownText:SetPoint (self.options.text_anchor or "center", iconFrame, self.options.text_rel_anchor or "center", self.options.text_x_offset or 0, self.options.text_y_offset or 0)
DF:SetFontSize (iconFrame.CountdownText, self.options.text_size)
DF:SetFontFace (iconFrame.CountdownText, self.options.text_font)
DF:SetFontOutline (iconFrame.CountdownText, self.options.text_outline)
iconFrame.CountdownText:SetText (formattedTime)
else
iconFrame.CountdownText:Hide()
end
iconFrame.Cooldown:SetHideCountdownNumbers (self.options.surpress_blizzard_cd_timer)
else
iconFrame.CountdownText:Hide()
end
if (descText and self.options.desc_text) then
iconFrame.Desc:Show()
iconFrame.Desc:SetText (descText.text)
iconFrame.Desc:SetTextColor (DF:ParseColors (descText.text_color or self.options.desc_text_color))
iconFrame.Desc:SetPoint(self.options.desc_text_anchor or "bottom", iconFrame, self.options.desc_text_rel_anchor or "top", self.options.desc_text_x_offset or 0, self.options.desc_text_y_offset or 2)
DF:SetFontSize (iconFrame.Desc, descText.text_size or self.options.desc_text_size)
DF:SetFontFace (iconFrame.Desc, self.options.desc_text_font)
DF:SetFontOutline (iconFrame.Desc, self.options.desc_text_outline)
else
iconFrame.Desc:Hide()
end
if (count and count > 1 and self.options.stack_text) then
iconFrame.StackText:Show()
iconFrame.StackText:SetText (count)
iconFrame.StackText:SetTextColor (DF:ParseColors (self.options.desc_text_color))
iconFrame.StackText:SetPoint (self.options.stack_text_anchor or "center", iconFrame, self.options.stack_text_rel_anchor or "bottomright", self.options.stack_text_x_offset or 0, self.options.stack_text_y_offset or 0)
DF:SetFontSize (iconFrame.StackText, self.options.stack_text_size)
DF:SetFontFace (iconFrame.StackText, self.options.stack_text_font)
DF:SetFontOutline (iconFrame.StackText, self.options.stack_text_outline)
else
iconFrame.StackText:Hide()
end
PixelUtil.SetSize (iconFrame, self.options.icon_width, self.options.icon_height)
iconFrame:Show()
--> update the size of the frame
self:SetWidth ((self.options.left_padding * 2) + (self.options.icon_padding * (self.NextIcon-2)) + (self.options.icon_width * (self.NextIcon - 1)))
self:SetHeight (self.options.icon_height + (self.options.top_padding * 2))
--> make information available
iconFrame.spellId = spellId
iconFrame.startTime = startTime
iconFrame.duration = duration
iconFrame.count = count
iconFrame.debuffType = debuffType
iconFrame.caster = caster
iconFrame.canStealOrPurge = canStealOrPurge
--> show the frame
self:Show()
return iconFrame
end
end,
ClearIcons = function (self)
for i = 1, self.NextIcon -1 do
self.IconPool [i]:Hide()
end
self.NextIcon = 1
self:Hide()
end,
GetIconGrowDirection = function (self)
local side = self.options.anchor.side
if (side == 1) then
return 1
elseif (side == 2) then
return 2
elseif (side == 3) then
return 1
elseif (side == 4) then
return 1
elseif (side == 5) then
return 2
elseif (side == 6) then
return 1
elseif (side == 7) then
return 2
elseif (side == 8) then
return 1
elseif (side == 9) then
return 1
elseif (side == 10) then
return 1
elseif (side == 11) then
return 2
elseif (side == 12) then
return 1
elseif (side == 13) then
return 1
end
end,
OnOptionChanged = function (self, optionName)
self:SetBackdropColor (unpack (self.options.backdrop_color))
self:SetBackdropBorderColor (unpack (self.options.backdrop_border_color))
end,
}
local default_icon_row_options = {
icon_width = 20,
icon_height = 20,
texcoord = {.1, .9, .1, .9},
show_text = true,
text_color = {1, 1, 1, 1},
text_size = 12,
text_font = "Arial Narrow",
text_outline = "NONE",
text_anchor = "center",
text_rel_anchor = "center",
text_x_offset = 0,
text_y_offset = 0,
desc_text = true,
desc_text_color = {1, 1, 1, 1},
desc_text_size = 7,
desc_text_font = "Arial Narrow",
desc_text_outline = "NONE",
desc_text_anchor = "bottom",
desc_text_rel_anchor = "top",
desc_text_x_offset = 0,
desc_text_y_offset = 2,
stack_text = true,
stack_text_color = {1, 1, 1, 1},
stack_text_size = 10,
stack_text_font = "Arial Narrow",
stack_text_outline = "NONE",
stack_text_anchor = "center",
stack_text_rel_anchor = "bottomright",
stack_text_x_offset = 0,
stack_text_y_offset = 0,
left_padding = 1, --distance between right and left
top_padding = 1, --distance between top and bottom
icon_padding = 1, --distance between each icon
backdrop = {},
backdrop_color = {0, 0, 0, 0.5},
backdrop_border_color = {0, 0, 0, 1},
anchor = {side = 6, x = 2, y = 0},
grow_direction = 1, --1 = to right 2 = to left
surpress_blizzard_cd_timer = false,
surpress_tulla_omni_cc = false,
}
function DF:CreateIconRow (parent, name, options)
local f = CreateFrame("frame", name, parent, "BackdropTemplate")
f.IconPool = {}
f.NextIcon = 1
DF:Mixin (f, DF.IconRowFunctions)
DF:Mixin (f, DF.OptionsFunctions)
f:BuildOptionsTable (default_icon_row_options, options)
f:SetSize (f.options.icon_width, f.options.icon_height + (f.options.top_padding * 2))
f:SetBackdrop (f.options.backdrop)
f:SetBackdropColor (unpack (f.options.backdrop_color))
f:SetBackdropBorderColor (unpack (f.options.backdrop_border_color))
return f
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> ~header
--mixed functions
DF.HeaderFunctions = {
AddFrameToHeaderAlignment = function (self, frame)
self.FramesToAlign = self.FramesToAlign or {}
tinsert (self.FramesToAlign, frame)
end,
--@self: an object like a line
--@headerFrame: the main header frame
--@anchor: which side the columnHeaders are attach
AlignWithHeader = function (self, headerFrame, anchor)
local columnHeaderFrames = headerFrame.columnHeadersCreated
anchor = anchor or "topleft"
for i = 1, #self.FramesToAlign do
local frame = self.FramesToAlign [i]
frame:ClearAllPoints()
local columnHeader = columnHeaderFrames [i]
local offset = 0
if (columnHeader.columnAlign == "right") then
offset = columnHeader:GetWidth()
if (frame:GetObjectType() == "FontString") then
frame:SetJustifyH ("right")
end
end
frame:SetPoint (columnHeader.columnAlign, self, anchor, columnHeader.XPosition + columnHeader.columnOffset + offset, 0)
end
end,
--@self: column header button
OnClick = function (self, buttonClicked)
--get the header main frame
local headerFrame = self:GetParent()
--if this header does not have a clickable header, just ignore
if (not headerFrame.columnSelected) then
return
end
--get the latest column header selected
local previousColumnHeader = headerFrame.columnHeadersCreated [headerFrame.columnSelected]
previousColumnHeader.Arrow:Hide()
headerFrame:ResetColumnHeaderBackdrop (previousColumnHeader)
headerFrame:SetBackdropColorForSelectedColumnHeader (self)
if (headerFrame.columnSelected == self.columnIndex) then
self.order = self.order ~= "ASC" and "ASC" or "DESC"
end
headerFrame.columnOrder = self.order
--set the new column header selected
headerFrame.columnSelected = self.columnIndex
headerFrame:UpdateSortArrow (self)
if (headerFrame.options.header_click_callback) then
--callback with the main header frame, column header, column index and column order as payload
local okay, errortext = pcall (headerFrame.options.header_click_callback, headerFrame, self, self.columnIndex, self.order)
if (not okay) then
print ("DF: Header onClick callback error:", errortext)
end
end
end,
}
DF.HeaderCoreFunctions = {
SetHeaderTable = function (self, newTable)
self.columnHeadersCreated = self.columnHeadersCreated or {}
self.HeaderTable = newTable
self.NextHeader = 1
self.HeaderWidth = 0
self.HeaderHeight = 0
self:Refresh()
end,
--return which header is current selected and the the order ASC DESC
GetSelectedColumn = function (self)
return self.columnSelected, self.columnHeadersCreated [self.columnSelected or 1].order
end,
--clean up and rebuild the header following the header options
--@self: main header frame
Refresh = function (self)
--> refresh background frame
self:SetBackdrop (self.options.backdrop)
self:SetBackdropColor (unpack (self.options.backdrop_color))
self:SetBackdropBorderColor (unpack (self.options.backdrop_border_color))
--> reset all header frames
for i = 1, #self.columnHeadersCreated do
local columnHeader = self.columnHeadersCreated [i]
columnHeader.InUse = false
columnHeader:Hide()
end
local previousColumnHeader
local growDirection = string.lower (self.options.grow_direction)
--> update header frames
local headerSize = #self.HeaderTable
for i = 1, headerSize do
--> get the header button, a new one is created if it doesn't exists yet
local columnHeader = self:GetNextHeader()
self:UpdateColumnHeader (columnHeader, i)
--> grow direction
if (not previousColumnHeader) then
columnHeader:SetPoint ("topleft", self, "topleft", 0, 0)
if (growDirection == "right") then
if (self.options.use_line_separators) then
columnHeader.Separator:Show()
columnHeader.Separator:SetWidth (self.options.line_separator_width)
columnHeader.Separator:SetColorTexture (unpack (self.options.line_separator_color))
columnHeader.Separator:ClearAllPoints()
if (self.options.line_separator_gap_align) then
columnHeader.Separator:SetPoint ("topleft", columnHeader, "topright", 0, 0)
else
columnHeader.Separator:SetPoint ("topright", columnHeader, "topright", 0, 0)
end
columnHeader.Separator:SetHeight (self.options.line_separator_height)
end
end
else
if (growDirection == "right") then
columnHeader:SetPoint ("topleft", previousColumnHeader, "topright", self.options.padding, 0)
if (self.options.use_line_separators) then
columnHeader.Separator:Show()
columnHeader.Separator:SetWidth (self.options.line_separator_width)
columnHeader.Separator:SetColorTexture (unpack (self.options.line_separator_color))
columnHeader.Separator:ClearAllPoints()
if (self.options.line_separator_gap_align) then
columnHeader.Separator:SetPoint ("topleft", columnHeader, "topright", 0, 0)
else
columnHeader.Separator:SetPoint ("topleft", columnHeader, "topright", 0, 0)
end
columnHeader.Separator:SetHeight (self.options.line_separator_height)
if (headerSize == i) then
columnHeader.Separator:Hide()
end
end
elseif (growDirection == "left") then
columnHeader:SetPoint ("topright", previousColumnHeader, "topleft", -self.options.padding, 0)
elseif (growDirection == "bottom") then
columnHeader:SetPoint ("topleft", previousColumnHeader, "bottomleft", 0, -self.options.padding)
elseif (growDirection == "top") then
columnHeader:SetPoint ("bottomleft", previousColumnHeader, "topleft", 0, self.options.padding)
end
end
previousColumnHeader = columnHeader
end
self:SetSize (self.HeaderWidth, self.HeaderHeight)
end,
--@self: main header frame
UpdateSortArrow = function (self, columnHeader, defaultShown, defaultOrder)
local options = self.options
local order = defaultOrder or columnHeader.order
local arrowIcon = columnHeader.Arrow
if (type (defaultShown) ~= "boolean") then
arrowIcon:Show()
else
arrowIcon:SetShown (defaultShown)
if (defaultShown) then
self:SetBackdropColorForSelectedColumnHeader (columnHeader)
end
end
arrowIcon:SetAlpha (options.arrow_alpha)
if (order == "ASC") then
arrowIcon:SetTexture (options.arrow_up_texture)
arrowIcon:SetTexCoord (unpack (options.arrow_up_texture_coords))
arrowIcon:SetSize (unpack (options.arrow_up_size))
elseif (order == "DESC") then
arrowIcon:SetTexture (options.arrow_down_texture)
arrowIcon:SetTexCoord (unpack (options.arrow_down_texture_coords))
arrowIcon:SetSize (unpack (options.arrow_down_size))
end
end,
--@self: main header frame
UpdateColumnHeader = function (self, columnHeader, headerIndex)
local headerData = self.HeaderTable [headerIndex]
if (headerData.icon) then
columnHeader.Icon:SetTexture (headerData.icon)
if (headerData.texcoord) then
columnHeader.Icon:SetTexCoord (unpack (headerData.texcoord))
else
columnHeader.Icon:SetTexCoord (0, 1, 0, 1)
end
columnHeader.Icon:SetPoint ("left", columnHeader, "left", self.options.padding, 0)
columnHeader.Icon:Show()
end
if (headerData.text) then
columnHeader.Text:SetText (headerData.text)
--> text options
DF:SetFontColor (columnHeader.Text, self.options.text_color)
DF:SetFontSize (columnHeader.Text, self.options.text_size)
DF:SetFontOutline (columnHeader.Text, self.options.text_shadow)
--> point
if (not headerData.icon) then
columnHeader.Text:SetPoint ("left", columnHeader, "left", self.options.padding, 0)
else
columnHeader.Text:SetPoint ("left", columnHeader.Icon, "right", self.options.padding, 0)
end
columnHeader.Text:Show()
end
--column header index
columnHeader.columnIndex = headerIndex
if (headerData.canSort) then
columnHeader.order = "DESC"
columnHeader.Arrow:SetTexture (self.options.arrow_up_texture)
else
columnHeader.Arrow:Hide()
end
if (headerData.selected) then
columnHeader.Arrow:Show()
columnHeader.Arrow:SetAlpha (.843)
self:UpdateSortArrow (columnHeader, true, columnHeader.order)
self.columnSelected = headerIndex
else
if (headerData.canSort) then
self:UpdateSortArrow (columnHeader, false, columnHeader.order)
end
end
--> size
if (headerData.width) then
columnHeader:SetWidth (headerData.width)
end
if (headerData.height) then
columnHeader:SetHeight (headerData.height)
end
columnHeader.XPosition = self.HeaderWidth-- + self.options.padding
columnHeader.YPosition = self.HeaderHeight-- + self.options.padding
columnHeader.columnAlign = headerData.align or "left"
columnHeader.columnOffset = headerData.offset or 0
--> add the header piece size to the total header size
local growDirection = string.lower (self.options.grow_direction)
if (growDirection == "right" or growDirection == "left") then
self.HeaderWidth = self.HeaderWidth + columnHeader:GetWidth() + self.options.padding
self.HeaderHeight = math.max (self.HeaderHeight, columnHeader:GetHeight())
elseif (growDirection == "top" or growDirection == "bottom") then
self.HeaderWidth = math.max (self.HeaderWidth, columnHeader:GetWidth())
self.HeaderHeight = self.HeaderHeight + columnHeader:GetHeight() + self.options.padding
end
columnHeader:Show()
columnHeader.InUse = true
end,
--reset column header backdrop
--@self: main header frame
ResetColumnHeaderBackdrop = function (self, columnHeader)
columnHeader:SetBackdrop (self.options.header_backdrop)
columnHeader:SetBackdropColor (unpack (self.options.header_backdrop_color))
columnHeader:SetBackdropBorderColor (unpack (self.options.header_backdrop_border_color))
end,
--@self: main header frame
SetBackdropColorForSelectedColumnHeader = function (self, columnHeader)
columnHeader:SetBackdropColor (unpack (self.options.header_backdrop_color_selected))
end,
--clear the column header
--@self: main header frame
ClearColumnHeader = function (self, columnHeader)
columnHeader:SetSize (self.options.header_width, self.options.header_height)
self:ResetColumnHeaderBackdrop (columnHeader)
columnHeader:ClearAllPoints()
columnHeader.Icon:SetTexture ("")
columnHeader.Icon:Hide()
columnHeader.Text:SetText ("")
columnHeader.Text:Hide()
end,
--get the next column header, create one if doesn't exists
--@self: main header frame
GetNextHeader = function (self)
local nextHeader = self.NextHeader
local columnHeader = self.columnHeadersCreated [nextHeader]
if (not columnHeader) then
--create a new column header
local newHeader = CreateFrame ("button", "$parentHeaderIndex" .. nextHeader, self,"BackdropTemplate")
newHeader:SetScript ("OnClick", DF.HeaderFunctions.OnClick)
--header icon
DF:CreateImage (newHeader, "", self.options.header_height, self.options.header_height, "ARTWORK", nil, "Icon", "$parentIcon")
--header separator
DF:CreateImage (newHeader, "", 1, 1, "ARTWORK", nil, "Separator", "$parentSeparator")
--header name text
DF:CreateLabel (newHeader, "", self.options.text_size, self.options.text_color, "GameFontNormal", "Text", "$parentText", "ARTWORK")
--header selected and order icon
DF:CreateImage (newHeader, self.options.arrow_up_texture, 12, 12, "ARTWORK", nil, "Arrow", "$parentArrow")
newHeader.Arrow:SetPoint ("right", newHeader, "right", -1, 0)
newHeader.Separator:Hide()
newHeader.Arrow:Hide()
self:UpdateSortArrow (newHeader, false, "DESC")
tinsert (self.columnHeadersCreated, newHeader)
columnHeader = newHeader
end
self:ClearColumnHeader (columnHeader)
self.NextHeader = self.NextHeader + 1
return columnHeader
end,
NextHeader = 1,
HeaderWidth = 0,
HeaderHeight = 0,
}
local default_header_options = {
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
text_color = {1, 1, 1, 1},
text_size = 10,
text_shadow = false,
grow_direction = "RIGHT",
padding = 2,
--each piece of the header
header_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
header_backdrop_color = {0, 0, 0, 0.5},
header_backdrop_color_selected = {0.3, 0.3, 0.3, 0.5},
header_backdrop_border_color = {0, 0, 0, 0},
header_width = 120,
header_height = 20,
arrow_up_texture = [[Interface\Buttons\Arrow-Up-Down]],
arrow_up_texture_coords = {0, 1, 6/16, 1},
arrow_up_size = {12, 11},
arrow_down_texture = [[Interface\Buttons\Arrow-Down-Down]],
arrow_down_texture_coords = {0, 1, 0, 11/16},
arrow_down_size = {12, 11},
arrow_alpha = 0.659,
use_line_separators = false,
line_separator_color = {.1, .1, .1, .6},
line_separator_width = 1,
line_separator_height = 200,
line_separator_gap_align = false,
}
function DF:CreateHeader (parent, headerTable, options, frameName)
local f = CreateFrame ("frame", frameName or "$parentHeaderLine", parent,"BackdropTemplate")
DF:Mixin (f, DF.OptionsFunctions)
DF:Mixin (f, DF.HeaderCoreFunctions)
f:BuildOptionsTable (default_header_options, options)
f:SetBackdrop (f.options.backdrop)
f:SetBackdropColor (unpack (f.options.backdrop_color))
f:SetBackdropBorderColor (unpack (f.options.backdrop_border_color))
f:SetHeaderTable (headerTable)
return f
end
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> radio group
local default_radiogroup_options = {
width = 1,
height = 1,
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
is_radio = false,
}
DF.RadioGroupCoreFunctions = {
RadioOnClick = function (self, fixedParam, value)
--turn off all checkboxes
local frameList = {self:GetParent():GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
checkbox:SetValue (false)
end
--turn on the clicked checkbox
self:SetValue (true)
--callback
DF:QuickDispatch (self._set, fixedParam)
end,
Disable = function (self)
local frameList = {self:GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
checkbox:Disable()
end
end,
Enable = function (self)
local frameList = {self:GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
checkbox:Enable()
end
end,
DeselectAll = function (self)
local frameList = {self:GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
checkbox:SetValue (false)
end
end,
FadeIn = function (self)
local frameList = {self:GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox:SetAlpha (1)
end
end,
FadeOut = function (self)
local frameList = {self:GetChildren()}
for _, checkbox in ipairs (frameList) do
checkbox:SetAlpha (.7)
end
end,
SetFadeState = function (self, state)
if (state) then
self:FadeIn()
else
self:FadeOut()
end
end,
CreateCheckbox = function (self)
local checkbox = DF:CreateSwitch (self, function()end, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, DF:GetTemplate ("switch", "OPTIONS_CHECKBOX_BRIGHT_TEMPLATE"))
checkbox:SetAsCheckBox()
checkbox.Icon = DF:CreateImage (checkbox, "", 16, 16)
checkbox.Label = DF:CreateLabel (checkbox, "")
return checkbox
end,
RefreshCheckbox = function (self, checkbox, optionTable)
checkbox = checkbox.GetCapsule and checkbox:GetCapsule() or checkbox
local setFunc = self.options.is_radio and self.RadioOnClick or optionTable.set
checkbox:SetSwitchFunction (setFunc)
checkbox._set = setFunc
checkbox:SetFixedParameter (optionTable.param)
local isChecked = DF:Dispatch (optionTable.get)
checkbox:SetValue (isChecked)
checkbox.Label:SetText (optionTable.name)
if (optionTable.texture) then
checkbox.Icon:SetTexture (optionTable.texture)
checkbox.Icon:SetPoint ("left", checkbox, "right", 2, 0)
checkbox.Label:SetPoint ("left", checkbox.Icon, "right", 2, 0)
if (optionTable.texcoord) then
checkbox.Icon:SetTexCoord (unpack (optionTable.texcoord))
else
checkbox.Icon:SetTexCoord (0, 1, 0, 1)
end
else
checkbox.Icon:SetTexture ("")
checkbox.Label:SetPoint ("left", checkbox, "right", 2, 0)
end
end,
Refresh = function (self)
local radioOptions = self.RadioOptionsTable
local radioCheckboxes = {self:GetChildren()}
for _, checkbox in ipairs (radioCheckboxes) do
checkbox:Hide()
end
for radioIndex, optionsTable in ipairs (radioOptions) do
local checkbox = radioCheckboxes [radioIndex]
if (not checkbox) then
checkbox = self:CreateCheckbox()
end
checkbox.OptionID = radioIndex
checkbox:Show()
self:RefreshCheckbox (checkbox, optionsTable)
end
--sending false to automatically use the radio group children
self:ArrangeFrames (false, self.AnchorOptions)
end,
SetOptions = function (self, radioOptions)
self.RadioOptionsTable = radioOptions
self:Refresh()
end,
}
--[=[
radionOptions: an index table with options for the radio group {name = "", set = func (self, param, value), param = value, get = func, texture = "", texcoord = {}}
set function receives as self the checkbox, use :GetParent() to get the radion group frame
if get function return nil or false the checkbox isn't checked
name: the name of the frame
options: override options for default_radiogroup_options table
anchorOptions: override options for default_framelayout_options table
--]=]
function DF:CreateRadionGroup (parent, radioOptions, name, options, anchorOptions)
local f = CreateFrame ("frame", name, parent, "BackdropTemplate")
DF:Mixin (f, DF.OptionsFunctions)
DF:Mixin (f, DF.RadioGroupCoreFunctions)
DF:Mixin (f, DF.LayoutFrame)
f:BuildOptionsTable (default_radiogroup_options, options)
f:SetSize (f.options.width, f.options.height)
f:SetBackdrop (f.options.backdrop)
f:SetBackdropColor (unpack (f.options.backdrop_color))
f:SetBackdropBorderColor (unpack (f.options.backdrop_border_color))
f.AnchorOptions = anchorOptions or {}
if (f.options.title) then
local titleLabel = DF:CreateLabel (f, f.options.title, DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
titleLabel:SetPoint ("bottomleft", f, "topleft", 0, 2)
f.Title = titleLabel
end
f:SetOptions (radioOptions)
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> load conditions panel
--this is the table prototype to hold load conditions settings
local default_load_conditions = {
class = {},
spec = {},
race = {},
talent = {},
pvptalent = {},
group = {},
role = {},
affix = {},
encounter_ids = {},
map_ids = {},
}
local default_load_conditions_frame_options = {
title = "Details! Framework: Load Conditions",
name = "Object",
}
function DF:CreateLoadFilterParser (callback)
local f = CreateFrame ("frame")
f:RegisterEvent ("PLAYER_ENTERING_WORLD")
if IS_WOW_PROJECT_MAINLINE then
f:RegisterEvent ("PLAYER_SPECIALIZATION_CHANGED")
f:RegisterEvent ("PLAYER_TALENT_UPDATE")
end
f:RegisterEvent ("PLAYER_ROLES_ASSIGNED")
f:RegisterEvent ("ZONE_CHANGED_NEW_AREA")
if IS_WOW_PROJECT_MAINLINE then
f:RegisterEvent ("CHALLENGE_MODE_START")
end
f:RegisterEvent ("ENCOUNTER_START")
f:RegisterEvent ("PLAYER_REGEN_ENABLED")
f:SetScript ("OnEvent", function (self, event, ...)
if (event == "ENCOUNTER_START") then
local encounterID = ...
f.EncounterIDCached = encounterID
elseif (event == "ENCOUNTER_END") then
f.EncounterIDCached = nil
elseif (event == "PLAYER_REGEN_ENABLED") then
--f.EncounterIDCached = nil
--when the player dies during an encounter, the game is triggering regen enabled
elseif (event == "PLAYER_SPECIALIZATION_CHANGED") then
if (DetailsFrameworkLoadConditionsPanel and DetailsFrameworkLoadConditionsPanel:IsShown()) then
DetailsFrameworkLoadConditionsPanel:Refresh()
end
local unit = ...
if (not unit or not UnitIsUnit ("player", unit)) then
return
end
elseif (event == "PLAYER_ROLES_ASSIGNED") then
local assignedRole = UnitGroupRolesAssigned ("player")
if (assignedRole == "NONE") then
local spec = DetailsFramework.GetSpecialization()
if (spec) then
assignedRole = DetailsFramework.GetSpecializationRole (spec)
end
end
if (DF.CurrentPlayerRole == assignedRole) then
return
end
DF.CurrentPlayerRole = assignedRole
end
--print ("Plater Script Update:", event, ...)
DF:QuickDispatch (callback, f.EncounterIDCached)
end)
end
function DF:PassLoadFilters (loadTable, encounterID)
--class
local passLoadClass
if (loadTable.class.Enabled) then
local _, classFileName = UnitClass ("player")
if (not loadTable.class [classFileName]) then
return false
else
passLoadClass = true
end
end
--spec
if (IS_WOW_PROJECT_MAINLINE and loadTable.spec.Enabled) then
local canCheckTalents = true
if (passLoadClass) then
--if is allowed to load on this class, check if the talents isn't from another class
local _, classFileName = UnitClass ("player")
local specsForThisClass = DF:GetClassSpecIDs (classFileName)
canCheckTalents = false
for _, specID in ipairs (specsForThisClass) do
if (loadTable.spec [specID] or loadTable.spec [specID..""]) then
--theres a talent for this class
canCheckTalents = true
break
end
end
end
if (canCheckTalents) then
local specIndex = DetailsFramework.GetSpecialization()
if (specIndex) then
local specID = DetailsFramework.GetSpecializationInfo (specIndex)
if not specID or (not loadTable.spec [specID] and not loadTable.spec [specID..""]) then
return false
end
else
return false
end
end
end
--race
if (loadTable.race.Enabled) then
local raceName, raceFileName, raceID = UnitRace ("player")
if (not loadTable.race [raceFileName]) then
return false
end
end
--talents
if (IS_WOW_PROJECT_MAINLINE and loadTable.talent.Enabled) then
local talentsInUse = DF:GetCharacterTalents (false, true)
local hasTalent
for talentID, _ in pairs (talentsInUse) do
if talentID and (loadTable.talent [talentID] or loadTable.talent [talentID .. ""]) then
hasTalent = true
break
end
end
if (not hasTalent) then
return false
end
end
--pvptalent
if (IS_WOW_PROJECT_MAINLINE and loadTable.pvptalent.Enabled) then
local talentsInUse = DF:GetCharacterPvPTalents (false, true)
local hasTalent
for talentID, _ in pairs (talentsInUse) do
if talentID and (loadTable.pvptalent [talentID] or loadTable.pvptalent [talentID .. ""]) then
hasTalent = true
break
end
end
if (not hasTalent) then
return false
end
end
--group
if (loadTable.group.Enabled) then
local _, zoneType = GetInstanceInfo()
if (not loadTable.group [zoneType]) then
return
end
end
--role
if (loadTable.role.Enabled) then
local assignedRole = UnitGroupRolesAssigned ("player")
if (assignedRole == "NONE") then
local spec = DetailsFramework.GetSpecialization()
if (spec) then
assignedRole = DetailsFramework.GetSpecializationRole (spec)
end
end
if (not loadTable.role [assignedRole]) then
return false
end
end
--affix
if (IS_WOW_PROJECT_MAINLINE and loadTable.affix.Enabled) then
local isInMythicDungeon = C_ChallengeMode.IsChallengeModeActive()
if (not isInMythicDungeon) then
return false
end
local level, affixes, wasEnergized = C_ChallengeMode.GetActiveKeystoneInfo()
local hasAffix = false
for _, affixID in ipairs (affixes) do
if affixID and (loadTable.affix [affixID] or loadTable.affix [affixID .. ""]) then
hasAffix = true
break
end
end
if (not hasAffix) then
return false
end
end
--encounter id
if (loadTable.encounter_ids.Enabled) then
if (not encounterID) then
return
end
local hasEncounter
for _, ID in ipairs (loadTable.encounter_ids) do
if (ID == encounterID) then
hasEncounter = true
break
end
if (not hasEncounter) then
return false
end
end
end
--map id
if (loadTable.map_ids.Enabled) then
local _, _, _, _, _, _, _, zoneMapID = GetInstanceInfo()
local uiMapID = C_Map.GetBestMapForUnit ("player")
local hasMapID
for _, ID in ipairs (loadTable.map_ids) do
if (ID == zoneMapID or ID == uiMapID) then
hasMapID = true
break
end
if (not hasMapID) then
return false
end
end
end
return true
end
--this func will deploy the default values from the prototype into the config table
function DF:UpdateLoadConditionsTable (configTable)
configTable = configTable or {}
DF.table.deploy (configTable, default_load_conditions)
return configTable
end
--/run Plater.OpenOptionsPanel()PlaterOptionsPanelContainer:SelectIndex (Plater, 14)
function DF:OpenLoadConditionsPanel (optionsTable, callback, frameOptions)
frameOptions = frameOptions or {}
DF.table.deploy (frameOptions, default_load_conditions_frame_options)
DF:UpdateLoadConditionsTable (optionsTable)
if (not DetailsFrameworkLoadConditionsPanel) then
local f = DF:CreateSimplePanel (UIParent, 970, 505, "Load Conditions", "DetailsFrameworkLoadConditionsPanel")
f:SetBackdropColor (0, 0, 0, 1)
f.AllRadioGroups = {}
f.AllTextEntries = {}
f.OptionsTable = optionsTable
DF:ApplyStandardBackdrop (f, false, 1.1)
local xStartAt = 10
local x2StartAt = 500
local anchorPositions = {
class = {xStartAt, -70},
spec = {xStartAt, -170},
race = {xStartAt, -210},
role = {xStartAt, -310},
talent = {xStartAt, -350},
pvptalent = {x2StartAt, -70},
group = {x2StartAt, -210},
affix = {x2StartAt, -270},
encounter_ids = {x2StartAt, -400},
map_ids = {x2StartAt, -440},
}
local editingLabel = DF:CreateLabel (f, "Load Conditions For:")
local editingWhatLabel = DF:CreateLabel (f, "")
editingLabel:SetPoint ("topleft", f, "topleft", 10, -35)
editingWhatLabel:SetPoint ("left", editingLabel, "right", 2, 0)
--this label store the name of what is being edited
f.EditingLabel = editingWhatLabel
--when the user click on an option, run the callback
f.RunCallback = function()
DF:Dispatch (f.CallbackFunc)
end
--when the user click on an option or when the panel is opened
--check if there's an option enabled and fadein all options, fadeout otherwise
f.OnRadioStateChanged = function (radioGroup, subConfigTable)
subConfigTable.Enabled = nil
subConfigTable.Enabled = next (subConfigTable) and true or nil
radioGroup:SetFadeState (subConfigTable.Enabled)
end
--create the radio group for character class
f.OnRadioCheckboxClick = function (self, key, value)
--hierarchy: DBKey ["class"] key ["HUNTER"] value TRUE
local DBKey = self:GetParent().DBKey
f.OptionsTable [DBKey] [key and key .. ""] = value and true or nil
if not value then -- cleanup "number" type values
f.OptionsTable [DBKey] [key] = nil
end
f.OnRadioStateChanged (self:GetParent(), f.OptionsTable [DBKey])
f.RunCallback()
end
--create the radio group for classes
local classes = {}
for _, classTable in pairs (DF:GetClassList()) do
tinsert (classes, {
name = classTable.Name,
set = f.OnRadioCheckboxClick,
param = classTable.FileString,
get = function() return f.OptionsTable.class [classTable.FileString] end,
texture = classTable.Texture,
texcoord = classTable.TexCoord,
})
end
local classGroup = DF:CreateRadionGroup (f, classes, name, {width = 200, height = 200, title = "Character Class"}, {offset_x = 130, amount_per_line = 3})
classGroup:SetPoint ("topleft", f, "topleft", anchorPositions.class [1], anchorPositions.class [2])
classGroup.DBKey = "class"
tinsert (f.AllRadioGroups, classGroup)
--create the radio group for character spec
if IS_WOW_PROJECT_MAINLINE then
local specs = {}
for _, specID in ipairs (DF:GetClassSpecIDs (select (2, UnitClass ("player")))) do
local specID, specName, specDescription, specIcon, specBackground, specRole, specClass = DetailsFramework.GetSpecializationInfoByID (specID)
tinsert (specs, {
name = specName,
set = f.OnRadioCheckboxClick,
param = specID,
get = function() return f.OptionsTable.spec [specID] or f.OptionsTable.spec [specID..""] end,
texture = specIcon,
})
end
local specGroup = DF:CreateRadionGroup (f, specs, name, {width = 200, height = 200, title = "Character Spec"}, {offset_x = 130, amount_per_line = 4})
specGroup:SetPoint ("topleft", f, "topleft", anchorPositions.spec [1], anchorPositions.spec [2])
specGroup.DBKey = "spec"
tinsert (f.AllRadioGroups, specGroup)
end
--create radio group for character races
local raceList = {}
for _, raceTable in ipairs (DF:GetCharacterRaceList()) do
tinsert (raceList, {
name = raceTable.Name,
set = f.OnRadioCheckboxClick,
param = raceTable.FileString,
get = function() return f.OptionsTable.race [raceTable.FileString] end,
})
end
local raceGroup = DF:CreateRadionGroup (f, raceList, name, {width = 200, height = 200, title = "Character Race"})
raceGroup:SetPoint ("topleft", f, "topleft", anchorPositions.race [1], anchorPositions.race [2])
raceGroup.DBKey = "race"
tinsert (f.AllRadioGroups, raceGroup)
--create radio group for talents
if IS_WOW_PROJECT_MAINLINE then
local talentList = {}
for _, talentTable in ipairs (DF:GetCharacterTalents()) do
tinsert (talentList, {
name = talentTable.Name,
set = f.OnRadioCheckboxClick,
param = talentTable.ID,
get = function() return f.OptionsTable.talent [talentTable.ID] or f.OptionsTable.talent [talentTable.ID .. ""] end,
texture = talentTable.Texture,
})
end
local talentGroup = DF:CreateRadionGroup (f, talentList, name, {width = 200, height = 200, title = "Characer Talents"}, {offset_x = 150, amount_per_line = 3})
talentGroup:SetPoint ("topleft", f, "topleft", anchorPositions.talent [1], anchorPositions.talent [2])
talentGroup.DBKey = "talent"
tinsert (f.AllRadioGroups, talentGroup)
f.TalentGroup = talentGroup
do
--create a frame to show talents selected in other specs or characters
local otherTalents = CreateFrame ("frame", nil, f, "BackdropTemplate")
otherTalents:SetSize (26, 26)
otherTalents:SetPoint ("left", talentGroup.Title.widget, "right", 10, -2)
otherTalents.Texture = DF:CreateImage (otherTalents, [[Interface\BUTTONS\AdventureGuideMicrobuttonAlert]], 24, 24)
otherTalents.Texture:SetAllPoints()
local removeTalent = function (_, _, talentID)
f.OptionsTable.talent [talentID] = nil
GameCooltip2:Hide()
f.OnRadioStateChanged (talentGroup, f.OptionsTable [talentGroup.DBKey])
f.CanShowTalentWarning()
end
local buildTalentMenu = function()
local playerTalents = DF:GetCharacterTalents()
local indexedTalents = {}
for _, talentTable in ipairs (playerTalents) do
tinsert (indexedTalents, talentTable.ID)
end
--talents selected to load
GameCooltip2:AddLine ("select a talent to remove it (added from a different spec or character)", "", 1, "orange", "orange", 9)
GameCooltip2:AddLine ("$div", nil, nil, -1, -1)
for talentID, _ in pairs (f.OptionsTable.talent) do
if (type (talentID) == "number" and not DF.table.find (indexedTalents, talentID)) then
local talentID, name, texture, selected, available = GetTalentInfoByID (talentID)
if (name) then
GameCooltip2:AddLine (name)
GameCooltip2:AddIcon (texture, 1, 1, 16, 16, .1, .9, .1, .9)
GameCooltip2:AddMenu (1, removeTalent, talentID)
end
end
end
end
otherTalents.CoolTip = {
Type = "menu",
BuildFunc = buildTalentMenu,
OnEnterFunc = function (self) end,
OnLeaveFunc = function (self) end,
FixedValue = "none",
ShowSpeed = 0.05,
Options = function()
GameCooltip2:SetOption ("TextFont", "Friz Quadrata TT")
GameCooltip2:SetOption ("TextColor", "orange")
GameCooltip2:SetOption ("TextSize", 12)
GameCooltip2:SetOption ("FixedWidth", 220)
GameCooltip2:SetOption ("ButtonsYMod", -4)
GameCooltip2:SetOption ("YSpacingMod", -4)
GameCooltip2:SetOption ("IgnoreButtonAutoHeight", true)
GameCooltip2:SetColor (1, 0.5, 0.5, 0.5, 0)
local preset2_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], edgeFile = [[Interface\Buttons\WHITE8X8]], tile = true, edgeSize = 1, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0}}
local gray_table = {0.37, 0.37, 0.37, 0.95}
local black_table = {0.2, 0.2, 0.2, 1}
GameCooltip2:SetBackdrop (1, preset2_backdrop, gray_table, black_table)
GameCooltip2:SetBackdrop (2, preset2_backdrop, gray_table, black_table)
end,
}
GameCooltip2:CoolTipInject (otherTalents)
function f.CanShowTalentWarning()
local playerTalents = DF:GetCharacterTalents()
local indexedTalents = {}
for _, talentTable in ipairs (playerTalents) do
tinsert (indexedTalents, talentTable.ID)
end
for talentID, _ in pairs (f.OptionsTable.talent) do
if (type (talentID) == "number" and not DF.table.find (indexedTalents, talentID)) then
otherTalents:Show()
return
end
end
otherTalents:Hide()
end
end
end
--create radio group for pvp talents
if IS_WOW_PROJECT_MAINLINE then
local pvpTalentList = {}
for _, talentTable in ipairs (DF:GetCharacterPvPTalents()) do
tinsert (pvpTalentList, {
name = talentTable.Name,
set = f.OnRadioCheckboxClick,
param = talentTable.ID,
get = function() return f.OptionsTable.pvptalent [talentTable.ID] or f.OptionsTable.pvptalent [talentTable.ID .. ""] end,
texture = talentTable.Texture,
})
end
local pvpTalentGroup = DF:CreateRadionGroup (f, pvpTalentList, name, {width = 200, height = 200, title = "Characer PvP Talents"}, {offset_x = 150, amount_per_line = 3})
pvpTalentGroup:SetPoint ("topleft", f, "topleft", anchorPositions.pvptalent [1], anchorPositions.pvptalent [2])
pvpTalentGroup.DBKey = "pvptalent"
tinsert (f.AllRadioGroups, pvpTalentGroup)
f.PvPTalentGroup = pvpTalentGroup
do
--create a frame to show talents selected in other specs or characters
local otherTalents = CreateFrame ("frame", nil, f, "BackdropTemplate")
otherTalents:SetSize (26, 26)
otherTalents:SetPoint ("left", pvpTalentGroup.Title.widget, "right", 10, -2)
otherTalents.Texture = DF:CreateImage (otherTalents, [[Interface\BUTTONS\AdventureGuideMicrobuttonAlert]], 24, 24)
otherTalents.Texture:SetAllPoints()
local removeTalent = function (_, _, talentID)
f.OptionsTable.pvptalent [talentID] = nil
GameCooltip2:Hide()
f.OnRadioStateChanged (pvpTalentGroup, f.OptionsTable [pvpTalentGroup.DBKey])
f.CanShowPvPTalentWarning()
end
local buildTalentMenu = function()
local playerTalents = DF:GetCharacterPvPTalents()
local indexedTalents = {}
for _, talentTable in ipairs (playerTalents) do
tinsert (indexedTalents, talentTable.ID)
end
--talents selected to load
GameCooltip2:AddLine ("select a talent to remove it (added from a different spec or character)", "", 1, "orange", "orange", 9)
GameCooltip2:AddLine ("$div", nil, nil, -1, -1)
for talentID, _ in pairs (f.OptionsTable.pvptalent) do
if (type (talentID) == "number" and not DF.table.find (indexedTalents, talentID)) then
local _, name, texture = GetPvpTalentInfoByID (talentID)
if (name) then
GameCooltip2:AddLine (name)
GameCooltip2:AddIcon (texture, 1, 1, 16, 16, .1, .9, .1, .9)
GameCooltip2:AddMenu (1, removeTalent, talentID)
end
end
end
end
otherTalents.CoolTip = {
Type = "menu",
BuildFunc = buildTalentMenu,
OnEnterFunc = function (self) end,
OnLeaveFunc = function (self) end,
FixedValue = "none",
ShowSpeed = 0.05,
Options = function()
GameCooltip2:SetOption ("TextFont", "Friz Quadrata TT")
GameCooltip2:SetOption ("TextColor", "orange")
GameCooltip2:SetOption ("TextSize", 12)
GameCooltip2:SetOption ("FixedWidth", 220)
GameCooltip2:SetOption ("ButtonsYMod", -4)
GameCooltip2:SetOption ("YSpacingMod", -4)
GameCooltip2:SetOption ("IgnoreButtonAutoHeight", true)
GameCooltip2:SetColor (1, 0.5, 0.5, 0.5, 0)
local preset2_backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeFile = [[Interface\Buttons\WHITE8X8]], tile = true, edgeSize = 1, tileSize = 16, insets = {left = 0, right = 0, top = 0, bottom = 0}}
local gray_table = {0.37, 0.37, 0.37, 0.95}
local black_table = {0.2, 0.2, 0.2, 1}
GameCooltip2:SetBackdrop (1, preset2_backdrop, gray_table, black_table)
GameCooltip2:SetBackdrop (2, preset2_backdrop, gray_table, black_table)
end,
}
GameCooltip2:CoolTipInject (otherTalents)
function f.CanShowPvPTalentWarning()
local playerTalents = DF:GetCharacterPvPTalents()
local indexedTalents = {}
for _, talentTable in ipairs (playerTalents) do
tinsert (indexedTalents, talentTable.ID)
end
for talentID, _ in pairs (f.OptionsTable.pvptalent) do
if (type (talentID) == "number" and not DF.table.find (indexedTalents, talentID)) then
otherTalents:Show()
return
end
end
otherTalents:Hide()
end
end
end
--create radio for group types
local groupTypes = {}
for _, groupTable in ipairs (DF:GetGroupTypes()) do
tinsert (groupTypes, {
name = groupTable.Name,
set = f.OnRadioCheckboxClick,
param = groupTable.ID,
get = function() return f.OptionsTable.group [groupTable.ID] or f.OptionsTable.group [groupTable.ID .. ""] end,
})
end
local groupTypesGroup = DF:CreateRadionGroup (f, groupTypes, name, {width = 200, height = 200, title = "Group Types"})
groupTypesGroup:SetPoint ("topleft", f, "topleft", anchorPositions.group [1], anchorPositions.group [2])
groupTypesGroup.DBKey = "group"
tinsert (f.AllRadioGroups, groupTypesGroup)
--create radio for character roles
local roleTypes = {}
for _, roleTable in ipairs (DF:GetRoleTypes()) do
tinsert (roleTypes, {
name = roleTable.Texture .. " " .. roleTable.Name,
set = f.OnRadioCheckboxClick,
param = roleTable.ID,
get = function() return f.OptionsTable.role [roleTable.ID] or f.OptionsTable.role [roleTable.ID .. ""] end,
})
end
local roleTypesGroup = DF:CreateRadionGroup (f, roleTypes, name, {width = 200, height = 200, title = "Role Types"})
roleTypesGroup:SetPoint ("topleft", f, "topleft", anchorPositions.role [1], anchorPositions.role [2])
roleTypesGroup.DBKey = "role"
tinsert (f.AllRadioGroups, roleTypesGroup)
--create radio group for mythic+ affixes
if IS_WOW_PROJECT_MAINLINE then
local affixes = {}
for i = 2, 1000 do
local affixName, desc, texture = C_ChallengeMode.GetAffixInfo (i)
if (affixName) then
tinsert (affixes, {
name = affixName,
set = f.OnRadioCheckboxClick,
param = i,
get = function() return f.OptionsTable.affix [i] or f.OptionsTable.affix [i .. ""] end,
texture = texture,
})
end
end
local affixTypesGroup = DF:CreateRadionGroup (f, affixes, name, {width = 200, height = 200, title = "M+ Affixes"})
affixTypesGroup:SetPoint ("topleft", f, "topleft", anchorPositions.affix [1], anchorPositions.affix [2])
affixTypesGroup.DBKey = "affix"
tinsert (f.AllRadioGroups, affixTypesGroup)
end
--text entries functions
local textEntryRefresh = function (self)
local idList = f.OptionsTable [self.DBKey]
self:SetText ("")
for i = 1, #idList do
self:SetText (self:GetText() .. " " .. idList [i])
end
self:SetText (self:GetText():gsub ("^ ", ""))
end
local textEntryOnEnterPressed = function (_, self)
wipe (f.OptionsTable [self.DBKey])
local text = self:GetText()
for _, ID in ipairs ({strsplit ("", text)}) do
ID = DF:trim (ID)
ID = tonumber (ID)
if (ID) then
tinsert (f.OptionsTable [self.DBKey], ID)
f.OptionsTable [self.DBKey].Enabled = true
end
end
end
--create the text entry to type the encounter ID
local encounterIDLabel = DF:CreateLabel (f, "Encounter ID", DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
local encounterIDEditbox = DF:CreateTextEntry (f, function()end, 200, 20, "EncounterEditbox", _, _, DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
encounterIDLabel:SetPoint ("topleft", f, "topleft", anchorPositions.encounter_ids [1], anchorPositions.encounter_ids [2])
encounterIDEditbox:SetPoint ("topleft", encounterIDLabel, "bottomleft", 0, -2)
encounterIDEditbox.DBKey = "encounter_ids"
encounterIDEditbox.Refresh = textEntryRefresh
encounterIDEditbox.tooltip = "Enter multiple IDs separating with a semicolon ()\nExample: 35 45 95\n\nUldir:\n"
for _, encounterTable in ipairs (DF:GetCLEncounterIDs()) do
encounterIDEditbox.tooltip = encounterIDEditbox.tooltip .. encounterTable.ID .. " - " .. encounterTable.Name .. "\n"
end
encounterIDEditbox:SetHook ("OnEnterPressed", textEntryOnEnterPressed)
tinsert (f.AllTextEntries, encounterIDEditbox)
--create the text entry for map ID
local mapIDLabel = DF:CreateLabel (f, "Map ID", DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
local mapIDEditbox = DF:CreateTextEntry (f, function()end, 200, 20, "MapEditbox", _, _, DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
mapIDLabel:SetPoint ("topleft", f, "topleft", anchorPositions.map_ids [1], anchorPositions.map_ids [2])
mapIDEditbox:SetPoint ("topleft", mapIDLabel, "bottomleft", 0, -2)
mapIDEditbox.DBKey = "map_ids"
mapIDEditbox.Refresh = textEntryRefresh
mapIDEditbox.tooltip = "Enter multiple IDs separating with a semicolon ()\nExample: 35 45 95"
mapIDEditbox:SetHook ("OnEnterPressed", textEntryOnEnterPressed)
tinsert (f.AllTextEntries, mapIDEditbox)
function f.Refresh (self)
if IS_WOW_PROJECT_MAINLINE then
--update the talents (might have changed if the player changed its specialization)
local talentList = {}
for _, talentTable in ipairs (DF:GetCharacterTalents()) do
tinsert (talentList, {
name = talentTable.Name,
set = DetailsFrameworkLoadConditionsPanel.OnRadioCheckboxClick,
param = talentTable.ID,
get = function() return DetailsFrameworkLoadConditionsPanel.OptionsTable.talent [talentTable.ID] or DetailsFrameworkLoadConditionsPanel.OptionsTable.talent [talentTable.ID .. ""] end,
texture = talentTable.Texture,
})
end
DetailsFrameworkLoadConditionsPanel.TalentGroup:SetOptions (talentList)
end
if IS_WOW_PROJECT_MAINLINE then
local pvpTalentList = {}
for _, talentTable in ipairs (DF:GetCharacterPvPTalents()) do
tinsert (pvpTalentList, {
name = talentTable.Name,
set = DetailsFrameworkLoadConditionsPanel.OnRadioCheckboxClick,
param = talentTable.ID,
get = function() return DetailsFrameworkLoadConditionsPanel.OptionsTable.pvptalent [talentTable.ID] or DetailsFrameworkLoadConditionsPanel.OptionsTable.pvptalent [talentTable.ID .. ""] end,
texture = talentTable.Texture,
})
end
DetailsFrameworkLoadConditionsPanel.PvPTalentGroup:SetOptions (pvpTalentList)
end
--refresh the radio group
for _, radioGroup in ipairs (DetailsFrameworkLoadConditionsPanel.AllRadioGroups) do
radioGroup:Refresh()
DetailsFrameworkLoadConditionsPanel.OnRadioStateChanged (radioGroup, DetailsFrameworkLoadConditionsPanel.OptionsTable [radioGroup.DBKey])
end
--refresh text entries
for _, textEntry in ipairs (DetailsFrameworkLoadConditionsPanel.AllTextEntries) do
textEntry:Refresh()
end
if IS_WOW_PROJECT_MAINLINE then
DetailsFrameworkLoadConditionsPanel.CanShowTalentWarning()
DetailsFrameworkLoadConditionsPanel.CanShowPvPTalentWarning()
end
end
end
--set the options table
DetailsFrameworkLoadConditionsPanel.OptionsTable = optionsTable
--set the callback func
DetailsFrameworkLoadConditionsPanel.CallbackFunc = callback
DetailsFrameworkLoadConditionsPanel.OptionsTable = optionsTable
--set title
DetailsFrameworkLoadConditionsPanel.EditingLabel:SetText (frameOptions.name)
DetailsFrameworkLoadConditionsPanel.Title:SetText (frameOptions.title)
--show the panel to the user
DetailsFrameworkLoadConditionsPanel:Show()
DetailsFrameworkLoadConditionsPanel:Refresh()
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> simple data scroll
DF.DataScrollFunctions = {
RefreshScroll = function (self, data, offset, totalLines)
local filter = self.Filter
local currentData = {}
if (type (filter) == "string" and filter ~= "") then
for i = 1, #data do
for o = 1, #data[i] do
if (data[i][o]:find (filter)) then
tinsert (currentData, data[i])
break
end
end
end
else
currentData = data
end
if (self.SortAlphabetical) then
table.sort (currentData, function(t1, t2) return t1[1] < t2[1] end)
end
--update the scroll
for i = 1, totalLines do
local index = i + offset
local thisData = currentData [index]
if (thisData) then
local line = self:GetLine (i)
line:Update (index, thisData)
end
end
end,
CreateLine = function (self, index)
--create a new line
local line = CreateFrame ("button", "$parentLine" .. index, self, "BackdropTemplate")
line.Update = self.options.update_line_func
--set its parameters
line:SetPoint ("topleft", self, "topleft", 1, -((index-1) * (self.options.line_height+1)) - 1)
line:SetSize (self.options.width - 2, self.options.line_height)
line:RegisterForClicks ("LeftButtonDown", "RightButtonDown")
line:SetScript ("OnEnter", self.options.on_enter)
line:SetScript ("OnLeave", self.options.on_leave)
line:SetScript ("OnClick", self.options.on_click)
line:SetBackdrop (self.options.backdrop)
line:SetBackdropColor (unpack (self.options.backdrop_color))
line:SetBackdropBorderColor (unpack (self.options.backdrop_border_color))
local title = DF:CreateLabel (line, "", DF:GetTemplate ("font", self.options.title_template))
local date = DF:CreateLabel (line, "", DF:GetTemplate ("font", self.options.title_template))
local text = DF:CreateLabel (line, "", DF:GetTemplate ("font", self.options.text_tempate))
title.textsize = 14
date.textsize = 14
text:SetSize (self.options.width - 20, self.options.line_height)
text:SetJustifyV ("top")
--setup anchors
if (self.options.show_title) then
title:SetPoint ("topleft", line, "topleft", 2, 0)
date:SetPoint ("topright", line, "topright", -2, 0)
text:SetPoint ("topleft", title, "bottomleft", 0, -4)
else
text:SetPoint ("topleft", line, "topleft", 2, 0)
end
line.Title = title
line.Date = date
line.Text = text
line.backdrop_color = self.options.backdrop_color or {.1, .1, .1, .3}
line.backdrop_color_highlight = self.options.backdrop_color_highlight or {.3, .3, .3, .5}
return line
end,
LineOnEnter = function (self)
self:SetBackdropColor (unpack (self.backdrop_color_highlight))
end,
LineOnLeave = function (self)
self:SetBackdropColor (unpack (self.backdrop_color))
end,
OnClick = function (self)
end,
UpdateLine = function (line, lineIndex, data)
local parent = line:GetParent()
if (parent.options.show_title) then
line.Title.text = data [2] or ""
line.Date.text = data [3] or ""
line.Text.text = data [4] or ""
else
line.Text.text = data [2] or ""
end
if (line:GetParent().OnUpdateLineHook) then
DF:CoreDispatch ((line:GetName() or "ScrollBoxDataScrollUpdateLineHook") .. ":UpdateLineHook()", line:GetParent().OnUpdateLineHook, line, lineIndex, data)
end
end,
}
local default_datascroll_options = {
width = 400,
height = 700,
line_amount = 10,
line_height = 20,
show_title = true,
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_color_highlight = {.2, .2, .2, 0.4},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
title_template = "ORANGE_FONT_TEMPLATE",
text_tempate = "OPTIONS_FONT_TEMPLATE",
create_line_func = DF.DataScrollFunctions.CreateLine,
update_line_func = DF.DataScrollFunctions.UpdateLine,
refresh_func = DF.DataScrollFunctions.RefreshScroll,
on_enter = DF.DataScrollFunctions.LineOnEnter,
on_leave = DF.DataScrollFunctions.LineOnLeave,
on_click = DF.DataScrollFunctions.OnClick,
data = {},
}
--[=[
Create a scroll frame to show text in an organized way
Functions in the options table can be overritten to customize the layout
@parent = the parent of the frame
@name = the frame name to use in the CreateFrame call
@options = options table to override default values from the table above
--]=]
function DF:CreateDataScrollFrame (parent, name, options)
--call the mixin with a dummy table to built the default options before the frame creation
--this is done because CreateScrollBox needs parameters at creation time
local optionsTable = {}
DF.OptionsFunctions.BuildOptionsTable (optionsTable, default_datascroll_options, options)
optionsTable = optionsTable.options
--scroll frame
local newScroll = DF:CreateScrollBox (parent, name, optionsTable.refresh_func, optionsTable.data, optionsTable.width, optionsTable.height, optionsTable.line_amount, optionsTable.line_height)
DF:ReskinSlider (newScroll)
DF:Mixin (newScroll, DF.OptionsFunctions)
DF:Mixin (newScroll, DF.LayoutFrame)
newScroll:BuildOptionsTable (default_datascroll_options, options)
--create the scrollbox lines
for i = 1, newScroll.options.line_amount do
newScroll:CreateLine (newScroll.options.create_line_func)
end
newScroll:Refresh()
return newScroll
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> "WHAT's NEW" window
local default_newsframe_options = {
width = 400,
height = 700,
line_amount = 16,
line_height = 40,
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
title = "What's New?",
show_title = true,
}
DF.NewsFrameFunctions = {
}
--[=[
Get the amount of news that the player didn't see yet
@newsTable = an indexed table of tables
@lastNewsTime = last time the player opened the news window
--]=]
function DF:GetNumNews (newsTable, lastNewsTime)
local now = time()
local nonReadNews = 0
for _, news in ipairs (newsTable) do
if (news[1] > lastNewsTime) then
nonReadNews = nonReadNews + 1
end
end
return nonReadNews
end
--[=[
Creates a panel with a scroll to show texts organized in separated lines
@parent = the parent of the frame
@name = the frame name to use in the CreateFrame call
@options = options table to override default values from the table above
@newsTable = an indexed table of tables
@db = (optional) an empty table from the addon database to store the position of the frame between game sessions
--]=]
function DF:CreateNewsFrame (parent, name, options, newsTable, db)
local f = DF:CreateSimplePanel (parent, 400, 700, options and options.title or default_newsframe_options.title, name, {UseScaleBar = db and true}, db)
f:SetFrameStrata ("MEDIUM")
DF:ApplyStandardBackdrop (f)
DF:Mixin (f, DF.OptionsFunctions)
DF:Mixin (f, DF.LayoutFrame)
f:BuildOptionsTable (default_newsframe_options, options)
f:SetSize (f.options.width, f.options.height)
f:SetBackdrop (f.options.backdrop)
f:SetBackdropColor (unpack (f.options.backdrop_color))
f:SetBackdropBorderColor (unpack (f.options.backdrop_border_color))
local scrollOptions = {
data = newsTable,
width = f.options.width - 32, --frame distance from walls and scroll bar space
height = f.options.height - 40 + (not f.options.show_title and 20 or 0),
line_amount = f.options.line_amount,
line_height = f.options.line_height,
}
local newsScroll = DF:CreateDataScrollFrame (f, "$parentScroll", scrollOptions)
if (not f.options.show_title) then
f.TitleBar:Hide()
newsScroll:SetPoint ("topleft", f, "topleft", 5, -10)
else
newsScroll:SetPoint ("topleft", f, "topleft", 5, -30)
end
f.NewsScroll = newsScroll
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> statusbar info
--[[
authorTable = {
{
authorName = "author name 1",
link = "twitter.com/author1Handle",
}
}
]]
function DF:BuildStatusbarAuthorInfo (f, addonBy, authorsNameString)
local authorName = DF:CreateLabel (f, "" .. (addonBy or "An addon by ") .. "|cFFFFFFFF" .. (authorsNameString or "Terciob") .. "|r")
authorName.textcolor = "silver"
local discordLabel = DF:CreateLabel (f, "Discord: ")
discordLabel.textcolor = "silver"
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local discordTextEntry = DF:CreateTextEntry (f, function()end, 200, 18, "DiscordTextBox", _, _, options_dropdown_template)
discordTextEntry:SetText ("https://discord.gg/AGSzAZX")
discordTextEntry:SetFrameLevel (5000)
authorName:SetPoint ("left", f, "left", 2, 0)
discordLabel:SetPoint ("left", authorName, "right", 20, 0)
discordTextEntry:SetPoint ("left", discordLabel, "right", 2, 0)
--format
authorName:SetAlpha (.4)
discordLabel:SetAlpha (.4)
discordTextEntry:SetAlpha (.4)
discordTextEntry:SetBackdropBorderColor (1, 1, 1, 0)
discordTextEntry:SetHook ("OnEditFocusGained", function()
discordTextEntry:HighlightText()
end)
f.authorName = authorName
f.discordLabel = discordLabel
f.discordTextEntry = discordTextEntry
end
local statusbar_default_options = {
attach = "bottom", --bottomleft from statusbar attach to bottomleft of the frame | other option is "top": topleft attach to bottomleft
}
function DF:CreateStatusBar(f, options)
local statusBar = CreateFrame ("frame", nil, f, "BackdropTemplate")
DF:Mixin (statusBar, DF.OptionsFunctions)
DF:Mixin (statusBar, DF.LayoutFrame)
statusBar:BuildOptionsTable (statusbar_default_options, options)
if (statusBar.options.attach == "bottom") then
statusBar:SetPoint ("bottomleft", f, "bottomleft")
statusBar:SetPoint ("bottomright", f, "bottomright")
else
statusBar:SetPoint ("topleft", f, "bottomleft")
statusBar:SetPoint ("topright", f, "bottomright")
end
statusBar:SetHeight (20)
DF:ApplyStandardBackdrop (statusBar)
statusBar:SetAlpha (0.8)
return statusBar
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> statusbar mixin
--[=[
collection of functions to embed into a statusbar
statusBar:GetTexture()
statusBar:SetTexture (texture)
statusBar:SetColor (unparsed color)
statusBar:GetColor()
statusBar:
statusBar:
--]=]
DF.StatusBarFunctions = {
GetTexture = function (self)
return self.barTexture:GetTexture()
end,
SetTexture = function (self, texture)
self.barTexture:SetTexture (texture)
end,
SetColor = function (self, r, g, b, a)
r, g, b, a = DF:ParseColors (r, g, b, a)
self:SetStatusBarColor (r, g, b, a)
end,
GetColor = function (self)
return self:GetStatusBarColor()
end,
}
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> health bar frame
--[=[
DF:CreateHealthBar (parent, name, settingsOverride)
creates a health bar to show an unit health
@parent = frame to pass for the CreateFrame function
@name = absolute name of the frame, if omitted it uses the parent's name .. "HealthBar"
@settingsOverride = table with keys and values to replace the defaults from the framework
methods:
healthbar:SetUnit (unit)
healthBar:GetTexture()
healthBar:SetTexture (texture)
--]=]
--debug performance isn't placed anywhere
--how to use debug performance: I don't remember
--Details:Dump (debugPerformance)
local debugPerformance = {
eventCall = {},
unitCall = {},
functionCall = {},
CPUUsageByFunction = {},
}
local function CalcPerformance (type, data)
if (type == "event") then
debugPerformance.eventCall [data] = (debugPerformance.eventCall [data] or 0) + 1
elseif (type == "unit") then
debugPerformance.unitCall [data] = (debugPerformance.unitCall [data] or 0) + 1
elseif (type == "call") then
debugPerformance.functionCall [data] = (debugPerformance.functionCall [data] or 0) + 1
end
end
function DF_CalcCpuUsage (name)
local cpu = debugPerformance.CPUUsageByFunction [name] or {usage = 0, last = 0, active = false}
debugPerformance.CPUUsageByFunction [name] = cpu
if (cpu.active) then
cpu.active = false
local diff = debugprofilestop() - cpu.last
cpu.usage = cpu.usage + diff
else
cpu.active = true
cpu.last = debugprofilestop()
end
end
function UnitFrameStats()
for functionName, functionTable in pairs (debugPerformance.CPUUsageByFunction) do
debugPerformance.CPUUsageByFunction [functionName] = floor (functionTable.usage)
end
for functionName, functionTable in pairs (debugPerformance.CPUUsageByFunction) do
debugPerformance.CPUUsageByFunction [functionName] = {usage = 0, last = 0, active = false}
end
end
--end of performance calcs
--healthBar meta prototype
local healthBarMetaPrototype = {
WidgetType = "healthBar",
SetHook = DF.SetHook,
RunHooksForWidget = DF.RunHooksForWidget,
dversion = DF.dversion,
}
--check if there's a metaPrototype already existing
if (_G[DF.GlobalWidgetControlNames["healthBar"]]) then
--get the already existing metaPrototype
local oldMetaPrototype = _G[DF.GlobalWidgetControlNames ["healthBar"]]
--check if is older
if ( (not oldMetaPrototype.dversion) or (oldMetaPrototype.dversion < DF.dversion) ) then
--the version is older them the currently loading one
--copy the new values into the old metatable
for funcName, _ in pairs(healthBarMetaPrototype) do
oldMetaPrototype[funcName] = healthBarMetaPrototype[funcName]
end
end
else
--first time loading the framework
_G[DF.GlobalWidgetControlNames ["healthBar"]] = healthBarMetaPrototype
end
local healthBarMetaFunctions = _G[DF.GlobalWidgetControlNames ["healthBar"]]
--hook list
local defaultHooksForHealthBar = {
OnHide = {},
OnShow = {},
OnHealthChange = {},
OnHealthMaxChange = {},
}
--use the hook already existing
healthBarMetaFunctions.HookList = healthBarMetaFunctions.HookList or defaultHooksForHealthBar
--copy the non existing values from a new version to the already existing hook table
DF.table.deploy (healthBarMetaFunctions.HookList, defaultHooksForHealthBar)
--> Health Bar Meta Functions
--health bar settings
healthBarMetaFunctions.Settings = {
CanTick = false, --> if true calls the method 'OnTick' every tick, the function needs to be overloaded, it receives self and deltaTime as parameters
ShowHealingPrediction = true, --> when casting a healing pass, show the amount of health that spell will heal
ShowShields = true, --> indicator of the amount of damage absortion the unit has
--appearance
BackgroundColor = DF:CreateColorTable (.2, .2, .2, .8),
Texture = [[Interface\RaidFrame\Raid-Bar-Hp-Fill]],
ShieldIndicatorTexture = [[Interface\RaidFrame\Shield-Fill]],
ShieldGlowTexture = [[Interface\RaidFrame\Shield-Overshield]],
ShieldGlowWidth = 16,
--default size
Width = 100,
Height = 20,
}
healthBarMetaFunctions.HealthBarEvents = {
{"PLAYER_ENTERING_WORLD"},
{"UNIT_HEALTH", true},
{"UNIT_MAXHEALTH", true},
{(IS_WOW_PROJECT_NOT_MAINLINE) and "UNIT_HEALTH_FREQUENT", true}, -- this one is classic-only...
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_HEAL_PREDICTION", true},
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_ABSORB_AMOUNT_CHANGED", true},
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_HEAL_ABSORB_AMOUNT_CHANGED", true},
}
--> setup the castbar to be used by another unit
healthBarMetaFunctions.SetUnit = function (self, unit, displayedUnit)
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
self.unit = unit
self.displayedUnit = displayedUnit or unit
--> register events
if (unit) then
self.currentHealth = UnitHealth (unit) or 0
self.currentHealthMax = UnitHealthMax (unit) or 0
for _, eventTable in ipairs (self.HealthBarEvents) do
local event = eventTable [1]
local isUnitEvent = eventTable [2]
if event then
if (isUnitEvent) then
self:RegisterUnitEvent (event, self.displayedUnit, self.unit)
else
self:RegisterEvent (event)
end
end
end
--> check for settings and update some events
if (not self.Settings.ShowHealingPrediction) then
if IS_WOW_PROJECT_MAINLINE then
self:UnregisterEvent ("UNIT_HEAL_PREDICTION")
self:UnregisterEvent ("UNIT_HEAL_ABSORB_AMOUNT_CHANGED")
end
self.incomingHealIndicator:Hide()
self.healAbsorbIndicator:Hide()
end
if (not self.Settings.ShowShields) then
if IS_WOW_PROJECT_MAINLINE then
self:UnregisterEvent ("UNIT_ABSORB_AMOUNT_CHANGED")
end
self.shieldAbsorbIndicator:Hide()
self.shieldAbsorbGlow:Hide()
end
--> set scripts
self:SetScript ("OnEvent", self.OnEvent)
if (self.Settings.CanTick) then
self:SetScript ("OnUpdate", self.OnTick)
end
self:PLAYER_ENTERING_WORLD (self.unit, self.displayedUnit)
else
--> remove all registered events
for _, eventTable in ipairs (self.HealthBarEvents) do
local event = eventTable [1]
if event then
self:UnregisterEvent (event)
end
end
--> remove scripts
self:SetScript ("OnEvent", nil)
self:SetScript ("OnUpdate", nil)
self:Hide()
end
end
end
healthBarMetaFunctions.Initialize = function (self)
PixelUtil.SetWidth (self, self.Settings.Width, 1)
PixelUtil.SetHeight (self, self.Settings.Height, 1)
self:SetTexture (self.Settings.Texture)
self.background:SetAllPoints()
self.background:SetColorTexture (self.Settings.BackgroundColor:GetColor())
--setpoint of these widgets are set inside the function that updates the incoming heal
self.incomingHealIndicator:SetTexture (self:GetTexture())
self.healAbsorbIndicator:SetTexture (self:GetTexture())
self.healAbsorbIndicator:SetVertexColor (.1, .8, .8)
self.shieldAbsorbIndicator:SetTexture (self.Settings.ShieldIndicatorTexture, true, true)
self.shieldAbsorbGlow:SetWidth (self.Settings.ShieldGlowWidth)
self.shieldAbsorbGlow:SetTexture (self.Settings.ShieldGlowTexture)
self.shieldAbsorbGlow:SetBlendMode ("ADD")
self.shieldAbsorbGlow:SetPoint ("topright", self, "topright", 8, 0)
self.shieldAbsorbGlow:SetPoint ("bottomright", self, "bottomright", 8, 0)
self.shieldAbsorbGlow:Hide()
self:SetUnit (nil)
end
--call every tick
healthBarMetaFunctions.OnTick = function (self, deltaTime) end --if overrided, set 'CanTick' to true on the settings table
--when an event happen for this unit, send it to the apropriate function
healthBarMetaFunctions.OnEvent = function (self, event, ...)
local eventFunc = self [event]
if (eventFunc) then
--the function doesn't receive which event was, only 'self' and the parameters
eventFunc (self, ...)
end
end
--when the unit max health is changed
healthBarMetaFunctions.UpdateMaxHealth = function (self)
local maxHealth = UnitHealthMax (self.displayedUnit)
self:SetMinMaxValues (0, maxHealth)
self.currentHealthMax = maxHealth
self:RunHooksForWidget ("OnHealthMaxChange", self, self.displayedUnit)
end
healthBarMetaFunctions.UpdateHealth = function (self)
-- update max health regardless to avoid weird wrong values on UpdateMaxHealth sometimes
-- local maxHealth = UnitHealthMax (self.displayedUnit)
-- self:SetMinMaxValues (0, maxHealth)
-- self.currentHealthMax = maxHealth
local health = UnitHealth (self.displayedUnit)
self.currentHealth = health
PixelUtil.SetStatusBarValue (self, health)
self:RunHooksForWidget ("OnHealthChange", self, self.displayedUnit)
end
--health and absorbs prediction
healthBarMetaFunctions.UpdateHealPrediction = function (self)
if IS_WOW_PROJECT_NOT_MAINLINE then return end
local currentHealth = self.currentHealth
local currentHealthMax = self.currentHealthMax
local healthPercent = currentHealth / currentHealthMax
if (not currentHealthMax or currentHealthMax <= 0) then
return
end
--order is: the health of the unit > damage absorb > heal absorb > incoming heal
local width = self:GetWidth()
if (self.Settings.ShowHealingPrediction) then
--incoming heal on the unit from all sources
local unitHealIncoming = self.displayedUnit and UnitGetIncomingHeals (self.displayedUnit) or 0
--heal absorbs
local unitHealAbsorb = self.displayedUnit and UnitGetTotalHealAbsorbs (self.displayedUnit) or 0
if (unitHealIncoming > 0) then
--calculate what is the percent of health incoming based on the max health the player has
local incomingPercent = unitHealIncoming / currentHealthMax
self.incomingHealIndicator:Show()
self.incomingHealIndicator:SetWidth (max (1, min (width * incomingPercent, abs (healthPercent - 1) * width)))
self.incomingHealIndicator:SetPoint ("topleft", self, "topleft", width * healthPercent, 0)
self.incomingHealIndicator:SetPoint ("bottomleft", self, "bottomleft", width * healthPercent, 0)
else
self.incomingHealIndicator:Hide()
end
if (unitHealAbsorb > 0) then
local healAbsorbPercent = unitHealAbsorb / currentHealthMax
self.healAbsorbIndicator:Show()
self.healAbsorbIndicator:SetWidth (max (1, min (width * healAbsorbPercent, abs (healthPercent - 1) * width)))
self.healAbsorbIndicator:SetPoint ("topleft", self, "topleft", width * healthPercent, 0)
self.healAbsorbIndicator:SetPoint ("bottomleft", self, "bottomleft", width * healthPercent, 0)
else
self.healAbsorbIndicator:Hide()
end
end
if (self.Settings.ShowShields) then
--damage absorbs
local unitDamageAbsorb = self.displayedUnit and UnitGetTotalAbsorbs (self.displayedUnit) or 0
if (unitDamageAbsorb > 0) then
local damageAbsorbPercent = unitDamageAbsorb / currentHealthMax
self.shieldAbsorbIndicator:Show()
--set the width where the max width size is what is lower: the absorb size or the missing amount of health in the health bar
--/dump NamePlate1PlaterUnitFrameHealthBar.shieldAbsorbIndicator:GetSize()
self.shieldAbsorbIndicator:SetWidth (max (1, min (width * damageAbsorbPercent, abs (healthPercent - 1) * width)))
self.shieldAbsorbIndicator:SetPoint ("topleft", self, "topleft", width * healthPercent, 0)
self.shieldAbsorbIndicator:SetPoint ("bottomleft", self, "bottomleft", width * healthPercent, 0)
--if the absorb percent pass 100%, show the glow
if ((healthPercent + damageAbsorbPercent) > 1) then
self.shieldAbsorbGlow:Show()
else
self.shieldAbsorbGlow:Hide()
end
else
self.shieldAbsorbIndicator:Hide()
self.shieldAbsorbGlow:Hide()
end
else
self.shieldAbsorbIndicator:Hide()
self.shieldAbsorbGlow:Hide()
end
end
--> Health Events
healthBarMetaFunctions.PLAYER_ENTERING_WORLD = function (self, ...)
self:UpdateMaxHealth()
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_HEALTH = function (self, ...)
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_HEALTH_FREQUENT = function (self, ...)
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_MAXHEALTH = function (self, ...)
self:UpdateMaxHealth()
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_HEAL_PREDICTION = function (self, ...)
self:UpdateMaxHealth()
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_ABSORB_AMOUNT_CHANGED = function (self, ...)
self:UpdateMaxHealth()
self:UpdateHealth()
self:UpdateHealPrediction()
end
healthBarMetaFunctions.UNIT_HEAL_ABSORB_AMOUNT_CHANGED = function (self, ...)
self:UpdateMaxHealth()
self:UpdateHealth()
self:UpdateHealPrediction()
end
-- ~healthbar
function DF:CreateHealthBar (parent, name, settingsOverride)
assert (name or parent:GetName(), "DetailsFramework:CreateHealthBar parameter 'name' omitted and parent has no name.")
local healthBar = CreateFrame ("StatusBar", name or (parent:GetName() .. "HealthBar"), parent, "BackdropTemplate")
do --layers
--background
healthBar.background = healthBar:CreateTexture (nil, "background")
healthBar.background:SetDrawLayer ("background", -6)
--artwork
--healing incoming
healthBar.incomingHealIndicator = healthBar:CreateTexture (nil, "artwork")
healthBar.incomingHealIndicator:SetDrawLayer ("artwork", 4)
--current shields on the unit
healthBar.shieldAbsorbIndicator = healthBar:CreateTexture (nil, "artwork")
healthBar.shieldAbsorbIndicator:SetDrawLayer ("artwork", 5)
--debuff absorbing heal
healthBar.healAbsorbIndicator = healthBar:CreateTexture (nil, "artwork")
healthBar.healAbsorbIndicator:SetDrawLayer ("artwork", 6)
--the shield fills all the bar, show that cool glow
healthBar.shieldAbsorbGlow = healthBar:CreateTexture (nil, "artwork")
healthBar.shieldAbsorbGlow:SetDrawLayer ("artwork", 7)
--statusbar texture
healthBar.barTexture = healthBar:CreateTexture (nil, "artwork")
healthBar:SetStatusBarTexture (healthBar.barTexture)
end
--> mixins
DF:Mixin (healthBar, healthBarMetaFunctions)
DF:Mixin (healthBar, DF.StatusBarFunctions)
--> settings and hooks
local settings = DF.table.copy ({}, healthBarMetaFunctions.Settings)
if (settingsOverride) then
DF.table.copy (settings, settingsOverride)
end
healthBar.Settings = settings
--> hook list
healthBar.HookList = DF.table.copy ({}, healthBarMetaFunctions.HookList)
--> initialize the cast bar
healthBar:Initialize()
return healthBar
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> power bar frame
--[=[
DF:CreatePowerBar (parent, name, settingsOverride)
creates statusbar frame to show the unit power bar
@parent = frame to pass for the CreateFrame function
@name = absolute name of the frame, if omitted it uses the parent's name .. "PPowerBar"
@settingsOverride = table with keys and values to replace the defaults from the framework
--]=]
DF.PowerFrameFunctions = {
WidgetType = "powerBar",
SetHook = DF.SetHook,
RunHooksForWidget = DF.RunHooksForWidget,
HookList = {
OnHide = {},
OnShow = {},
},
Settings = {
--> misc
ShowAlternatePower = true, --> if true it'll show alternate power over the regular power the unit uses
ShowPercentText = true, --> if true show a text with the current energy percent
HideIfNoPower = true, --> if true and the UnitMaxPower returns zero, it'll hide the power bar with self:Hide()
CanTick = false, --> if it calls the OnTick function every tick
--appearance
BackgroundColor = DF:CreateColorTable (.2, .2, .2, .8),
Texture = [[Interface\RaidFrame\Raid-Bar-Resource-Fill]],
--> default size
Width = 100,
Height = 20,
},
PowerBarEvents = {
{"PLAYER_ENTERING_WORLD"},
{"UNIT_DISPLAYPOWER", true},
{"UNIT_POWER_BAR_SHOW", true},
{"UNIT_POWER_BAR_HIDE", true},
{"UNIT_MAXPOWER", true},
{"UNIT_POWER_UPDATE", true},
{"UNIT_POWER_FREQUENT", true},
},
--> setup the castbar to be used by another unit
SetUnit = function (self, unit, displayedUnit)
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
self.unit = unit
self.displayedUnit = displayedUnit or unit
--> register events
if (unit) then
for _, eventTable in ipairs (self.PowerBarEvents) do
local event = eventTable [1]
local isUnitEvent = eventTable [2]
if (isUnitEvent) then
self:RegisterUnitEvent (event, self.displayedUnit)
else
self:RegisterEvent (event)
end
end
--> set scripts
self:SetScript ("OnEvent", self.OnEvent)
if (self.Settings.CanTick) then
self:SetScript ("OnUpdate", self.OnTick)
end
self:Show()
self:UpdatePowerBar()
else
--> remove all registered events
for _, eventTable in ipairs (self.PowerBarEvents) do
local event = eventTable [1]
self:UnregisterEvent (event)
end
--> remove scripts
self:SetScript ("OnEvent", nil)
self:SetScript ("OnUpdate", nil)
self:Hide()
end
end
end,
Initialize = function (self)
PixelUtil.SetWidth (self, self.Settings.Width)
PixelUtil.SetHeight (self, self.Settings.Height)
self:SetTexture (self.Settings.Texture)
self.background:SetAllPoints()
self.background:SetColorTexture (self.Settings.BackgroundColor:GetColor())
if (self.Settings.ShowPercentText) then
self.percentText:Show()
PixelUtil.SetPoint (self.percentText, "center", self, "center", 0, 0)
DF:SetFontSize (self.percentText, 9)
DF:SetFontColor (self.percentText, "white")
DF:SetFontOutline (self.percentText, "OUTLINE")
else
self.percentText:Hide()
end
self:SetUnit (nil)
end,
--> call every tick
OnTick = function (self, deltaTime) end, --if overrided, set 'CanTick' to true on the settings table
--> when an event happen for this unit, send it to the apropriate function
OnEvent = function (self, event, ...)
local eventFunc = self [event]
if (eventFunc) then
--the function doesn't receive which event was, only 'self' and the parameters
eventFunc (self, ...)
end
end,
UpdatePowerBar = function (self)
self:UpdatePowerInfo()
self:UpdateMaxPower()
self:UpdatePower()
self:UpdatePowerColor()
end,
--> power update
UpdateMaxPower = function (self)
self.currentPowerMax = UnitPowerMax (self.displayedUnit, self.powerType)
self:SetMinMaxValues (self.minPower, self.currentPowerMax)
if (self.currentPowerMax == 0 and self.Settings.HideIfNoPower) then
self:Hide()
end
end,
UpdatePower = function (self)
self.currentPower = UnitPower (self.displayedUnit, self.powerType)
PixelUtil.SetStatusBarValue (self, self.currentPower)
if (self.Settings.ShowPercentText) then
self.percentText:SetText (floor (self.currentPower / self.currentPowerMax * 100) .. "%")
end
end,
--> when a event different from unit_power_update is triggered, update which type of power the unit should show
UpdatePowerInfo = function (self)
if (IS_WOW_PROJECT_MAINLINE and self.Settings.ShowAlternatePower) then -- not available in classic
local barID = UnitPowerBarID(self.displayedUnit)
local barInfo = GetUnitPowerBarInfoByID(barID)
--local name, tooltip, cost = GetUnitPowerBarStringsByID(barID);
--barInfo.barType,barInfo.minPower, barInfo.startInset, barInfo.endInset, barInfo.smooth, barInfo.hideFromOthers, barInfo.showOnRaid, barInfo.opaqueSpark, barInfo.opaqueFlash, barInfo.anchorTop, name, tooltip, cost, barInfo.ID, barInfo.forcePercentage, barInfo.sparkUnderFrame;
if (barInfo and barInfo.showOnRaid and IsInGroup()) then
self.powerType = ALTERNATE_POWER_INDEX
self.minPower = barInfo.minPower
return
end
end
self.powerType = UnitPowerType (self.displayedUnit)
self.minPower = 0
end,
--> tint the bar with the color of the power, e.g. blue for a mana bar
UpdatePowerColor = function (self)
if (not UnitIsConnected (self.unit)) then
self:SetStatusBarColor (.5, .5, .5)
return
end
if (self.powerType == ALTERNATE_POWER_INDEX) then
--> don't change this, keep the same color as the game tints on CompactUnitFrame.lua
self:SetStatusBarColor (0.7, 0.7, 0.6)
return
end
local powerColor = PowerBarColor [self.powerType] --> don't appear to be, but PowerBarColor is a global table with all power colors /run Details:Dump (PowerBarColor)
if (powerColor) then
self:SetStatusBarColor (powerColor.r, powerColor.g, powerColor.b)
return
end
local _, _, r, g, b = UnitPowerType (self.displayedUnit)
if (r) then
self:SetStatusBarColor (r, g, b)
return
end
--> if everything else fails, tint as rogue energy
powerColor = PowerBarColor ["ENERGY"]
self:SetStatusBarColor (powerColor.r, powerColor.g, powerColor.b)
end,
--> events
PLAYER_ENTERING_WORLD = function (self, ...)
self:UpdatePowerBar()
end,
UNIT_DISPLAYPOWER = function (self, ...)
self:UpdatePowerBar()
end,
UNIT_POWER_BAR_SHOW = function (self, ...)
self:UpdatePowerBar()
end,
UNIT_POWER_BAR_HIDE = function (self, ...)
self:UpdatePowerBar()
end,
UNIT_MAXPOWER = function (self, ...)
self:UpdateMaxPower()
self:UpdatePower()
end,
UNIT_POWER_UPDATE = function (self, ...)
self:UpdatePower()
end,
UNIT_POWER_FREQUENT = function (self, ...)
self:UpdatePower()
end,
}
-- ~powerbar
function DF:CreatePowerBar (parent, name, settingsOverride)
assert (name or parent:GetName(), "DetailsFramework:CreatePowerBar parameter 'name' omitted and parent has no name.")
local powerBar = CreateFrame ("StatusBar", name or (parent:GetName() .. "PowerBar"), parent, "BackdropTemplate")
do --layers
--background
powerBar.background = powerBar:CreateTexture (nil, "background")
powerBar.background:SetDrawLayer ("background", -6)
--artwork
powerBar.barTexture = powerBar:CreateTexture (nil, "artwork")
powerBar:SetStatusBarTexture (powerBar.barTexture)
--overlay
powerBar.percentText = powerBar:CreateFontString (nil, "overlay", "GameFontNormal")
end
--> mixins
DF:Mixin (powerBar, DF.PowerFrameFunctions)
DF:Mixin (powerBar, DF.StatusBarFunctions)
--> settings and hooks
local settings = DF.table.copy ({}, DF.PowerFrameFunctions.Settings)
if (settingsOverride) then
DF.table.copy (settings, settingsOverride)
end
powerBar.Settings = settings
local hookList = DF.table.copy ({}, DF.PowerFrameFunctions.HookList)
powerBar.HookList = hookList
--> initialize the cast bar
powerBar:Initialize()
return powerBar
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> cast bar frame
--[=[
DF:CreateCastBar (parent, name, settingsOverride)
creates a cast bar to show an unit cast
@parent = frame to pass for the CreateFrame function
@name = absolute name of the frame, if omitted it uses the parent's name .. "CastBar"
@settingsOverride = table with keys and values to replace the defaults from the framework
--]=]
DF.CastFrameFunctions = {
WidgetType = "castBar",
SetHook = DF.SetHook,
RunHooksForWidget = DF.RunHooksForWidget,
HookList = {
OnHide = {},
OnShow = {},
--can be regular cast or channel
OnCastStart = {},
},
CastBarEvents = {
{"UNIT_SPELLCAST_INTERRUPTED"},
{"UNIT_SPELLCAST_DELAYED"},
{"UNIT_SPELLCAST_CHANNEL_START"},
{"UNIT_SPELLCAST_CHANNEL_UPDATE"},
{"UNIT_SPELLCAST_CHANNEL_STOP"},
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_INTERRUPTIBLE"},
{(IS_WOW_PROJECT_MAINLINE) and "UNIT_SPELLCAST_NOT_INTERRUPTIBLE"},
{"PLAYER_ENTERING_WORLD"},
{"UNIT_SPELLCAST_START", true},
{"UNIT_SPELLCAST_STOP", true},
{"UNIT_SPELLCAST_FAILED", true},
},
Settings = {
NoFadeEffects = false, --if true it won't play fade effects when a cast if finished
ShowTradeSkills = false, --if true, it shows cast for trade skills, e.g. creating an icon with blacksmith
ShowShield = true, --if true, shows the shield above the spell icon for non interruptible casts
CanTick = true, --if true it will run its OnTick function every tick.
ShowCastTime = true, --if true, show the remaining time to finish the cast, lazy tick must be enabled
FadeInTime = 0.1, --amount of time in seconds to go from zero to 100% alpha when starting to cast
FadeOutTime = 0.5, --amount of time in seconds to go from 100% to zero alpha when the cast finishes
CanLazyTick = true, --if true, it'll execute the lazy tick function, it ticks in a much slower pace comparece with the regular tick
LazyUpdateCooldown = 0.2, --amount of time to wait for the next lazy update, this updates non critical things like the cast timer
--default size
Width = 100,
Height = 20,
--colour the castbar statusbar by the type of the cast
Colors = {
Casting = DF:CreateColorTable (1, 0.73, .1, 1),
Channeling = DF:CreateColorTable (1, 0.73, .1, 1),
Finished = DF:CreateColorTable (0, 1, 0, 1),
NonInterruptible = DF:CreateColorTable (.7, .7, .7, 1),
Failed = DF:CreateColorTable (.4, .4, .4, 1),
Interrupted = DF:CreateColorTable (.965, .754, .154, 1),
},
--appearance
BackgroundColor = DF:CreateColorTable (.2, .2, .2, .8),
Texture = [[Interface\TargetingFrame\UI-StatusBar]],
BorderShieldWidth = 10,
BorderShieldHeight = 12,
BorderShieldCoords = {0.26171875, 0.31640625, 0.53125, 0.65625},
BorderShieldTexture = 1300837,
SpellIconWidth = 10,
SpellIconHeight = 10,
ShieldIndicatorTexture = [[Interface\RaidFrame\Shield-Fill]],
ShieldGlowTexture = [[Interface\RaidFrame\Shield-Overshield]],
SparkTexture = [[Interface\CastingBar\UI-CastingBar-Spark]],
SparkWidth = 16,
SparkHeight = 16,
SparkOffset = 0,
},
Initialize = function (self)
self.unit = "unutilized unit"
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
self.Colors = self.Settings.Colors
self:SetUnit (nil)
PixelUtil.SetWidth (self, self.Settings.Width)
PixelUtil.SetHeight (self, self.Settings.Height)
self.background:SetColorTexture (self.Settings.BackgroundColor:GetColor())
self.background:SetAllPoints()
self.extraBackground:SetColorTexture (0, 0, 0, 1)
self.extraBackground:SetVertexColor (self.Settings.BackgroundColor:GetColor())
self.extraBackground:SetAllPoints()
self:SetTexture (self.Settings.Texture)
self.BorderShield:SetPoint ("center", self, "left", 0, 0)
self.BorderShield:SetTexture (self.Settings.BorderShieldTexture)
self.BorderShield:SetTexCoord (unpack (self.Settings.BorderShieldCoords))
self.BorderShield:SetSize (self.Settings.BorderShieldWidth, self.Settings.BorderShieldHeight)
self.Icon:SetPoint ("center", self, "left", 2, 0)
self.Icon:SetSize (self.Settings.SpellIconWidth, self.Settings.SpellIconHeight)
self.Spark:SetTexture (self.Settings.SparkTexture)
self.Spark:SetSize (self.Settings.SparkWidth, self.Settings.SparkHeight)
self.percentText:SetPoint ("right", self, "right", -2, 0)
self.percentText:SetJustifyH ("right")
self.fadeOutAnimation.alpha1:SetDuration (self.Settings.FadeOutTime)
self.fadeInAnimation.alpha1:SetDuration (self.Settings.FadeInTime)
end,
SetDefaultColor = function (self, colorType, r, g, b, a)
assert (type (colorType) == "string", "DetailsFramework: CastBar:SetDefaultColor require a string in the first argument.")
self.Colors [colorType]:SetColor (r, g, b, a)
end,
--> this get a color suggestion based on the type of cast being shown in the cast bar
GetCastColor = function (self)
if (not self.canInterrupt) then
return self.Colors.NonInterruptible
elseif (self.channeling) then
return self.Colors.Channeling
elseif (self.failed) then
return self.Colors.Failed
elseif (self.interrupted) then
return self.Colors.Interrupted
elseif (self.finished) then
return self.Colors.Finished
else
return self.Colors.Casting
end
end,
--> update all colors of the cast bar
UpdateCastColor = function (self)
local castColor = self:GetCastColor()
self:SetColor (castColor) --SetColor handles with ParseColors()
end,
--> initial checks to know if this is a valid cast and should show the cast bar, if this fails the cast bar won't show
IsValid = function (self, unit, castName, isTradeSkill, ignoreVisibility)
if (not ignoreVisibility and not self:IsShown()) then
return false
end
if (not self.Settings.ShowTradeSkills) then
if (isTradeSkill) then
return false
end
end
if (not castName) then
return false
end
return true
end,
--> handle the interrupt state of the cast
--> this does not change the cast bar color because this function is called inside the start cast where is already handles the cast color
UpdateInterruptState = function (self)
if (self.Settings.ShowShield and not self.canInterrupt) then
self.BorderShield:Show()
else
self.BorderShield:Hide()
end
end,
--> this check if the cast did reach 100% in the statusbar, mostly called from OnTick
CheckCastIsDone = function (self, event, isFinished)
--> check max value
if (not isFinished and not self.finished) then
if (self.casting) then
if (self.value >= self.maxValue) then
isFinished = true
end
elseif (self.channeling) then
if (self.value > self.maxValue or self.value <= 0) then
isFinished = true
end
end
--> check if passed an event (not begin used at the moment)
if (event) then
if (event == UNIT_SPELLCAST_STOP or event == UNIT_SPELLCAST_CHANNEL_STOP) then
isFinished = true
end
end
end
--> the cast is finished
if (isFinished) then
if (self.casting) then
self.UNIT_SPELLCAST_STOP (self, self.unit, self.unit, self.castID, self.spellID)
elseif (self.channeling) then
self.UNIT_SPELLCAST_CHANNEL_STOP (self, self.unit, self.unit, self.castID, self.spellID)
end
return true
end
end,
--> setup the castbar to be used by another unit
SetUnit = function (self, unit, displayedUnit)
if (self.unit ~= unit or self.displayedUnit ~= displayedUnit or unit == nil) then
self.unit = unit
self.displayedUnit = displayedUnit or unit
--> reset the cast bar
self.casting = nil
self.channeling = nil
self.caninterrupt = nil
--> register events
if (unit) then
for _, eventTable in ipairs (self.CastBarEvents) do
local event = eventTable [1]
local isUnitEvent = eventTable [2]
if event then
if (isUnitEvent) then
self:RegisterUnitEvent (event, unit)
else
self:RegisterEvent (event)
end
end
end
--> set scripts
self:SetScript ("OnEvent", self.OnEvent)
self:SetScript ("OnShow", self.OnShow)
self:SetScript ("OnHide", self.OnHide)
if (self.Settings.CanTick) then
self:SetScript ("OnUpdate", self.OnTick)
end
--> check is can show the cast time text
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
self.percentText:Show()
else
self.percentText:Hide()
end
--> setup animtions
self:CancelScheduleToHide()
--self:PLAYER_ENTERING_WORLD (unit, unit)
self:OnEvent ("PLAYER_ENTERING_WORLD", unit, unit)
else
for _, eventTable in ipairs (self.CastBarEvents) do
local event = eventTable [1]
if event then
self:UnregisterEvent (event)
end
end
--> register main events
self:SetScript ("OnUpdate", nil)
self:SetScript ("OnEvent", nil)
self:SetScript ("OnShow", nil)
self:SetScript ("OnHide", nil)
self:Hide()
end
end
end,
--> executed after a scheduled to hide timer is done
DoScheduledHide = function (timerObject)
timerObject.castBar.scheduledHideTime = nil
--just to make sure it isn't casting
if (not timerObject.castBar.casting and not timerObject.castBar.channeling) then
if (not timerObject.castBar.Settings.NoFadeEffects) then
timerObject.castBar:Animation_FadeOut()
else
timerObject.castBar:Hide()
end
end
end,
HasScheduledHide = function (self)
return self.scheduledHideTime and not self.scheduledHideTime._cancelled
end,
CancelScheduleToHide = function (self)
if (self:HasScheduledHide()) then
self.scheduledHideTime:Cancel()
end
end,
--> after an interrupt, do not immediately hide the cast bar, let it up for short amount of time to give feedback to the player
ScheduleToHide = function (self, delay)
if (not delay) then
if (self.scheduledHideTime and not self.scheduledHideTime._cancelled) then
self.scheduledHideTime:Cancel()
end
self.scheduledHideTime = nil
return
end
--> already have a scheduled timer?
if (self.scheduledHideTime and not self.scheduledHideTime._cancelled) then
self.scheduledHideTime:Cancel()
end
self.scheduledHideTime = C_Timer.NewTimer (delay, self.DoScheduledHide)
self.scheduledHideTime.castBar = self
end,
OnHide = function (self)
--> just in case some other effects made it have a different alpha since SetUnit won't load if the unit is the same.
self:SetAlpha (1)
--> cancel any timer to hide scheduled
self:CancelScheduleToHide()
end,
--> just update the current value if a spell is being cast since it wasn't running its tick function during the hide state
--> everything else should be in the correct state
OnShow = function (self)
self.flashTexture:Hide()
if (self.unit) then
if (self.casting) then
local name, text, texture, startTime = UnitCastingInfo (self.unit)
if (name) then
self.value = GetTime() - self.spellStartTime
end
self:RunHooksForWidget ("OnShow", self, self.unit)
elseif (self.channeling) then
local name, text, texture, endTime = UnitChannelInfo (self.unit)
if (name) then
self.value = self.spellEndTime - GetTime()
end
self:RunHooksForWidget ("OnShow", self, self.unit)
end
end
end,
--it's triggering several events since it's not registered for the unit with RegisterUnitEvent
OnEvent = function (self, event, ...)
local arg1 = ...
local unit = self.unit
if (event == "PLAYER_ENTERING_WORLD") then
local newEvent = self.PLAYER_ENTERING_WORLD (self, unit, ...)
if (newEvent) then
self.OnEvent (self, newEvent, unit)
return
end
elseif (arg1 ~= unit) then
return
end
local eventFunc = self [event]
if (eventFunc) then
eventFunc (self, unit, ...)
end
end,
OnTick_LazyTick = function (self)
--> run the lazy tick if allowed
if (self.Settings.CanLazyTick) then
--> update the cast time
if (self.Settings.ShowCastTime) then
if (self.casting) then
self.percentText:SetText (format ("%.1f", abs (self.value - self.maxValue)))
elseif (self.channeling) then
local remainingTime = abs (self.value)
if (remainingTime > 999) then
self.percentText:SetText ("")
else
self.percentText:SetText (format ("%.1f", remainingTime))
end
else
self.percentText:SetText ("")
end
end
return true
else
return false
end
end,
--> tick function for regular casts
OnTick_Casting = function (self, deltaTime)
self.value = self.value + deltaTime
if (self:CheckCastIsDone()) then
return
else
self:SetValue (self.value)
end
--update spark position
local sparkPosition = self.value / self.maxValue * self:GetWidth()
self.Spark:SetPoint ("center", self, "left", sparkPosition + self.Settings.SparkOffset, 0)
--in order to allow the lazy tick run, it must return true, it tell that the cast didn't finished
return true
end,
--> tick function for channeling casts
OnTick_Channeling = function (self, deltaTime)
self.value = self.value - deltaTime
if (self:CheckCastIsDone()) then
return
else
self:SetValue (self.value)
end
--update spark position
local sparkPosition = self.value / self.maxValue * self:GetWidth()
self.Spark:SetPoint ("center", self, "left", sparkPosition + self.Settings.SparkOffset, 0)
return true
end,
OnTick = function (self, deltaTime)
if (self.casting) then
if (not self:OnTick_Casting (deltaTime)) then
return
end
--lazy tick
self.lazyUpdateCooldown = self.lazyUpdateCooldown - deltaTime
if (self.lazyUpdateCooldown < 0) then
self:OnTick_LazyTick()
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
end
elseif (self.channeling) then
if (not self:OnTick_Channeling (deltaTime)) then
return
end
--lazy tick
self.lazyUpdateCooldown = self.lazyUpdateCooldown - deltaTime
if (self.lazyUpdateCooldown < 0) then
self:OnTick_LazyTick()
self.lazyUpdateCooldown = self.Settings.LazyUpdateCooldown
end
end
end,
--> animation start script
Animation_FadeOutStarted = function (self)
end,
--> animation finished script
Animation_FadeOutFinished = function (self)
local castBar = self:GetParent()
castBar:SetAlpha (1)
castBar:Hide()
end,
--> animation start script
Animation_FadeInStarted = function (self)
end,
--> animation finished script
Animation_FadeInFinished = function (self)
local castBar = self:GetParent()
castBar:Show()
castBar:SetAlpha (1)
end,
--> animation calls
Animation_FadeOut = function (self)
self:ScheduleToHide (false)
if (self.fadeInAnimation:IsPlaying()) then
self.fadeInAnimation:Stop()
end
if (not self.fadeOutAnimation:IsPlaying()) then
self.fadeOutAnimation:Play()
end
end,
Animation_FadeIn = function (self)
self:ScheduleToHide (false)
if (self.fadeOutAnimation:IsPlaying()) then
self.fadeOutAnimation:Stop()
end
if (not self.fadeInAnimation:IsPlaying()) then
self.fadeInAnimation:Play()
end
end,
Animation_Flash = function (self)
if (not self.flashAnimation:IsPlaying()) then
self.flashAnimation:Play()
end
end,
Animation_StopAllAnimations = function (self)
if (self.flashAnimation:IsPlaying()) then
self.flashAnimation:Stop()
end
if (self.fadeOutAnimation:IsPlaying()) then
self.fadeOutAnimation:Stop()
end
if (self.fadeInAnimation:IsPlaying()) then
self.fadeInAnimation:Stop()
end
end,
PLAYER_ENTERING_WORLD = function (self, unit, arg1)
local isChannel = UnitChannelInfo (unit)
local isRegularCast = UnitCastingInfo (unit)
if (isChannel) then
self.channeling = true
return self.unit == arg1 and "UNIT_SPELLCAST_CHANNEL_START"
elseif (isRegularCast) then
self.casting = true
return self.unit == arg1 and "UNIT_SPELLCAST_START"
else
self.casting = nil
self.channeling = nil
self.failed = nil
self.finished = nil
self.interrupted = nil
self.Spark:Hide()
self:Hide()
end
end,
UNIT_SPELLCAST_START = function (self, unit)
local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID
if IS_WOW_PROJECT_MAINLINE then
name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID = UnitCastingInfo (unit)
else
name, text, texture, startTime, endTime, isTradeSkill, castID, spellID = UnitCastingInfo (unit)
notInterruptible = false
end
--> is valid?
if (not self:IsValid (unit, name, isTradeSkill, true)) then
return
end
--> setup cast
self.casting = true
self.channeling = nil
self.interrupted = nil
self.failed = nil
self.finished = nil
self.canInterrupt = not notInterruptible
self.spellID = spellID
self.castID = castID
self.spellName = name
self.spellTexture = texture
self.spellStartTime = startTime / 1000
self.spellEndTime = endTime / 1000
self.value = GetTime() - self.spellStartTime
self.maxValue = self.spellEndTime - self.spellStartTime
self:SetMinMaxValues (0, self.maxValue)
self:SetValue (self.value)
self:SetAlpha (1)
self.Icon:SetTexture (texture)
self.Icon:Show()
self.Text:SetText (text)
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
self.percentText:Show()
end
self.flashTexture:Hide()
self:Animation_StopAllAnimations()
self:SetAlpha (1)
--> set the statusbar color
self:UpdateCastColor()
if (not self:IsShown()) then
self:Animation_FadeIn()
end
self.Spark:Show()
self:Show()
--> update the interrupt cast border
self:UpdateInterruptState()
self:RunHooksForWidget ("OnCastStart", self, self.unit, "UNIT_SPELLCAST_START")
end,
UNIT_SPELLCAST_CHANNEL_START = function (self, unit, ...)
local name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID
if IS_WOW_PROJECT_MAINLINE then
name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID = UnitChannelInfo (unit)
else
name, text, texture, startTime, endTime, isTradeSkill, spellID = UnitChannelInfo (unit)
end
--> is valid?
if (not self:IsValid (unit, name, isTradeSkill, true)) then
return
end
--> setup cast
self.casting = nil
self.channeling = true
self.interrupted = nil
self.failed = nil
self.finished = nil
self.canInterrupt = not notInterruptible
self.spellID = spellID
self.castID = castID
self.spellName = name
self.spellTexture = texture
self.spellStartTime = startTime / 1000
self.spellEndTime = endTime / 1000
self.value = self.spellEndTime - GetTime()
self.maxValue = self.spellEndTime - self.spellStartTime
self:SetMinMaxValues (0, self.maxValue)
self:SetValue (self.value)
self:SetAlpha (1)
self.Icon:SetTexture (texture)
self.Icon:Show()
self.Text:SetText (text)
if (self.Settings.ShowCastTime and self.Settings.CanLazyTick) then
self.percentText:Show()
end
self.flashTexture:Hide()
self:Animation_StopAllAnimations()
self:SetAlpha (1)
--> set the statusbar color
self:UpdateCastColor()
if (not self:IsShown()) then
self:Animation_FadeIn()
end
self.Spark:Show()
self:Show()
--> update the interrupt cast border
self:UpdateInterruptState()
self:RunHooksForWidget ("OnCastStart", self, self.unit, "UNIT_SPELLCAST_CHANNEL_START")
end,
UNIT_SPELLCAST_STOP = function (self, unit, ...)
local unitID, castID, spellID = ...
if (self.castID == castID) then
self.Spark:Hide()
self.percentText:Hide()
local value = self:GetValue()
local _, maxValue = self:GetMinMaxValues()
self:SetValue (self.maxValue or maxValue or 1)
self.casting = nil
self.finished = true
if (not self:HasScheduledHide()) then
--> check if settings has no fade option or if its parents are not visible
if (not self:IsVisible()) then
self:Hide()
elseif (self.Settings.NoFadeEffects) then
self:ScheduleToHide (0.3)
else
self:Animation_Flash()
self:Animation_FadeOut()
end
end
self:UpdateCastColor()
end
end,
UNIT_SPELLCAST_CHANNEL_STOP = function (self, unit, ...)
local unitID, castID, spellID = ...
if (self.channeling and castID == self.castID) then
self.Spark:Hide()
self.percentText:Hide()
local value = self:GetValue()
local _, maxValue = self:GetMinMaxValues()
self:SetValue (self.maxValue or maxValue or 1)
self.channeling = nil
self.finished = true
if (not self:HasScheduledHide()) then
--> check if settings has no fade option or if its parents are not visible
if (not self:IsVisible()) then
self:Hide()
elseif (self.Settings.NoFadeEffects) then
self:ScheduleToHide (0.3)
else
self:Animation_Flash()
self:Animation_FadeOut()
end
end
self:UpdateCastColor()
end
end,
UNIT_SPELLCAST_FAILED = function (self, unit, ...)
local unitID, castID, spellID = ...
if (self.casting and castID == self.castID and not self.fadeOut) then
self.casting = nil
self.channeling = nil
self.failed = true
self.finished = true
self:SetValue (self.maxValue or select (2, self:GetMinMaxValues()) or 1)
--> set the statusbar color
self:UpdateCastColor()
self.Spark:Hide()
self.percentText:Hide()
self.Text:SetText (FAILED) --> auto locale within the global namespace
self:ScheduleToHide (1)
end
end,
UNIT_SPELLCAST_INTERRUPTED = function (self, unit, ...)
local unitID, castID, spellID = ...
if (self.casting and castID == self.castID and not self.fadeOut) then
self.casting = nil
self.channeling = nil
self.interrupted = true
self.finished = true
self:SetValue (self.maxValue or select (2, self:GetMinMaxValues()) or 1)
local castColor = self:GetCastColor()
self:SetColor (castColor) --SetColor handles with ParseColors()
self.Spark:Hide()
self.percentText:Hide()
self.Text:SetText (INTERRUPTED) --> auto locale within the global namespace
self:ScheduleToHide (1)
end
end,
UNIT_SPELLCAST_DELAYED = function (self, unit, ...)
local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible = UnitCastingInfo (unit)
if (not self:IsValid (unit, name, isTradeSkill)) then
return
end
--> update the cast time
self.spellStartTime = startTime / 1000
self.spellEndTime = endTime / 1000
self.value = GetTime() - self.spellStartTime
self.maxValue = self.spellEndTime - self.spellStartTime
self:SetMinMaxValues (0, self.maxValue)
end,
UNIT_SPELLCAST_CHANNEL_UPDATE = function (self, unit, ...)
local name, text, texture, startTime, endTime, isTradeSkill = UnitChannelInfo (unit)
if (not self:IsValid (unit, name, isTradeSkill)) then
return
end
--> update the cast time
self.spellStartTime = startTime / 1000
self.spellEndTime = endTime / 1000
self.value = self.spellEndTime - GetTime()
if (self.value < 0) then
self.value = 0
end
self.maxValue = self.spellEndTime - self.spellStartTime
self:SetMinMaxValues (0, self.maxValue)
self:SetValue (self.value)
end,
--> cast changed its state to interruptable
UNIT_SPELLCAST_INTERRUPTIBLE = function (self, unit, ...)
self.canInterrupt = true
self:UpdateCastColor()
self:UpdateInterruptState()
end,
--> cast changed its state to non interruptable
UNIT_SPELLCAST_NOT_INTERRUPTIBLE = function (self, unit, ...)
self.canInterrupt = false
self:UpdateCastColor()
self:UpdateInterruptState()
end,
}
-- ~castbar
function DF:CreateCastBar (parent, name, settingsOverride)
assert (name or parent:GetName(), "DetailsFramework:CreateCastBar parameter 'name' omitted and parent has no name.")
local castBar = CreateFrame ("StatusBar", name or (parent:GetName() .. "CastBar"), parent, "BackdropTemplate")
do --layers
--these widgets was been made with back compatibility in mind
--they are using the same names as the retail game uses on the nameplate castbar
--this should make Plater core and Plater scripts made by users compatible with the new unit frame made on the framework
--background
castBar.background = castBar:CreateTexture (nil, "background")
castBar.background:SetDrawLayer ("background", -6)
castBar.extraBackground = castBar:CreateTexture (nil, "background")
castBar.extraBackground:SetDrawLayer ("background", -5)
--overlay
castBar.Text = castBar:CreateFontString (nil, "overlay", "SystemFont_Shadow_Small")
castBar.Text:SetPoint ("center", 0, 0)
castBar.Text:SetDrawLayer ("overlay", 1)
castBar.BorderShield = castBar:CreateTexture (nil, "overlay")
castBar.BorderShield:SetDrawLayer ("overlay", 5)
castBar.BorderShield:Hide()
castBar.Icon = castBar:CreateTexture (nil, "overlay")
castBar.Icon:SetDrawLayer ("overlay", 4)
castBar.Icon:Hide()
castBar.Spark = castBar:CreateTexture (nil, "overlay")
castBar.Spark:SetDrawLayer ("overlay", 3)
castBar.Spark:SetBlendMode ("ADD")
--time left on the cast
castBar.percentText = castBar:CreateFontString (nil, "overlay", "SystemFont_Shadow_Small")
castBar.percentText:SetDrawLayer ("overlay", 7)
--statusbar texture
castBar.barTexture = castBar:CreateTexture (nil, "artwork")
castBar:SetStatusBarTexture (castBar.barTexture)
--animations fade in and out
local fadeOutAnimationHub = DF:CreateAnimationHub (castBar, DF.CastFrameFunctions.Animation_FadeOutStarted, DF.CastFrameFunctions.Animation_FadeOutFinished)
fadeOutAnimationHub.alpha1 = DF:CreateAnimation (fadeOutAnimationHub, "ALPHA", 1, 1, 1, 0)
castBar.fadeOutAnimation = fadeOutAnimationHub
local fadeInAnimationHub = DF:CreateAnimationHub (castBar, DF.CastFrameFunctions.Animation_FadeInStarted, DF.CastFrameFunctions.Animation_FadeInFinished)
fadeInAnimationHub.alpha1 = DF:CreateAnimation (fadeInAnimationHub, "ALPHA", 1, 0.150, 0, 1)
castBar.fadeInAnimation = fadeInAnimationHub
--animatios flash
local flashTexture = castBar:CreateTexture (nil, "overlay")
flashTexture:SetDrawLayer ("overlay", 7)
flashTexture:SetColorTexture (1, 1, 1, 1)
flashTexture:SetAllPoints()
flashTexture:SetAlpha (0)
flashTexture:Hide()
flashTexture:SetBlendMode ("ADD")
castBar.flashTexture = flashTexture
local flashAnimationHub = DF:CreateAnimationHub (flashTexture, function() flashTexture:Show() end, function() flashTexture:Hide() end)
DF:CreateAnimation (flashAnimationHub, "ALPHA", 1, 0.2, 0, 0.8)
DF:CreateAnimation (flashAnimationHub, "ALPHA", 2, 0.2, 1, 0)
castBar.flashAnimation = flashAnimationHub
end
--> mixins
DF:Mixin (castBar, DF.CastFrameFunctions)
DF:Mixin (castBar, DF.StatusBarFunctions)
--> settings and hooks
local settings = DF.table.copy ({}, DF.CastFrameFunctions.Settings)
if (settingsOverride) then
DF.table.copy (settings, settingsOverride)
end
castBar.Settings = settings
local hookList = DF.table.copy ({}, DF.CastFrameFunctions.HookList)
castBar.HookList = hookList
--> initialize the cast bar
castBar:Initialize()
return castBar
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> border frame
--[=[
DF:CreateBorderFrame (parent, name)
creates a frame with 4 child textures attached to each one of the 4 sides of a frame
@parent = parent frame to pass to CreateFrame function
@name = name of the frame, if omitted a random name is created
--]=]
DF.BorderFunctions = {
SetBorderColor = function (self, r, g, b, a)
r, g, b, a = DF:ParseColors (r, g, b, a)
for _, texture in ipairs (self.allTextures) do
texture:SetVertexColor (r, g, b, a)
end
end,
SetBorderThickness = function (self, newThickness)
PixelUtil.SetWidth (self.leftBorder, newThickness, newThickness)
PixelUtil.SetWidth (self.rightBorder, newThickness, newThickness)
PixelUtil.SetHeight (self.topBorder, newThickness, newThickness)
PixelUtil.SetHeight (self.bottomBorder, newThickness, newThickness)
end,
WidgetType = "border",
}
-- ~borderframe
function DF:CreateBorderFrame (parent, name)
local parentName = name or "DetailsFrameworkBorderFrame" .. tostring (math.random (1, 100000000))
local f = CreateFrame ("frame", parentName, parent, "BackdropTemplate")
f:SetFrameLevel (f:GetFrameLevel()+1)
f:SetAllPoints()
DF:Mixin (f, DF.BorderFunctions)
f.allTextures = {}
--> create left border
local leftBorder = f:CreateTexture (nil, "overlay")
leftBorder:SetDrawLayer ("overlay", 7)
leftBorder:SetColorTexture (1, 1, 1, 1)
tinsert (f.allTextures, leftBorder)
f.leftBorder = leftBorder
PixelUtil.SetPoint (leftBorder, "topright", f, "topleft", 0, 1, 0, 1)
PixelUtil.SetPoint (leftBorder, "bottomright", f, "bottomleft", 0, -1, 0, -1)
PixelUtil.SetWidth (leftBorder, 1, 1)
--> create right border
local rightBorder = f:CreateTexture (nil, "overlay")
rightBorder:SetDrawLayer ("overlay", 7)
rightBorder:SetColorTexture (1, 1, 1, 1)
tinsert (f.allTextures, rightBorder)
f.rightBorder = rightBorder
PixelUtil.SetPoint (rightBorder, "topleft", f, "topright", 0, 1, 0, 1)
PixelUtil.SetPoint (rightBorder, "bottomleft", f, "bottomright", 0, -1, 0, -1)
PixelUtil.SetWidth (rightBorder, 1, 1)
--> create top border
local topBorder = f:CreateTexture (nil, "overlay")
topBorder:SetDrawLayer ("overlay", 7)
topBorder:SetColorTexture (1, 1, 1, 1)
tinsert (f.allTextures, topBorder)
f.topBorder = topBorder
PixelUtil.SetPoint (topBorder, "bottomleft", f, "topleft", 0, 0, 0, 0)
PixelUtil.SetPoint (topBorder, "bottomright", f, "topright", 0, 0, 0, 0)
PixelUtil.SetHeight (topBorder, 1, 1)
--> create border
local bottomBorder = f:CreateTexture (nil, "overlay")
bottomBorder:SetDrawLayer ("overlay", 7)
bottomBorder:SetColorTexture (1, 1, 1, 1)
tinsert (f.allTextures, bottomBorder)
f.bottomBorder = bottomBorder
PixelUtil.SetPoint (bottomBorder, "topleft", f, "bottomleft", 0, 0, 0, 0)
PixelUtil.SetPoint (bottomBorder, "topright", f, "bottomright", 0, 0, 0, 0)
PixelUtil.SetHeight (bottomBorder, 1, 1)
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> unit frame
--[=[
DF:CreateUnitFrame (parent, name, settingsOverride)
creates a very basic unit frame with a healthbar, castbar and power bar
each unit frame has a .Settings table which isn't shared among other unit frames created with this method
all members names are the same as the unit frame from the retail game
@parent = frame to pass for the CreateFrame function
@name = absolute name of the frame, if omitted a random name is created
@settingsOverride = table with keys and values to replace the defaults from the framework
--]=]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> unit frame
--> return true if the unit has been claimed by another player (health bar is gray)
local unit_is_tap_denied = function (unit)
return unit and not UnitPlayerControlled (unit) and UnitIsTapDenied (unit)
end
DF.UnitFrameFunctions = {
WidgetType = "unitFrame",
Settings = {
--> unit frames
ClearUnitOnHide = true, --> if tue it'll set the unit to nil when the unit frame is set to hide
ShowCastBar = true, --if this is false, the cast bar for the unit won't be shown
ShowPowerBar = true, --if true it'll show the power bar for the unit, e.g. the mana bar
ShowUnitName = true, --if false, the unit name won't show
ShowBorder = true, --if false won't show the border frame
--> health bar color
CanModifyHealhBarColor = true, --> if false it won't change the color of the health bar
ColorByAggro = false, --if true it'll color the healthbar with red color when the unit has aggro on player
FixedHealthColor = false, --color override with a table {r=1, g=1, b=1}
UseFriendlyClassColor = true, --make the healthbar class color for friendly players
UseEnemyClassColor = true, --make the healthbar class color for enemy players
--> misc
ShowTargetOverlay = true, --shows a highlighht for the player current target
BorderColor = DF:CreateColorTable (0, 0, 0, 1), --border color, set to alpha zero for no border
CanTick = false, --if true it'll run the OnTick event
--> size
Width = 100,
Height = 20,
PowerBarHeight = 4,
CastBarHeight = 8,
},
UnitFrameEvents = {
--> run for all units
{"PLAYER_ENTERING_WORLD"},
{"PARTY_MEMBER_DISABLE"},
{"PARTY_MEMBER_ENABLE"},
{"PLAYER_TARGET_CHANGED"},
--> run for one unit
{"UNIT_NAME_UPDATE", true},
{"UNIT_CONNECTION", true},
{"UNIT_ENTERED_VEHICLE", true},
{"UNIT_EXITED_VEHICLE", true},
{"UNIT_PET", true},
{"UNIT_THREAT_LIST_UPDATE", true},
},
--> used when a event is triggered to quickly check if is a unit event
IsUnitEvent = {
["UNIT_NAME_UPDATE"] = true,
["UNIT_CONNECTION"] = true,
["UNIT_ENTERED_VEHICLE"] = true,
["UNIT_EXITED_VEHICLE"] = true,
["UNIT_PET"] = true,
["UNIT_THREAT_LIST_UPDATE"] = true,
},
Initialize = function (self)
self.border:SetBorderColor (self.Settings.BorderColor)
PixelUtil.SetWidth (self, self.Settings.Width, 1)
PixelUtil.SetHeight (self, self.Settings.Height, 1)
PixelUtil.SetPoint (self.powerBar, "bottomleft", self, "bottomleft", 0, 0, 1, 1)
PixelUtil.SetPoint (self.powerBar, "bottomright", self, "bottomright", 0, 0, 1, 1)
PixelUtil.SetHeight (self.powerBar, self.Settings.PowerBarHeight, 1)
--make the castbar overlap the powerbar
PixelUtil.SetPoint (self.castBar, "bottomleft", self, "bottomleft", 0, 0, 1, 1)
PixelUtil.SetPoint (self.castBar, "bottomright", self, "bottomright", 0, 0, 1, 1)
PixelUtil.SetHeight (self.castBar, self.Settings.CastBarHeight, 1)
end,
SetHealthBarColor = function (self, r, g, b, a)
self.healthBar:SetColor (r, g, b, a)
end,
--> register all events which will be used by the unit frame
RegisterEvents = function (self)
--> register events
for index, eventTable in ipairs (self.UnitFrameEvents) do
local event, isUnitEvent = unpack (eventTable)
if (not isUnitEvent) then
self:RegisterEvent (event)
else
self:RegisterUnitEvent (event, self.unit, self.displayedUnit ~= unit and self.displayedUnit or nil)
end
end
--> check settings and unregister events for disabled features
if (not self.Settings.ColorByAggro) then
self:UnregisterEvent ("UNIT_THREAT_LIST_UPDATE")
end
--> set scripts
self:SetScript ("OnEvent", self.OnEvent)
self:SetScript ("OnHide", self.OnHide)
if (self.Settings.CanTick) then
self:SetScript ("OnUpdate", self.OnTick)
end
end,
--> unregister events, called when this unit frame losses its unit
UnregisterEvents = function (self)
for index, eventTable in ipairs (self.UnitFrameEvents) do
local event, firstUnit, secondUnit = unpack (eventTable)
self:UnregisterEvent (event)
end
self:SetScript ("OnEvent", nil)
self:SetScript ("OnUpdate", nil)
self:SetScript ("OnHide", nil)
end,
--> call every tick
OnTick = function (self, deltaTime) end, --if overrided, set 'CanTick' to true on the settings table
--> when an event happen for this unit, send it to the apropriate function
OnEvent = function (self, event, ...)
--> run the function for this event
local eventFunc = self [event]
if (eventFunc) then
--> is this event an unit event?
if (self.IsUnitEvent [event]) then
local unit = ...
--> check if is for this unit (even if the event is registered only for the unit)
if (unit == self.unit or unit == self.displayedUnit) then
eventFunc (self, ...)
end
else
eventFunc (self, ...)
end
end
end,
OnHide = function (self)
if (self.Settings.ClearUnitOnHide) then
self:SetUnit (nil)
end
end,
--> run if the unit currently shown is different than the new one
SetUnit = function (self, unit)
if (unit ~= self.unit or unit == nil) then
self.unit = unit --absolute unit
self.displayedUnit = unit --~todo rename to 'displayedUnit' for back compatibility with older scripts in Plater
self.unitInVehicle = nil --true when the unit is in a vehicle
if (unit) then
self:RegisterEvents()
self.healthBar:SetUnit (unit, self.displayedUnit)
--> is using castbars?
if (self.Settings.ShowCastBar) then
self.castBar:SetUnit (unit, self.displayedUnit)
else
self.castBar:SetUnit (nil)
end
--> is using powerbars?
if (self.Settings.ShowPowerBar) then
self.powerBar:SetUnit (unit, self.displayedUnit)
else
self.powerBar:SetUnit (nil)
end
--> is using the border?
if (self.Settings.ShowBorder) then
self.border:Show()
else
self.border:Hide()
end
if (not self.Settings.ShowUnitName) then
self.unitName:Hide()
end
else
self:UnregisterEvents()
self.healthBar:SetUnit (nil)
self.castBar:SetUnit (nil)
self.powerBar:SetUnit (nil)
end
self:UpdateUnitFrame()
end
end,
--> if the unit is controlling a vehicle, need to show the vehicle instead
--> .unit and .displayedUnit is always the same execept when the unit is controlling a vehicle, then .displayedUnit is the unitID for the vehicle
--> todo: see what 'UnitTargetsVehicleInRaidUI' is, there's a call for this in the CompactUnitFrame.lua but zero documentation
CheckVehiclePossession = function (self)
--> this unit is possessing a vehicle?
local unitPossessVehicle = (IS_WOW_PROJECT_MAINLINE) and UnitHasVehicleUI (self.unit) or false
if (unitPossessVehicle) then
if (not self.unitInVehicle) then
if (UnitIsUnit ("player", self.unit)) then
self.displayedUnit = "vehicle"
self.unitInVehicle = true
self:RegisterEvents()
self:UpdateAllWidgets()
return true
end
local prefix, id, suffix = string.match (self.unit, "([^%d]+)([%d]*)(.*)") --CompactUnitFrame.lua
local vehicleUnitID = prefix .. "pet" .. id .. suffix
if (UnitExists (vehicleUnitID)) then
self.displayedUnit = vehicleUnitID
self.unitInVehicle = true
self:RegisterEvents()
self:UpdateAllWidgets()
return true
end
end
end
if (self.unitInVehicle) then
self.displayedUnit = self.unit
self.unitInVehicle = nil
self:RegisterEvents()
self:UpdateAllWidgets()
end
end,
--> find a color for the health bar, if a color has been passed in the arguments use it instead, 'CanModifyHealhBarColor' must be true for this function run
UpdateHealthColor = function (self, r, g, b)
--> check if color changes is disabled
if (not self.Settings.CanModifyHealhBarColor) then
return
end
local unit = self.displayedUnit
--> check if a color has been passed within the parameters
if (r) then
--> check if passed a special color
if (type (r) ~= "number") then
r, g, b = DF:ParseColors (r)
end
self:SetHealthBarColor (r, g, b)
return
end
--> check if there is a color override in the settings
if (self.Settings.FixedHealthColor) then
local FixedHealthColor = self.Settings.FixedHealthColor
r, g, b = FixedHealthColor.r, FixedHealthColor.g, FixedHealthColor.b
self:SetHealthBarColor (r, g, b)
return
end
--> check if the unit is a player
if (UnitIsPlayer (unit)) then
--> check if the unit is disconnected (in case it is a player
if (not UnitIsConnected (unit)) then
self:SetHealthBarColor (.5, .5, .5)
return
end
--is a friendly or enemy player?
if (UnitIsFriend ("player", unit)) then
if (self.Settings.UseFriendlyClassColor) then
local _, className = UnitClass (unit)
if (className) then
local classColor = RAID_CLASS_COLORS [className]
if (classColor) then
self:SetHealthBarColor (classColor.r, classColor.g, classColor.b)
return
end
end
else
self:SetHealthBarColor (0, 1, 0)
return
end
else
if (self.Settings.UseEnemyClassColor) then
local _, className = UnitClass (unit)
if (className) then
local classColor = RAID_CLASS_COLORS [className]
if (classColor) then
self:SetHealthBarColor (classColor.r, classColor.g, classColor.b)
return
end
end
else
self:SetHealthBarColor (1, 0, 0)
return
end
end
end
--> is tapped?
if (unit_is_tap_denied (unit)) then
self:SetHealthBarColor (.6, .6, .6)
return
end
--> is this is a npc attacking the player?
if (self.Settings.ColorByAggro) then
local _, threatStatus = UnitDetailedThreatSituation ("player", unit)
if (threatStatus) then
self:SetHealthBarColor (1, 0, 0)
return
end
end
--> get the regular color by selection
r, g, b = UnitSelectionColor (unit)
self:SetHealthBarColor (r, g, b)
end,
--> misc
UpdateName = function (self)
if (not self.Settings.ShowUnitName) then
return
end
--unit name without realm names by default
local name = UnitName (self.unit)
self.unitName:SetText (name)
self.unitName:Show()
end,
--> this runs when the player it self changes its target, need to update the current target overlay
--> todo: add focus overlay
UpdateTargetOverlay = function (self)
if (not self.Settings.ShowTargetOverlay) then
self.targetOverlay:Hide()
return
end
if (UnitIsUnit (self.displayedUnit, "target")) then
self.targetOverlay:Show()
else
self.targetOverlay:Hide()
end
end,
UpdateAllWidgets = function (self)
if (UnitExists (self.displayedUnit)) then
local unit = self.unit
local displayedUnit = self.displayedUnit
self:SetUnit (unit, displayedUnit)
--> is using castbars?
if (self.Settings.ShowCastBar) then
self.castBar:SetUnit (unit, displayedUnit)
end
--> is using powerbars?
if (self.Settings.ShowPowerBar) then
self.powerBar:SetUnit (unit, displayedUnit)
end
self:UpdateName()
self:UpdateTargetOverlay()
self:UpdateHealthColor()
end
end,
--> update the unit frame and its widgets
UpdateUnitFrame = function (self)
local unitInVehicle = self:CheckVehiclePossession()
--> if the unit is inside a vehicle, the vehicle possession function will call an update on all widgets
if (not unitInVehicle) then
self:UpdateAllWidgets()
end
end,
--> event handles
PLAYER_ENTERING_WORLD = function (self, ...)
self:UpdateUnitFrame()
end,
--> update overlays when the player changes its target
PLAYER_TARGET_CHANGED = function (self, ...)
self:UpdateTargetOverlay()
end,
--> unit received a name update
UNIT_NAME_UPDATE = function (self, ...)
self:UpdateName()
end,
--> this is registered only if .settings.ColorByAggro is true
UNIT_THREAT_LIST_UPDATE = function (self, ...)
if (self.Settings.ColorByAggro) then
self:UpdateHealthColor()
end
end,
--> vehicle
UNIT_ENTERED_VEHICLE = function (self, ...)
self:UpdateUnitFrame()
end,
UNIT_EXITED_VEHICLE = function (self, ...)
self:UpdateUnitFrame()
end,
--> pet
UNIT_PET = function (self, ...)
self:UpdateUnitFrame()
end,
--> player connection
UNIT_CONNECTION = function (self, ...)
if (UnitIsConnected (self.unit)) then
self:UpdateUnitFrame()
end
end,
PARTY_MEMBER_ENABLE = function (self, ...)
if (UnitIsConnected (self.unit)) then
self:UpdateName()
end
end,
}
-- ~unitframe
local globalBaseFrameLevel = 1 -- to be increased + used across each new plate
function DF:CreateUnitFrame (parent, name, unitFrameSettingsOverride, healthBarSettingsOverride, castBarSettingsOverride, powerBarSettingsOverride)
local parentName = name or ("DetailsFrameworkUnitFrame" .. tostring (math.random (1, 100000000)))
--> create the main unit frame
local f = CreateFrame ("button", parentName, parent, "BackdropTemplate")
--> base level
--local baseFrameLevel = f:GetFrameLevel()
local baseFrameLevel = globalBaseFrameLevel
globalBaseFrameLevel = globalBaseFrameLevel + 50
f:SetFrameLevel (baseFrameLevel)
--> create the healthBar
local healthBar = DF:CreateHealthBar (f, false, healthBarSettingsOverride)
healthBar:SetFrameLevel (baseFrameLevel + 1)
f.healthBar = healthBar
--> create the power bar
local powerBar = DF:CreatePowerBar (f, false, powerBarSettingsOverride)
powerBar:SetFrameLevel (baseFrameLevel + 2)
f.powerBar = powerBar
--> create the castBar
local castBar = DF:CreateCastBar (f, false, castBarSettingsOverride)
castBar:SetFrameLevel (baseFrameLevel + 3)
f.castBar = castBar
--> border frame
local borderFrame = DF:CreateBorderFrame (f, f:GetName() .. "Border")
borderFrame:SetFrameLevel (f:GetFrameLevel() + 5)
f.border = borderFrame
--> overlay frame (widgets that need to stay above the unit frame)
local overlayFrame = CreateFrame ("frame", "$parentOverlayFrame", f, "BackdropTemplate")
overlayFrame:SetFrameLevel (f:GetFrameLevel() + 6)
f.overlayFrame = overlayFrame
--> unit frame layers
do
--artwork
f.unitName = f:CreateFontString (nil, "artwork", "GameFontHighlightSmall")
PixelUtil.SetPoint (f.unitName, "topleft", healthBar, "topleft", 2, -2, 1, 1)
--target overlay - it's parented in the healthbar so other widgets won't get the overlay
f.targetOverlay = overlayFrame:CreateTexture (nil, "artwork")
f.targetOverlay:SetTexture (healthBar:GetTexture())
f.targetOverlay:SetBlendMode ("ADD")
f.targetOverlay:SetAlpha (.5)
f.targetOverlay:SetAllPoints (healthBar)
end
--> mixins
--inject mixins
DF:Mixin (f, DF.UnitFrameFunctions)
--create the settings table and copy the overrides into it, the table is set into the frame after the mixin
local unitFrameSettings = DF.table.copy ({}, DF.UnitFrameFunctions.Settings)
if (unitFrameSettingsOverride) then
unitFrameSettings = DF.table.copy (unitFrameSettings, unitFrameSettingsOverride)
end
f.Settings = unitFrameSettings
--> initialize scripts
--unitframe
f:Initialize()
return f
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> horizontal scroll frame
local timeline_options = {
width = 400,
height = 700,
line_height = 20,
line_padding = 1,
show_elapsed_timeline = true,
elapsed_timeline_height = 20,
--space to put the player/spell name and icons
header_width = 150,
--how many pixels will be use to represent 1 second
pixels_per_second = 20,
scale_min = 0.15,
scale_max = 1,
backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {0, 0, 0, 0.2},
backdrop_color_highlight = {.2, .2, .2, 0.4},
backdrop_border_color = {0.1, 0.1, 0.1, .2},
slider_backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
slider_backdrop_color = {0, 0, 0, 0.2},
slider_backdrop_border_color = {0.1, 0.1, 0.1, .2},
title_template = "ORANGE_FONT_TEMPLATE",
text_tempate = "OPTIONS_FONT_TEMPLATE",
on_enter = function (self)
self:SetBackdropColor (unpack (self.backdrop_color_highlight))
end,
on_leave = function (self)
self:SetBackdropColor (unpack (self.backdrop_color))
end,
block_on_enter = function (self)
end,
block_on_leave = function (self)
end,
}
local elapsedtime_frame_options = {
backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
backdrop_color = {.3, .3, .3, .7},
text_color = {1, 1, 1, 1},
text_size = 12,
text_font = "Arial Narrow",
text_outline = "NONE",
height = 20,
distance = 200, --distance in pixels between each label informing the time
distance_min = 50, --minimum distance in pixels
draw_line = true, --if true it'll draw a vertical line to represent a segment
draw_line_color = {1, 1, 1, 0.2},
draw_line_thickness = 1,
}
DF.TimeLineElapsedTimeFunctions = {
--get a label and update its appearance
GetLabel = function (self, index)
local label = self.labels [index]
if (not label) then
label = self:CreateFontString (nil, "artwork", "GameFontNormal")
label.line = self:CreateTexture (nil, "artwork")
label.line:SetColorTexture (1, 1, 1)
label.line:SetPoint ("topleft", label, "bottomleft", 0, -2)
self.labels [index] = label
end
DF:SetFontColor (label, self.options.text_color)
DF:SetFontSize (label, self.options.text_size)
DF:SetFontFace (label, self.options.text_font)
DF:SetFontOutline (label, self.options.text_outline)
if (self.options.draw_line) then
label.line:SetVertexColor (unpack (self.options.draw_line_color))
label.line:SetWidth (self.options.draw_line_thickness)
label.line:Show()
else
label.line:Hide()
end
return label
end,
Reset = function (self)
for i = 1, #self.labels do
self.labels [i]:Hide()
end
end,
Refresh = function (self, elapsedTime, scale)
local parent = self:GetParent()
self:SetHeight (self.options.height)
local effectiveArea = self:GetWidth() --already scaled down width
local pixelPerSecond = elapsedTime / effectiveArea --how much 1 pixels correlate to time
local distance = self.options.distance --pixels between each segment
local minDistance = self.options.distance_min --min pixels between each segment
--scale the distance between each label showing the time with the parent's scale
distance = distance * scale
distance = max (distance, minDistance)
local amountSegments = ceil (effectiveArea / distance)
for i = 1, amountSegments do
local label = self:GetLabel (i)
local xOffset = distance * (i - 1)
label:SetPoint ("left", self, "left", xOffset, 0)
local secondsOfTime = pixelPerSecond * xOffset
label:SetText (DF:IntegerToTimer (floor (secondsOfTime)))
if (label.line:IsShown()) then
label.line:SetHeight (parent:GetParent():GetHeight())
end
label:Show()
end
end,
}
--creates a frame to show the elapsed time in a row
function DF:CreateElapsedTimeFrame (parent, name, options)
local elapsedTimeFrame = CreateFrame ("frame", name, parent, "BackdropTemplate")
DF:Mixin (elapsedTimeFrame, DF.OptionsFunctions)
DF:Mixin (elapsedTimeFrame, DF.LayoutFrame)
elapsedTimeFrame:BuildOptionsTable (elapsedtime_frame_options, options)
DF:Mixin (elapsedTimeFrame, DF.TimeLineElapsedTimeFunctions)
elapsedTimeFrame:SetBackdrop (elapsedTimeFrame.options.backdrop)
elapsedTimeFrame:SetBackdropColor (unpack (elapsedTimeFrame.options.backdrop_color))
elapsedTimeFrame.labels = {}
return elapsedTimeFrame
end
DF.TimeLineBlockFunctions = {
--self is the line
SetBlock = function (self, index, blockInfo)
--get the block information
--see what is the current scale
--adjust the block position
local block = self:GetBlock (index)
--need:
--the total time of the timeline
--the current scale of the timeline
--the elapsed time of this block
--icon of the block
--text
--background color
end,
SetBlocksFromData = function (self)
local parent = self:GetParent():GetParent()
local data = parent.data
local defaultColor = parent.defaultColor --guarantee to have a value
self:Show()
--none of these values are scaled, need to calculate
local pixelPerSecond = parent.pixelPerSecond
local totalLength = parent.totalLength
local scale = parent.currentScale
pixelPerSecond = pixelPerSecond * scale
local headerWidth = parent.headerWidth
--dataIndex stores which line index from the data this line will use
--lineData store members: .text .icon .timeline
local lineData = data.lines [self.dataIndex]
--if there's an icon, anchor the text at the right side of the icon
--this is the title and icon of the title
if (lineData.icon) then
self.icon:SetTexture (lineData.icon)
self.icon:SetTexCoord (.1, .9, .1, .9)
self.text:SetText (lineData.text or "")
self.text:SetPoint ("left", self.icon.widget, "right", 2, 0)
else
self.icon:SetTexture (nil)
self.text:SetText (lineData.text or "")
self.text:SetPoint ("left", self, "left", 2, 0)
end
if (self.dataIndex % 2 == 1) then
self:SetBackdropColor (0, 0, 0, 0)
else
local r, g, b, a = unpack (self.backdrop_color)
self:SetBackdropColor (r, g, b, a)
end
self:SetWidth(5000)
local timelineData = lineData.timeline
local spellId = lineData.spellId
local useIconOnBlock = data.useIconOnBlocks
local baseFrameLevel = parent:GetFrameLevel() + 10
for i = 1, #timelineData do
local blockInfo = timelineData [i]
local time = blockInfo [1]
local length = blockInfo [2]
local isAura = blockInfo [3]
local auraDuration = blockInfo [4]
local payload = blockInfo.payload
local xOffset = pixelPerSecond * time
local width = pixelPerSecond * length
if (time < -0.2) then
xOffset = xOffset / 2.5
end
local block = self:GetBlock(i)
block:Show()
block:SetFrameLevel(baseFrameLevel + i)
PixelUtil.SetPoint(block, "left", self, "left", xOffset + headerWidth, 0)
block.info.spellId = spellId
block.info.time = time
block.info.duration = auraDuration
block.info.payload = payload
if (useIconOnBlock) then
block.icon:SetTexture (lineData.icon)
block.icon:SetTexCoord (.1, .9, .1, .9)
block.icon:SetAlpha (.834)
block.icon:SetSize (self:GetHeight(), self:GetHeight())
if (time < -0.2) then
block.icon:SetDesaturated (true)
else
block.icon:SetDesaturated (false)
end
PixelUtil.SetSize (block, self:GetHeight(), self:GetHeight())
if (isAura) then
block.auraLength:Show()
block.auraLength:SetWidth (pixelPerSecond * isAura)
block:SetWidth (max (pixelPerSecond * isAura, 16))
else
block.auraLength:Hide()
end
block.background:SetVertexColor (0, 0, 0, 0)
else
block.background:SetVertexColor (unpack (color))
PixelUtil.SetSize (block, max (width, 16), self:GetHeight())
block.auraLength:Hide()
end
end
end,
GetBlock = function (self, index)
local block = self.blocks [index]
if (not block) then
block = CreateFrame ("frame", nil, self, "BackdropTemplate")
self.blocks [index] = block
local background = block:CreateTexture (nil, "background")
background:SetColorTexture (1, 1, 1, 1)
local icon = block:CreateTexture (nil, "artwork")
local text = block:CreateFontString (nil, "artwork")
local auraLength = block:CreateTexture (nil, "border")
background:SetAllPoints()
icon:SetPoint ("left")
text:SetPoint ("left", icon, "left", 2, 0)
auraLength:SetPoint ("topleft", icon, "topleft", 0, 0)
auraLength:SetPoint ("bottomleft", icon, "bottomleft", 0, 0)
auraLength:SetColorTexture (1, 1, 1, 1)
auraLength:SetVertexColor (1, 1, 1, 0.1)
block.icon = icon
block.text = text
block.background = background
block.auraLength = auraLength
block:SetScript ("OnEnter", self:GetParent():GetParent().options.block_on_enter)
block:SetScript ("OnLeave", self:GetParent():GetParent().options.block_on_leave)
block:SetMouseClickEnabled (false)
block.info = {}
end
return block
end,
Reset = function (self)
--attention, it doesn't reset icon texture, text and background color
for i = 1, #self.blocks do
self.blocks [i]:Hide()
end
self:Hide()
end,
}
DF.TimeLineFunctions = {
GetLine = function (self, index)
local line = self.lines [index]
if (not line) then
--create a new line
line = CreateFrame ("frame", "$parentLine" .. index, self.body, "BackdropTemplate")
DF:Mixin (line, DF.TimeLineBlockFunctions)
self.lines [index] = line
--store the individual textures that shows the timeline information
line.blocks = {}
line.SetBlock = DF.TimeLineBlockFunctions.SetBlock
line.GetBlock = DF.TimeLineBlockFunctions.GetBlock
--set its parameters
if (self.options.show_elapsed_timeline) then
line:SetPoint ("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 2 - self.options.elapsed_timeline_height)
else
line:SetPoint ("topleft", self.body, "topleft", 1, -((index-1) * (self.options.line_height + 1)) - 1)
end
line:SetSize (1, self.options.line_height) --width is set when updating the frame
line:SetScript ("OnEnter", self.options.on_enter)
line:SetScript ("OnLeave", self.options.on_leave)
line:SetMouseClickEnabled (false)
line:SetBackdrop (self.options.backdrop)
line:SetBackdropColor (unpack (self.options.backdrop_color))
line:SetBackdropBorderColor (unpack (self.options.backdrop_border_color))
local icon = DF:CreateImage (line, "", self.options.line_height, self.options.line_height)
icon:SetPoint ("left", line, "left", 2, 0)
line.icon = icon
local text = DF:CreateLabel (line, "", DF:GetTemplate ("font", self.options.title_template))
text:SetPoint ("left", icon.widget, "right", 2, 0)
line.text = text
line.backdrop_color = self.options.backdrop_color or {.1, .1, .1, .3}
line.backdrop_color_highlight = self.options.backdrop_color_highlight or {.3, .3, .3, .5}
end
return line
end,
ResetAllLines = function (self)
for i = 1, #self.lines do
self.lines [i]:Reset()
end
end,
AdjustScale = function (self, index)
end,
--todo
--make the on enter and leave tooltips
--set icons and texts
--skin the sliders
RefreshTimeLine = function (self)
--debug
--self.currentScale = 1
--calculate the total width
local pixelPerSecond = self.options.pixels_per_second
local totalLength = self.data.length or 1
local currentScale = self.currentScale
self.scaleSlider:Enable()
--how many pixels represent 1 second
local bodyWidth = totalLength * pixelPerSecond * currentScale
self.body:SetWidth (bodyWidth + self.options.header_width)
self.body.effectiveWidth = bodyWidth
--reduce the default canvas size from the body with and don't allow the max value be negative
local newMaxValue = max (bodyWidth - (self:GetWidth() - self.options.header_width), 0)
--adjust the scale slider range
local oldMin, oldMax = self.horizontalSlider:GetMinMaxValues()
self.horizontalSlider:SetMinMaxValues (0, newMaxValue)
self.horizontalSlider:SetValue (DF:MapRangeClamped (oldMin, oldMax, 0, newMaxValue, self.horizontalSlider:GetValue()))
local defaultColor = self.data.defaultColor or {1, 1, 1, 1}
--cache values
self.pixelPerSecond = pixelPerSecond
self.totalLength = totalLength
self.defaultColor = defaultColor
self.headerWidth = self.options.header_width
--calculate the total height
local lineHeight = self.options.line_height
local linePadding = self.options.line_padding
local bodyHeight = (lineHeight + linePadding) * #self.data.lines
self.body:SetHeight (bodyHeight)
self.verticalSlider:SetMinMaxValues (0, max (bodyHeight - self:GetHeight(), 0))
self.verticalSlider:SetValue (0)
--refresh lines
self:ResetAllLines()
for i = 1, #self.data.lines do
local line = self:GetLine (i)
line.dataIndex = i --this index is used inside the line update function to know which data to get
line:SetBlocksFromData() --the function to update runs within the line object
end
--refresh elapsed time frame
--the elapsed frame must have a width before the refresh function is called
self.elapsedTimeFrame:ClearAllPoints()
self.elapsedTimeFrame:SetPoint ("topleft", self.body, "topleft", self.options.header_width, 0)
self.elapsedTimeFrame:SetPoint ("topright", self.body, "topright", 0, 0)
self.elapsedTimeFrame:Reset()
self.elapsedTimeFrame:Refresh (self.data.length, self.currentScale)
end,
SetData = function (self, data)
self.data = data
self:RefreshTimeLine()
end,
}
--creates a regular scroll in horizontal position
function DF:CreateTimeLineFrame (parent, name, options, timelineOptions)
local width = options and options.width or timeline_options.width
local height = options and options.height or timeline_options.height
local scrollWidth = 800 --placeholder until the timeline receives data
local scrollHeight = 800 --placeholder until the timeline receives data
local frameCanvas = CreateFrame ("scrollframe", name, parent, "BackdropTemplate")
DF:Mixin (frameCanvas, DF.TimeLineFunctions)
frameCanvas.data = {}
frameCanvas.lines = {}
frameCanvas.currentScale = 0.5
frameCanvas:SetSize (width, height)
frameCanvas:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
tile = true, tileSize = 16,
insets = {left = 1, right = 1, top = 0, bottom = 1},})
frameCanvas:SetBackdropColor (.1, .1, .1, .3)
local frameBody = CreateFrame ("frame", nil, frameCanvas, "BackdropTemplate")
frameBody:SetSize (scrollWidth, scrollHeight)
frameCanvas:SetScrollChild (frameBody)
frameCanvas.body = frameBody
DF:Mixin (frameCanvas, DF.OptionsFunctions)
DF:Mixin (frameCanvas, DF.LayoutFrame)
frameCanvas:BuildOptionsTable (timeline_options, options)
--create elapsed time frame
frameCanvas.elapsedTimeFrame = DF:CreateElapsedTimeFrame (frameBody, frameCanvas:GetName() and frameCanvas:GetName() .. "ElapsedTimeFrame", timelineOptions)
--create horizontal slider
local horizontalSlider = CreateFrame ("slider", nil, parent, "BackdropTemplate")
horizontalSlider.bg = horizontalSlider:CreateTexture (nil, "background")
horizontalSlider.bg:SetAllPoints (true)
horizontalSlider.bg:SetTexture (0, 0, 0, 0.5)
horizontalSlider:SetBackdrop (frameCanvas.options.slider_backdrop)
horizontalSlider:SetBackdropColor (unpack (frameCanvas.options.slider_backdrop_color))
horizontalSlider:SetBackdropBorderColor (unpack(frameCanvas.options.slider_backdrop_border_color))
horizontalSlider.thumb = horizontalSlider:CreateTexture (nil, "OVERLAY")
horizontalSlider.thumb:SetTexture ([[Interface\AddOns\Details\images\icons2]])
horizontalSlider.thumb:SetTexCoord (478/512, 496/512, 104/512, 120/512)
horizontalSlider.thumb:SetSize (20, 18)
horizontalSlider.thumb:SetVertexColor (0.6, 0.6, 0.6, 0.95)
horizontalSlider:SetThumbTexture (horizontalSlider.thumb)
horizontalSlider:SetOrientation ("horizontal")
horizontalSlider:SetSize (width + 20, 20)
horizontalSlider:SetPoint ("topleft", frameCanvas, "bottomleft")
horizontalSlider:SetMinMaxValues (0, scrollWidth)
horizontalSlider:SetValue (0)
horizontalSlider:SetScript ("OnValueChanged", function (self)
local _, maxValue = horizontalSlider:GetMinMaxValues()
local stepValue = ceil (ceil(self:GetValue() * maxValue) / max(maxValue, SMALL_FLOAT))
if (stepValue ~= horizontalSlider.currentValue) then
horizontalSlider.currentValue = stepValue
frameCanvas:SetHorizontalScroll (stepValue)
end
end)
frameCanvas.horizontalSlider = horizontalSlider
--create scale slider
local scaleSlider = CreateFrame ("slider", nil, parent, "BackdropTemplate")
scaleSlider.bg = scaleSlider:CreateTexture (nil, "background")
scaleSlider.bg:SetAllPoints (true)
scaleSlider.bg:SetTexture (0, 0, 0, 0.5)
scaleSlider:Disable()
frameCanvas.scaleSlider = scaleSlider
scaleSlider:SetBackdrop (frameCanvas.options.slider_backdrop)
scaleSlider:SetBackdropColor (unpack (frameCanvas.options.slider_backdrop_color))
scaleSlider:SetBackdropBorderColor (unpack(frameCanvas.options.slider_backdrop_border_color))
scaleSlider.thumb = scaleSlider:CreateTexture (nil, "OVERLAY")
scaleSlider.thumb:SetTexture ([[Interface\AddOns\Details\images\icons2]])
scaleSlider.thumb:SetTexCoord (478/512, 496/512, 104/512, 120/512)
scaleSlider.thumb:SetSize (20, 18)
scaleSlider.thumb:SetVertexColor (0.6, 0.6, 0.6, 0.95)
scaleSlider:SetThumbTexture (scaleSlider.thumb)
scaleSlider:SetOrientation ("horizontal")
scaleSlider:SetSize (width + 20, 20)
scaleSlider:SetPoint ("topleft", horizontalSlider, "bottomleft", 0, -2)
scaleSlider:SetMinMaxValues (frameCanvas.options.scale_min, frameCanvas.options.scale_max)
scaleSlider:SetValue (DF:GetRangeValue (frameCanvas.options.scale_min, frameCanvas.options.scale_max, 0.5))
scaleSlider:SetScript ("OnValueChanged", function (self)
local stepValue = ceil(self:GetValue() * 100)/100
if (stepValue ~= frameCanvas.currentScale) then
local current = stepValue
frameCanvas.currentScale = stepValue
frameCanvas:RefreshTimeLine()
end
end)
--create vertical slider
local verticalSlider = CreateFrame ("slider", nil, parent, "BackdropTemplate")
verticalSlider.bg = verticalSlider:CreateTexture (nil, "background")
verticalSlider.bg:SetAllPoints (true)
verticalSlider.bg:SetTexture (0, 0, 0, 0.5)
verticalSlider:SetBackdrop (frameCanvas.options.slider_backdrop)
verticalSlider:SetBackdropColor (unpack (frameCanvas.options.slider_backdrop_color))
verticalSlider:SetBackdropBorderColor (unpack(frameCanvas.options.slider_backdrop_border_color))
verticalSlider.thumb = verticalSlider:CreateTexture (nil, "OVERLAY")
verticalSlider.thumb:SetTexture ([[Interface\AddOns\Details\images\icons2]])
verticalSlider.thumb:SetTexCoord (482/512, 492/512, 104/512, 120/512)
verticalSlider.thumb:SetSize (12, 12)
verticalSlider.thumb:SetVertexColor (0.6, 0.6, 0.6, 0.95)
verticalSlider:SetThumbTexture (verticalSlider.thumb)
verticalSlider:SetOrientation ("vertical")
verticalSlider:SetSize (20, height - 2)
verticalSlider:SetPoint ("topleft", frameCanvas, "topright", 0, 0)
verticalSlider:SetMinMaxValues (0, scrollHeight)
verticalSlider:SetValue (0)
verticalSlider:SetScript ("OnValueChanged", function (self)
frameCanvas:SetVerticalScroll (self:GetValue())
end)
frameCanvas.verticalSlider = verticalSlider
--mouse scroll
frameCanvas:EnableMouseWheel (true)
frameCanvas:SetScript ("OnMouseWheel", function (self, delta)
local minValue, maxValue = horizontalSlider:GetMinMaxValues()
local currentHorizontal = horizontalSlider:GetValue()
if (IsShiftKeyDown() and delta < 0) then
local amountToScroll = frameBody:GetHeight() / 20
verticalSlider:SetValue (verticalSlider:GetValue() + amountToScroll)
elseif (IsShiftKeyDown() and delta > 0) then
local amountToScroll = frameBody:GetHeight() / 20
verticalSlider:SetValue (verticalSlider:GetValue() - amountToScroll)
elseif (IsControlKeyDown() and delta > 0) then
scaleSlider:SetValue (min (scaleSlider:GetValue() + 0.1, 1))
elseif (IsControlKeyDown() and delta < 0) then
scaleSlider:SetValue (max (scaleSlider:GetValue() - 0.1, 0.15))
elseif (delta < 0 and currentHorizontal < maxValue) then
local amountToScroll = frameBody:GetWidth() / 20
horizontalSlider:SetValue (currentHorizontal + amountToScroll)
elseif (delta > 0 and maxValue > 1) then
local amountToScroll = frameBody:GetWidth() / 20
horizontalSlider:SetValue (currentHorizontal - amountToScroll)
end
end)
--mouse drag
frameBody:SetScript ("OnMouseDown", function (self, button)
local x = GetCursorPosition()
self.MouseX = x
frameBody:SetScript ("OnUpdate", function (self, deltaTime)
local x = GetCursorPosition()
local deltaX = self.MouseX - x
local current = horizontalSlider:GetValue()
horizontalSlider:SetValue (current + (deltaX * 1.2) * ((IsShiftKeyDown() and 2) or (IsAltKeyDown() and 0.5) or 1))
self.MouseX = x
end)
end)
frameBody:SetScript ("OnMouseUp", function (self, button)
frameBody:SetScript ("OnUpdate", nil)
end)
return frameCanvas
end
--[=[
local f = CreateFrame ("frame", "TestFrame", UIParent)
f:SetPoint ("center")
f:SetSize (900, 420)
f:SetBackdrop({bgFile = "Interface\\Tooltips\\UI-Tooltip-Background", tile = true, tileSize = 16, insets = {left = 1, right = 1, top = 0, bottom = 1}})
local scroll = DF:CreateTimeLineFrame (f, "$parentTimeLine", {width = 880, height = 400})
scroll:SetPoint ("topleft", f, "topleft", 0, 0)
--need fake data to test fills
scroll:SetData ({
length = 360,
defaultColor = {1, 1, 1, 1},
lines = {
{text = "player 1", icon = "", timeline = {
--each table here is a block shown in the line
--is an indexed table with: [1] time [2] length [3] color (if false, use the default) [4] text [5] icon [6] tooltip: if number = spellID tooltip, if table is text lines
{1, 10}, {13, 11}, {25, 7}, {36, 5}, {55, 18}, {76, 30}, {105, 20}, {130, 11}, {155, 11}, {169, 7}, {199, 16}, {220, 18}, {260, 10}, {290, 23}, {310, 30}, {350, 10}
}
}, --end of line 1
},
})
f:Hide()
--scroll.body:SetScale (0.5)
--]=]
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> error message box
function DF:ShowErrorMessage (errorMessage, titleText)
if (not DF.ErrorMessagePanel) then
local f = CreateFrame ("frame", "DetailsFrameworkErrorMessagePanel", UIParent, "BackdropTemplate")
f:SetSize (400, 120)
f:SetFrameStrata ("FULLSCREEN")
f:SetPoint ("center", UIParent, "center", 0, 100)
f:EnableMouse (true)
f:SetMovable (true)
f:RegisterForDrag ("LeftButton")
f:SetScript ("OnDragStart", function() f:StartMoving() end)
f:SetScript ("OnDragStop", function() f:StopMovingOrSizing() end)
f:SetScript ("OnMouseDown", function (self, button) if (button == "RightButton") then f:Hide() end end)
tinsert (UISpecialFrames, "DetailsFrameworkErrorMessagePanel")
DF.ErrorMessagePanel = f
DF:CreateTitleBar (f, "Details! Framework Error!")
DF:ApplyStandardBackdrop (f)
local errorLabel = f:CreateFontString (nil, "overlay", "GameFontNormal")
errorLabel:SetPoint ("top", f, "top", 0, -25)
errorLabel:SetJustifyH ("center")
errorLabel:SetSize (360, 66)
f.errorLabel = errorLabel
local button_text_template = DF:GetTemplate ("font", "OPTIONS_FONT_TEMPLATE")
local options_dropdown_template = DF:GetTemplate ("dropdown", "OPTIONS_DROPDOWN_TEMPLATE")
local closeButton = DF:CreateButton (f, nil, 60, 20, "close", nil, nil, nil, nil, nil, nil, options_dropdown_template)
closeButton:SetPoint ("bottom", f, "bottom", 0, 5)
f.closeButton = closeButton
closeButton:SetClickFunction (function()
f:Hide()
end)
f.ShowAnimation = DF:CreateAnimationHub (f, function()
f:SetBackdropBorderColor (0, 0, 0, 0)
f.TitleBar:SetBackdropBorderColor (0, 0, 0, 0)
end, function()
f:SetBackdropBorderColor (0, 0, 0, 1)
f.TitleBar:SetBackdropBorderColor (0, 0, 0, 1)
end)
DF:CreateAnimation (f.ShowAnimation, "scale", 1, .075, .2, .2, 1.1, 1.1, "center", 0, 0)
DF:CreateAnimation (f.ShowAnimation, "scale", 2, .075, 1, 1, .90, .90, "center", 0, 0)
f.FlashTexture = f:CreateTexture (nil, "overlay")
f.FlashTexture:SetColorTexture (1, 1, 1, 1)
f.FlashTexture:SetAllPoints()
f.FlashAnimation = DF:CreateAnimationHub (f.FlashTexture, function() f.FlashTexture:Show() end, function() f.FlashTexture:Hide() end)
DF:CreateAnimation (f.FlashAnimation, "alpha", 1, .075, 0, .05)
DF:CreateAnimation (f.FlashAnimation, "alpha", 2, .075, .1, 0)
f:Hide()
end
DF.ErrorMessagePanel:Show()
DF.ErrorMessagePanel.errorLabel:SetText (errorMessage)
DF.ErrorMessagePanel.TitleLabel:SetText (titleText)
DF.ErrorMessagePanel.ShowAnimation:Play()
DF.ErrorMessagePanel.FlashAnimation:Play()
end
--[[
DF:SetPointOffsets(frame, xOffset, yOffset)
Set an offset into the already existing offset of the frame
If passed xOffset:1 and yOffset:1 and the frame has 1 -1, the new offset will be 2 -2
This function is great to create a 1 knob for distance
@frame: a frame to have the offsets changed
@xOffset: the amount to apply into the x offset
@yOffset: the amount to apply into the y offset
--]]
function DF:SetPointOffsets(frame, xOffset, yOffset)
for i = 1, frame:GetNumPoints() do
local anchor1, anchorTo, anchor2, x, y = frame:GetPoint(i)
x = x or 0
y = y or 0
if (x >= 0) then
xOffset = x + xOffset
elseif (x < 0) then
xOffset = x - xOffset
end
if (y >= 0) then
yOffset = y + yOffset
elseif (y < 0) then
yOffset = y - yOffset
end
frame:SetPoint(anchor1, anchorTo, anchor2, xOffset, yOffset)
end
end
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--> list box
DF.ListboxFunctions = {
scrollRefresh = function(self, data, offset, totalLines)
for i = 1, totalLines do
local index = i + offset
local lineData = data[index] --what is shown in the textentries, array
if (lineData) then
local line = self:GetLine(i)
line.dataIndex = index
line.deleteButton:SetClickFunction(DF.ListboxFunctions.deleteEntry, data, index)
line.indexText:SetText(index)
local amountEntries = #lineData
for o = 1, amountEntries do
--data
local textEntry = line.widgets[o]
textEntry.dataTable = lineData
textEntry.dataTableIndex = o
local text = lineData[o]
textEntry:SetText(text)
end
end
end
end,
addEntry = function(self)
local frameCanvas = self:GetParent()
local data = frameCanvas.data
local newEntry = {}
for i = 1, frameCanvas.headerLength do
tinsert(newEntry, "")
end
tinsert(data, newEntry)
frameCanvas.scrollBox:Refresh()
end,
deleteEntry = function(self, button, data, index)
tremove(data, index)
--get the line, get the scrollframe
self:GetParent():GetParent():Refresh()
end,
createScrollLine = function(self, index)
local listBox = self:GetParent()
local line = CreateFrame("frame", self:GetName().. "line_" .. index, self, "BackdropTemplate")
line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(self.lineHeight+1)) - 1)
line:SetSize(self:GetWidth() - 28, self.lineHeight) -- -28 space for the scrollbar
local options = listBox.options
line:SetBackdrop(options.line_backdrop)
line:SetBackdropColor(unpack(options.line_backdrop_color))
line:SetBackdropBorderColor(unpack(options.line_backdrop_border_color))
DF:Mixin(line, DF.HeaderFunctions)
line.widgets = {}
for i = 1, (listBox.headerLength+2) do --+2 to add the delete button and index
local headerColumn = listBox.headerTable[i]
if (headerColumn.isDelete) then
local deleteButton = DF:CreateButton(line, DF.ListboxFunctions.deleteEntry, 20, self.lineHeight, "X", listBox.data, index, nil, nil, nil, nil, DF:GetTemplate ("button", "OPTIONS_BUTTON_TEMPLATE"), DF:GetTemplate ("font", "ORANGE_FONT_TEMPLATE"))
line.deleteButton = deleteButton
line:AddFrameToHeaderAlignment(deleteButton)
elseif (headerColumn.isIndex) then
local indexText = DF:CreateLabel(line)
line.indexText = indexText
line:AddFrameToHeaderAlignment(indexText)
elseif (headerColumn.text) then
local template = DF.table.copy({}, DF:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE"))
template.backdropcolor = {.1, .1, .1, .7}
template.backdropbordercolor = {.2, .2, .2, .6}
local textEntry = DF:CreateTextEntry(line, function()end, headerColumn.width, self.lineHeight, nil, nil, nil, template)
textEntry:SetHook("OnEditFocusGained", function() textEntry:HighlightText(0) end)
textEntry:SetHook("OnEditFocusLost", function()
textEntry:HighlightText(0, 0)
local text = textEntry.text
local dataTable = textEntry.dataTable
dataTable[textEntry.dataTableIndex] = text
end)
tinsert(line.widgets, textEntry)
line:AddFrameToHeaderAlignment(textEntry)
end
end
line:AlignWithHeader(listBox.header, "left")
return line
end,
SetData = function(frameCanvas, newData)
if (type(newData) ~= "table") then
error("ListBox:SetData received an invalid newData on parameter 2.")
return
end
frameCanvas.data = newData
frameCanvas.scrollBox:SetData(newData)
frameCanvas.scrollBox:Refresh()
end,
}
local listbox_options = {
width = 800,
height = 600,
auto_width = true,
line_height = 16,
line_backdrop = {bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true},
line_backdrop_color = {.1, .1, .1, .6},
line_backdrop_border_color = {0, 0, 0, .5},
}
--@parent: parent frame
--@name: name of the frame to be created
--@data: table with current data to fill the column, this table are also used for values changed or added
--@options: table with options to overwrite the default setting from 'listbox_options'
--@header: a table to create a header widget
--@header_options: a table with options to overwrite the default header options
function DF:CreateListBox(parent, name, data, options, headerTable, headerOptions)
options = options or {}
name = name or "ListboxUnamed_" .. (math.random(100000, 1000000))
--canvas
local frameCanvas = CreateFrame("scrollframe", name, parent, "BackdropTemplate")
DF:Mixin(frameCanvas, DF.ListboxFunctions)
DF:Mixin(frameCanvas, DF.OptionsFunctions)
DF:Mixin(frameCanvas, DF.LayoutFrame)
frameCanvas.headerTable = headerTable
if (not data or type(data) ~= "table") then
error("CreateListBox() parameter 3 'data' must be a table.")
end
frameCanvas.data = data
frameCanvas.lines = {}
DF:ApplyStandardBackdrop(frameCanvas)
frameCanvas:BuildOptionsTable(listbox_options, options)
--> header
--check for default values in the header
headerTable = headerTable or {
{text = "Spell Id", width = 70},
{text = "Spell Name", width = 70},
}
headerOptions = headerOptions or {
padding = 2,
}
--each header is an entry in the data, if the header has 4 indexes the data has sub tables with 4 indexes as well
frameCanvas.headerLength = #headerTable
--add the detele line column into the header frame
tinsert(headerTable, 1, {text = "#", width = 20, isIndex = true}) --isDelete signals the createScrollLine() to make the delete button for the line
tinsert(headerTable, {text = "Delete", width = 50, isDelete = true}) --isDelete signals the createScrollLine() to make the delete button for the line
local header = DF:CreateHeader(frameCanvas, headerTable, headerOptions)
--set the header point
header:SetPoint("topleft", frameCanvas, "topleft", 5, -5)
frameCanvas.header = header
--> auto size
if (frameCanvas.options.auto_width) then
local width = 10 --padding 5 on each side
width = width + 20 --scrollbar reserved space
local headerPadding = headerOptions.padding or 0
for _, header in pairs(headerTable) do
if (header.width) then
width = width + header.width + headerPadding
end
end
frameCanvas.options.width = width
frameCanvas:SetWidth(width)
end
local width = frameCanvas.options.width
local height = frameCanvas.options.height
frameCanvas:SetSize(frameCanvas.options.width, height)
--> scroll frame
local lineHeight = frameCanvas.options.line_height
--calc the size of the space occupied by the add button, header etc
local lineAmount = floor((height - 60) / lineHeight)
-- -12 is padding: 5 on top, 7 bottom, 2 header scrollbar blank space | -24 to leave space to the add button
local scrollBox = DF:CreateScrollBox(frameCanvas, "$parentScrollbox", frameCanvas.scrollRefresh, data, width-4, height - header:GetHeight() - 12 - 24, lineAmount, lineHeight)
scrollBox:SetPoint("topleft", header, "bottomleft", 0, -2)
scrollBox:SetPoint("topright", header, "bottomright", 0, -2) -- -20 for the scrollbar
DF:ReskinSlider(scrollBox)
scrollBox.lineHeight = lineHeight
scrollBox.lineAmount = lineAmount
frameCanvas.scrollBox = scrollBox
for i = 1, lineAmount do
scrollBox:CreateLine(frameCanvas.createScrollLine)
end
scrollBox:Refresh()
--> add line button
local addLineButton = DF:CreateButton(frameCanvas, DF.ListboxFunctions.addEntry, 80, 20, "Add", nil, nil, nil, nil, nil, nil, DF:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"), DF:GetTemplate("font", "ORANGE_FONT_TEMPLATE"))
addLineButton:SetPoint("topleft", scrollBox, "bottomleft", 0, -4)
return frameCanvas
end
--[=[ -- test case
local pframe = ListBoxTest or CreateFrame("frame", "ListBoxTest", UIParent)
pframe:SetSize(900, 700)
pframe:SetPoint("left")
local data = {{254154, "spell name 1", 45}, {299154, "spell name 2", 05}, {354154, "spell name 3", 99}}
local headerTable = {
{text = "spell id", width = 120},
{text = "spell name", width = 180},
{text = "number", width = 90},
}
local listbox = DetailsFramework:CreateListBox(pframe, "$parentlistbox", data, nil, headerTable, nil)
listbox:SetPoint("topleft", pframe, "topleft", 10, -10)
--]=]