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 IS_WOW_PROJECT_CLASSIC_ERA = WOW_PROJECT_ID == WOW_PROJECT_CLASSIC local IS_WOW_PROJECT_CLASSIC_TBC = WOW_PROJECT_ID == WOW_PROJECT_BURNING_CRUSADE_CLASSIC local UnitCastingInfo = UnitCastingInfo local UnitChannelInfo = UnitChannelInfo if IS_WOW_PROJECT_CLASSIC_ERA then UnitCastingInfo = CastingInfo UnitChannelInfo = ChannelInfo end 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:") 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) --~scale 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.thumb:SetWidth(24) scaleBar:SetValueStep(0.1) scaleBar:SetObeyStepOnDrag(true) scaleBar.mouseDown = false rawset(scaleBar, "lockdown", true) --create a custom editbox to enter the scale from text local editbox = CreateFrame("editbox", nil, scaleBar.widget, "BackdropTemplate") editbox:SetSize(40, 20) editbox:SetJustifyH("center") editbox:SetBackdrop({bgFile = [[Interface\ACHIEVEMENTFRAME\UI-GuildAchievement-Parchment-Horizontal-Desaturated]], edgeFile = [[Interface\Buttons\WHITE8X8]], tile = true, edgeSize = 1, tileSize = 64}) editbox:SetFontObject("GameFontHighlightSmall") editbox:SetBackdropColor(0, 0, 0, 1) editbox:SetScript("OnEditFocusGained", function() end) editbox:SetScript("OnEnterPressed", function() editbox:ClearFocus() editbox:Hide() local text = editbox:GetText() local newScale = DF.TextToFloor(text) if (newScale) then config.scale = newScale scaleBar:SetValue(newScale) frame:SetScale(newScale) editbox.defaultValue = newScale end end) editbox:SetScript("OnEscapePressed", function() editbox:ClearFocus() editbox:Hide() editbox:SetText(editbox.defaultValue) end) scaleBar:SetScript("OnMouseDown", function(_, mouseButton) if (mouseButton == "RightButton") then editbox:Show() editbox:SetAllPoints() editbox:SetText(config.scale) editbox:SetFocus(true) editbox.defaultValue = config.scale elseif (mouseButton == "LeftButton") then scaleBar.mouseDown = true end end) scaleBar:SetScript("OnMouseUp", function(_, mouseButton) if (mouseButton == "LeftButton") then scaleBar.mouseDown = false frame:SetScale(config.scale) editbox.defaultValue = config.scale end end) text:SetPoint("topleft", frame, "topleft", 12, -7) scaleBar:SetFrameLevel(DF.FRAMELEVEL_OVERLAY) scaleBar.OnValueChanged = function(_, _, value) if (scaleBar.mouseDown) then config.scale = value end end scaleBar:SetAlpha(0.70) editbox.defaultValue = config.scale editbox:SetFocus(false) editbox:SetAutoFocus(false) editbox:ClearFocus() C_Timer.After(1, function() editbox:SetFocus(false) editbox:SetAutoFocus(false) editbox:ClearFocus() end) 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 if (mainFrame.hookList.OnSelectIndex) then DF:QuickDispatch(mainFrame.hookList.OnSelectIndex, mainFrame, mainFrame.AllButtons[menuIndex]) end 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, frameList, options_table, hookList) 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 frameWidth = 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 buttonAnchorX = options_table.button_x or 230 local buttonAnchorY = 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) mainFrame.hookList = hookList 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 (frameList) 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, "$parentTabButton" .. frame.name, 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 = buttonAnchorX local y = buttonAnchorY local spaceBetweenButtons = 3 local space_for_buttons = frameWidth - (#frameList * spaceBetweenButtons) - buttonAnchorX 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 = buttonAnchorX 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 ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ -- ~right ~click to ~close function DF:CreateRightClickToClose(parent, xOffset, yOffset, color, fontSize) --default values xOffset = xOffset or 0 yOffset = yOffset or 0 color = color or "white" fontSize = fontSize or 10 local label = DF:CreateLabel(parent, "right click to close", fontSize, color) label:SetPoint("bottomright", parent, "bottomright", -4 + xOffset, 5 + yOffset) return label 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.GetLines = function (self) --alias of GetFrames 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.parentIconRow = self 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 or 1 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, --adds only if not existing already in the cache AddSpecificIcon = function (self, identifierKey, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff) if not identifierKey or identifierKey == "" then return end if not self.AuraCache[identifierKey] then local icon = self:SetIcon (spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff or false) icon.identifierKey = identifierKey self.AuraCache[identifierKey] = true end end, SetIcon = function (self, spellId, borderColor, startTime, duration, forceTexture, descText, count, debuffType, caster, canStealOrPurge, spellName, isBuff) local actualSpellName, _, spellIcon = GetSpellInfo (spellId) if forceTexture then spellIcon = forceTexture end spellName = spellName or actualSpellName or "unknown_aura" 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 now = GetTime() iconFrame.timeRemaining = startTime + duration - now iconFrame.expirationTime = startTime + duration local formattedTime = (iconFrame.timeRemaining > 0) and self.options.decimal_timer and iconFrame.parentIconRow.FormatCooldownTimeDecimal(iconFrame.timeRemaining) or iconFrame.parentIconRow.FormatCooldownTime(iconFrame.timeRemaining) or "" iconFrame.CountdownText:SetText (formattedTime) 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) if self.options.on_tick_cooldown_update then iconFrame.lastUpdateCooldown = now iconFrame:SetScript("OnUpdate", self.OnIconTick) else iconFrame:SetScript("OnUpdate", nil) end else iconFrame:SetScript("OnUpdate", nil) iconFrame.CountdownText:Hide() end iconFrame.Cooldown:SetReverse (self.options.cooldown_reverse) iconFrame.Cooldown:SetHideCountdownNumbers (self.options.surpress_blizzard_cd_timer) else iconFrame.timeRemaining = nil iconFrame.expirationTime = nil iconFrame:SetScript("OnUpdate", nil) 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 iconFrame.isBuff = isBuff iconFrame.spellName = spellName iconFrame.identifierKey = nil -- only used for "specific" add/remove --add the spell into the cache self.AuraCache [spellId or -1] = true self.AuraCache [spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or debuffType == "" --yes, enrages are empty-string... --> show the frame self:Show() return iconFrame end end, OnIconTick = function (self, deltaTime) local now = GetTime() if (self.lastUpdateCooldown + 0.05) <= now then self.timeRemaining = self.expirationTime - now if self.timeRemaining > 0 then if self.parentIconRow.options.decimal_timer then self.CountdownText:SetText (self.parentIconRow.FormatCooldownTimeDecimal(self.timeRemaining)) else self.CountdownText:SetText (self.parentIconRow.FormatCooldownTime(self.timeRemaining)) end else self.CountdownText:SetText ("") end self.lastUpdateCooldown = now end end, FormatCooldownTime = function (formattedTime) if (formattedTime >= 3600) then formattedTime = floor (formattedTime / 3600) .. "h" elseif (formattedTime >= 60) then formattedTime = floor (formattedTime / 60) .. "m" else formattedTime = floor (formattedTime) end return formattedTime end, FormatCooldownTimeDecimal = function (formattedTime) if formattedTime < 10 then return ("%.1f"):format(formattedTime) elseif formattedTime < 60 then return ("%d"):format(formattedTime) elseif formattedTime < 3600 then return ("%d:%02d"):format(formattedTime/60%60, formattedTime%60) elseif formattedTime < 86400 then return ("%dh %02dm"):format(formattedTime/(3600), formattedTime/60%60) else return ("%dd %02dh"):format(formattedTime/86400, (formattedTime/3600) - (floor(formattedTime/86400) * 24)) end end, RemoveSpecificIcon = function (self, identifierKey) if not identifierKey or identifierKey == "" then return end table.wipe (self.AuraCache) local iconPool = self.IconPool local countStillShown = 0 for i = 1, self.NextIcon -1 do local iconFrame = iconPool[i] if iconFrame.identifierKey and iconFrame.identifierKey == identifierKey then iconFrame:Hide() iconFrame:ClearAllPoints() iconFrame.identifierKey = nil else self.AuraCache [iconFrame.spellId] = true self.AuraCache [iconFrame.spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string... countStillShown = countStillShown + 1 end end self:AlignAuraIcons() end, ClearIcons = function (self, resetBuffs, resetDebuffs) resetBuffs = resetBuffs ~= false resetDebuffs = resetDebuffs ~= false table.wipe (self.AuraCache) local iconPool = self.IconPool for i = 1, self.NextIcon -1 do local iconFrame = iconPool[i] if iconFrame.isBuff == nil then iconFrame:Hide() iconFrame:ClearAllPoints() elseif resetBuffs and iconFrame.isBuff then iconFrame:Hide() iconFrame:ClearAllPoints() elseif resetDebuffs and not iconFrame.isBuff then iconFrame:Hide() iconFrame:ClearAllPoints() else self.AuraCache [iconFrame.spellId] = true self.AuraCache [iconFrame.spellName] = true self.AuraCache.canStealOrPurge = self.AuraCache.canStealOrPurge or iconFrame.canStealOrPurge self.AuraCache.hasEnrage = self.AuraCache.hasEnrage or iconFrame.debuffType == "" --yes, enrages are empty-string... end end self:AlignAuraIcons() end, AlignAuraIcons = function (self) local iconPool = self.IconPool local iconAmount = #iconPool local countStillShown = 0 table.sort (iconPool, function(i1, i2) return i1:IsShown() and not i2:IsShown() end) if iconAmount == 0 then self:Hide() else -- re-anchor not hidden for i = 1, iconAmount do local iconFrame = iconPool[i] local anchor = self.options.anchor local anchorTo = i == 1 and self or self.IconPool [i - 1] local xPadding = i == 1 and self.options.left_padding or self.options.icon_padding or 1 local growDirection = self.options.grow_direction countStillShown = countStillShown + (iconFrame:IsShown() and 1 or 0) iconFrame:ClearAllPoints() if (growDirection == 1) then --grow to right if (i == 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 (i == 1) then PixelUtil.SetPoint (iconFrame, "right", anchorTo, "right", xPadding, 0) else PixelUtil.SetPoint (iconFrame, "right", anchorTo, "left", xPadding, 0) end end end end self.NextIcon = countStillShown + 1 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, on_tick_cooldown_update = true, decimal_timer = false, cooldown_reverse = false, } function DF:CreateIconRow (parent, name, options) local f = CreateFrame("frame", name, parent, "BackdropTemplate") f.IconPool = {} f.NextIcon = 1 f.AuraCache = {} 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 pairs (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 pairs (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 _, id in pairs(idList) do if tonumber(id) then self:SetText (self:GetText() .. " " .. id) end 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 whitespace.\nExample: 35 45 95\n\nSanctum of Domination:\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 whitespace\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... {"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 self:UnregisterEvent ("UNIT_HEAL_PREDICTION") if IS_WOW_PROJECT_MAINLINE then 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 self.oldHealth = self.currentHealth 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) 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 = IS_WOW_PROJECT_MAINLINE and 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 and IS_WOW_PROJECT_MAINLINE) 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 --[[if not self.spellStartTime then self:UpdateCastingInfo(self.unit) end]]-- 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 --[[if not self.spellEndTime then self:UpdateChannelInfo(self.unit) end]]-- 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 self:UpdateChannelInfo(unit) return self.unit == arg1 and "UNIT_SPELLCAST_CHANNEL_START" elseif (isRegularCast) then self.casting = true self:UpdateCastingInfo(unit) 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, UpdateCastingInfo = function (self, unit) local name, text, texture, startTime, endTime, isTradeSkill, castID, notInterruptible, spellID = UnitCastingInfo (unit) --> 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 or name) 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() end, UNIT_SPELLCAST_START = function (self, unit) self:UpdateCastingInfo(unit) self:RunHooksForWidget ("OnCastStart", self, self.unit, "UNIT_SPELLCAST_START") end, UpdateChannelInfo = function (self, unit, ...) local name, text, texture, startTime, endTime, isTradeSkill, notInterruptible, spellID if not IS_WOW_PROJECT_CLASSIC_TBC 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() end, UNIT_SPELLCAST_CHANNEL_START = function (self, unit, ...) self:UpdateChannelInfo(unit, ...) 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 or self.channeling) 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 or self.channeling) 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, } -- for classic era use LibClassicCasterino: local LibCC = LibStub ("LibClassicCasterino", true) if IS_WOW_PROJECT_CLASSIC_ERA and LibCC then local fCast = CreateFrame("frame") local getCastBar = function (unitId) local plateFrame = C_NamePlate.GetNamePlateForUnit (unitId) if (not plateFrame) then return end local castBar = plateFrame.unitFrame and plateFrame.unitFrame.castBar if (not castBar) then return end return castBar end local triggerCastEvent = function (castBar, event, unitId, ...) if (castBar and castBar.OnEvent) then castBar.OnEvent (castBar, event, unitId) end end local funcCast = function (event, unitId, ...) local castBar = getCastBar (unitId) if (castBar) then triggerCastEvent (castBar, event, unitId) end end fCast.UNIT_SPELLCAST_START = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_STOP = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_DELAYED = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_FAILED = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_INTERRUPTED = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_CHANNEL_START = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_CHANNEL_UPDATE = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end fCast.UNIT_SPELLCAST_CHANNEL_STOP = function (self, event, unitId, ...) triggerCastEvent (getCastBar (unitId), event, unitId) end if LibCC then LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_START", funcCast) LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_DELAYED", funcCast) -- only for player LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_STOP", funcCast) LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_FAILED", funcCast) LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_INTERRUPTED", funcCast) LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_START", funcCast) LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_UPDATE", funcCast) -- only for player LibCC.RegisterCallback(fCast,"UNIT_SPELLCAST_CHANNEL_STOP", funcCast) UnitCastingInfo = function(unit) return LibCC:UnitCastingInfo (unit) end UnitChannelInfo = function(unit) return LibCC:UnitChannelInfo (unit) end end end -- end classic era -- ~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] self.spellId = lineData.spellId --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 local lineHeader = CreateFrame("frame", nil, line, "BackdropTemplate") lineHeader:SetPoint("topleft", line, "topleft", 0, 0) lineHeader:SetPoint("bottomleft", line, "bottomleft", 0, 0) lineHeader:SetScript("OnEnter", self.options.header_on_enter) lineHeader:SetScript("OnLeave", self.options.header_on_leave) line.lineHeader = lineHeader --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.lineHeader:SetWidth(self.options.header_width) 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", frameCanvas:GetName() .. "HorizontalSlider", parent, "BackdropTemplate") horizontalSlider.bg = horizontalSlider:CreateTexture (nil, "background") horizontalSlider.bg:SetAllPoints (true) horizontalSlider.bg:SetTexture (0, 0, 0, 0.5) frameCanvas.horizontalSlider = horizontalSlider 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) --create scale slider local scaleSlider = CreateFrame("slider", frameCanvas:GetName() .. "ScaleSlider", 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", frameCanvas:GetName() .. "VerticalSlider", parent, "BackdropTemplate") verticalSlider.bg = verticalSlider:CreateTexture (nil, "background") verticalSlider.bg:SetAllPoints (true) verticalSlider.bg:SetTexture (0, 0, 0, 0.5) frameCanvas.verticalSlider = verticalSlider 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) --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) --]=]