From 86f9458ded6386ecf625e9b86b93ab2b5d51cb19 Mon Sep 17 00:00:00 2001 From: Tercio Jose Date: Thu, 19 Dec 2024 15:15:15 -0300 Subject: [PATCH] Updates --- WorldQuestTracker.toc | 4 +- libs/DF/LibDFramework-1.0.toc | 2 +- libs/DF/auras.lua | 2 +- libs/DF/buildmenu.lua | 63 +++ libs/DF/button.lua | 12 +- libs/DF/charts.lua | 6 +- libs/DF/cooltip.lua | 54 ++- libs/DF/definitions.lua | 168 ++++---- libs/DF/dropdown.lua | 73 +++- libs/DF/editor.lua | 12 +- libs/DF/ejournal.lua | 53 ++- libs/DF/elapsedtime.lua | 3 +- libs/DF/fw.lua | 180 ++++++-- libs/DF/header.lua | 2 +- libs/DF/label.lua | 52 +++ libs/DF/languages.lua | 2 +- libs/DF/math.lua | 7 + libs/DF/mixins.lua | 8 + libs/DF/packtable.lua | 79 +++- libs/DF/packtable.tests.lua | 30 +- libs/DF/panel.lua | 85 ++-- libs/DF/picture.lua | 3 +- libs/DF/schedules.lua | 3 +- libs/DF/slider.lua | 64 ++- libs/DF/spells.lua | 2 +- libs/DF/textentry.lua | 14 +- libs/DF/timebar.lua | 47 ++- libs/DF/timeline.lua | 770 +++++++++++++++++++++++----------- luaserver.lua | 25 +- 29 files changed, 1374 insertions(+), 451 deletions(-) diff --git a/WorldQuestTracker.toc b/WorldQuestTracker.toc index e695871d..60cac50c 100644 --- a/WorldQuestTracker.toc +++ b/WorldQuestTracker.toc @@ -1,5 +1,5 @@ -## Interface: 110005 -## Interface-Mainline: 110005 +## Interface: 110007 +## Interface-Mainline: 110007 ## Interface-Wrath: 30402 ## Title: World Quest Tracker diff --git a/libs/DF/LibDFramework-1.0.toc b/libs/DF/LibDFramework-1.0.toc index 48ecdae3..d59b3611 100644 --- a/libs/DF/LibDFramework-1.0.toc +++ b/libs/DF/LibDFramework-1.0.toc @@ -1,4 +1,4 @@ -## Interface: 110002 +## Interface: 110007 ## Title: Lib: LibDFramework-1.0 ## Notes: Base Framework for many Addons diff --git a/libs/DF/auras.lua b/libs/DF/auras.lua index 1d5bce9c..3e49d937 100644 --- a/libs/DF/auras.lua +++ b/libs/DF/auras.lua @@ -19,7 +19,7 @@ local GetSpellTabInfo = GetSpellTabInfo or function(tabLine) local skillLine = C local unpack = unpack local CreateFrame = CreateFrame local GameTooltip = GameTooltip -local tremove = tremove +local tremove = table.remove local CONST_MAX_SPELLS = 500000 diff --git a/libs/DF/buildmenu.lua b/libs/DF/buildmenu.lua index 93f7a5a8..248dc12c 100644 --- a/libs/DF/buildmenu.lua +++ b/libs/DF/buildmenu.lua @@ -654,6 +654,36 @@ local setExecuteProperties = function(parent, widget, widgetTable, currentXOffse return maxColumnWidth, maxWidgetWidth, latestInlineWidget end +local setImageProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset) + --.texture .width .height .filterType .texcoord + if (type(widgetTable.texture) == "table") then + local r, g, b, a = detailsFramework:ParseColors(widgetTable.texture) + widget:SetColorTexture(r, g, b, a) + else + widget:SetTexture(widgetTable.texture, "CLAMP", "CLAMP", widgetTable.filterType) + end + + widget:SetSize(widgetTable.width, widgetTable.height) + + local left, right, top, bottom = 0, 1, 0, 1 + if (widgetTable.texcoord) then + left, right, top, bottom = unpack(widgetTable.texcoord) + end + widget:SetTexCoord(left, right, top, bottom) + + if (widgetTable.vertexcolor) then + local r, g, b, a = detailsFramework:ParseColors(widgetTable.vertexcolor) + widget:SetVertexColor(r, g, b, a) + else + widget:SetVertexColor(1, 1, 1, 1) + end + + setWidgetId(parent, widgetTable, widget) + + widget:ClearAllPoints() + widget:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) +end + local setTextEntryProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate, latestInlineWidget) widget._get = widgetTable.get widget.text = widgetTable.get() @@ -889,6 +919,7 @@ function detailsFramework:SetAsOptionsPanel(frame) ["button"] = {}, -- "execute" ["textentry"] = {}, -- ["label"] = {}, --"text" + ["image"] = {}, } frame.widgetids = {} frame.GetWidgetById = getFrameById @@ -997,6 +1028,15 @@ local getMenuWidgetVolative = function(parent, widgetType, indexTable) widgetObject:ClearHooks() end indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "image") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = parent:CreateTexture("$parentWidget" .. widgetType .. indexTable[widgetType], "overlay") + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + end + indexTable[widgetType] = indexTable[widgetType] + 1 end --if the widget is inside the no combat table, remove it @@ -1251,6 +1291,15 @@ function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffse maxColumnWidth, maxWidgetWidth = setTextEntryProperties(parent, textentry, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate) amountLineWidgetAdded = amountLineWidgetAdded + 1 + + --image + elseif (widgetTable.type == "image") then + local image = getMenuWidgetVolative(parent, "image", widgetIndexes) + widgetCreated = image + + setImageProperties(parent, image, widgetTable, currentXOffset, currentYOffset) + + amountLineWidgetAdded = amountLineWidgetAdded + 1 end --end loop if (widgetTable.nocombat) then @@ -1565,6 +1614,20 @@ function detailsFramework:BuildMenu(parent, menuOptions, xOffset, yOffset, heigh widgetCreated = textentry amountLineWidgetAdded = amountLineWidgetAdded + 1 + + elseif (widgetTable.type == "image") then + local image = parent:CreateTexture("$parentMenuImage" .. index, "overlay") + + setImageProperties(parent, image, widgetTable, currentXOffset, currentYOffset) + + currentYOffset = currentYOffset - widgetTable.height + 10 + + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, image) + table.insert(parent.widget_list_by_type.textentry, image) + + widgetCreated = image + amountLineWidgetAdded = amountLineWidgetAdded + 1 end if (widgetTable.nocombat) then diff --git a/libs/DF/button.lua b/libs/DF/button.lua index 5adc8073..848f8183 100644 --- a/libs/DF/button.lua +++ b/libs/DF/button.lua @@ -655,7 +655,7 @@ detailsFramework:Mixin(ButtonMetaFunctions, detailsFramework.ScriptHookMixin) if (object.capsule_textalign) then if (object.icon) then - object.icon:SetPoint("left", button, "left", 5 + (object.icon.leftpadding or 0), -1) + object.icon:SetPoint("left", button, "left", 5 + (object.icon.leftPadding or 0), -1) elseif (object.capsule_textalign == "left") then button.text:SetPoint("left", button, "left", 3, -1) @@ -668,7 +668,7 @@ detailsFramework:Mixin(ButtonMetaFunctions, detailsFramework.ScriptHookMixin) end else if (object.icon) then - object.icon:SetPoint("left", button, "left", 5 + (object.icon.leftpadding or 0), -1) + object.icon:SetPoint("left", button, "left", 5 + (object.icon.leftPadding or 0), -1) else button.text:SetPoint("center", button,"center", 1, -1) end @@ -729,7 +729,7 @@ detailsFramework:Mixin(ButtonMetaFunctions, detailsFramework.ScriptHookMixin) if (object.capsule_textalign) then if (object.icon) then - object.icon:SetPoint("left", button, "left", 4 + (object.icon.leftpadding or 0), 0) + object.icon:SetPoint("left", button, "left", 4 + (object.icon.leftPadding or 0), 0) elseif (object.capsule_textalign == "left") then button.text:SetPoint("left", button, "left", 2, 0) @@ -742,7 +742,7 @@ detailsFramework:Mixin(ButtonMetaFunctions, detailsFramework.ScriptHookMixin) end else if (object.icon) then - object.icon:SetPoint("left", button, "left", 4 + (object.icon.leftpadding or 0), 0) + object.icon:SetPoint("left", button, "left", 4 + (object.icon.leftPadding or 0), 0) else button.text:SetPoint("center", button,"center", 0, 0) end @@ -828,7 +828,7 @@ function ButtonMetaFunctions:SetTemplate(template) if (template.icon) then local iconInfo = template.icon - self:SetIcon(iconInfo.texture, iconInfo.width, iconInfo.height, iconInfo.layout, iconInfo.texcoord, iconInfo.color, iconInfo.textdistance, iconInfo.leftpadding) + self:SetIcon(iconInfo.texture, iconInfo.width, iconInfo.height, iconInfo.layout, iconInfo.texcoord, iconInfo.color, iconInfo.textdistance, iconInfo.leftPadding) end if (template.textsize) then @@ -1543,4 +1543,4 @@ end local closeButton = detailsFramework:CreateCloseButton(frame, "$parentCloseButton") closeButton:SetPoint("topright", frame, "topright", 0, 0) ---]=] \ No newline at end of file +--]=] diff --git a/libs/DF/charts.lua b/libs/DF/charts.lua index 8acfa1fa..117466f1 100644 --- a/libs/DF/charts.lua +++ b/libs/DF/charts.lua @@ -1459,8 +1459,8 @@ detailsFramework.MultiChartFrameMixin = { biggestAverage[i] = {average = chartFrame.average, chartIndex = i} end - --sort the averages by the biggest average placing the biggest average in the first position - table.sort(biggestAverage, function(a, b) return a.average > b.average end) + -- sort the averages by the biggest average placing the biggest average in the first position + table.sort(biggestAverage, function(a, b) if not (a.average and b.average) then return a.average ~= nil end return a.average > b.average end) local minValue, multiChartMaxValue = multiChartFrame:GetMinMaxValues() local plotAreaWidth = multiChartFrame.plotFrame:GetWidth() --if there's no axis, the plotFrame has no width @@ -1518,4 +1518,4 @@ function detailsFramework:CreateGraphicMultiLineFrame(parent, name) createPlotFrame(chartFrame) --creates chartFrame.plotFrame return chartFrame -end \ No newline at end of file +end diff --git a/libs/DF/cooltip.lua b/libs/DF/cooltip.lua index 2e38a47a..66a66243 100644 --- a/libs/DF/cooltip.lua +++ b/libs/DF/cooltip.lua @@ -28,7 +28,7 @@ end --api locals local PixelUtil = PixelUtil or DFPixelUtil -local version = 28 +local version = 30 local CONST_MENU_TYPE_MAINMENU = "main" local CONST_MENU_TYPE_SUBMENU = "sub" @@ -1053,6 +1053,16 @@ function DF:CreateCoolTip() if (gameCooltip.OptionsTable.TextSize and not leftTextSettings[6]) then DF:SetFontSize(menuButton.leftText, gameCooltip.OptionsTable.TextSize) + elseif (leftTextSettings[6]) then + DF:SetFontSize(menuButton.leftText, leftTextSettings[6]) + else + DF:SetFontSize(menuButton.leftText, 10) + end + + if (leftTextSettings[8]) then + DF:SetFontOutline(menuButton.leftText, leftTextSettings[8]) + else + DF:SetFontOutline(menuButton.leftText, "NONE") end if (gameCooltip.OptionsTable.LeftTextWidth) then @@ -1160,6 +1170,20 @@ function DF:CreateCoolTip() DF:SetFontSize(menuButton.rightText, gameCooltip.OptionsTable.TextSize) end + if (gameCooltip.OptionsTable.TextSize and not rightTextSettings[6]) then + DF:SetFontSize(menuButton.rightText, gameCooltip.OptionsTable.TextSize) + elseif (rightTextSettings[6]) then + DF:SetFontSize(menuButton.rightText, rightTextSettings[6]) + else + DF:SetFontSize(menuButton.rightText, 10) + end + + if (rightTextSettings[8]) then + DF:SetFontOutline(menuButton.rightText, rightTextSettings[8]) + else + DF:SetFontOutline(menuButton.rightText, "NONE") + end + if (gameCooltip.OptionsTable.RightTextWidth) then menuButton.rightText:SetWidth(gameCooltip.OptionsTable.RightTextWidth) else @@ -2424,6 +2448,11 @@ function DF:CreateCoolTip() end function gameCooltip:SetMyPoint(host, xOffset, yOffset) + if (host and xOffset == "cursor") then + frame1.attachToCursor = true + return + end + local moveX = xOffset or 0 local moveY = yOffset or 0 local anchor = gameCooltip.OptionsTable.Anchor or gameCooltip.Host @@ -2592,7 +2621,11 @@ function DF:CreateCoolTip() end gameCooltip.Host = frame - gameCooltip.frame1:SetFrameLevel(frame:GetFrameLevel() + 1) + if (not frame.GetFrameLevel) then + gameCooltip.frame1:SetFrameLevel(frame:GetParent():GetFrameLevel() + 1) + else + gameCooltip.frame1:SetFrameLevel(frame:GetFrameLevel() + 1) + end --defaults myPoint = myPoint or gameCooltip.OptionsTable.MyAnchor or "bottom" @@ -2766,6 +2799,8 @@ function DF:CreateCoolTip() gameCooltip:HideSelectedTexture(frame1) gameCooltip:HideSelectedTexture(frame2) + frame1.attachToCursor = false + gameCooltip:HideRoundedCorner() GameCooltip.frame1:SetBorderCornerColor(unpack(gameCooltip.RoundedFramePreset.border_color)) GameCooltip.frame2:SetBorderCornerColor(unpack(gameCooltip.RoundedFramePreset.border_color)) @@ -3677,10 +3712,19 @@ function DF:CreateCoolTip() end if (gameCooltip.Type == 1 or gameCooltip.Type == 2) then - return gameCooltip:BuildTooltip() + gameCooltip:BuildTooltip() elseif (gameCooltip.Type == 3) then - return gameCooltip:BuildCooltip() + gameCooltip:BuildCooltip() + end + + if (frame1.attachToCursor) then + frame1:SetScript("OnUpdate", function() + frame1:ClearAllPoints() + frame1:SetPoint("bottom", UIParent, "bottomleft", DF:GetCursorPosition()) + end) + frame1:ClearAllPoints() + frame1:SetPoint("bottom", UIParent, "bottomleft", DF:GetCursorPosition()) end end @@ -3694,6 +3738,8 @@ function DF:CreateCoolTip() DF:FadeFrame(frame1, 1) DF:FadeFrame(frame2, 1) + frame1:SetScript("OnUpdate", nil) + --release custom icon texture objects, these are TextureObject passed with AddIcon() instead of a texture path or textureId for i = 1, #frame1.Lines do local menuButton = frame1.Lines[i] diff --git a/libs/DF/definitions.lua b/libs/DF/definitions.lua index 9121fac1..4f88767e 100644 --- a/libs/DF/definitions.lua +++ b/libs/DF/definitions.lua @@ -15,6 +15,17 @@ ---@field dump fun(tbl:table, resultString:string, deep:number) : string dump a table to a string ---@field findsubtable fun(tbl:table, index:number, value:any) : integer|nil find the value passed inside a sub table, return the index of the main table where the sub table with the value found is located ---@field remove fun(tbl:table, value:any) : boolean, number remove all values found inside the array, return true if any value was removed and the amount of values removed +---@field pack fun(...) : table pack a table into a string separating values with commas, example: {1, 2, 3, 4, 5, 6, 7}, result: "9,1,2,3,4,5,6,7,8,9" +---@field packsubmerge fun(tbl:table) : table pack a numerical table with numerical subtables into a string separating values with commas, example: { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }, result: "9,1,2,3,4,5,6,7,8,9" +---@field unpack fun(tbl:table, startIndex:number?) : table, number unpack a table packed with pack() and packsubmerge(), return the unpacked table and the index of the next index to be read +---@field packsub fun(tbl:table) : table pack a numerical table with numerical subtables into a string separating values with commas, example: { {1, 2, 3}, {4, 5, 6}, {7, 8, 9} }, result: "3,1,2,3,3,4,5,6,3,7,8,9" +---@field unpacksub fun(tbl:table, startIndex:number?) : table unpack a table packed with packsub() +---@field packhash fun(tbl:table) : string merge a key-value table into a single string separating values with commas, example: {key1 = value1, key2 = value2, key3 = value3}, result: "key1,value1,key2,value2,key3,value3" +---@field unpackhash fun(tbl:table) : table unpack a table packed with packhash() +---@field packhashsubtable fun(tbl:table) : string pack a hash table where the value of the key is a numerical table, example: {key1 = {1, 2, 3}, key2 = {4, 6}, key3 = {7}}, result: "key1,3,1,2,3,key2,2,4,6,key3,1,7" +---@field unpackhashsubtable fun(tbl:table) : table unpack a table packed with packhashsubtable() +---@field inserts fun(tbl:table, ...) : table receives an indexed table and N arguments, add all arguments passed into the table + ---@class df_language : table ---@field Register fun(addonId:any, languageId:string, gameLanguageOnly:boolean?) : table @@ -50,6 +61,9 @@ ---@field Color any? ---@field Texture any +GameCooltipFrame1 = {} +GameCooltipFrame2 = {} + ---df version of an atlasinfo from the game API, it include color and desaturation information ---a df atlas can be created using DetailsFramework:CreateAtlas() and then used with DetailsFramework:SetAtlas() ---@class df_atlasinfo : atlasinfo @@ -131,6 +145,10 @@ ---@field RoundedCornerPanelMixin df_roundedcornermixin ---@field Schedules df_schedule ---@field HeaderFunctions df_headerfunctions +---@field TimeLine_LineMixin df_timeline_line_mixin +---@field TimeLineMixin df_timeline_mixin +---@field NameplateBorderMixin df_nameplate_border_mixin +---@field RoleTypes roleinfo[] ---@field Language df_language ---@field Ejc df_ejc ---@field KeybindMixin df_keybindmixin @@ -151,6 +169,7 @@ ---@field alias_text_colors table ---@field ClassFileNameToIndex table engClass -> classIndex ---@field ClientLanguage string +---@field ClassIndexToFileName table classIndex -> engClass ---@field dropdown_templates table ---@field switch_templates table ---@field button_templates table @@ -186,6 +205,80 @@ ---@field IsDragonflightWow fun():boolean ---@field IsWarWow fun():boolean ---@field IsTWWWow fun():boolean +---@field CreateFullBorder fun(self:table, name:string, parent:frame) : border_frame +---@field CreateButton fun(self:table, parent:frame, func:function, width:number, height:number, text:any, param1:any, param2:any, texture:atlasname|texturepath|textureid|nil, member:string?, name:string?, shortMethod:any, buttonTemplate:table?, textTemplate:table?) : df_button callback function(blizzButton, clickType, param1, param2) end +---@field CreateCloseButton fun(self:table, parent:frame, frameName:string?) : df_closebutton +---@field CreateTabButton fun(self:table, parent:frame, frameName:string?) : df_tabbutton +---@field CreateRoundedPanel fun(self:table, parent:frame, frameName:string?, optionsTable:df_roundedpanel_options?) : df_roundedpanel +---@field CreateScaleBar fun(self:table, parent:frame, config:table) : df_scalebar +---@field CreateFadeAnimation fun(self:table, UIObject:uiobject, fadeInTime:number?, fadeOutTime:number?, fadeInAlpha:number?, fadeOutAlpha:number?) +---@field CreateTextureInfo fun(self:table, texture:atlasname|texturepath|textureid, width:number?, height:number?, left:number?, right:number?, top:number?, bottom:number?, imageWidthnumber?, imageHeightnumber?) : table deprecated, use: DetailsFramework:CreateAtlas() +---@field CreateLabel fun(self:table, parent:frame, text:any, size:any?, color:any?, font:string?, member:string?, name:string?, layer:drawlayer?) : df_label +---@field CreateDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateFontDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?, bIncludeDefault:boolean?) : df_dropdown +---@field CreateColorDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateOutlineDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateAnchorPointDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateAudioDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateRaidInstanceSelectorDroDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateBossSelectorDroDown fun(self:table, parent:frame, func:function, instanceId:any, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown_bossselector +---@field CreateFontListGenerator fun(self:table, callback:function, bIncludeDefault:boolean?) : function return a function which when called returns a table filled with all fonts available and ready to be used on dropdowns +---@field CreateAnchorPointListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all anchor points available and ready to be used on dropdowns +---@field CreateColorListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all colors available and ready to be used on dropdowns +---@field CreateOutlineListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all outline options available and ready to be used on dropdowns +---@field CreateBossListGenerator fun(self:table, callback:function, instanceId:any) : function return a function which when called returns a table filled with all boss options available and ready to be used on dropdowns +---@field CreateRaidInstanceListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all raid instance options available and ready to be used on dropdowns +---@field CreateAudioListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all audio options available and ready to be used on dropdowns +---@field CreateTextEntry fun(self:table, parent:frame, textChangedCallback:function, width:number, height:number, member:string?, name:string?, labelText:string?, textentryTemplate:table?, labelTemplate:table?) : df_textentry +---@field CreateKeybindFrame fun(self:table, parent:frame, name:string?, options:table?, setKeybindCallback:function?, keybindData:table?) : df_keybindframe +---@field CreateStatusBar fun(self:table, parent:frame, options:table?) : frame +---@field CreateLoadFilterParser fun(self:table, callback:fun(encounterId:number?)) create a helper which will callback when encounterId, spec, talent, role, combatstate changes +---@field CreateSwitch fun(self:table, parent:frame, onSwitch:function, defaultValue:boolean, width:number?, height:number?, leftText:string?, rightText:string?, member:string?, name:string?, colorInverted:boolean?, switchFunc:function?, returnFunc:function?, withLabel:string?, switch_template:table?, label_template:table?) : df_checkbox, df_label? +---@field CreateCheckboxGroup fun(self:table, parent:frame, radioOptions:df_radiooptions[], name:string?, options:table?, anchorOptions:table?) : df_checkboxgroup +---@field CreateRadioGroup fun(self:table, parent:frame, radioOptions:df_radiooptions[], name:string?, options:table?, anchorOptions:table?) : df_radiogroup +---@field CreateScrollBox fun(self:table, parent:frame, name:string, refreshFunc:function, data:table, width:number, height:number, lineAmount:number, lineHeight:number, createLineFunc:function?, autoAmount:boolean?, noScroll:boolean?, noBackdrop:boolean?) : df_scrollbox +---@field CreateAuraScrollBox fun(self:table, parent:frame, name:string?, data:table?, onRemoveCallback:function?, options:table?, onSetupAuraClick:function?) : df_aurascrollbox +---@field CreateGridScrollBox fun(self:table, parent:frame, name:string?, refreshFunc:function, data:table?, createColumnFrameFunc:function, options:table?) : df_gridscrollbox +---@field CreateCanvasScrollBox fun(self:table, parent:frame, child:frame?, name:string?, options:table?) : df_canvasscrollbox +---@field CreateTabContainer fun(self:table, parent:frame, title:string, frameName:string, tabList:df_tabinfotable[], optionsTable:table?, hookList:table?, languageInfo:table?) : df_tabcontainer +---@field CreateColorTable fun(self:table, r:number, g:number, b:number, a:number) : table return a table with {r, g, b, a} +---@field CreateEditor fun(self:table, parent:frame, name:string?, options:df_editor_defaultoptions?) : df_editor +---@field CreateHighlightTexture fun(self:table, parent:frame, parentKey:string?, alpha:number?, name:string?, texture:any) : texture +---@field CreateIconRowGeneric fun(self:table, parent:frame, name:string?, options:table?) +---@field CreateColorPickButton fun(self:table, parent:frame, name:string?, member:string?, callback:function, alpha:number?, buttonTemplate:table?) : df_colorpickbutton +---@field CreateSlider fun(self:table, parent:frame, width:number?, height:number?, minValue:number?, maxValue:number?, step:number?, defaultv:number?, isDecemal:boolean?, member:string?, name:string?, label:string?, sliderTemplate:string|table?, labelTemplate:string|table?) : df_slider, df_label? When the value of the slider is changed, it'll call self.OnValueChanged if the value exists. slider.OnValueChanged = function(self, FixedValue, value) end +---@field CreateFrameContainer fun(self:table, parent:frame, options:table?, frameName:string?) : df_framecontainer create a frame container, which is a frame that envelops another frame, and can be moved, resized, etc. +---@field CreateAnimationHub fun(self:table, parent:uiobject, onPlay:function?, onFinished:function?) : animationgroup +---@field CreateAnimation fun(self:table, animationGroup:animationgroup, animationType:animationtype, order:number, duration:number, arg1:any, arg2:any, arg3:any, arg4:any, arg5:any, arg6:any, arg7:any, arg8:any) : animation +---@field CreatePunchAnimation fun(self:table, frame:uiobject, scale:number?):animationgroup +---@field CreateTexture fun(self:table, parent:frame, texture:atlasname|texturepath|textureid|nil, width:number?, height:number?, layer:drawlayer?, coords:table?, member:string?, name:string?) : df_image +---@field CreateImage fun(self:table, parent:frame, texture:atlasname|texturepath|textureid|nil, width:number?, height:number?, layer:drawlayer?, coords:table?, member:string?, name:string?) : df_image +---@field CreateFrameShake fun(self:table, parent:uiobject, duration:number?, amplitude:number?, frequency:number?, absoluteSineX:boolean?, absoluteSineY:boolean?, scaleX:number?, scaleY:number?, fadeInTime:number?, fadeOutTime:number?, anchorPoints:table?) : df_frameshake +---@field CreateErrorLabel fun(self:table, parent:frame, text:string?, size:number?, color:any?, layer:drawlayer?, name:string?) : df_errorlabel +---@field CreateGlowOverlay fun(self:table, parent:frame, antsColor:any, glowColor:any) : frame +---@field CreateSimpleFrame fun(self:table, parent:frame, width:number?, height:number?, title:string?, frameName:string?, panelOptions:table?, savedVariableTable:table?) : simplepanel +---@field CreateAnts fun(self:table, parent:frame, antTable:df_anttable, leftOffset:number?, rightOffset:number?, topOffset:number?, bottomOffset:number?) : frame +---@field CreateBorder fun(self:table, parent:frame, alpha1:number?, alpha2:number?, alpha3:number?) : frame +---@field CreateMenuWithGridScrollBox fun(self:table, parent:frame, name:string?, refreshMeFunc:function, refreshButtonFunc:function, clickFunc:function, onCreateButton:function, gridScrollBoxOptions:df_gridscrollbox_options) : df_gridscrollbox create a scrollbox with a grid layout to be used as a menu +---@field CreateSearchBox fun(self:table, parent:frame, callback:function) : df_searchbox +---@field CreateHeader fun(self:table, parent:frame, headerTable:df_headercolumndata[], options:table?, frameName:string?) : df_headerframe +---@field CreateGraphicMultiLineFrame fun(self:table, parent:frame, name:string) : df_chartmulti +---@field CreateGraphicLineFrame fun(self:table, parent:frame, name:string) : df_chart +---@field CreateFlashAnimation fun(self:table, frame:uiobject, onFinishFunc:function?, onLoopFunc:function?) : animationgroup +---@field CreateTimeBar fun(self:table, parent:frame, texture:texturepath|textureid, width:number?, height:number?, value:number?, member:string?, name:string?) : df_timebar +---@field CreatePool fun(self:table, func:function, ...) : df_pool +---@field CreateObjectPool fun(self:table, func:function, ...) : df_pool alias of CreatePool +---@field CreateResizeGrips fun(self:table, parent:frame, options:df_resizergrip_options?, leftResizerName:string?, rightResizerName:string?) : df_resizergrip, df_resizergrip +---@field CreateAtlas fun(self:table, file:texturepath|textureid, width:number?, height:number?, leftTexCoord:number?, rightTexCoord:number?, topTexCoord:number?, bottomTexCoord:number?, tilesHorizontally:boolean?, tilesVertically:boolean?, vertexRed:any, vertexGreen:number?, vertexBlue:number?, vertexAlpha:number?, desaturated:boolean?, desaturation:number?, alpha:number) : atlasinfo +---@field CreateAtlasString fun(self:table, atlas:atlasinfo|atlasname, textureHeight:number?, textureWidth:number?) : string +---@field CreateSimplePanel fun(self:table, parent:frame, width:number?, height:number?, title:string?, frameName:string?, panelOptions:table?, savedVariableTable:table?) : simplepanel +---@field CreateNewAddOn fun(self:table, addonName:string, globalSavedVariablesName:string, savedVarsTemplate:table) : table +---@field CreateBossScrollSelectorForInstance fun(self:table, instanceId:any, parent:uiobject, name:string?, options:df_bossscrollselector_options?, callback:function?, ...) : df_bossscrollselector +---@field CreateTimeLineFrame fun(self:table, parent:frame, name:string, timelineOptions:df_timeline_options, elapsedtimeOptions:df_elapsedtime_options) : df_timeline, df_timeline_header?, df_timeline_header_body? create a timeline frame, header and headerBody is nil if the timelineOptions has no header_detached set to true +---@field CreateTitleBar fun(self:table, parent:frame, titleText:string) : df_titlebar +---@field CreateElapsedTimeFrame fun(self:table, parent:frame, name:string?, options:df_elapsedtime_options?) : df_elapsedtime +---@field CreateIconRow fun(self:table, parent:frame, name:string?, options:table?) : df_iconrow +---@field ConvertAnchorPointToInside fun(self:table, anchorPoint:anchorid) : anchorid ---@field ExpansionHasAugEvoker fun():boolean ---@field LoadSpellCache fun(self:table, hashMap:table, indexTable:table, allSpellsSameName:table) : hashMap:table, indexTable:table, allSpellsSameName:table load all spells in the game and add them into the passed tables ---@field UnloadSpellCache fun(self:table) wipe the table contents filled with LoadSpellCache() @@ -196,6 +289,9 @@ ---@field GetCurrentSpecId fun(self:table):number? return the specId of the current spec, retuns nil if the expansion the player is playing does not support specs ---@field GetClassSpecIds fun(self:table, engClass:string):number[] ---@field GetClassSpecIDs fun(self:table, engClass:string):number[] +---@field GetTextWidth fun(self:table, text:string, fontSize:number?) : number return the width of a text string +---@field GetCursorPosition fun(self:table) : number, number return the mouse position scaled by UIScale, use :SetPoint("bottomleft", UIParent, "bottomleft", DetailsFramework:GetMousePosition()) to anchor a frame to where the mouse is +---@field GetClassIdByFileName fun(self:table, fileName:string) : number return the classId of a class by its file name ---@field IsValidSpecId fun(self:table, specId:number):boolean check if the passed specId is valid for the player class, also return false for tutorial specs ---@field GetDragonlightTalentString fun(self:table):string return the talent config string ---@field GetClassList fun(self:table):{ID:number, Name:string, FileString:string, Texture:string, TexCoord:number[]}[] @@ -206,16 +302,10 @@ ---@field GetDefaultBackdropColor fun(self:table) : red, green, blue, alpha return the standard backdrop color used by blizzard on their own frames ---@field Msg fun(self:table, message:string, ...) show a message in the chat frame ---@field MsgWarning fun(self:table, message:string, ...) show a warning message in the chat frame ----@field CreateButton fun(self:table, parent:frame, func:function, width:number, height:number, text:any, param1:any, param2:any, texture:atlasname|texturepath|textureid|nil, member:string?, name:string?, shortMethod:any, buttonTemplate:table?, textTemplate:table?) : df_button callback function(blizzButton, clickType, param1, param2) end ----@field CreateCloseButton fun(self:table, parent:frame, frameName:string?) : df_closebutton ----@field CreateTabButton fun(self:table, parent:frame, frameName:string?) : df_tabbutton ----@field CreateRoundedPanel fun(self:table, parent:frame, frameName:string?, optionsTable:df_roundedpanel_options?) : df_roundedpanel ----@field CreateScaleBar fun(self:table, parent:frame, config:table) : df_scalebar ---@field AddRoundedCornersToFrame fun(self:table, frame:frame, optionsTable:df_roundedpanel_preset?) ---@field ParseColors fun(self:table, red:any, green:number?, blue:number?, alpha:number?) : red, green, blue, alpha ---@field Mixin fun(self:table, target:table, ...) : table ---@field SetButtonTexture fun(self:table, button:button|df_button, texture:atlasname|texturepath|textureid) ----@field CreateFadeAnimation fun(self:table, UIObject:uiobject, fadeInTime:number?, fadeOutTime:number?, fadeInAlpha:number?, fadeOutAlpha:number?) ---@field SetFontSize fun(self:table, fontstring:fontstring, size:number) ---@field GetFontSize fun(self:table, fontstring:fontstring) : number return the font size of the fontstring ---@field SetFontColor fun(self:table, fontstring:fontstring, red:any, green:number?, blue:number?, alpha:number?) @@ -233,33 +323,13 @@ ---@field SplitTextInLines fun(self:table, text:string) : string[] split a text into lines ---@field SetAnchor fun(self:table, widget:uiobject, anchorTable:df_anchor, anchorTo:uiobject?) only adjust the anchors of a widget, does not save values ---@field AddTextureToText fun(self:table, text:string, textureInfo:table, bAddSpace:boolean?, bAddAfterText:any) : string textureInfo is a table with .texture .width .height .coords{left, right, top, bottom} ----@field CreateTextureInfo fun(self:table, texture:atlasname|texturepath|textureid, width:number?, height:number?, left:number?, right:number?, top:number?, bottom:number?, imageWidthnumber?, imageHeightnumber?) : table deprecated, use: DetailsFramework:CreateAtlas() ---@field ApplyStandardBackdrop fun(self:table, frame:frame, bUseSolidColor:boolean?, alphaScale:number?) ---@field NewLabel fun(self:table, parent:frame, container:frame, name:string?, member:string?, text:string|table, font:string?, size:any?, color:any?, layer:drawlayer?) : df_label ----@field CreateLabel fun(self:table, parent:frame, text:any, size:any?, color:any?, font:string?, member:string?, name:string?, layer:drawlayer?) : df_label ---@field NewDropDown fun(self:table, parent:frame, container:frame?, name:string?, member:string?, width:number?, height:number?, func:function, default:any, template:table?) : df_dropdown ----@field CreateDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateFontDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?, bIncludeDefault:boolean?) : df_dropdown ----@field CreateColorDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateOutlineDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateAnchorPointDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateAudioDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateRaidInstanceSelectorDroDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown ----@field CreateBossSelectorDroDown fun(self:table, parent:frame, func:function, instanceId:any, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown_bossselector ----@field CreateFontListGenerator fun(self:table, callback:function, bIncludeDefault:boolean?) : function return a function which when called returns a table filled with all fonts available and ready to be used on dropdowns ----@field CreateAnchorPointListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all anchor points available and ready to be used on dropdowns ----@field CreateColorListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all colors available and ready to be used on dropdowns ----@field CreateOutlineListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all outline options available and ready to be used on dropdowns ----@field CreateBossListGenerator fun(self:table, callback:function, instanceId:any) : function return a function which when called returns a table filled with all boss options available and ready to be used on dropdowns ----@field CreateRaidInstanceListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all raid instance options available and ready to be used on dropdowns ----@field CreateAudioListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all audio options available and ready to be used on dropdowns ---@field BuildDropDownFontList fun(self:table, onClick:function, icon:atlasname|texturepath|textureid|nil, iconTexcoord:table?, iconSize:number?, bIncludeDefault:boolean?) : table build a list of fonts to be used as optionsTable for a dropdown ----@field CreateTextEntry fun(self:table, parent:frame, textChangedCallback:function, width:number, height:number, member:string?, name:string?, labelText:string?, textentryTemplate:table?, labelTemplate:table?) : df_textentry ---@field ReskinSlider fun(self:table, slider:frame) ---@field GetAvailableSpells fun(self:table) : table ---@field NewColor fun(self:table, colorName:string, red:number, green:number, blue:number, alpha:number?) : table ----@field CreateKeybindFrame fun(self:table, parent:frame, name:string?, options:table?, setKeybindCallback:function?, keybindData:table?) : df_keybindframe ----@field CreateStatusBar fun(self:table, parent:frame, options:table?) : frame ---@field GetTemplate fun(self:table, templatecategory:templatecategory, templateName:string) : table ---@field UpdateLoadConditionsTable fun(self:table, loadConditionsTable:table) ---@field IconPick fun(self:table, callback:function, bCloseWhenSelect:boolean?, param1:any?, param2:any?) @@ -267,36 +337,15 @@ ---@field InstallTemplate fun(self:table, widgetType:string, templateName:string, template:table, parentName:any) : table ---@field NewSpecialLuaEditorEntry fun(self:table, parent:frame, width:number, height:number, member:string?, name:string?, nointent:boolean?, showLineNumbers:boolean?, bNoName:boolean?) : df_luaeditor ---@field PassLoadFilters fun(self:table, loadTable:table, encounterID:number?) : boolean, string ----@field CreateLoadFilterParser fun(self:table, callback:fun(encounterId:number?)) create a helper which will callback when encounterId, spec, talent, role, combatstate changes ----@field CreateSwitch fun(self:table, parent:frame, onSwitch:function, defaultValue:boolean, width:number?, height:number?, leftText:string?, rightText:string?, member:string?, name:string?, colorInverted:boolean?, switchFunc:function?, returnFunc:function?, withLabel:string?, switch_template:table?, label_template:table?) : df_checkbox, df_label? ----@field CreateCheckboxGroup fun(self:table, parent:frame, radioOptions:df_radiooptions[], name:string?, options:table?, anchorOptions:table?) : df_checkboxgroup ----@field CreateRadioGroup fun(self:table, parent:frame, radioOptions:df_radiooptions[], name:string?, options:table?, anchorOptions:table?) : df_radiogroup ----@field CreateScrollBox fun(self:table, parent:frame, name:string, refreshFunc:function, data:table, width:number, height:number, lineAmount:number, lineHeight:number, createLineFunc:function?, autoAmount:boolean?, noScroll:boolean?, noBackdrop:boolean?) : df_scrollbox ----@field CreateAuraScrollBox fun(self:table, parent:frame, name:string?, data:table?, onRemoveCallback:function?, options:table?, onSetupAuraClick:function?) : df_aurascrollbox ----@field CreateGridScrollBox fun(self:table, parent:frame, name:string?, refreshFunc:function, data:table?, createColumnFrameFunc:function, options:table?) : df_gridscrollbox ----@field CreateCanvasScrollBox fun(self:table, parent:frame, child:frame?, name:string?, options:table?) : df_canvasscrollbox ----@field CreateTabContainer fun(self:table, parent:frame, title:string, frameName:string, tabList:df_tabinfotable[], optionsTable:table?, hookList:table?, languageInfo:table?) : df_tabcontainer ---@field GetSizeFromPercent fun(self:table, uiObject:uiobject, percent:number) : number get the min size of a uiObject and multiply it by the percent passed ---@field BuildMenu fun(self:table, parent:frame, menuOptions:df_menu_table[], xOffset:number?, yOffset:number?, height:number?, useColon:boolean?, textTemplate:table?, dropdownTemplate:table?, switchTemplate:table?, switchIsCheckbox:boolean?, sliderTemplate:table?, buttonTemplate:table?, valueChangeHook:function?) ---@field BuildMenuVolatile fun(self:table, parent:frame, menuOptions:df_menu_table[], xOffset:number?, yOffset:number?, height:number?, useColon:boolean?, textTemplate:table?, dropdownTemplate:table?, switchTemplate:table?, switchIsCheckbox:boolean?, sliderTemplate:table?, buttonTemplate:table?, valueChangeHook:function?) ---@field GetColorBrightness fun(self:table, r:number, g:number, b:number) : number return the brightness of a color from zero to one ---@field GetColorHue fun(self:table, r:number, g:number, b:number) : number return the hue of a color from red to blue to green to yellow and back to red ---@field IsHtmlColor fun(self:table, colorName:any) : unknown return true if DF.alias_text_colors has the colorName as a key ----@field CreateColorTable fun(self:table, r:number, g:number, b:number, a:number) : table return a table with {r, g, b, a} ---@field FormatColor fun(self:table, newFormat:string, r:number|string|table, g:number?, b:number?, a:number?, decimalsAmount:number?) : string|table|number|nil, number|nil, number|nil, number|nil takes in a color in one format and converts it to another specified format. ----@field CreateEditor fun(self:table, parent:frame, name:string?, options:df_editor_defaultoptions?) : df_editor ---@field RandomBool fun(self:table, odds: number?) : boolean return a random boolean ----@field CreateHighlightTexture fun(self:table, parent:frame, parentKey:string?, alpha:number?, name:string?, texture:any) : texture ----@field CreateIconRowGeneric fun(self:table, parent:frame, name:string?, options:table?) ----@field CreateColorPickButton fun(self:table, parent:frame, name:string?, member:string?, callback:function, alpha:number?, buttonTemplate:table?) : df_colorpickbutton ----@field CreateSlider fun(self:table, parent:frame, width:number?, height:number?, minValue:number?, maxValue:number?, step:number?, defaultv:number?, isDecemal:boolean?, member:string?, name:string?, label:string?, sliderTemplate:string|table?, labelTemplate:string|table?) : df_slider, df_label? When the value of the slider is changed, it'll call self.OnValueChanged if the value exists. slider.OnValueChanged = function(self, FixedValue, value) end ----@field CreateFrameContainer fun(self:table, parent:frame, options:table?, frameName:string?) : df_framecontainer create a frame container, which is a frame that envelops another frame, and can be moved, resized, etc. ----@field CreateAnimationHub fun(self:table, parent:uiobject, onPlay:function?, onFinished:function?) : animationgroup ----@field CreateAnimation fun(self:table, animationGroup:animationgroup, animationType:animationtype, order:number, duration:number, arg1:any, arg2:any, arg3:any, arg4:any, arg5:any, arg6:any, arg7:any, arg8:any) : animation ---@field NewImage fun(self:table, parent:frame, texture:atlasname|texturepath|textureid|df_gradienttable|nil, width:number?, height:number?, layer:drawlayer?, texCoord:table?, member:string?, name:string?) : df_image ----@field CreateTexture fun(self:table, parent:frame, texture:atlasname|texturepath|textureid|nil, width:number?, height:number?, layer:drawlayer?, coords:table?, member:string?, name:string?) : df_image ----@field CreateImage fun(self:table, parent:frame, texture:atlasname|texturepath|textureid|nil, width:number?, height:number?, layer:drawlayer?, coords:table?, member:string?, name:string?) : df_image ----@field CreateFrameShake fun(self:table, parent:uiobject, duration:number?, amplitude:number?, frequency:number?, absoluteSineX:boolean?, absoluteSineY:boolean?, scaleX:number?, scaleY:number?, fadeInTime:number?, fadeOutTime:number?, anchorPoints:table?) : df_frameshake ---@field SetTexCoordFromAtlasInfo fun(self:table, texture:texture, atlasInfo:atlasinfo) : nil ---@field TruncateNumber fun(self:table, number:number, fractionDigits:number) : number ---@field GetNpcIdFromGuid fun(self:table, GUID:string) : number @@ -317,10 +366,8 @@ ---@field PreviewTexture fun(self:table, texture:atlasname|texturepath|textureid, left:number?, right:number?, top:number?, bottom:number?) : nil ---@field TableIsAtlas fun(self:table, atlasTale:table) : boolean ---@field SetAtlas fun(self:table, textureObject:texture, atlas:atlasinfo|atlasname, useAtlasSize:boolean?, filterMode:texturefilter?, resetTexCoords:boolean?) : nil ----@field CreateAtlas fun(self:table, file:texturepath|textureid, width:number?, height:number?, leftTexCoord:number?, rightTexCoord:number?, topTexCoord:number?, bottomTexCoord:number?, tilesHorizontally:boolean?, tilesVertically:boolean?, vertexRed:any, vertexGreen:number?, vertexBlue:number?, vertexAlpha:number?, desaturated:boolean?, desaturation:number?, alpha:number) : atlasinfo ---@field ParseTexture fun(self:table, texture:texturepath|textureid|atlasname|atlasinfo, width: number?, height: number?, leftTexCoord: number?, rightTexCoord: number?, topTexCoord: number?, bottomTexCoord: number?, vertexRed:number|string?, vertexGreenvertexRed:number?, vertexBluevertexRed:number?, vertexAlphavertexRed:number?) : any, number?, number?, number?, number?, number?, number?, number?, number?, number?, number?, number?, number? ---@field IsTexture fun(self:table, texture:any, bCheckTextureObject: boolean?) : boolean ----@field CreateAtlasString fun(self:table, atlas:atlasinfo|atlasname, textureHeight:number?, textureWidth:number?) : string ---@field SetMask fun(self:table, texture:texture, maskTexture:atlasname|texturepath|textureid|table|string) : nil ---@field GetClientRegion fun(self:table) : string ---@field GetBestFontPathForLanguage fun(self:table, languageId:string) : string @@ -333,32 +380,13 @@ ---@field GetParentNamePath fun(self:table, object:uiobject) : string ---@field GetAsianNumberSymbols fun(self:table) : string, string, string return the abbreviation for 1,000 10,000 and 100,000,000 ---@field GetBestFontForLanguage fun(self:table, languageId:string?, western:string?, cyrillic:string?, china:string?, korean:string?, taiwan:string?) : string ----@field CreateGlowOverlay fun(self:table, parent:frame, antsColor:any, glowColor:any) : frame ----@field CreateSimpleFrame fun(self:table, parent:frame, width:number?, height:number?, title:string?, frameName:string?, panelOptions:table?, savedVariableTable:table?) : simplepanel ----@field CreateAnts fun(self:table, parent:frame, antTable:df_anttable, leftOffset:number?, rightOffset:number?, topOffset:number?, bottomOffset:number?) : frame ----@field CreateBorder fun(self:table, parent:frame, alpha1:number?, alpha2:number?, alpha3:number?) : frame ----@field CreateMenuWithGridScrollBox fun(self:table, parent:frame, name:string?, refreshMeFunc:function, refreshButtonFunc:function, clickFunc:function, onCreateButton:function, gridScrollBoxOptions:df_gridscrollbox_options) : df_gridscrollbox create a scrollbox with a grid layout to be used as a menu ----@field CreateSearchBox fun(self:table, parent:frame, callback:function) : df_searchbox ----@field ConvertAnchorPointToInside fun(self:table, anchorPoint:anchorid) : anchorid ----@field CreateHeader fun(self:table, parent:frame, headerTable:df_headercolumndata[], options:table?, frameName:string?) : df_headerframe ----@field CreateGraphicMultiLineFrame fun(self:table, parent:frame, name:string) : df_chartmulti ----@field CreateGraphicLineFrame fun(self:table, parent:frame, name:string) : df_chart ----@field CreateFlashAnimation fun(self:table, frame:uiobject, onFinishFunc:function?, onLoopFunc:function?) : animationgroup ----@field CreateTimeBar fun(self:table, parent:frame, texture:texturepath|textureid, width:number?, height:number?, value:number?, member:string?, name:string?) : df_timebar ----@field CreatePool fun(self:table, func:function, ...) : df_pool ----@field CreateObjectPool fun(self:table, func:function, ...) : df_pool alias of CreatePool ---@field GetRoleIconAndCoords fun(self:table, role:string) : string, number, number, number, number return the texture path and texcoords for a role ---@field AddRoleIconToText fun(self:table, text:string, role:string, size:number?) : string add a role icon to a text using escape codes ---@field GetRoleTCoordsAndTexture fun(self:table, roleID:number) : number, number, number, number, string ---@field AddColorToText fun(self:table, text:string, color:any) : string wrap text with a color ----@field AddClassColorToText fun(self:table, text:string, className:class) : string wrap text with a class color ----@field CreateSimplePanel fun(self:table, parent:frame, width:number?, height:number?, title:string?, frameName:string?, panelOptions:table?, savedVariableTable:table?) : simplepanel +---@field AddClassColorToText fun(self:table, text:string, className:class|number) : string wrap text with a class color ---@field MakeDraggable fun(self:table, frame:frame) : nil ----@field CreateNewAddOn fun(self:table, addonName:string, globalSavedVariablesName:string, savedVarsTemplate:table) : table ----@field CreateBossScrollSelectorForInstance fun(self:table, instanceId:any, parent:uiobject, name:string?, options:df_bossscrollselector_options?, callback:function?, ...) : df_bossscrollselector ----@field CreateTimeLineFrame fun(self:table, parent:frame, name:string, timelineOptions:df_timeline_options, elapsedtimeOptions:df_elapsedtime_options) : df_timeline ----@field CreateElapsedTimeFrame fun(self:table, parent:frame, name:string?, options:df_elapsedtime_options?) : df_elapsedtime ----@field GetClassTCoordsAndTexture fun(self:table, class:string) : number, number, number, number, string return the class icon texture coordinates and texture file path +---@field GetClassTCoordsAndTexture fun(self:table, class:string|number) : number, number, number, number, string return the class icon texture coordinates and texture file path ---@field GetClassColorByClassId fun(self:table, classId:number) : number, number, number return the class color by classId ---@field MakeStringFromSpellId fun(self:table, spellId:any) : string return a string with the spell icon and name using escape codes ---@field AddClassIconToText fun(self:table, text:string, playerName:string, englishClassName:string, useSpec:boolean?, iconSize:number?) : string wrap 'text' with the class icon of 'playerName' using |T|t scape codes diff --git a/libs/DF/dropdown.lua b/libs/DF/dropdown.lua index a91b483a..211187b8 100644 --- a/libs/DF/dropdown.lua +++ b/libs/DF/dropdown.lua @@ -11,6 +11,41 @@ ]=] +---@class df_dropdown : table, frame, df_widgets +---@field func function +---@field SetTemplate fun(self:df_dropdown, template:table|string) +---@field SetFixedParameter fun(self:df_dropdown, value:any) is sent as 2nd argument to the callback function, the value is the same no matter which option is selected +---@field BuildDropDownFontList fun(self:df_dropdown, onClick:function, icon:any, iconTexcoord:table?, iconSize:table?):table make a dropdown list with all fonts available, on select a font, call the function onClick +---@field SetFunction fun(self:df_dropdown, func:function) +---@field SetEmptyTextAndIcon fun(self:df_dropdown, text:string, icon:any) +---@field Select fun(self:df_dropdown, optionName:string|number, byOptionNumber:boolean?, bOnlyShown:boolean?, runCallback:boolean?):boolean +---@field SelectDelayed fun(self:df_dropdown, optionName:string|number, byOptionNumber:boolean?, bOnlyShown:boolean?, runCallback:boolean?) --call Select() after a random delay +---@field Open fun(self:df_dropdown) +---@field Close fun(self:df_dropdown) +---@field Refresh fun(self:df_dropdown) +---@field GetValue fun(self:df_dropdown):any +---@field GetFunction fun(self:df_dropdown):function +---@field GetMenuSize fun(self:df_dropdown):number, number +---@field SetMenuSize fun(self:df_dropdown, width:number, height:number) +---@field Disable fun(self:df_dropdown) +---@field Enable fun(self:df_dropdown) + +---@class dropdownoption : table +---@field value any +---@field label string text shown in the dropdown option +---@field onclick fun(dropdownObject:table, fixedValue:any, value:any)? function to call when the option is selected +---@field icon string|number? texture +---@field color any any color format +---@field font string? +---@field texcoord number[]? left, right, top, bottom +---@field iconcolor any any color format +---@field iconsize number[]? width, height +---@field languageId string? +---@field rightbutton function? function to call on right click +---@field statusbar string|number? statusbar texture +---@field statusbarcolor any any color format +---@field rightTexture string|number? texture +---@field centerTexture string|number? texture ---@type detailsframework local DF = _G ["DetailsFramework"] @@ -393,6 +428,12 @@ local canRunCallbackFunctionForOption = function(canRunCallback, optionTable, dr end end +function DropDownMetaFunctions:SelectDelayed(optionName, byOptionNumber, bOnlyShown, runCallback) + DF.Schedules.After(DF.Math.RandomFraction(0.016, 0.3), function() + self:Select(optionName, byOptionNumber, bOnlyShown, runCallback) + end) +end + ---if bOnlyShown is true it'll first create a table with visible options that has .shown and then select in this table the index passed (if byOptionNumber) ---@param optionName string value or string shown in the name of the option ---@param byOptionNumber number the option name is considered a number and selects the index of the menu @@ -522,10 +563,20 @@ function DropDownMetaFunctions:Selected(thisOption) end end + ---@type fontstring + local thisLabel = self.label + + local parentWidth = self:GetWidth() + if (addonId and phraseId) then self.label:SetText(DF.Language.GetText(addonId, phraseId)) else - self.label:SetText(thisOption.label) + thisLabel:SetText(thisOption.label) + thisLabel:SetWordWrap(false) + thisLabel:SetIndentedWordWrap(false) + thisLabel:SetWidth(parentWidth + 30) + DF:TruncateText(thisLabel, parentWidth-30) + thisLabel:Show() end self.icon:SetTexture(thisOption.icon) @@ -702,6 +753,7 @@ function DetailsFrameworkDropDownOptionOnLeave(frame) frame:GetParent().mouseover:Hide() end + --@button is the raw button frame, object is the button capsule --click on the main dropdown frame (not the menu options popup) function DetailsFrameworkDropDownOnMouseDown(button, buttontype) @@ -736,6 +788,8 @@ function DetailsFrameworkDropDownOnMouseDown(button, buttontype) for tindex, thisOption in ipairs(optionsTable) do local bIsOptionVisible = isOptionVisible(button, thisOption) + ---@cast thisOption dropdownoption + if (bIsOptionVisible) then local thisOptionFrame = object.menus[i] showing = showing + 1 @@ -1126,23 +1180,6 @@ end ------------------------------------------------------------------------------------------------------------ --object constructor ----@class df_dropdown : table, frame, df_widgets ----@field func function ----@field SetTemplate fun(self:df_dropdown, template:table|string) ----@field SetFixedParameter fun(self:df_dropdown, value:any) is sent as 2nd argument to the callback function, the value is the same no matter which option is selected ----@field BuildDropDownFontList fun(self:df_dropdown, onClick:function, icon:any, iconTexcoord:table?, iconSize:table?):table make a dropdown list with all fonts available, on select a font, call the function onClick ----@field SetFunction fun(self:df_dropdown, func:function) ----@field SetEmptyTextAndIcon fun(self:df_dropdown, text:string, icon:any) ----@field Select fun(self:df_dropdown, optionName:string|number, byOptionNumber:boolean?, bOnlyShown:boolean?, runCallback:boolean?):boolean ----@field Open fun(self:df_dropdown) ----@field Close fun(self:df_dropdown) ----@field Refresh fun(self:df_dropdown) ----@field GetValue fun(self:df_dropdown):any ----@field GetFunction fun(self:df_dropdown):function ----@field GetMenuSize fun(self:df_dropdown):number, number ----@field SetMenuSize fun(self:df_dropdown, width:number, height:number) ----@field Disable fun(self:df_dropdown) ----@field Enable fun(self:df_dropdown) ---return a function which when called returns a table filled with all fonts available and ready to be used on dropdowns ---@param callback function diff --git a/libs/DF/editor.lua b/libs/DF/editor.lua index 4d6010e6..4d9044cd 100644 --- a/libs/DF/editor.lua +++ b/libs/DF/editor.lua @@ -647,7 +647,7 @@ detailsFramework.EditorMixin = { end local x, y = moverFrame:GetCenter() - moverFrame:SetPoint("center", UIParent, "bottomleft", x, y) + --moverFrame:SetPoint("center", UIParent, "bottomleft", x, y) --current position of object selected local x, y = object:GetCenter() @@ -655,8 +655,12 @@ detailsFramework.EditorMixin = { moverFrame.MovingInfo.restingY = y moverFrame:SetScript("OnUpdate", onTickNotMoving) - moverFrame:GetScript("OnMouseDown")(moverFrame) - moverFrame:GetScript("OnMouseUp")(moverFrame) + --problem, I don't remember why caused the issue of the hide on click on others tabs, I think the mover was anchored to parent frame all points + if (not moverFrame.moved) then + moverFrame:GetScript("OnMouseDown")(moverFrame) + moverFrame:GetScript("OnMouseUp")(moverFrame) + --moverFrame.moved = true + end end self.ObjectBackgroundTexture:SetPoint("topleft", object, "topleft", 0, 0) @@ -1011,7 +1015,7 @@ detailsFramework.EditorMixin = { if (objectX ~= moverFrame.MovingInfo.restingX or objectY ~= moverFrame.MovingInfo.restingY) then moverFrame:SetPoint("center", object, moverFrame.anchorName, 0, 0) local x, y = moverFrame:GetCenter() - moverFrame:SetPoint("center", UIParent, "bottomleft", x, y) + --moverFrame:SetPoint("center", UIParent, "bottomleft", x, y) moverFrame.MovingInfo.restingX = objectX moverFrame.MovingInfo.restingY = objectY end diff --git a/libs/DF/ejournal.lua b/libs/DF/ejournal.lua index dbfb30af..96fda67e 100644 --- a/libs/DF/ejournal.lua +++ b/libs/DF/ejournal.lua @@ -50,6 +50,7 @@ local defaultCreatureIconCoords = {0, 1, 0, 0.95} ---@field CreateEncounterJournalDump fun() ---@field GetAllRaidInstances fun():df_instanceinfo[] ---@field GetAllDungeonInstances fun():df_instanceinfo[] +---@field GetEncounterSpells fun(journalInstanceId:number, journalEncounterId:number, difficulty:number):df_ejspell[] ---@field CacheRaidData_OnlyRaidInstances table ---@field CacheRaidData_OnlyDungeonInstances table ---@field CacheRaidData_ByInstanceId table @@ -169,6 +170,56 @@ function Ejc.GetAllDungeonInstances() return Ejc.CacheRaidData_OnlyDungeonInstances end +---@class df_ejspell : table +---@field spellID number +---@field title string header name in the encounter journal +---@field abilityIcon number journal spell icon + +function Ejc.GetEncounterSpells(journalInstanceId, journalEncounterId, difficulty) + EJ_SetDifficulty(difficulty or 16) + EJ_SelectInstance(journalInstanceId) + EJ_SelectEncounter(journalEncounterId) + + local encounterName, encounterDescription, journalEncounterID, rootSectionID, link, journalInstanceID, dungeonEncounterID, instanceID = EJ_GetEncounterInfo(journalEncounterId) + local sectionStack = {} + local currentSectionId = rootSectionID + + local spells = {} + + repeat + local sectionInfo = C_EncounterJournal.GetSectionInfo(currentSectionId) + if (not sectionInfo) then + break + end + + if (sectionInfo.spellID) then + local spellInfo = C_Spell.GetSpellInfo(sectionInfo.spellID) + sectionInfo.spellName = spellInfo and spellInfo.name + sectionInfo.spellIcon = spellInfo and spellInfo.iconID + + table.insert(spells, sectionInfo) + + spells[sectionInfo.spellID] = sectionInfo + if (sectionInfo.spellName) then + spells[sectionInfo.spellName] = sectionInfo + end + if (sectionInfo.title) then + spells[sectionInfo.title] = sectionInfo + end + end + + if (sectionInfo.siblingSectionID) then + table.insert(sectionStack, sectionInfo.siblingSectionID) + end + + if (sectionInfo.firstChildSectionID) then + table.insert(sectionStack, sectionInfo.firstChildSectionID) + end + + currentSectionId = table.remove(sectionStack) + until not currentSectionId +end + function Ejc.CreateEncounterJournalDump() --if the cache has been already created, then return if (bHasLoaded) then @@ -310,7 +361,7 @@ function Ejc.CreateEncounterJournalDump() else Ejc.CacheRaidData_OnlyDungeonInstances[#Ejc.CacheRaidData_OnlyDungeonInstances+1] = instanceData end - + --get information about the bosses in the raid for encounterIndex = 1, maxRaidBosses do local encounterName, encounterDescription, journalEncounterID, rootSectionID, link, journalInstanceID, dungeonEncounterID, instanceID = EJ_GetEncounterInfoByIndex(encounterIndex, journalInstanceID) diff --git a/libs/DF/elapsedtime.lua b/libs/DF/elapsedtime.lua index 3fae3026..335f4830 100644 --- a/libs/DF/elapsedtime.lua +++ b/libs/DF/elapsedtime.lua @@ -122,7 +122,8 @@ detailsFramework.TimeLineElapsedTimeFunctions = { label:SetText(detailsFramework:IntegerToTimer(floor(secondsOfTime))) if (label.line:IsShown()) then - label.line:SetHeight(self.scrollChild:GetHeight()) + --label.line:SetHeight(self.scrollChild:GetHeight()) + label.line:SetHeight(self:GetParent():GetParent():GetHeight()) end label:Show() diff --git a/libs/DF/fw.lua b/libs/DF/fw.lua index d4cb77a3..fd02512f 100644 --- a/libs/DF/fw.lua +++ b/libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 575 +local dversion = 586 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) @@ -131,6 +131,10 @@ function DF.IsDragonflightAndBeyond() return buildInfo >= 100000 end +function DF.ExpansionHasEvoker() + return buildInfo >= 100000 +end + ---return true if the wow version is Dragonflight or below ---@return boolean function DF.IsDragonflightOrBelow() @@ -937,6 +941,16 @@ function DF.table.append(t1, t2) return t1 end +---receive a table and N arguments, add each argument to the table +---@param t1 table +---@vararg any +function DF.table.inserts(t1, ...) + for i = 1, select("#", ...) do + t1[#t1+1] = select(i, ...) + end + return t1 +end + ---copy values that does exist on table2 but not on table1 ---@param t1 table ---@param t2 table @@ -1289,6 +1303,17 @@ end local dummyFontString = UIParent:CreateFontString(nil, "background", "GameFontNormal") local defaultFontFile = dummyFontString:GetFont() +function DF:GetTextWidth(text, size) + if (size) then + DF:SetFontSize(dummyFontString, size) + else + DF:SetFontSize(dummyFontString, 12) + end + + dummyFontString:SetText(text) + return dummyFontString:GetStringWidth() +end + ---get the UIObject of type 'FontString' and set the default game font into it ---@param self table ---@param fontString fontstring @@ -1384,6 +1409,10 @@ end ---@param className class ---@return string function DF:AddClassColorToText(text, className) + if (type(className) == "number") then + className = DF.ClassIndexToFileName[className] + end + if (type(className) ~= "string") then return DF:RemoveRealName(text) @@ -1402,9 +1431,12 @@ function DF:AddClassColorToText(text, className) end ---returns the class icon texture coordinates and texture file path ----@param class string +---@param class string|number ---@return number, number, number, number, string function DF:GetClassTCoordsAndTexture(class) + if (type(class) == "number") then + class = DF.ClassIndexToFileName[class] + end local l, r, t, b = unpack(CLASS_ICON_TCOORDS[class]) return l, r, t, b, [[Interface\WORLDSTATEFRAME\Icons-Classes]] end @@ -1627,7 +1659,6 @@ function DF:trim(string) return from > #string and "" or string:match(".*%S", from) end - ---truncate removing at a maximum of 10 character from the string ---@param fontString table ---@param maxWidth number @@ -1773,6 +1804,12 @@ function DF:TruncateNumber(number, fractionDigits) return truncatedNumber end +function DF:GetCursorPosition() + local x, y = GetCursorPosition() + local scale = UIParent:GetEffectiveScale() + return x / scale, y / scale +end + ---attempt to get the ID of an npc from a GUID ---@param GUID string ---@return number @@ -1849,6 +1886,34 @@ function DF:GetSpellBookSpells() return spellNamesInSpellBook, spellIdsInSpellBook end +function DF:GetHeroTalentId() + local configId = C_ClassTalents.GetActiveConfigID() + if (not configId) then + return 0 + end + local configInfo = C_Traits.GetConfigInfo(configId) + for treeIndex, treeId in ipairs(configInfo.treeIDs) do + local treeNodes = C_Traits.GetTreeNodes(treeId) + for nodeIdIndex, treeNodeID in ipairs(treeNodes) do + local traitNodeInfo = C_Traits.GetNodeInfo(configId, treeNodeID) + if (traitNodeInfo) then + local activeEntry = traitNodeInfo.activeEntry + if (activeEntry) then + local entryId = activeEntry.entryID + local rank = activeEntry.rank + if (rank > 0) then + local entryInfo = C_Traits.GetEntryInfo(configId, entryId) + if (not entryInfo.definitionID and entryInfo.subTreeID) then + return entryInfo.subTreeID + end + end + end + end + end + end + return 0 +end + ---return a table of passive talents, format: [spellId] = true ---@return {Name: string, ID: number, Texture: any, IsSelected: boolean}[] function DF:GetAllTalents() @@ -2086,6 +2151,27 @@ function DF:CreateFlashAnimation(frame, onFinishFunc, onLoopFunc) return flashAnimation end +local onStartPunchAnimation = function(animationGroup) + local parent = animationGroup:GetParent() + animationGroup.parentWidth = parent:GetWidth() + animationGroup.parentHeight = parent:GetHeight() +end + +local onStopPunchAnimation = function(animationGroup) + local parent = animationGroup:GetParent() + parent:SetWidth(animationGroup.parentWidth) + parent:SetHeight(animationGroup.parentHeight) +end + +function DF:CreatePunchAnimation(frame, scale) + scale = scale or 1.1 + scale = math.min(scale, 1.9) + local animationHub = DF:CreateAnimationHub(frame, onStartPunchAnimation, onStopPunchAnimation) + local scaleUp = DF:CreateAnimation(animationHub, "scale", 1, 0.05, 1, 1, scale, scale, "center", 0, 0) + local scaleDown = DF:CreateAnimation(animationHub, "scale", 2, 0.05, 1, 1, 1-(scale - 1), 1-(scale - 1), "center", 0, 0) + return animationHub +end + ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --anchoring @@ -3168,7 +3254,7 @@ function DF:ParseTemplate(templateCategory, template) if (objectType == "label") then templateCategory = "font" - elseif (objectType == "dropdown") then + elseif (objectType == "dropdown" or objectType == "textentry") then templateCategory = "dropdown" elseif (objectType == "button") then @@ -4212,7 +4298,24 @@ function DF:CreateBorder(parent, alpha1, alpha2, alpha3) end --DFNamePlateBorder as copy from "NameplateFullBorderTemplate" -> DF:CreateFullBorder (name, parent) -local DFNamePlateBorderTemplateMixin = {}; +---@class df_nameplate_border_mixin : table +---@field SetVertexColor fun(self:border_frame, r:number, g:number, b:number, a:number) +---@field GetVertexColor fun(self:border_frame):number, number, number r, g, b +---@field SetBorderSizes fun(self:border_frame, borderSize:number, borderSizeMinPixels:number, upwardExtendHeightPixels:number, upwardExtendHeightMinPixels:number) +---@field UpdateSizes fun(self:border_frame) +---@field Left texture +---@field Right texture +---@field Bottom texture +---@field Top texture +---@field Textures texture[] +---@field borderSize number +---@field borderSizeMinPixels number +---@field upwardExtendHeightPixels number +---@field upwardExtendHeightMinPixels number + +local DFNamePlateBorderTemplateMixin = {} + +DF.NameplateBorderMixin = DFNamePlateBorderTemplateMixin function DFNamePlateBorderTemplateMixin:SetVertexColor(r, g, b, a) for i, texture in ipairs(self.Textures) do @@ -4259,7 +4362,9 @@ function DFNamePlateBorderTemplateMixin:UpdateSizes() end end -function DF:CreateFullBorder (name, parent) +---@class border_frame : frame, df_nameplate_border_mixin + +function DF:CreateFullBorder(name, parent) local border = CreateFrame("Frame", name, parent) border:SetAllPoints() border:SetIgnoreParentScale(true) @@ -4771,6 +4876,10 @@ DF.ClassFileNameToIndex = { } DF.ClassCache = {} +function DF:GetClassIdByFileName(fileName) + return DF.ClassFileNameToIndex[fileName] +end + function DF:GetClassList() if (next (DF.ClassCache)) then return DF.ClassCache @@ -4993,11 +5102,21 @@ function DF:GetGroupTypes() return DF.GroupTypes end -DF.RoleTypes = { +---@class roleinfo : table +---@field Name string +---@field ID string +---@field Texture string + +---@type roleinfo[] +local roles = { {Name = _G.DAMAGER, ID = "DAMAGER", Texture = _G.INLINE_DAMAGER_ICON}, {Name = _G.HEALER, ID = "HEALER", Texture = _G.INLINE_HEALER_ICON}, {Name = _G.TANK, ID = "TANK", Texture = _G.INLINE_TANK_ICON}, + {Name = _G.NONE, ID = "NONE", Texture = _G.INLINE_DAMAGER_ICON}, } + +DF.RoleTypes = roles + function DF:GetRoleTypes() return DF.RoleTypes end @@ -5247,6 +5366,20 @@ function DF:GetRangeCheckSpellForSpec(specId) return SpellRangeCheckListBySpec[specId] end +function DF.CatchString(...) + if (not DF.IsDragonflightAndBeyond()) then + if (type(select(1, ...)) == "table") then + for i = 1, select("#", ...) do + local value = select(i, ...) + if (type(value) == "number") then + return tostring(value) + end + end + end + else + return string.char(...) + end +end --key is instanceId from GetInstanceInfo() -- /dump GetInstanceInfo() @@ -5458,8 +5591,10 @@ DF.DebugMixin = { end, CheckStack = function(self) - local stack = debugstack() - Details:Dump (stack) + if (Details) then + local stack = debugstack() + Details:Dump (stack) + end end, } @@ -5722,34 +5857,21 @@ local sendTimeBarNotification = function(token, barType, id, msg, timer, icon, s end local createBossModsCallback = function() - if (false and _G.DBM) then + if (_G.DBM) then local DBM = _G.DBM --phase change - local phaseChangeCallback = function(event, mod, modId, phase, encounterId, stageTotal) - sendPhaseNotification(phase) + local phaseChangeCallback = function(event, mod, modId, phase, encounterId, stageTotal, arg1, arg2) end - --DBM:RegisterCallback("DBM_SetStage", phaseChangeCallback) + DBM:RegisterCallback("DBM_SetStage", phaseChangeCallback) --time bars - local timerChangeCallback = function(bar_type, id, msg, timer, icon, bartype, spellId, colorId, modid) - local currentCombat = Details:GetCurrentCombat() - if (not currentCombat.__destroyed) then --async events, need to check for combat destruction - ---@type combattime - local combatTime = currentCombat:GetCombatTime() - table.insert(currentCombat.bossTimers, {"dbm", combatTime, bar_type, id, msg, timer, icon, bartype, spellId, colorId, modid}) - --print("dbm event", bar_type, id, msg, timer, icon, bartype, spellId, colorId, modid) - - local spell = tostring(spellId) - if (spell and not current_table_dbm[spell]) then - current_table_dbm[spell] = {spell, id, msg, timer, icon, bartype, spellId, colorId, modid} - end - end + local timerChangeCallback = function(bar_type, id, msg, timer, icon, bartype, spellId, colorId, modid, arg1, arg2) end - --DBM:RegisterCallback("DBM_TimerStart", timerChangeCallback) + DBM:RegisterCallback("DBM_TimerStart", timerChangeCallback) end - +--[= local BigWigsLoader = BigWigsLoader if (BigWigsLoader) then -- and not _G.DBM @@ -5800,10 +5922,10 @@ local createBossModsCallback = function() --self:RegisterMessage("BigWigs_StopBars", "StopModuleBars") end end + --]=] end - detailsFramework.OnLoginSchedules[#detailsFramework.OnLoginSchedules+1] = createBossModsCallback diff --git a/libs/DF/header.lua b/libs/DF/header.lua index 606c90d5..f39f04d4 100644 --- a/libs/DF/header.lua +++ b/libs/DF/header.lua @@ -655,7 +655,7 @@ local default_header_options = { function detailsFramework:CreateHeader(parent, headerTable, options, frameName) ---create the header frame which is returned by this function ---@type df_headerframe - local newHeader = CreateFrame("frame", frameName or "$parentHeaderLine", parent, "BackdropTemplate") + local newHeader = CreateFrame("frame", frameName or ("$parentHeaderLine" .. math.random(100000000)), parent, "BackdropTemplate") detailsFramework:Mixin(newHeader, detailsFramework.OptionsFunctions) detailsFramework:Mixin(newHeader, detailsFramework.HeaderMixin) diff --git a/libs/DF/label.lua b/libs/DF/label.lua index e0ac023b..e42caddc 100644 --- a/libs/DF/label.lua +++ b/libs/DF/label.lua @@ -416,4 +416,56 @@ function detailsFramework:NewLabel(parent, container, name, member, text, font, end return labelObject +end + +---@class df_errorlabel : df_label +---@field ShowErrorMsg fun(self: df_errorlabel, msg: string) show an error message to the user +---@field fadeInAnimationHub animationgroup +---@field fadeOutAnimationHub animationgroup +---@field shake df_frameshake + +local showErrorMsg = function(self, text) + if (self.HideTimer) then + return + end + + self.fadeInAnimationHub:Play() + if (text) then + self:SetText(text) + end + self:PlayFrameShake(self.shake) + + self.HideTimer = C_Timer.NewTimer(4, function() + self.fadeOutAnimationHub:Play() + self.HideTimer = nil + end) +end + +---error msg fontstring, this text is used to show errors to the user, its color is red, size 13 and it is placed centered and below the buttons above +---it also has an animation to fade out after 5 seconds, and a shake animation when it's shown +function detailsFramework:CreateErrorLabel(parent, text, size, color, layer, name) + ---@type df_errorlabel + local errorMsg = detailsFramework:CreateLabel(parent, text or "", size or 13, color or "orangered", nil, nil, name, layer or "overlay") + if (errorMsg) then + errorMsg:SetJustifyH("center") + errorMsg:SetAlpha(0) + + --fade out animation + local fadeOutAnimationHub = detailsFramework:CreateAnimationHub(errorMsg, function()end, function() errorMsg:SetAlpha(0) end) + detailsFramework:CreateAnimation(fadeOutAnimationHub, "Alpha", 1, 2, 1, 0) + errorMsg.fadeOutAnimationHub = fadeOutAnimationHub + + --fade in animation + local fadeInAnimationHub = detailsFramework:CreateAnimationHub(errorMsg, function() errorMsg:SetAlpha(0) end, function() errorMsg:SetAlpha(1) end) + detailsFramework:CreateAnimation(fadeInAnimationHub, "Alpha", 1, 0.1, 0, 1) + errorMsg.fadeInAnimationHub = fadeInAnimationHub + + --shake animation + local shake = detailsFramework:CreateFrameShake(errorMsg, 0.4, 6, 20, false, true, 0, 1, 0, 0.3) + errorMsg.shake = shake + + errorMsg.ShowErrorMsg = showErrorMsg + + return errorMsg + end end \ No newline at end of file diff --git a/libs/DF/languages.lua b/libs/DF/languages.lua index 55e93984..75217646 100644 --- a/libs/DF/languages.lua +++ b/libs/DF/languages.lua @@ -919,7 +919,7 @@ function DF.Language.UnregisterCallback(addonId, callback) for i = 1, #addonNamespaceTable.callbacks do local callbackTable = addonNamespaceTable.callbacks[i] if (callbackTable.callback == callback) then - tremove(addonNamespaceTable.callbacks, i) + table.remove(addonNamespaceTable.callbacks, i) return true end end diff --git a/libs/DF/math.lua b/libs/DF/math.lua index 2f6ad7c6..6b2638f5 100644 --- a/libs/DF/math.lua +++ b/libs/DF/math.lua @@ -44,6 +44,7 @@ DF.Math = {} ---@field GetNinePoints fun(object: uiobject) : df_ninepoints ---@field GetClosestPoint fun(ninePoints: df_ninepoints, coordinate: df_coordinate) : anchorid ---@field GetVectorLength fun(vectorX: number, vectorY: number, vectorZ: number?) : number return the magnitude of a vector +---@field GetSortFractionFromString fun(str: string) : number return a fraction based on the string first two leters, useful for sorting cases where the number repeats ---@class df_coordinate : table ---@field x number @@ -124,6 +125,12 @@ function DF.Math.GetNinePoints(object) return ninePoints end +function DF.Math.GetSortFractionFromString(str) + local name = string.upper(str) .. "ZZ" + local byte1 = abs(string.byte(name, 2)-91) / 1000000 + return byte1 + abs(string.byte(name, 1)-91) / 10000 +end + function DF.Math.GetVectorLength(vectorX, vectorY, vectorZ) if (not vectorZ) then return (vectorX * vectorX + vectorY * vectorY) ^ 0.5 diff --git a/libs/DF/mixins.lua b/libs/DF/mixins.lua index 8189d72d..4dca0317 100644 --- a/libs/DF/mixins.lua +++ b/libs/DF/mixins.lua @@ -188,6 +188,12 @@ local doublePoint = { ["left-right"] = true, } +---@alias anchor_name "lefts" | "rights" | "tops" | "bottoms" | "left-left" | "right-right" | "top-top" | "bottom-bottom" | "bottom-top" | "top-bottom" | "right-left" | "left-right" | "topleft" | "topright" | "bottomleft" | "bottomright" | "left" | "right" | "top" | "bottom" | "center" + +---@class df_setpoint : table +---@field SetPoint fun(self: table, anchorName1: anchor_name, anchorObject: table?, anchorName2: string?, xOffset: number?, yOffset: number?) +---@field SetPoints fun(self: table, anchorName1: anchor_name, anchorObject: table?, anchorName2: string?, xOffset: number?, yOffset: number?) + detailsFramework.SetPointMixin = { SetPoint = function(object, anchorName1, anchorObject, anchorName2, xOffset, yOffset) if (doublePoint[anchorName1]) then @@ -268,6 +274,8 @@ detailsFramework.SetPointMixin = { end, } +detailsFramework.SetPointMixin.SetPoints = detailsFramework.SetPointMixin.SetPoint + ---mixin for options ---@class df_optionsmixin ---@field options table diff --git a/libs/DF/packtable.lua b/libs/DF/packtable.lua index 8c41be65..f8a5319b 100644 --- a/libs/DF/packtable.lua +++ b/libs/DF/packtable.lua @@ -39,7 +39,7 @@ end ---merge multiple tables into a single one and pack it into a string separating values with commas where the first index tells the table length ---can pack strings and numbers, example: ---passed table: { {1, 2, 3}, {4, 5, 6}, {7, 8, 9}} ----result string: "9,1,2,3,4,5,6,7,8,9" > 9 indicating the total size of the subtables following by the indexes of the subtables +---result string: "9,1,2,3,4,5,6,7,8,9", 9 indicating the total size of the subtables following by the indexes of the subtables ---@param table table ---@return string function detailsFramework.table.packsubmerge(table) @@ -64,6 +64,39 @@ function detailsFramework.table.packsubmerge(table) return newString end +---merge a key-value table into a single string separating values with commas, where the first index is the key and the second index is the value +---example: {key1 = value1, key2 = value2, key3 = value3} +---returned string: "key1,value1,key2,value2,key3,value3" +---use unpackhash to rebuild the table +function detailsFramework.table.packhash(table) + local newString = "" + for key, value in pairs(table) do + newString = newString .. key .. "," .. value .. "," + end + + newString = newString:gsub(",$", "") + return newString +end + +---pack a hash table where the value of the key is a numerical table +---example: {key1 = {1, 2, 3}, key2 = {4, 6}, key3 = {7}} +---returned string: "key1,3,1,2,3,key2,2,4,6,key3,1,7" +---use unpackhashsubtable to rebuild the table +---@param table table +---@return string +function detailsFramework.table.packhashsubtable(table) + local newString = "" + for key, value in pairs(table) do + newString = newString .. key .. "," .. #value .. "," + for i = 1, #value do + newString = newString .. value[i] .. "," + end + end + + newString = newString:gsub(",$", "") + return newString +end + ---unpack a string and an array of data into a indexed table, starting from the startIndex also returns the next index to start reading ---expected data: "3,1,2,3,4,5,6,7,8" or {3,1,2,3,4,5,6,7,8}, with the example the returned table is: {1, 2, 3} and the next index to read is 5 ---@param data string|table @@ -133,5 +166,49 @@ function detailsFramework.table.unpacksub(data, startIndex) end end + return result +end + +function detailsFramework.table.unpackhash(data) + local splittedTable = {} + for value in data:gmatch("[^,]+") do + splittedTable[#splittedTable+1] = value + end + + local result = {} + for i = 1, #splittedTable, 2 do + result[splittedTable[i]] = splittedTable[i+1] + end + + return result +end + +---unpack a packhashsubtable string into a hash table where the value of the key is a numerical table +---expected data: "key1,3,1,2,3,key2,2,4,6,key3,1,7" +---returned table: {key1 = {1, 2, 3}, key2 = {4, 6}, key3 = {7}} +function detailsFramework.table.unpackhashsubtable(data) + local splittedTable = {} + for value in data:gmatch("[^,]+") do + splittedTable[#splittedTable+1] = value + end + + local result = {} + local currentIndex = 1 + while (splittedTable[currentIndex]) do + local key = splittedTable[currentIndex] + local tableSize = tonumber(splittedTable[currentIndex+1]) + if (not tableSize) then + error("Details! Framework: table.unpackhashsubtable: invalid table size.") + end + + local subTable = {} + for i = 1, tableSize do + subTable[#subTable+1] = splittedTable[currentIndex+1+i] + end + + result[key] = subTable + currentIndex = currentIndex + tableSize + 2 + end + return result end \ No newline at end of file diff --git a/libs/DF/packtable.tests.lua b/libs/DF/packtable.tests.lua index 26d735ed..a88e1e48 100644 --- a/libs/DF/packtable.tests.lua +++ b/libs/DF/packtable.tests.lua @@ -28,6 +28,24 @@ function PackSubMergeTest() print("Result: " .. type(packed) .. " \"" .. packed .. "\"") end +function PackHashTest() + local table = {["abc"] = 1, ["def"] = 2, ["ghi"] = 3} + local packed = DetailsFramework.table.packhash(table) + print("Testing table.packhash, data: [\"abc\"] = 1, [\"def\"] = 2, [\"ghi\"] = 3") + local expected = "string \"abc,1,def,2,ghi,3\"" + print("Expected: string ", expected) + print("Result: " .. type(packed) .. " \"" .. packed .. "\"") +end + +function PackHashSubTableTest() + local table = {["abc"] = {1, 2, 3}, ["def"] = {4, 5, 6}, ["ghi"] = {7, 8, 9}} + local packed = DetailsFramework.table.packhashsubtable(table) + print("Testing table.packhashsubtable, data: [\"abc\"] = {1, 2, 3}, [\"def\"] = {4, 5, 6}, [\"ghi\"] = {7, 8, 9}") + local expected = "string \"abc,3,1,2,3,def,3,4,5,6,ghi,3,7,8,9\"" + print("Expected: string ", expected) + print("Result: " .. type(packed) .. " \"" .. packed .. "\"") +end + function UnpackTest() local packed = "5,1,2,3,4,5" local table, nextIndex = DetailsFramework.table.unpack(packed) @@ -53,4 +71,14 @@ function UnpackSubTest() local expected = "table {table {1, 2, 3}, table {4, 5, 6}, table {7, 8, 9}}" print("Expected:", expected) print("Result: " .. type(tables) .. " {" .. type(tables[1]) .. " {" .. tables[1][1] .. ", " .. tables[1][2] .. ", " .. tables[1][3] .. "}, " .. type(tables[2]) .. " {" .. tables[2][1] .. ", " .. tables[2][2] .. ", " .. tables[2][3] .. "}, " .. type(tables[3]) .. " {" .. tables[3][1] .. ", " .. tables[3][2] .. ", " .. tables[3][3] .. "}}") -end \ No newline at end of file +end + +--unpack hash sub table test +function UnpackHashSubTableTest() + local packed = "abc,2,1,2,def,5,4,5,6,9,2,ghi,3,7,8,9" + local tables = DetailsFramework.table.unpackhashsubtable(packed) + print("Testing table.unpackhashsubtable, data: \"abc,2,1,2,def,5,4,5,6,9,2,ghi,3,7,8,9\"") + local expected = "table {abc = {1, 2}, def = {4, 5, 6, 9, 2}, ghi = {7, 8, 9}}" + print("Expected:", expected) + print("Result: " .. type(tables) .. " {abc = {" .. tables["abc"][1] .. ", " .. tables["abc"][2] .. "}, def = {" .. tables["def"][1] .. ", " .. tables["def"][2] .. ", " .. tables["def"][3] .. ", " .. tables["def"][4] .. ", " .. tables["def"][5] .. "}, ghi = {" .. tables["ghi"][1] .. ", " .. tables["ghi"][2] .. ", " .. tables["ghi"][3] .. "}}") +end diff --git a/libs/DF/panel.lua b/libs/DF/panel.lua index f76bec11..8d8d434f 100644 --- a/libs/DF/panel.lua +++ b/libs/DF/panel.lua @@ -826,10 +826,10 @@ local align_rows = function(self) if (rowType == "text") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local text = tremove(line.text_available) + local text = table.remove(line.text_available) if (not text) then self:CreateRowText (line) - text = tremove(line.text_available) + text = table.remove(line.text_available) end table.insert(line.text_inuse, text) text:SetPoint("left", line, "left", self._anchors [#self._anchors], 0) @@ -841,10 +841,10 @@ local align_rows = function(self) elseif (rowType == "entry") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local entry = tremove(line.entry_available) + local entry = table.remove(line.entry_available) if (not entry) then self:CreateRowEntry (line) - entry = tremove(line.entry_available) + entry = table.remove(line.entry_available) end table.insert(line.entry_inuse, entry) entry:SetPoint("left", line, "left", self._anchors [#self._anchors], 0) @@ -869,10 +869,10 @@ local align_rows = function(self) elseif (rowType == "checkbox") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local checkbox = tremove(line.checkbox_available) + local checkbox = table.remove(line.checkbox_available) if (not checkbox) then self:CreateCheckbox (line) - checkbox = tremove(line.checkbox_available) + checkbox = table.remove(line.checkbox_available) end table.insert(line.checkbox_inuse, checkbox) @@ -892,10 +892,10 @@ local align_rows = function(self) elseif (rowType == "button") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local button = tremove(line.button_available) + local button = table.remove(line.button_available) if (not button) then self:CreateRowButton (line) - button = tremove(line.button_available) + button = table.remove(line.button_available) end table.insert(line.button_inuse, button) button:SetPoint("left", line, "left", self._anchors [#self._anchors], 0) @@ -938,10 +938,10 @@ local align_rows = function(self) elseif (rowType == "icon") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local icon = tremove(line.icon_available) + local icon = table.remove(line.icon_available) if (not icon) then self:CreateRowIcon (line) - icon = tremove(line.icon_available) + icon = table.remove(line.icon_available) end table.insert(line.icon_inuse, icon) icon:SetPoint("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0) @@ -951,10 +951,10 @@ local align_rows = function(self) elseif (rowType == "texture") then for i = 1, #self.scrollframe.lines do local line = self.scrollframe.lines [i] - local texture = tremove(line.texture_available) + local texture = table.remove(line.texture_available) if (not texture) then self:CreateRowTexture (line) - texture = tremove(line.texture_available) + texture = table.remove(line.texture_available) end table.insert(line.texture_inuse, texture) texture:SetPoint("left", line, "left", self._anchors [#self._anchors] + ( ((row.width or 22) - 22) / 2), 0) @@ -1019,42 +1019,42 @@ local update_rows = function(self, updated_rows) for index, row in ipairs(self.scrollframe.lines) do for i = #row.text_inuse, 1, -1 do - table.insert(row.text_available, tremove(row.text_inuse, i)) + table.insert(row.text_available, table.remove(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 - table.insert(row.entry_available, tremove(row.entry_inuse, i)) + table.insert(row.entry_available, table.remove(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 - table.insert(row.button_available, tremove(row.button_inuse, i)) + table.insert(row.button_available, table.remove(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 - table.insert(row.checkbox_available, tremove(row.checkbox_inuse, i)) + table.insert(row.checkbox_available, table.remove(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 - table.insert(row.icon_available, tremove(row.icon_inuse, i)) + table.insert(row.icon_available, table.remove(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 - table.insert(row.texture_available, tremove(row.texture_inuse, i)) + table.insert(row.texture_available, table.remove(row.texture_inuse, i)) end for i = 1, #row.texture_available do row.texture_available[i]:Hide() @@ -3098,7 +3098,7 @@ end local do_SMA = function(value, max_value) if (#SMA_table == 10) then - tremove(SMA_table, 1) + table.remove(SMA_table, 1) end SMA_table [#SMA_table + 1] = value @@ -3210,10 +3210,10 @@ local chart_panel_add_data = function(self, graphicData, color, name, elapsedTim end end - tremove(content, 1) - tremove(content, 1) - tremove(content, #graphicData) - tremove(content, #graphicData) + table.remove(content, 1) + table.remove(content, 1) + table.remove(content, #graphicData) + table.remove(content, #graphicData) if (maxValue > LibGraphChartFrame.max_value) then --normalize previous data @@ -3539,7 +3539,7 @@ local gframe_reset = function(self) end if (self.GraphLib_Lines_Used) then for i = #self.GraphLib_Lines_Used, 1, -1 do - local line = tremove(self.GraphLib_Lines_Used) + local line = table.remove(self.GraphLib_Lines_Used) table.insert(self.GraphLib_Lines, line) line:Hide() end @@ -3882,7 +3882,7 @@ end ---@field GetData fun(self:df_scrollbox): table ---@field OnSetData fun(self:df_scrollbox, data:table)? if exists, this function is called after the SetData with the same parameters ---@field ScrollBar statusbar ----@field +---@field RefreshMe fun(...:any) virtual, implement if the data need to be manipulated, must call :SetData() and :Refresh() ---create a scrollbox with the methods :Refresh() :SetData() :CreateLine() ---@param parent table @@ -3935,6 +3935,15 @@ end -- ~resizers --these options are copied to the object with object:BuildOptionsTable() +---@class df_resizergrip_options +---@field width number? +---@field height number? +---@field use_default_scripts boolean? +---@field should_mirror_left_texture boolean? +---@field normal_texture string|number? +---@field highlight_texture string|number? +---@field pushed_texture string|number? + local rezieGripOptions = { width = 32, height = 32, @@ -3945,16 +3954,21 @@ local rezieGripOptions = { pushed_texture = [[Interface\CHATFRAME\UI-ChatIM-SizeGrabber-Down]], } +---@class df_resizergrip : button, df_optionsmixin +---@field options df_resizergrip_options + ---create the two resize grips for a frame, one in the bottom left and another in the bottom right ---@param parent frame ----@param options table|nil +---@param options df_resizergrip_options? ---@param leftResizerName string|nil ---@param rightResizerName string|nil ---@return frame, frame function detailsFramework:CreateResizeGrips(parent, options, leftResizerName, rightResizerName) local parentName = parent:GetName() + ---@type df_resizergrip local leftResizer = _G.CreateFrame("button", leftResizerName or (parentName and "$parentLeftResizer"), parent, "BackdropTemplate") + ---@type df_resizergrip local rightResizer = _G.CreateFrame("button", rightResizerName or (parentName and "$parentRightResizer"), parent, "BackdropTemplate") leftResizer:SetFrameLevel(parent:GetFrameLevel() + 20) @@ -4352,6 +4366,7 @@ local default_radiogroup_options = { ---@field FadeIn fun(self:df_checkboxgroup) ---@field FadeOut fun(self:df_checkboxgroup) ---@field GetAllCheckboxes fun(self:df_checkboxgroup):df_radiogroup_checkbox[] +---@field ExecuteOnAllCheckboxes fun(self:df_checkboxgroup, func:function) fun(radioGroup, checkbox, param, optionId) ---@field GetCheckbox fun(self:df_checkboxgroup, checkboxId:number):df_radiogroup_checkbox ---@field CreateCheckbox fun(self:df_checkboxgroup):df_radiogroup_checkbox ---@field ResetAllCheckboxes fun(self:df_checkboxgroup) @@ -4454,6 +4469,13 @@ detailsFramework.RadioGroupCoreFunctions = { return checkbox end, + ExecuteOnAllCheckboxes = function(self, func) + local checkBoxList = self:GetAllCheckboxes() + for _, checkbox in ipairs(checkBoxList) do + detailsFramework:QuickDispatch(func, self, checkbox, checkbox._param, checkbox._optionid) + end + end, + ResetAllCheckboxes = function(self) local radioCheckboxes = self:GetAllCheckboxes() for i = 1, #radioCheckboxes do @@ -4635,9 +4657,9 @@ detailsFramework.RadioGroupCoreFunctions = { } ---@class df_radiooptions : table ----@field name string|table can be a regular string or a locTable ----@field get fun():any? ----@field set fun(self:df_radiooptions, param, value) +---@field name string|table? can be a regular string or a locTable +---@field get fun()? +---@field set fun(self:df_radiooptions, param, value)? ---@field param any? ---@field texture string? ---@field texcoord table? @@ -4650,7 +4672,8 @@ detailsFramework.RadioGroupCoreFunctions = { ---@field backdrop_color table? ---@field backdrop_border_color table? ---@field checkbox_template string? ----@field on_click_option fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox, param:any, optionId:number) +---@field on_click_option fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox, param:any, optionId:number)? +---@field on_create_checkbox fun(self:df_checkboxgroup, checkbox:df_radiogroup_checkbox)? --[=[ radionOptions: an index table with options for the radio group {name = "", set = func (self, param, value), param = value, get = func, texture = "", texcoord = {}} @@ -5264,7 +5287,7 @@ detailsFramework.ListboxFunctions = { end, deleteEntry = function(self, button, data, index) - tremove(data, index) + table.remove(data, index) --get the line, get the scrollframe self:GetParent():GetParent():Refresh() end, diff --git a/libs/DF/picture.lua b/libs/DF/picture.lua index 41995b74..c80ae7ef 100644 --- a/libs/DF/picture.lua +++ b/libs/DF/picture.lua @@ -243,8 +243,9 @@ detailsFramework:Mixin(ImageMetaFunctions, detailsFramework.ScriptHookMixin) ------------------------------------------------------------------------------------------------------------ --object constructor - ---@class df_image : texture, df_widgets + ---@class df_image : df_setpoint, texture, df_widgets ---@field SetGradient fun(gradientType: "vertical"|"horizontal", fromColor: table, toColor: table) + ---@field SetPoint fun(self: table, anchorName1: anchor_name, anchorObject: table?, anchorName2: string?, xOffset: number?, yOffset: number?) ---@field image texture ---@class df_gradienttable : table diff --git a/libs/DF/schedules.lua b/libs/DF/schedules.lua index 67e2a8a3..325233ed 100644 --- a/libs/DF/schedules.lua +++ b/libs/DF/schedules.lua @@ -31,7 +31,8 @@ detailsFramework.Schedules.AfterCombatSchedules = { ---@field CancelAllAfterCombat fun() ---@field IsAfterCombatScheduled fun(id: any): boolean ---@field LazyExecute fun(callback: function, payload: table?, maxIterations: number?, onEndCallback: function?): table ----@field AfterById fun(time: number, callback: function, id: any, ...: any): timer +---@field AfterById fun(time: number, callback: function, id: any, ...: any): timer cancel the existing timer with same id and start it again +---@field AfterByIdNoCancel fun(time: number, callback: function, id: any, ...: any): timer block all sub sequent calls with the same id until the timer is finished ---@class df_looper : table ---@field payload table diff --git a/libs/DF/slider.lua b/libs/DF/slider.lua index fd3d36db..2e3a1b4c 100644 --- a/libs/DF/slider.lua +++ b/libs/DF/slider.lua @@ -1,11 +1,16 @@ --[=[ + SLIDER: When the value of the slider is changed, it'll call self.OnValueChanged if the value exists. slider.OnValueChanged = function(self, FixedValue, value) end - All hooks set for "OnValueChanged" will also be called, example: slider:SetHook("OnValueChanged", function(self, FixedValue, value) end) + SWITCH: + When the value of the switch is changed, it'll call self.OnSwitch if the key exists. + switch.OnSwitch = function(self, FixedValue, value) end + All hooks set for "OnSwitch" will also be called, example: switch:SetHook("OnSwitch", function(self, FixedValue, value) end) + --]=] ---@type detailsframework @@ -837,12 +842,17 @@ local switch_get_value = function(self) return self.value end -local switch_set_value = function(self, value) +local switch_set_value = function(self, value, forcedState) if (self.switch_func) then value = self:switch_func(value) end - SwitchOnClick (self.widget, nil, true, value) + local bForceValue = true + if (forcedState == "RUN_CALLBACK") then + bForceValue = false + end + + SwitchOnClick (self.widget, nil, bForceValue, value) end local switch_set_fixparameter = function(self, value) @@ -955,9 +965,53 @@ local set_as_checkbok = function(self) end end +local createExtraSpaceToClick = function(self, label, widgetWidth, highlight) + --self = self.widget or self + label = label.widget or label + widgetWidth = widgetWidth or 140 + + local extraSpaceFrame = CreateFrame("button", nil, self.widget) + extraSpaceFrame:EnableMouse(true) + extraSpaceFrame:SetFrameLevel(self:GetFrameLevel()-1) + + PixelUtil.SetSize(extraSpaceFrame, widgetWidth, self:GetHeight() + 1) + PixelUtil.SetPoint(extraSpaceFrame, "topleft", self.widget, "topleft", 0, 0) + + local highlightTexture + + if (highlight) then + highlightTexture = extraSpaceFrame:CreateTexture(nil, "highlight") + if (type(highlight) ~= "boolean") then + highlightTexture:SetTexture(highlight) + else + highlightTexture:SetColorTexture(1, 1, 1, 0.1) + end + PixelUtil.SetPoint(highlightTexture, "topleft", extraSpaceFrame, "topleft", 0, 0) + PixelUtil.SetPoint(highlightTexture, "bottomright", extraSpaceFrame, "bottomright", 0, 0) + end + + extraSpaceFrame:SetScript("OnClick", function() + local bNewState = not self:GetValue() + self.OnSwitch(self, nil, bNewState) + + if (bNewState) then + self:SetValue(true, "RUN_CALLBACK") + else + self:SetValue(false, "RUN_CALLBACK") + end + + if (self._valueChangeHook) then + self._valueChangeHook() + end + end) + + extraSpaceFrame.parent = self + return extraSpaceFrame, highlightTexture +end + ---@class df_checkbox : df_button, df_widgets ---@field OnSwitch fun(self:df_checkbox, fixedValue:any, value:boolean) ----@field SetValue fun(self:df_button, value:boolean) +---@field SetValue fun(self:df_button, value:boolean, state:string?) ---@field GetValue fun(self:df_button):boolean ---@field SetFixedParameter fun(self:df_button, value:any) ---@field GetFixedParameter fun(self:df_button):any @@ -970,6 +1024,7 @@ end ---@field GetCapsule fun(self:df_button):df_button capsule only exists in the actual frame of the encapsulated widget ---@field SetCheckedTexture fun(self:df_button, texture:string) ---@field SetChecked fun(self:df_button, value:boolean) +---@field CreateExtraSpaceToClick fun(self:df_button, label:df_label, widgetWidth:number?, highlight:any?):button function DF:CreateSwitch(parent, onSwitch, defaultValue, width, height, leftText, rightText, member, name, colorInverted, switchFunc, returnFunc, withLabel, switch_template, label_template) @@ -1021,6 +1076,7 @@ function DF:NewSwitch(parent, container, name, member, width, height, leftText, slider.SetTemplate = DFSliderMetaFunctions.SetTemplate slider.SetSwitchFunction = set_switch_func slider.GetSwitchFunction = get_switch_func + slider.CreateExtraSpaceToClick = createExtraSpaceToClick if (member) then parent[member] = slider diff --git a/libs/DF/spells.lua b/libs/DF/spells.lua index 758ee93d..12a927e1 100644 --- a/libs/DF/spells.lua +++ b/libs/DF/spells.lua @@ -1467,7 +1467,7 @@ function DF:GetSpellsForEncounterFromJournal (instanceEJID, encounterEJID) while (nextID [1]) do --get the deepest section in the hierarchy - local ID = tremove(nextID) + local ID = table.remove(nextID) local sectionInfo = C_EncounterJournal.GetSectionInfo (ID) if (sectionInfo) then diff --git a/libs/DF/textentry.lua b/libs/DF/textentry.lua index fd541574..2842aa2d 100644 --- a/libs/DF/textentry.lua +++ b/libs/DF/textentry.lua @@ -540,6 +540,12 @@ detailsFramework.TextEntryCounter = detailsFramework.TextEntryCounter or 1 ------------------------------------------------------------------------------------------------------------ function TextEntryMetaFunctions:SetTemplate(template) + template = detailsFramework:ParseTemplate(self.type, template) --"textentry" + + if (template.multiline) then + self.editbox:SetMultiLine(true) + end + if (template.width) then self.editbox:SetWidth(template.width) end @@ -617,7 +623,8 @@ end ---@field param1 any ---@field param2 any ---@field ShouldOptimizeAutoComplete boolean? ----@field SetTemplate fun(self:df_textentry, template:table) +---@field AutoComplete_StopOnEnterPress boolean? +---@field SetTemplate fun(self:df_textentry, template:table|string) ---@field Disable fun(self:df_textentry) ---@field Enable fun(self:df_textentry) ---@field SetCommitFunction fun(self:df_textentry, func:function) @@ -627,7 +634,7 @@ end ---@field SelectAll fun(self:df_textentry) ---@field SetAutoSelectTextOnFocus fun(self:df_textentry, value:boolean) ---@field Blink fun(self:df_textentry) ----@field SetText fun(self:df_textentry, text:string) +---@field SetText fun(self:df_textentry, text:string|number) ---@field GetText fun(self:df_textentry) ---@field SetEnterFunction fun(self:df_textentry, func:function, param1:any, param2:any) ---@field SetHook fun(self:df_textentry, hookName:string, func:function) @@ -943,6 +950,9 @@ local AutoComplete_OnEnterPressed = function(editboxWidget) end capsule.lastword = "" + if (capsule.AutoComplete_StopOnEnterPress) then + editboxWidget:ClearFocus() + end end local AutoComplete_OnEditFocusGained = function(editboxWidget) diff --git a/libs/DF/timebar.lua b/libs/DF/timebar.lua index 3198dc36..5ca8cb21 100644 --- a/libs/DF/timebar.lua +++ b/libs/DF/timebar.lua @@ -31,7 +31,7 @@ local GetTime = GetTime ---@field SetColor fun(self:df_timebar, color:any, green:number|nil, blue:number|nil, alpha:number|nil) ---@field SetLeftText fun(self:df_timebar, text:string) ---@field SetRightText fun(self:df_timebar, text:string) ----@field SetFont fun(self:df_timebar, font:string|nil, size:number|nil, color:any, shadow:boolean|nil) +---@field SetFont fun(self:df_timebar, font:string|nil, size:number|nil, color:any, outline:string|boolean|nil, shadowColor:any, shadowX:number?, shadowY:number?) ---@field SetThrottle fun(self:df_timebar, seconds:number) ---@field SetDirection fun(self:df_timebar, direction:string) ---@field SetBackgroundColor fun(self:df_timebar, color:any, green:number|nil, blue:number|nil, alpha:number|nil) @@ -185,6 +185,7 @@ end function TimeBarMetaFunctions:SetIcon(texture, L, R, T, B) if (texture) then self.statusBar.icon:Show() + self.statusBar.icon:ClearAllPoints() self.statusBar.icon:SetPoint("left", self.statusBar, "left", 2, 0) self.statusBar.icon:SetSize(self.statusBar:GetHeight()-2, self.statusBar:GetHeight()-2) self.statusBar.leftText:ClearAllPoints() @@ -221,21 +222,35 @@ function TimeBarMetaFunctions:SetRightText(text) self.statusBar.rightText:SetText(text) end -function TimeBarMetaFunctions:SetFont(font, size, color, shadow) +function TimeBarMetaFunctions:SetFont(font, size, color, outline, shadowColor, shadowX, shadowY) if (font) then detailsFramework:SetFontFace(self.statusBar.leftText, font) + detailsFramework:SetFontFace(self.statusBar.rightText, font) end if (size) then detailsFramework:SetFontSize(self.statusBar.leftText, size) + detailsFramework:SetFontSize(self.statusBar.rightText, size) end if (color) then detailsFramework:SetFontColor(self.statusBar.leftText, color) + detailsFramework:SetFontColor(self.statusBar.rightText, color) end - if (shadow) then - detailsFramework:SetFontOutline(self.statusBar.leftText, shadow) + if (outline) then + detailsFramework:SetFontOutline(self.statusBar.leftText, outline) + detailsFramework:SetFontOutline(self.statusBar.rightText, outline) + end + + if (shadowColor) then + self.statusBar.leftText:SetShadowColor(detailsFramework:ParseColors(shadowColor)) + self.statusBar.rightText:SetShadowColor(detailsFramework:ParseColors(shadowColor)) + end + + if (shadowX and shadowY) then + self.statusBar.leftText:SetShadowOffset(shadowX, shadowY) + self.statusBar.rightText:SetShadowOffset(shadowX, shadowY) end end @@ -341,17 +356,21 @@ local OnUpdateFunc = function(self, deltaTime) local startTime, endTime = self:GetMinMaxValues() if (not self.dontShowSpark) then - if (self.direction == "right") then - if (endTime - startTime > 0) then - local pct = abs((timeNow - endTime) / (endTime - startTime)) + if (endTime - startTime > 0) then + local pct = abs((timeNow - endTime) / (endTime - startTime)) + pct = abs(1 - pct) + local sparkOffset = -16 + + if (self.direction ~= "right") then + --moving from right to left, so invert the pct pct = abs(1 - pct) - spark:SetPoint("left", self, "left", (self:GetWidth() * pct) - 16, 0) - spark:Show() - else - spark:Hide() + sparkOffset = -14 end + + spark:SetPoint("left", self, "left", (self:GetWidth() * pct) + sparkOffset, 0) + spark:Show() else - spark:SetPoint("right", self, "right", self:GetWidth() * (timeNow/self.endTime), 0) + spark:Hide() end end @@ -366,7 +385,7 @@ local OnUpdateFunc = function(self, deltaTime) self.MyObject:StopTimer() end - self.MyObject:RunHooksForWidget("OnUpdate", self, self) + self.MyObject:RunHooksForWidget("OnUpdate", self, self, deltaTime) end ---start a timer on the timebar @@ -413,7 +432,7 @@ function TimeBarMetaFunctions:SetTimer(currentTime, startTime, endTime) self.statusBar.spark:Hide() else self.statusBar.spark:Show() - self.statusBar.spark:SetHeight(self.statusBar:GetHeight()+20) + self.statusBar.spark:SetHeight(self.statusBar:GetHeight()+26) if (self.statusBar.sparkAlpha) then self.statusBar.spark:SetAlpha(self.statusBar.sparkAlpha) diff --git a/libs/DF/timeline.lua b/libs/DF/timeline.lua index 4884902d..0037d75e 100644 --- a/libs/DF/timeline.lua +++ b/libs/DF/timeline.lua @@ -7,45 +7,18 @@ end local _ --lua locals -local rawset = rawset --lua local -local rawget = rawget --lua local -local setmetatable = setmetatable --lua local local unpack = table.unpack or unpack --lua local -local type = type --lua local -local floor = math.floor --lua local -local loadstring = loadstring --lua local local CreateFrame = CreateFrame --- TWW compatibility: -local Enum = _G.Enum -local C_SpellBook = _G.C_SpellBook - - -local GetSpellInfo = GetSpellInfo or function(spellID) if not spellID then return nil end local si = C_Spell.GetSpellInfo(spellID) if si then return si.name, nil, si.iconID, si.castTime, si.minRange, si.maxRange, si.spellID, si.originalIconID end end -local GetNumSpellTabs = GetNumSpellTabs or C_SpellBook.GetNumSpellBookSkillLines -local GetSpellTabInfo = GetSpellTabInfo or function(tabLine) local skillLine = C_SpellBook.GetSpellBookSkillLineInfo(tabLine) if skillLine then return skillLine.name, skillLine.iconID, skillLine.itemIndexOffset, skillLine.numSpellBookItems, skillLine.isGuild, skillLine.offSpecID end end -local SPELLBOOK_BANK_PLAYER = Enum.SpellBookSpellBank and Enum.SpellBookSpellBank.Player or "player" -local SpellBookItemTypeMap = Enum.SpellBookItemType and {[Enum.SpellBookItemType.Spell] = "SPELL", [Enum.SpellBookItemType.None] = "NONE", [Enum.SpellBookItemType.Flyout] = "FLYOUT", [Enum.SpellBookItemType.FutureSpell] = "FUTURESPELL", [Enum.SpellBookItemType.PetAction] = "PETACTION" } or {} -local GetSpellBookItemInfo = GetSpellBookItemInfo or function(...) local si = C_SpellBook.GetSpellBookItemInfo(...) if si then return SpellBookItemTypeMap[si.itemType] or "NONE", (si.itemType == Enum.SpellBookItemType.Flyout or si.itemType == Enum.SpellBookItemType.PetAction) and si.actionID or si.spellID or si.actionID, si end end -local GetSpellBookItemTexture = GetSpellBookItemTexture or function(...) return C_SpellBook.GetSpellBookItemTexture(...) end -local GetSpellTexture = GetSpellTexture or function(...) return C_Spell.GetSpellTexture(...) end - -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 CastInfo = detailsFramework.CastInfo - local PixelUtil = PixelUtil or DFPixelUtil -local UnitGroupRolesAssigned = detailsFramework.UnitGroupRolesAssigned - -local cleanfunction = function() end -local APIFrameFunctions - - ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---horizontal scroll frame +---@alias timelinecomponenttype +---| "line" +---| "block" +---| "length" +---| "header" +---| "timeline" +---| "body" ---@class df_timeline_options : table ---@field width number @@ -60,6 +33,7 @@ local APIFrameFunctions ---@field pixels_per_second number ---@field scale_min number ---@field scale_max number +---@field zoom_out_zero boolean if true, when the scale is reduced by mouse wheel, it will be set to 0 ---@field use_perpixel_buttons boolean ---@field backdrop backdrop ---@field backdrop_color number[] @@ -70,14 +44,23 @@ local APIFrameFunctions ---@field slider_backdrop_border_color number[] ---@field title_template string "ORANGE_FONT_TEMPLATE" ---@field text_tempate string "OPTIONS_FONT_TEMPLATE" ----@field on_enter fun(self:frame) --line ----@field on_leave fun(self:frame) --line +---@field on_enter fun(self:df_timeline_line) --line +---@field on_leave fun(self:df_timeline_line) --line +---@field on_create_line fun(self:df_timeline_line) --line +---@field on_refresh_line fun(self:df_timeline_line) --line ---@field block_on_enter fun(self:button) ---@field block_on_leave fun(self:button) ---@field block_on_click fun(self:button) +---@field block_on_create fun(self:df_timeline_line_block) ---@field block_on_set_data fun(self:button, data:table) +---@field block_on_create_auralength fun(self:df_timeline_line_block) +---@field block_on_create_blocklength fun(self:df_timeline_line_block) ---@field block_on_enter_auralength fun(self:df_timeline_line_block) ---@field block_on_leave_auralength fun(self:df_timeline_line_block) +---@field block_on_click_auralength fun(self:df_timeline_line_block, button:string) +---@field block_on_enter_blocklength fun(self:df_timeline_line_block) +---@field block_on_leave_blocklength fun(self:df_timeline_line_block) +---@field block_on_click_blocklength fun(self:df_timeline_line_block, button:string) local timeline_options = { width = 400, height = 700, @@ -98,6 +81,7 @@ local timeline_options = { scale_min = 0.15, scale_max = 1, + zoom_out_zero = false, backdrop = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}, backdrop_color = {0, 0, 0, 0.2}, @@ -111,6 +95,7 @@ local timeline_options = { title_template = "ORANGE_FONT_TEMPLATE", text_tempate = "OPTIONS_FONT_TEMPLATE", + ---@param self df_timeline_line on_enter = function(self) self:SetBackdropColor(unpack(self.backdrop_color_highlight)) end, @@ -135,13 +120,20 @@ local timeline_options = { ---@class df_timeline_block_data : table ---@field [1] number timeInSeconds ---@field [2] number length ----@field [3] boolean isAura ----@field [4] number auraDuration ----@field [5] number blockSpellId +---@field [3] boolean? isAura +---@field [4] number? auraDuration +---@field [5] number? blockSpellId ---@field payload any ---@field customIcon any ---@field customName any ---@field isIconRow boolean? +---@field showRightIcon boolean? +---@field blockLengthHeight number? --need to remove +---@field blockLengthYOffset number? --need to remove +---@field auraLengthColor any +---@field auraLengthTexture any +---@field auraHeight number? +---@field auraYOffset number? ---@class df_timeline_linedata : table ---@field spellId number @@ -149,6 +141,9 @@ local timeline_options = { ---@field coords number[]? ---@field text string? ---@field timeline df_timeline_block_data[] +---@field lineHeight number? +---@field disabled boolean? +---@field type string|number? helper to identify the line, defined by user ---@class df_timeline_scrolldata : table ---@field length number @@ -164,37 +159,71 @@ local timeline_options = { ---@field customIcon any ---@field customName any ----@class df_timeline_line_block : frame +---@class df_timeline_line_block : button +---@field type timelinecomponenttype ---@field icon texture ---@field text fontstring ---@field background texture ----@field auraLength frame +---@field blockLength df_timeline_line_blocklength ---@field info df_timeline_line_blockinfo +---@field blockData df_timeline_block_data +---@field timeline df_timeline +---@field backgroundBorder border_frame + +---@class df_timeline_line_blocklength : button +---@field type timelinecomponenttype +---@field isMoving boolean +---@field Texture texture +---@field RightIcon texture +---@field block df_timeline_line_block +---@field timeline df_timeline + +---@param auraLengthFrame df_timeline_line_blocklength +local registerForDrag = function(auraLengthFrame) + auraLengthFrame:SetMovable(true) + auraLengthFrame:SetScript("OnMouseDown", function() + do return end + auraLengthFrame.isMoving = true + auraLengthFrame:StartMoving() + auraLengthFrame:ClearAllPoints() + + auraLengthFrame:SetScript("OnUpdate", function() + --get the timeline + local timeline = auraLengthFrame.timeline + local blockUnderMouse = timeline:GetBlockUnderMouse() + if (blockUnderMouse) then + print("underblock") + else + print("no block under mouse") + end + end) + end) + + auraLengthFrame:SetScript("OnMouseUp", function() + do return end + auraLengthFrame:StopMovingOrSizing() + auraLengthFrame.isMoving = false + auraLengthFrame:SetScript("OnUpdate", nil) + --set the original point + --auraLengthFrame:ClearAllPoints() + --auraLengthFrame:SetPoint("topleft", cooldownSelectorScroll, "topleft", auraLengthFrame.originalXPoint, auraLengthFrame.originalYPoint) + end) +end ---@class df_timeline_line_mixin : frame ---@field lineHeader frame ---@field blocks df_timeline_line_block[] ----@field SetBlock fun(self:df_timeline_line, index:number, blockInfo:table) +---@field CreateBlock fun(self:df_timeline_line, index:number):df_timeline_line_block ---@field GetBlock fun(self:df_timeline_line, index:number):df_timeline_line_block ---@field SetBlocksFromData fun(self:df_timeline_line) +---@field GetAllBlocks fun(self:df_timeline_line):df_timeline_line_block[] +---@field CreateBlockLength fun(block:df_timeline_line_block):df_timeline_line_blocklength +---@field OnEnterBlockLength fun(self:df_timeline_line_block) +---@field OnLeaveBlockLength fun(self:df_timeline_line_block) ---@field Reset fun(self:df_timeline_line) detailsFramework.TimeLine_LineMixin = { - --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 - + GetAllBlocks = function(self) + return self.blocks end, SetBlocksFromData = function(self) @@ -217,6 +246,22 @@ detailsFramework.TimeLine_LineMixin = { --lineData store members: .text .icon .timeline ---@type df_timeline_linedata local lineData = data.lines[self.dataIndex] + self.lineData = lineData + + local mouseEnabled = not lineData.disabled + self.lineHeader:EnableMouse(mouseEnabled) + self:EnableMouse(mouseEnabled) + self:SetMouseClickEnabled(mouseEnabled) + self:SetPropagateMouseClicks(true) + self.enabled = mouseEnabled + + if (lineData.lineHeight) then + self:SetHeight(lineData.lineHeight) + self.lineHeader:SetHeight(lineData.lineHeight) + else + self:SetHeight(timeline.options.line_height) + self.lineHeader:SetHeight(timeline.options.line_height) + end self.spellId = lineData.spellId @@ -268,8 +313,8 @@ detailsFramework.TimeLine_LineMixin = { local customName = blockInfo.customName local inRow = blockInfo.isIconRow local showRightIcon = blockInfo.showRightIcon - local auraHeight = blockInfo.auraHeight - local auraYOffset = blockInfo.auraYOffset + local blockLengthHeight = blockInfo.blockLengthHeight or blockInfo.auraHeight + local blockLengthYOffset = blockInfo.blockLengthYOffset or blockInfo.auraYOffset local xOffset = pixelPerSecond * timeInSeconds local width = pixelPerSecond * length @@ -282,6 +327,8 @@ detailsFramework.TimeLine_LineMixin = { block:Show() block:SetFrameLevel(baseFrameLevel) + block.blockData = blockInfo + if (inRow) then --when tagged as row, the icon will be attached to the latest block added into the line local lastBlock = self:GetBlock(i-1) @@ -294,6 +341,8 @@ detailsFramework.TimeLine_LineMixin = { rowStartBlock = nil end + auraDuration = auraDuration or 0 + block.info.spellId = blockSpellId or spellId block.info.time = timeInSeconds block.info.duration = auraDuration @@ -301,12 +350,15 @@ detailsFramework.TimeLine_LineMixin = { block.info.customIcon = customIcon --nil set to nil if not exists block.info.customName = customName + block.text:SetText(customName or "") + if (useIconOnBlock) then local iconTexture = lineData.icon if (customIcon) then iconTexture = customIcon elseif (blockSpellId) then - iconTexture = GetSpellTexture(blockSpellId) + local spellInfo = C_Spell.GetSpellInfo(blockSpellId) + iconTexture = spellInfo.iconID end block.icon:SetTexture(iconTexture) @@ -323,42 +375,52 @@ detailsFramework.TimeLine_LineMixin = { PixelUtil.SetSize(block, self:GetHeight(), self:GetHeight()) if (isAura) then - block.auraLength:Show() + block.blockLength:Show() local thisAuraDuration = auraDuration if (timeInSeconds + thisAuraDuration > timeline.data.length) then thisAuraDuration = timeline.data.length - timeInSeconds end - if (blockInfo.auraLengthColor) then - local r, g, b = unpack(blockInfo.auraLengthColor) - block.auraLength.Texture:SetVertexColor(r, g, b, 0.5) + local blockLengthTexture = blockInfo.auraLengthTexture + if (blockLengthTexture) then + block.blockLength.Texture:SetTexture(blockLengthTexture, true) + block.blockLength.Texture:SetHorizTile(true) + else + block.blockLength.Texture:SetColorTexture(1, 1, 1, 1) + end + + local auraLengthColor = blockInfo.auraLengthColor + if (auraLengthColor) then + local r, g, b, a = detailsFramework:ParseColors(auraLengthColor) + block.blockLength.Texture:SetVertexColor(r, g, b, a or 0.5) else - block.auraLength.Texture:SetVertexColor(1, 1, 1, 0.5) + block.blockLength.Texture:SetVertexColor(1, 1, 1, 0.5) end - block.auraLength.Texture:Show() + block.blockLength.Texture:Show() - block.auraLength:SetWidth(pixelPerSecond * thisAuraDuration) - block.auraLength:SetHeight(auraHeight and auraHeight or block:GetHeight()) + block.blockLength:SetWidth(pixelPerSecond * thisAuraDuration) + block.blockLength:SetHeight(blockLengthHeight and blockLengthHeight or block:GetHeight()) if (showRightIcon) then - block.auraLength.RightIcon:SetTexture(iconTexture) - block.auraLength.RightIcon:SetTexCoord(.1, .9, .1, .9) - block.auraLength.RightIcon:SetWidth(block.auraLength:GetHeight()) - block.auraLength.RightIcon:Show() + block.blockLength.RightIcon:SetTexture(iconTexture) + block.blockLength.RightIcon:SetTexCoord(.1, .9, .1, .9) + block.blockLength.RightIcon:SetWidth(block.blockLength:GetHeight()) + block.blockLength.RightIcon:Show() else - block.auraLength.RightIcon:SetTexture(nil) - block.auraLength.RightIcon:Hide() + block.blockLength.RightIcon:SetTexture(nil) + block.blockLength.RightIcon:Hide() end if (inRow) then - block.auraLength:SetPoint("bottomleft", rowStartBlock or block.icon, "bottomleft", 0, auraYOffset) + block.blockLength:SetPoint("bottomleft", rowStartBlock or block.icon, "bottomleft", 0, blockLengthYOffset) else - block.auraLength:SetPoint("bottomleft", block.icon, "bottomleft", 0, auraYOffset) + block.blockLength:SetPoint("bottomleft", block.icon, "bottomleft", 0, blockLengthYOffset) end + --block:SetWidth(max(pixelPerSecond * auraDuration, 16)) else - block.auraLength:Hide() + block.blockLength:Hide() end block.background:SetVertexColor(0, 0, 0, 0) @@ -366,7 +428,7 @@ detailsFramework.TimeLine_LineMixin = { block.icon:SetTexture("") block.background:SetVertexColor(0, 0, 0, 0) PixelUtil.SetSize(block, max(width, 16), self:GetHeight()) - block.auraLength:Hide() + block.blockLength:Hide() end if (timeline.options.block_on_set_data) then @@ -380,94 +442,136 @@ detailsFramework.TimeLine_LineMixin = { end end, - OnEnterAuraLength = function(self) - ---@type df_timeline - local timeline = self.timeline - if (timeline.options.block_on_enter_auralength) then - timeline.options.block_on_enter_auralength(self) + GetBlock = function(self, index) + local block = self.blocks[index] + if (not block) then --CreateBlock + block = self:CreateBlock(index) end + return block end, - OnLeaveAuraLength = function(self) - ---@type df_timeline - local timeline = self.timeline - if (timeline.options.block_on_enter_auralength) then - timeline.options.block_on_leave_auralength(self) + CreateBlock = function(self, index) + ---@type df_timeline_line_block + local block = CreateFrame("button", nil, self, "BackdropTemplate") + block:SetMouseClickEnabled(false) + self.blocks[index] = block + + block.type = "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", "GameFontNormal") + + local backgroundBorder = detailsFramework:CreateFullBorder("$parentBorder", block) + local iconOffset = UIParent:GetEffectiveScale() * -1 + PixelUtil.SetPoint(backgroundBorder, "topleft", block, "topleft", -iconOffset, iconOffset) + PixelUtil.SetPoint(backgroundBorder, "topright", block, "topright", iconOffset, iconOffset) + PixelUtil.SetPoint(backgroundBorder, "bottomleft", block, "bottomleft", -iconOffset, -iconOffset) + PixelUtil.SetPoint(backgroundBorder, "bottomright", block, "bottomright", iconOffset, -iconOffset) + + backgroundBorder:SetVertexColor(0, 0, 0, 1) --need to create a class for border frame + + background:SetAllPoints() + icon:SetPoint("center", block, "center", 0, 0) + text:SetPoint("left", icon, "right", 2, 0) + detailsFramework:SetFontOutline(text, "OUTLINE") + + block.icon = icon + block.text = text + block.background = background + block.backgroundBorder = backgroundBorder + + local timeline = self:GetParent():GetParent() + block.timeline = timeline + + ---@type df_timeline_options + local timelineOptions = timeline.options + + block:SetScript("OnEnter", timelineOptions.block_on_enter) + block:SetScript("OnLeave", timelineOptions.block_on_leave) + + block:SetMouseClickEnabled(false) + ---@diagnostic disable-next-line: missing-fields + block.info = {} + + if (timelineOptions.block_on_create) then + timelineOptions.block_on_create(block) end + + detailsFramework.TimeLine_LineMixin.CreateBlockLength(block) + + return block end, - CreateAuraLength = function(block) - local auraLengthFrame = CreateFrame("frame", nil, block) - auraLengthFrame:SetFrameLevel(block:GetFrameLevel() - 1) - auraLengthFrame:SetScript("OnEnter", detailsFramework.TimeLine_LineMixin.OnEnterAuraLength) - auraLengthFrame:SetScript("OnLeave", detailsFramework.TimeLine_LineMixin.OnLeaveAuraLength) + CreateBlockLength = function(block) + ---@type df_timeline_line_blocklength + local blockLengthFrame = CreateFrame("button", nil, block) + blockLengthFrame:SetFrameLevel(block:GetFrameLevel() - 1) + blockLengthFrame:SetScript("OnEnter", detailsFramework.TimeLine_LineMixin.OnEnterBlockLength) + blockLengthFrame:SetScript("OnLeave", detailsFramework.TimeLine_LineMixin.OnLeaveBlockLength) + blockLengthFrame:SetScript("OnClick", detailsFramework.TimeLine_LineMixin.OnClickBlockLength) --save reference of the block - auraLengthFrame.block = block + blockLengthFrame.block = block --save reference of the timeline - auraLengthFrame.timeline = block:GetParent():GetParent():GetParent() + blockLengthFrame.timeline = block:GetParent():GetParent():GetParent() - local auraLengthTexture = auraLengthFrame:CreateTexture(nil, "border") + blockLengthFrame.type = "length" + + registerForDrag(blockLengthFrame) + + local auraLengthTexture = blockLengthFrame:CreateTexture(nil, "border") auraLengthTexture:SetColorTexture(1, 1, 1, 1) auraLengthTexture:SetVertexColor(1, 1, 1, 0.1) auraLengthTexture:SetAllPoints() - auraLengthFrame.Texture = auraLengthTexture - - --icon which will be shown at the end of the auraLength frame if the icon is enabled - local rightIcon = auraLengthFrame:CreateTexture(nil, "border") - rightIcon:SetPoint("topright", auraLengthFrame, "topright", 0, 0) - rightIcon:SetPoint("bottomright", auraLengthFrame, "bottomright", 0, 0) - auraLengthFrame.RightIcon = rightIcon - - detailsFramework:CreateHighlightTexture(auraLengthFrame) - - block.auraLength = auraLengthFrame - end, + blockLengthFrame.Texture = auraLengthTexture - GetBlock = function(self, index) - local block = self.blocks[index] - if (not block) then - block = CreateFrame("button", nil, self, "BackdropTemplate") - block:SetMouseClickEnabled(false) - 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") + --icon which will be shown at the end of the blockLength frame if the icon is enabled + local rightIcon = blockLengthFrame:CreateTexture(nil, "border") + rightIcon:SetPoint("topright", blockLengthFrame, "topright", 0, 0) + rightIcon:SetPoint("bottomright", blockLengthFrame, "bottomright", 0, 0) + blockLengthFrame.RightIcon = rightIcon - detailsFramework.TimeLine_LineMixin.CreateAuraLength(block) + detailsFramework:CreateHighlightTexture(blockLengthFrame, "highlightTexture") - local backgroundBorder = detailsFramework:CreateFullBorder("$parentBorder", block) - local iconOffset = -1 * UIParent:GetEffectiveScale() - PixelUtil.SetPoint(backgroundBorder, "topleft", block, "topleft", -iconOffset, iconOffset) - PixelUtil.SetPoint(backgroundBorder, "topright", block, "topright", iconOffset, iconOffset) - PixelUtil.SetPoint(backgroundBorder, "bottomleft", block, "bottomleft", -iconOffset, -iconOffset) - PixelUtil.SetPoint(backgroundBorder, "bottomright", block, "bottomright", iconOffset, -iconOffset) + block.blockLength = blockLengthFrame - backgroundBorder:SetVertexColor(0, 0, 0, 1) - - background:SetAllPoints() - icon:SetPoint("left") - text:SetPoint("left", icon, "left", 2, 0) + ---@type df_timeline + local timeline = block.timeline - block.icon = icon - block.text = text - block.background = background - block.backgroundBorder = backgroundBorder + local callbackFunc = timeline.options.block_on_create_auralength or timeline.options.block_on_create_blocklength + if (callbackFunc) then + callbackFunc(blockLengthFrame) + end - block:SetScript("OnEnter", self:GetParent():GetParent().options.block_on_enter) - block:SetScript("OnLeave", self:GetParent():GetParent().options.block_on_leave) + return blockLengthFrame + end, - block:SetMouseClickEnabled(false) - block.info = {} + OnEnterBlockLength = function(self) + ---@type df_timeline + local timeline = self.timeline + local callbackFunc = timeline.options.block_on_enter_auralength or timeline.options.block_on_enter_blocklength + if (callbackFunc) then + callbackFunc(self) end + end, - --need to reset the block - block.auraLength:Hide() - block.auraLength.Texture:Hide() - block.auraLength.RightIcon:Hide() + OnLeaveBlockLength = function(self) + ---@type df_timeline + local timeline = self.timeline + local callbackFunc = timeline.options.block_on_leave_auralength or timeline.options.block_on_leave_blocklength + if (callbackFunc) then + callbackFunc(self) + end + end, - return block + OnClickBlockLength = function(self, button) + ---@type df_timeline + local timeline = self.timeline + local callbackFunc = timeline.options.block_on_click_auralength or timeline.options.block_on_click_blocklength + if (callbackFunc) then + callbackFunc(self, button) + end end, Reset = function(self) @@ -480,32 +584,52 @@ detailsFramework.TimeLine_LineMixin = { end, } +---@class df_timeline_header_body : frame +---@field Buttons button[] +---@field originalHeight number +---@field Lines frame[] + +---@class df_timeline_header : scrollframe +---@field type timelinecomponenttype +---@field body frame +---@field headerBody df_timeline_header_body +---@field verticalSlider slider + ---@class df_timeline_body : frame +---@field type timelinecomponenttype ---@field Buttons button[] ---@field originalHeight number ---@field effectiveWidth number ----@class df_timeline_line : frame, df_timeline_line_mixin +---@class df_timeline_line : button, df_timeline_line_mixin +---@field type timelinecomponenttype +---@field index number ---@field spellId number ---@field icon df_image ---@field text df_label ---@field dataIndex number ---@field backdrop_color table ---@field backdrop_color_highlight table +---@field enabled boolean +---@field lineData df_timeline_linedata ---@class df_timeline : scrollframe, df_timeline_mixin, df_optionsmixin, df_framelayout, df_lineindicator +---@field type timelinecomponenttype ---@field body df_timeline_body ----@field onClickCallback fun() ----@field onClickCallbackFunc fun() +---@field onClickCallback fun(...) +---@field onClickCallbackFunc fun(...) ---@field onClickCallbackArgs any[] ----@field headerFrame scrollframe headerFrame only exists if the options.header_detached is true +---@field headerFrame df_timeline_header headerFrame only exists if the options.header_detached is true ---@field headerBody frame headerBody only exists if the options.header_detached is true ---@field resizeButton button ---@field elapsedTimeFrame df_elapsedtime ---@field horizontalSlider slider ---@field scaleSlider slider ---@field verticalSlider slider +---@field oldScale number ---@field currentScale number +---@field oldMinWidth number +---@field oldMaxWidth number ---@field scrolledWidth number ---@field data df_timeline_scrolldata ---@field lines df_timeline_line[] @@ -514,21 +638,32 @@ detailsFramework.TimeLine_LineMixin = { ---@field totalLength number ---@field defaultColor table ---@field headerWidth number +---@field delayButtonRefreshTimer timer ---@class df_timeline_mixin : table ---@field GetLine fun(self:df_timeline, index:number):df_timeline_line ----@field ResetAllLines fun(self:df_timeline) ----@field RefreshTimeLine fun(self:df_timeline, bDoNotRefreshButtons:boolean?) +---@field GetAllLines fun(self:df_timeline):df_timeline_line[] +---@field GetBlockUnderMouse fun(self:df_timeline):df_timeline_line_block? +---@field GetBlockOrLengthUnderMouse fun(self:df_timeline):df_timeline_line_block|df_timeline_line_blocklength? +---@field GetLineUnderMouse fun(self:df_timeline):df_timeline_line? +---@field GetTimeUnderMouse fun(self:df_timeline):number +---@field GetBodyWidthUnderMouse fun(self:df_timeline):number +---@field GetBlocksAtTime fun(self:df_timeline, time:number?):df_timeline_line_block[] +---@field GetHorizontalScrolledWidth fun(self:df_timeline):number +---@field GetEffectivePixelPerSecond fun(self:df_timeline):number ---@field SetData fun(self:df_timeline, data:table) ---@field GetData fun(self:df_timeline):table +---@field RefreshTimeLine fun(self:df_timeline, bDelayButtonRefresh:boolean?, bFromScale:boolean?) ---@field RefreshResize fun(self:df_timeline) +---@field RefreshPerPixelButtons fun(self:df_timeline) +---@field ResetAllLines fun(self:df_timeline) ---@field SetCanResize fun(self:df_timeline, canResize:boolean) ---@field OnSizeChanged fun(self:df_timeline) ---@field SetOnClickCallback fun(self:df_timeline, callback:fun(), ...:any) ---@field UpdateOnClickCallback fun(self:df_timeline, button:button?) ----@field GetHorizontalScrolledWidth fun(self:df_timeline):number ----@field HideVerticalScrollBar fun(self:df_timeline) +---@field HideVerticalScroll fun(self:df_timeline) ---@field SetScale fun(self:df_timeline, scale:number) + detailsFramework.TimeLineMixin = { GetHorizontalScrolledWidth = function(self) return self.scrolledWidth @@ -618,32 +753,61 @@ detailsFramework.TimeLineMixin = { if (not line) then --create a new line ---@type df_timeline_line - line = CreateFrame("frame", "$parentLine" .. index, self.body, "BackdropTemplate") + line = CreateFrame("button", "$parentLine" .. index, self.body, "BackdropTemplate") detailsFramework:Mixin(line, detailsFramework.TimeLine_LineMixin) self.lines[index] = line - local xPosition + line.type = "line" + line.index = index + + local yPosition if (self.options.show_elapsed_timeline) then - xPosition = -((index-1) * (self.options.line_height + 1)) - 2 - self.options.elapsed_timeline_height + yPosition = -((index-1) * (self.options.line_height + 1)) - 2 - self.options.elapsed_timeline_height else - xPosition = -((index-1) * (self.options.line_height + 1)) - 1 + --need code cleanup as the 'else' stuff isn't in use anymore + yPosition = -((index-1) * (self.options.line_height + 1)) - 1 + end + + local yPadding = -10 + yPosition = yPosition + yPadding + + if (index == 1) then + line:SetPoint("topleft", self.body, "topleft", 1, yPosition) + else + line:SetPoint("topleft", self.lines[index-1], "bottomleft", 0, -1) end - line:SetPoint("topleft", self.body, "topleft", 1, xPosition) line:SetSize(1, self.options.line_height) --width is set when updating the frame local detachedHeaderFrame = self.headerFrame local lineHeader if (detachedHeaderFrame) then - lineHeader = CreateFrame("frame", nil, self.headerBody, "BackdropTemplate") + lineHeader = CreateFrame("frame", "$parentHeader", self.headerBody, "BackdropTemplate") + lineHeader.type = "header" lineHeader:SetSize(detachedHeaderFrame:GetWidth(), self.options.line_height) - lineHeader:SetPoint("topleft", self.headerBody, "topleft", 0, xPosition) + if (index == 1) then + lineHeader:SetPoint("topleft", self.headerBody, "topleft", 0, yPosition) + else + lineHeader:SetPoint("topleft", self.lines[index-1].lineHeader, "bottomleft", 0, -1) + end + detailsFramework:CreateHighlightTexture(lineHeader, "HighlightTexture") + lineHeader.HighlightTexture:SetDrawLayer("overlay", 1) + lineHeader.HighlightTexture:Hide() + lineHeader:EnableMouse(true) + lineHeader:SetScript("OnEnter", function() self.options.on_enter(line) lineHeader.HighlightTexture:Show() end) + lineHeader:SetScript("OnLeave", function() self.options.on_leave(line) lineHeader.HighlightTexture:Hide() end) + line:SetScript("OnEnter", function() self.options.on_enter(line) lineHeader.HighlightTexture:Show() end) + line:SetScript("OnLeave", function() self.options.on_leave(line) lineHeader.HighlightTexture:Hide() end) + lineHeader.Line = line else - lineHeader = CreateFrame("frame", nil, line, "BackdropTemplate") + lineHeader = CreateFrame("frame", "$parentHeader", line, "BackdropTemplate") + lineHeader.type = "header" lineHeader:SetPoint("topleft", line, "topleft", 0, 0) lineHeader:SetPoint("bottomleft", line, "bottomleft", 0, 0) + line:SetScript("OnEnter", self.options.on_enter) + line:SetScript("OnLeave", self.options.on_leave) end --lineHeader:SetScript("OnEnter", self.options.header_on_enter) --lineHeader:SetScript("OnLeave", self.options.header_on_leave) @@ -653,8 +817,6 @@ detailsFramework.TimeLineMixin = { --store the individual textures that shows the timeline information line.blocks = {} - line:SetScript("OnEnter", self.options.on_enter) - line:SetScript("OnLeave", self.options.on_leave) line:SetMouseClickEnabled(false) line:SetBackdrop(self.options.backdrop) @@ -671,6 +833,10 @@ detailsFramework.TimeLineMixin = { 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} + + if (self.options.on_create_line) then + self.options.on_create_line(line) + end end return line @@ -725,12 +891,127 @@ detailsFramework.TimeLineMixin = { end end, + GetBlockUnderMouse = function(self) + local allLines = self:GetAllLines() + + for i = 1, #allLines do + local thisLine = allLines[i] + local allBlocksInTheLine = thisLine:GetAllBlocks() + for j = 1, #allBlocksInTheLine do + local thisBlock = allBlocksInTheLine[j] + if (thisBlock:IsShown()) then + if (thisBlock:IsMouseOver()) then + return thisBlock + end + end + end + end + + return nil + end, + + GetBlockOrLengthUnderMouse = function(self) + local allLines = self:GetAllLines() + + for i = 1, #allLines do + local thisLine = allLines[i] + local allBlocksInTheLine = thisLine:GetAllBlocks() + for j = 1, #allBlocksInTheLine do + local thisBlock = allBlocksInTheLine[j] + if (thisBlock:IsShown()) then + if (thisBlock:IsMouseOver()) then + return thisBlock + end + + local blockLength = thisBlock.blockLength + if (blockLength and blockLength:IsShown()) then + if (blockLength:IsMouseOver()) then + return blockLength + end + end + end + end + end + + return nil + end, + + GetLineUnderMouse = function(self) + local allLines = self:GetAllLines() + + for i = 1, #allLines do + local thisLine = allLines[i] + if (thisLine:IsMouseOver()) then + return thisLine + end + end + + return nil + end, + + GetBodyWidthUnderMouse = function(self) + local x, y = GetCursorPosition() + local scale = 1 / UIParent:GetEffectiveScale() + x = x * scale + + local left = self.body:GetLeft() + local width = x - left + + return width + end, + + GetTimeUnderMouse = function(self) + local bodyWidthUnderMouse = self:GetBodyWidthUnderMouse() + local time = bodyWidthUnderMouse / (self.pixelPerSecond * self.currentScale) + return time + end, + + GetBlocksAtTime = function(self, time) + if (not time) then + time = self:GetTimeUnderMouse() + end + + local blocks = {} + local allLines = self:GetAllLines() + + local pixelsPerSecond = self:GetEffectivePixelPerSecond() + + for i = 1, #allLines do + local thisLine = allLines[i] + local allBlocksInTheLine = thisLine:GetAllBlocks() + for j = 1, #allBlocksInTheLine do + local thisBlock = allBlocksInTheLine[j] + if (thisBlock:IsShown()) then + local blockWidth = thisBlock:GetWidth() + ---@type df_timeline_line_blockinfo + local blockInfo = thisBlock.info + local blockTime = blockInfo.time + + local startTime = blockTime + local endTime = blockTime + (blockWidth / pixelsPerSecond) + + --blockTime = math.floor(blockTime) + --time = math.floor(time) + if (time >= startTime and time <= endTime) then + table.insert(blocks, thisBlock) + end + end + end + end + + return blocks + end, + --todo --make the on enter and leave tooltips --set icons and texts --skin the sliders - RefreshTimeLine = function(self, bDoNotRefreshButtons) --~refresh + GetEffectivePixelPerSecond = function(self) + return self.pixelPerSecond * self.currentScale + end, + + RefreshTimeLine = function(self, bDelayButtonRefresh, bFromScale) --~refresh if (not self.data.lines) then return end @@ -746,61 +1027,58 @@ detailsFramework.TimeLineMixin = { local timelineWidth = self:GetWidth() - --original code - --[=[ - --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(detailsFramework:MapRangeClamped(oldMin, oldMax, 0, newMaxValue, self.horizontalSlider:GetValue())) - ]=] - --calculate the width that the body width should be local bodyWidth = totalLength * pixelPerSecond * currentScale - --get the biggest value between the calculated body width and (timeline width minus header width) in case the desired body width is smaller than the timeline width - --local bodyFrameWidth = max(bodyWidth + effectiveHeaderWidth, timelineWidth - effectiveHeaderWidth) - --self.body:SetWidth(bodyFrameWidth) self.body:SetWidth(bodyWidth + effectiveHeaderWidth) - --[=[ - print("effectiveHeaderWidth", effectiveHeaderWidth) - if (bodyWidth + effectiveHeaderWidth > timelineWidth - effectiveHeaderWidth) then - print(1) - else - print(2) --this is fucking with the elapsed time bar | need to see further in the script that's happening - end - --]=] - --reduce the default timeline size from the body width and don't allow the max value be negative - local newMaxValue = max(bodyWidth - timelineWidth + effectiveHeaderWidth, 0) - - --print(desiredBodyWidth + effectiveHeaderWidth, timelineWidth - effectiveHeaderWidth) --1020, 750 + local diff = bodyWidth - timelineWidth + local newMaxValue = max(diff + effectiveHeaderWidth, 0) self.body.effectiveWidth = bodyWidth + --cache values + self.pixelPerSecond = pixelPerSecond + self.totalLength = totalLength + self.headerWidth = effectiveHeaderWidth + --adjust the scale slider range + --new max value is zero when all content is shown in the timeline (no scroll needed) local oldMin, oldMax = self.horizontalSlider:GetMinMaxValues() - self.horizontalSlider:SetMinMaxValues(0, newMaxValue) - self.horizontalSlider:SetValue(detailsFramework:MapRangeClamped(oldMin, oldMax, 0, newMaxValue, self.horizontalSlider:GetValue())) + local newHorizontalSliderValue = self.horizontalSlider:GetValue() + if (bFromScale) then + local timeUnderMouse = self:GetTimeUnderMouse() + local timeUnderMouseInPixels = (timeUnderMouse * pixelPerSecond * self.currentScale) + newHorizontalSliderValue = timeUnderMouseInPixels + end + + if (newMaxValue == 0) then + --no scroll is needed + self.horizontalSlider:SetMinMaxValues(0, 0) + self.horizontalSlider:SetValue(0) + + elseif (oldMax ~= newMaxValue) then + self.horizontalSlider:SetMinMaxValues(0, newMaxValue) + self.horizontalSlider:SetValue(newHorizontalSliderValue) --it is setting for zoom even when if the refresh is not from zoom + end + + self.oldMinWidth = 0 + self.oldMaxWidth = newMaxValue local defaultColor = self.data.defaultColor or {1, 1, 1, 1} - --cache values - self.pixelPerSecond = pixelPerSecond - self.totalLength = totalLength self.defaultColor = defaultColor - self.headerWidth = effectiveHeaderWidth --buttons are the vertical clickable areas inside the timeline, each second on the time line has one - if (not bDoNotRefreshButtons and self.options.use_perpixel_buttons) then + if (not bDelayButtonRefresh and self.options.use_perpixel_buttons) then self:RefreshPerPixelButtons() + elseif (bDelayButtonRefresh and self.options.use_perpixel_buttons) then + if (self.delayButtonRefreshTimer and not self.delayButtonRefreshTimer:IsCancelled()) then + self.delayButtonRefreshTimer:Cancel() + end + self.delayButtonRefreshTimer = detailsFramework.Schedules.NewTimer(0.1, function() + self:RefreshPerPixelButtons() + end) end --calculate the total height @@ -808,6 +1086,7 @@ detailsFramework.TimeLineMixin = { local linePadding = self.options.line_padding local bodyHeight = (lineHeight + linePadding) * #self.data.lines + bodyHeight = bodyHeight + 40 self.body:SetHeight(bodyHeight) self.verticalSlider:SetMinMaxValues(0, max(bodyHeight - self:GetHeight(), 0)) self.verticalSlider:SetValue(0) @@ -825,14 +1104,16 @@ detailsFramework.TimeLineMixin = { end --refresh lines - local howManyLinesTheTimelineCanShow = floor(self:GetHeight() / (lineHeight + linePadding)) - 1 - --for i = 1, math.min(#self.data.lines, howManyLinesTheTimelineCanShow) do 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 line.lineHeader:Show() + + if (self.options.on_refresh_line) then + self.options.on_refresh_line(line) + end end --refresh elapsed time frame @@ -869,10 +1150,12 @@ detailsFramework.TimeLineMixin = { local timelineHeader = { ---@param self df_timeline CreateDetachedHeader = function(self) + ---@type df_timeline_header local headerFrame = CreateFrame("scrollframe", nil, self, "BackdropTemplate") headerFrame:SetWidth(self.options.header_width) self.headerFrame = headerFrame + ---@type df_timeline_header_body local headerBody = CreateFrame("frame", nil, headerFrame, "BackdropTemplate") headerBody:SetSize(headerFrame:GetSize()) headerBody.Lines = {} @@ -900,6 +1183,7 @@ local onScaleChange_RefreshTimer = nil ---@class df_timeline_body : frame, df_lineindicator + ---creates a scrollable panel with vertical, horizontal and scale sliders to show a timeline ---also creates a frame for the elapsed timeline at the top, it shows the time in seconds ---@param parent frame @@ -916,6 +1200,8 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela ---@type df_timeline local frameCanvas = CreateFrame("scrollframe", name, parent, "BackdropTemplate") + frameCanvas.type = "timeline" + detailsFramework:Mixin(frameCanvas, detailsFramework.TimeLineMixin) detailsFramework:Mixin(frameCanvas, detailsFramework.OptionsFunctions) detailsFramework:Mixin(frameCanvas, detailsFramework.LayoutFrame) @@ -930,6 +1216,7 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela frameCanvas.lines = {} frameCanvas.currentScale = 0.5 + frameCanvas.oldScale = 0.5 frameCanvas:SetSize(width, height) detailsFramework:ApplyStandardBackdrop(frameCanvas) @@ -938,6 +1225,8 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela frameBody:SetSize(scrollWidth, scrollHeight) frameCanvas:LineIndicatorSetTarget(frameBody) + frameBody.type = "body" + frameBody.Buttons = {} frameCanvas:SetScrollChild(frameBody) @@ -1001,23 +1290,16 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela scaleSlider:SetOrientation("horizontal") scaleSlider:SetMinMaxValues(frameCanvas.options.scale_min, frameCanvas.options.scale_max) - scaleSlider:SetValue(detailsFramework:GetRangeValue(frameCanvas.options.scale_min, frameCanvas.options.scale_max, 0.5)) + scaleSlider:SetValue(detailsFramework.Math.GetRangeValue(frameCanvas.options.scale_min, frameCanvas.options.scale_max, 0.5)) - scaleSlider:SetScript("OnValueChanged", function(self) + scaleSlider:SetScript("OnValueChanged", function(self, delta) local stepValue = ceil(self:GetValue() * 100) / 100 if (stepValue ~= frameCanvas.currentScale) then - local current = stepValue + frameCanvas.oldScale = frameCanvas.currentScale frameCanvas.currentScale = stepValue - local bDoNotRefreshButtons = true - frameCanvas:RefreshTimeLine(bDoNotRefreshButtons) - - if (onScaleChange_RefreshTimer and not onScaleChange_RefreshTimer:IsCancelled()) then - onScaleChange_RefreshTimer:Cancel() - end - - onScaleChange_RefreshTimer = detailsFramework.Schedules.NewTimer(0.1, function() - frameCanvas:RefreshTimeLine() - end) + local bDelayButtonRefresh = true + local bFromScale = true + frameCanvas:RefreshTimeLine(bDelayButtonRefresh, bFromScale) end end) @@ -1053,21 +1335,6 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela local minValue, maxValue = horizontalSlider:GetMinMaxValues() local currentHorizontal = horizontalSlider:GetValue() - if (delta < 0) then - if (verticalSlider:IsShown()) then - local amountToScroll = frameBody:GetHeight() / 20 - verticalSlider:SetValue(verticalSlider:GetValue() + amountToScroll) - return - end - - elseif (delta > 0) then - if (verticalSlider:IsShown()) then - local amountToScroll = frameBody:GetHeight() / 20 - verticalSlider:SetValue(verticalSlider:GetValue() - amountToScroll) - return - end - end - if (IsShiftKeyDown() and delta < 0) then if (verticalSlider:IsShown()) then local amountToScroll = frameBody:GetHeight() / 20 @@ -1081,10 +1348,14 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela end elseif (IsControlKeyDown() and delta > 0) then - scaleSlider:SetValue(min(scaleSlider:GetValue() + 0.1, 1)) + scaleSlider:SetValue(min(scaleSlider:GetValue() + 0.1, frameCanvas.options.scale_max)) elseif (IsControlKeyDown() and delta < 0) then - scaleSlider:SetValue(max(scaleSlider:GetValue() - 0.1, 0.15)) + if (self.options.zoom_out_zero) then + scaleSlider:SetValue(0) + else + scaleSlider:SetValue(max(scaleSlider:GetValue() - 0.1, frameCanvas.options.scale_min)) + end elseif (delta < 0 and currentHorizontal < maxValue) then local amountToScroll = frameBody:GetWidth() / 20 @@ -1104,22 +1375,35 @@ function detailsFramework:CreateTimeLineFrame(parent, name, timelineOptions, ela frameBody.GetHorizontalScrolledWidth = frameCanvas.GetHorizontalScrolledWidth + local dragOnTickFunc = function() + local x = GetCursorPosition() + local deltaX = frameBody.MouseX - x + local current = horizontalSlider:GetValue() + horizontalSlider:SetValue(current + (deltaX * 1.2) * ((IsShiftKeyDown() and 2) or (IsAltKeyDown() and 0.5) or 1)) + frameBody.MouseX = x + end + + frameBody.isDragging = false + + frameBody:SetScript("OnUpdate", function(thisFrameBody, deltaTime) + if (frameBody.isDragging) then + dragOnTickFunc() + end + + if (frameBody:IsMouseOver()) then + frameBody.mouseTime = frameCanvas:GetTimeUnderMouse() + 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) + frameBody.isDragging = true end) frameBody:SetScript("OnMouseUp", function(self, button) - frameBody:SetScript("OnUpdate", nil) + frameBody.isDragging = false end) local headerFrame, headerBody diff --git a/luaserver.lua b/luaserver.lua index c8cb6699..d8d4de85 100644 --- a/luaserver.lua +++ b/luaserver.lua @@ -492,7 +492,7 @@ BackdropTemplateMixin = {} ---@field GetRight fun(self: uiobject): number ---@field GetTop fun(self: uiobject): number ---@field GetBottom fun(self: uiobject): number ----@field SetPoint fun(self: uiobject, point: anchorpoint, relativeFrame: uiobject, relativePoint: anchorpoint, xOffset: number, yOffset: number) +---@field SetPoint fun(self: uiobject, point: anchorpoint, relativeFrame: uiobject?, relativePoint: anchorpoint?, xOffset: number?, yOffset: number?) ---@field ClearAllPoints fun(self: uiobject) ---@field CreateAnimationGroup fun(self: uiobject, name: string|nil, templateName: string|nil) : animationgroup ---@field SetIgnoreParentAlpha fun(self: region, ignore: boolean) @@ -615,7 +615,7 @@ BackdropTemplateMixin = {} ---@field GetChildren fun(self: frame) : frame[] ---@field GetRegions fun(self: frame) : region[] ---@field CreateTexture fun(self: frame, name: string?, layer: drawlayer, inherits: string?, subLayer: number?) : texture ----@field CreateMaskTexture fun(self: frame, name: string?, layer: drawlayer, inherits: string?, subLayer: number?) : texture +---@field CreateMaskTexture fun(self: frame, name: string?, layer: drawlayer?, inherits: string?, subLayer: number?) : texture ---@field CreateFontString fun(self: frame, name: string?, layer: drawlayer, inherits: string?, subLayer: number?) : fontstring ---@field EnableMouse fun(self: frame, enable: boolean) enable mouse interaction ---@field SetResizable fun(self: frame, enable: boolean) enable resizing of the frame @@ -668,9 +668,8 @@ BackdropTemplateMixin = {} ---@field ClearPointsOffset fun(self: frame) ---@field GetPoint fun(self: frame, anchorIndex: number?, resolveCollapsed: boolean?) : string, frame, string, number, number ---@field GetPointByName fun(self: frame, point: string, resolveCollapsed: boolean?) : string, frame, string, number, number ----@field SetAllPoints fun(self: frame, relativeTo: frame?, doResize: boolean?) +---@field SetAllPoints fun(self: frame, relativeTo: uiobject?, doResize: boolean?) ---@field SetHeight fun(self: frame, height: number) ----@field SetPoint fun(self: frame, point: string, relativeTo: frame?, relativePoint: string?, offsetX: number?, offsetY: number?) ---@field SetSize fun(self: frame, x: number, y: number) ---@field SetWidth fun(self: frame, width: number) ---@field CreateAnimationGroup fun(self: frame, name: string?, templateName: string?) : animationgroup @@ -892,6 +891,12 @@ BackdropTemplateMixin = {} ---@field SetThumbTexture fun(self: slider, texture: textureid|texturepath) ---@field SetStepsPerPage fun(self: slider, steps: number) +---get all frames under the cursor that has mouse focus +---@return uiobject[] +function GetMouseFoci() + return {} +end + ---@return number function debugprofilestop() return 0 end @@ -5127,13 +5132,13 @@ SetAllowLowLevelRaid = function(enabled) end ---@param index number SetRaidRosterSelection = function(index) end ----@param unit string ----@param subgroup number -SetRaidSubgroup = function(unit, subgroup) end +---@param unitNumber number +---@param subGroup number +SetRaidSubgroup = function(unitNumber, subGroup) end ----@param unit1 string ----@param unit2 string -SwapRaidSubgroup = function(unit1, unit2) end +---@param unitNumber1 number +---@param unitNumber2 number +SwapRaidSubgroup = function(unitNumber1, unitNumber2) end ---@param unit string ---@param raidTargetIndex number