diff --git a/Definitions.lua b/Definitions.lua index fd419fced..d311bc1aa 100644 --- a/Definitions.lua +++ b/Definitions.lua @@ -112,6 +112,7 @@ ---@field __destroyedBy string ---@field amountCasts {[string]: table} ---@field instance_type instancetype "raid" or "party" or "pvp" or "arena" or "none" or "scenario" +---@field run_time number ---@field end_time number ---@field start_time number ---@field combat_counter number @@ -130,10 +131,12 @@ ---@field is_boss table ---@field is_world_trash_combat boolean when true this combat is a regular combat done in the world, not in a dungeon, raid, battleground, arena, ... ---@field player_last_events table record the latest events of each player, latter used to build the death log +---@field GetCombatType fun(combat: combat) : number ---@field GetCombatUID fun(combat: combat) : uniquecombatid ---@field GetTimeData fun(combat: combat, dataName: string) : table ---@field GetPhases fun(combat: combat) : table ---@field GetCombatTime fun(combat) : number +---@field GetRunTime fun(combat) : number ---@field GetDeaths fun(combat) : table --get the table which contains the deaths of the combat ---@field GetStartTime fun(combat: combat) : number ---@field SetStartTime fun(combat: combat, time: number) diff --git a/Libs/DF/auras.lua b/Libs/DF/auras.lua index 9a6944899..2f0b8059e 100644 --- a/Libs/DF/auras.lua +++ b/Libs/DF/auras.lua @@ -855,6 +855,11 @@ end debuffTrackedAuraScrollBox:SetPoint("topleft", auraPanel_Auto, "topleft", 16 +(scrollWidth * 2) + xLocation, y) buffTrackedAuraScrollBox:SetPoint("topleft", auraPanel_Auto, "topleft", 24 +(scrollWidth * 3) + xLocation, y) + buffTrackedAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.BUFFS_TRACKED) + debuffTrackedAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.DEBUFFS_TRACKED) + buffIgnoredAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.BUFFS_IGNORED) + debuffIgnoredAuraScrollBox:GetTitleFontString():SetText(newAuraPanel.LocTexts.DEBUFFS_IGNORED) + newAuraPanel.buff_ignored = buffIgnoredAuraScrollBox newAuraPanel.debuff_ignored = debuffIgnoredAuraScrollBox newAuraPanel.buff_tracked = buffTrackedAuraScrollBox diff --git a/Libs/DF/buildmenu.lua b/Libs/DF/buildmenu.lua index 882db5b89..e5b04099d 100644 --- a/Libs/DF/buildmenu.lua +++ b/Libs/DF/buildmenu.lua @@ -93,6 +93,16 @@ local _ ---@field nocombat boolean can't edit when in combat ---@field spacement boolean gives a little of more space from the next widget +detailsFramework.OptionsFrameMixin = { + +} + +local setWidgetId = function(parent, widgetTable, widgetObject) + if (widgetTable.id) then + parent.widgetids[widgetTable.id] = widgetObject + end +end + local onEnterHighlight = function(self) self.highlightTexture:Show() if (self.parent:GetScript("OnEnter")) then @@ -139,7 +149,578 @@ local createOptionHighlightTexture = function(frame, label, widgetWidth) return highlightTexture end +local setLabelProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template) + widget._get = widgetTable.get + widget.widget_type = "label" + widget:SetPoint(currentXOffset, currentYOffset) + + if (widgetTable.text_template or template) then + widget:SetTemplate(widgetTable.text_template or template) + else + widget.fontsize = widgetTable.size or 10 + end + + if (widgetTable.font) then + widget.fontface = widgetTable.font + end + + setWidgetId(parent, widgetTable, widget) +end + +local setDropdownProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth) + widget._get = widgetTable.get + widget.widget_type = "select" + + widget:Refresh() + widget:Select(widgetTable.get()) + widget:SetTemplate(template) + + if (widgetWidth) then + widget:SetWidth(widgetWidth) + end + if (widgetHeight) then + widget:SetHeight(widgetHeight) + end + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + if (bAlignAsPairs) then --regular + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + end + + --global callback + if (valueChangeHook) then + widget:SetHook("OnOptionSelected", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + local widgetTotalSize = label:GetStringWidth() + 144 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth +end + +local setToggleProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, switchIsCheckbox, bUseBoxFirstOnAllWidgets, menuOptions, index, maxWidgetWidth) + widget._get = widgetTable.get + widget.widget_type = "toggle" + widget.OnSwitch = widgetTable.set + + if (switchIsCheckbox) then + widget:SetAsCheckBox() + end + + widget:SetValue(widgetTable.get()) + + if (widgetWidth) then + PixelUtil.SetWidth(widget.widget, widgetWidth) + end + if (widgetHeight) then + PixelUtil.SetHeight(widget.widget, widgetHeight) + end + + widget:SetTemplate(template) + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + local extraPaddingY = 0 + + if (bAlignAsPairs) then + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + + local nextWidgetTable = menuOptions[index+1] + if (nextWidgetTable) then + if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then + extraPaddingY = 4 + end + end + else + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + end + end + + --global callback + if (valueChangeHook) then + widget:SetHook("OnSwitch", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + local widgetTotalSize = label:GetStringWidth() + 32 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth, extraPaddingY +end + +local setRangeProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, bIsDecimals) + widget._get = widgetTable.get + widget.widget_type = "range" + widget:SetTemplate(template) + + if (bIsDecimals) then + widget.slider:SetValueStep(0.01) + else + widget.slider:SetValueStep(widgetTable.step or 1) + end + widget.useDecimals = bIsDecimals + + widget.slider:SetMinMaxValues(widgetTable.min, widgetTable.max) + widget.slider:SetValue(widgetTable.get() or 0) + widget.ivalue = widget.slider:GetValue() + + if (widgetWidth) then + widget:SetWidth(widgetWidth) + end + if (widgetHeight) then + widget:SetHeight(widgetHeight) + end + + widget:SetHook("OnValueChange", widgetTable.set) + + if (valueChangeHook) then + widget:SetHook("OnValueChange", valueChangeHook) + end + + if (widgetTable.thumbscale) then + widget:SetThumbSize(widget.thumb.originalWidth * widgetTable.thumbscale, nil) + else + widget:SetThumbSize(widget.thumb.originalWidth * 1.3, nil) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + if (bAlignAsPairs) then + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + end + + local widgetTotalSize = label:GetStringWidth() + 146 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth +end + +local setColorProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, bUseBoxFirstOnAllWidgets, extraPaddingY) + widget._get = widgetTable.get + widget.widget_type = "color" + + local r, g, b, a = detailsFramework:ParseColors(widgetTable.get()) + widget:SetColor(r, g, b, a) + + widget.color_callback = widgetTable.set --callback + + --[=[ + if (widgetWidth) then + widget:SetWidth(widgetWidth) + else + widget:SetWidth(18) + end + + if (widgetHeight) then + widget:SetHeight(widgetHeight) + else + widget:SetHeight(18) + end + --]=] + + widget:SetTemplate(template) + widget:SetWidth(18) + widget:SetHeight(18) + + widget:SetHook("OnColorChanged", widgetTable.set) + + if (valueChangeHook) then + widget:SetHook("OnColorChanged", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + if (bAlignAsPairs) then + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then + label:SetPoint("left", widget.widget, "right", 2, 0) + widget:SetPoint(currentXOffset, currentYOffset) + extraPaddingY = 1 + else + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + end + end + + local widgetTotalSize = label:GetStringWidth() + 32 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth, extraPaddingY +end + +local setExecuteProperties = function(parent, widget, widgetTable, currentXOffset, currentYOffset, template, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate, latestInlineWidget) + widget._get = widgetTable.get + widget.widget_type = "execute" + widget:SetTemplate(template) + widget:SetWidth(widgetWidth or widgetTable.width or 120, widgetHeight or widgetTable.height or 18) + + widget:SetClickFunction(widgetTable.func, widgetTable.param1, widgetTable.param2) + + --button icon + if (widgetTable.icontexture) then + widget:SetIcon(widgetTable.icontexture, nil, nil, nil, widgetTable.icontexcoords, nil, nil, 2) + end + + textTemplate = widgetTable.text_template or textTemplate or detailsFramework.font_templates["ORANGE_FONT_TEMPLATE"] + widget.textcolor = textTemplate.color + widget.textfont = textTemplate.font + widget.textsize = textTemplate.size + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + if (bAlignAsPairs) then + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + if (widgetTable.inline) then + if (latestInlineWidget) then + widget:SetPoint("left", latestInlineWidget, "right", 2, 0) + latestInlineWidget = widget + else + widget:SetPoint(currentXOffset, currentYOffset) + latestInlineWidget = widget + end + else + widget:SetPoint(currentXOffset, currentYOffset) + end + end + + local widgetTotalSize = widget:GetWidth() + 4 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth, latestInlineWidget +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() + widget.widget_type = "textentry" + widget:SetTemplate(widgetTable.template or widgetTable.button_template or template) + widget:SetWidth(widgetWidth or widgetTable.width or 120, widgetHeight or widgetTable.height or 18) + widget:SetCommitFunction(widgetTable.func or widgetTable.set) + + widget:SetHook("OnEnterPressed", function(...) + local upFunc = widgetTable.func or widgetTable.set + upFunc(...) + if (valueChangeHook) then + valueChangeHook() + end + end) + + widget:SetHook("OnEditFocusLost", function(...) + local upFunc = widgetTable.func or widgetTable.set + upFunc(...) + if (valueChangeHook) then + valueChangeHook() + end + end) + + textTemplate = widgetTable.text_template or textTemplate or detailsFramework.font_templates["ORANGE_FONT_TEMPLATE"] + widget.textcolor = textTemplate.color + widget.textfont = textTemplate.font + widget.textsize = textTemplate.size + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + widget:SetHook(hookName, hookFunc) + end + end + + setWidgetId(parent, widgetTable, widget) + + local label = widget.hasLabel.widget + + widget:ClearAllPoints() + label:ClearAllPoints() + + if (bAlignAsPairs) then + PixelUtil.SetPoint(label, "topleft", widget:GetParent(), "topleft", currentXOffset, currentYOffset) + PixelUtil.SetPoint(widget.widget, "left", label, "left", nAlignAsPairsLength, 0) + + if (not widget.highlightFrame) then + local highlightFrame = createOptionHighlightTexture(widget, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + widget.highlightFrame = highlightFrame + end + else + widget:SetPoint("left", label, "right", 2, 0) + label:SetPoint("topleft", parent, "topleft", currentXOffset, currentYOffset) + end + + local widgetTotalSize = label:GetStringWidth() + 64 --need review, might not be correct + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (widget:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = widget:GetWidth() + end + + return maxColumnWidth, maxWidgetWidth +end + +local refreshOptions = function(self) + for _, widget in ipairs(self.widget_list) do + if (widget._get) then + if (widget.widget_type == "label") then + if (widget._get() and not widget.languageAddonId) then + widget:SetText(widget._get()) + end + + elseif (widget.widget_type == "select") then + widget:Select(widget._get()) + + elseif (widget.widget_type == "toggle" or widget.widget_type == "range") then + widget:SetValue(widget._get()) + + elseif (widget.widget_type == "textentry") then + widget:SetText(widget._get()) + + elseif (widget.widget_type == "color") then + local default_value, g, b, a = widget._get() + if (type(default_value) == "table") then + widget:SetColor (unpack(default_value)) + + else + widget:SetColor (default_value, g, b, a) + end + end + end + end +end + +detailsFramework.internalFunctions.RefreshOptionsPanel = refreshOptions + +local parseOptionsTypes = function(menuOptions) + --normalize format types + for index, widgetTable in ipairs(menuOptions) do + if (widgetTable.type == "space") then + widgetTable.type = "blank" + + elseif (widgetTable.type == "fontdropdown") then + widgetTable.type = "selectfont" + elseif (widgetTable.type == "colordropdown") then + widgetTable.type = "selectcolor" + elseif (widgetTable.type == "outlinedropdown") then + widgetTable.type = "selectoutline" + elseif (widgetTable.type == "anchordropdown") then + widgetTable.type = "selectanchor" + elseif (widgetTable.type == "dropdown") then + widgetTable.type = "select" + + elseif (widgetTable.type == "switch") then + widgetTable.type = "toggle" + + elseif (widgetTable.type == "slider") then + widgetTable.type = "range" + + elseif (widgetTable.type == "button") then + widgetTable.type = "execute" + end + end +end + +local parseOptionsTable = function(menuOptions) + local bUseBoxFirstOnAllWidgets = menuOptions.always_boxfirst + local widgetWidth = menuOptions.widget_width --a width to be used on all widgets + local widgetHeight = menuOptions.widget_height --a height to be used on all widgets + local bAlignAsPairs = menuOptions.align_as_pairs + local nAlignAsPairsLength = menuOptions.align_as_pairs_string_space or 160 + local nAlignAsPairsSpacing = menuOptions.align_as_pairs_spacing or 20 + + --if a scrollbox is passed, the height can be ignored + --the scrollBox child will be used as the parent, and the height of the child will be resized to fit the widgets + local bUseScrollFrame = menuOptions.use_scrollframe + local languageAddonId = menuOptions.language_addonId + return bUseBoxFirstOnAllWidgets, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, nAlignAsPairsSpacing, bUseScrollFrame, languageAddonId +end + +local parseParent = function(bUseScrollFrame, parent, height, yOffset) + if (bUseScrollFrame) then + local width, height = parent:GetSize() + parent = parent:GetScrollChild() + parent:SetSize(width, height) + else + if (height and type(height) == "number") then + height = math.abs((height or parent:GetHeight()) - math.abs(yOffset) + 20) + height = height * -1 + else + height = parent:GetHeight() + end + end + + return parent, height +end + +local parseLanguageTable = function(languageAddonId) + local languageTable + if (languageAddonId) then + languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) + end + return languageTable +end + +local getFrameById = function(self, id) + return self.widgetids[id] +end + +function detailsFramework:ClearOptionsPanel(frame) + for i = 1, #frame.widget_list do + frame.widget_list[i]:Hide() + if (frame.widget_list[i].hasLabel) then + frame.widget_list[i].hasLabel:SetText("") + end + end + table.wipe(frame.widgetids) +end +function detailsFramework:SetAsOptionsPanel(frame) + --print("refresh_options", refresh_options) + frame.RefreshOptions = refreshOptions + frame.widget_list = {} + frame.widget_list_by_type = { + ["dropdown"] = {}, -- "select" + ["switch"] = {}, -- "toggle" + ["slider"] = {}, -- "range" + ["color"] = {}, -- + ["button"] = {}, -- "execute" + ["textentry"] = {}, -- + ["label"] = {}, --"text" + } + frame.widgetids = {} + frame.GetWidgetById = getFrameById +end local formatOptionNameWithColon = function(text, useColon) if (text) then @@ -314,12 +895,15 @@ function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffse end detailsFramework:ClearOptionsPanel(parent) + local amountLineWidgetAdded = 0 + local biggestColumnHeight = 0 --used to resize the scrollbox child when a scrollbox is passed + local latestInlineWidget local currentXOffset = xOffset or 0 local currentYOffset = yOffset or 0 - local maxColumnWidth = 0 - - local latestInlineWidget + local maxColumnWidth = 0 --biggest width of widget + text size on the current column loop pass + local maxWidgetWidth = 0 --biggest widget width on the current column loop pass + --which is the next widget to get from the pool local widgetIndexes = { label = 1, dropdown = 1, @@ -330,52 +914,10 @@ function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffse textentry = 1, } - if (height and type(height) == "number") then - height = math.abs((height or parent:GetHeight()) - math.abs(yOffset) + 20) - height = height * -1 - else - height = parent:GetHeight() - end - - --normalize format types - for index, widgetTable in ipairs(menuOptions) do - if (widgetTable.type == "space") then - widgetTable.type = "blank" - - elseif (widgetTable.type == "fontdropdown") then - widgetTable.type = "selectfont" - elseif (widgetTable.type == "colordropdown") then - widgetTable.type = "selectcolor" - elseif (widgetTable.type == "outlinedropdown") then - widgetTable.type = "selectoutline" - elseif (widgetTable.type == "anchordropdown") then - widgetTable.type = "selectanchor" - elseif (widgetTable.type == "dropdown") then - widgetTable.type = "select" - - elseif (widgetTable.type == "switch") then - widgetTable.type = "toggle" - - elseif (widgetTable.type == "slider") then - widgetTable.type = "range" - - elseif (widgetTable.type == "button") then - widgetTable.type = "execute" - end - end - - --catch some options added in the hash part of the menu table - local bUseBoxFirstOnAllWidgets = menuOptions.always_boxfirst - local bAlignAsPairs = menuOptions.align_as_pairs - local nAlignAsPairsLength = menuOptions.align_as_pairs_string_space or 160 - local languageAddonId = menuOptions.language_addonId - local widgetWidth = menuOptions.widget_width - local widgetHeight = menuOptions.widget_height - local languageTable - - if (languageAddonId) then - languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) - end + parseOptionsTypes(menuOptions) + local bUseBoxFirstOnAllWidgets, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, nAlignAsPairsSpacing, bUseScrollFrame, languageAddonId = parseOptionsTable(menuOptions) + parent, height = parseParent(bUseScrollFrame, parent, height, yOffset) + local languageTable = parseLanguageTable(languageAddonId) for index, widgetTable in ipairs(menuOptions) do if (not widgetTable.hidden) then @@ -396,439 +938,131 @@ function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffse elseif (widgetTable.type == "label" or widgetTable.type == "text") then local label = getMenuWidgetVolative(parent, "label", widgetIndexes) + label:ClearAllPoints() widgetCreated = label + setLabelProperties(parent, label, widgetTable, currentXOffset, currentYOffset, textTemplate) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) local namePhrase = (languageTable and (languageTable[namePhraseId] or languageTable[widgetTable.namePhraseId] or languageTable[widgetTable.name])) or (widgetTable.get and widgetTable.get()) or widgetTable.text or (widgetTable.namePhraseId) or "" label.text = namePhrase label.color = widgetTable.color - if (widgetTable.font) then - label.fontface = widgetTable.font - end - - if (widgetTable.text_template or textTemplate) then - label:SetTemplate(widgetTable.text_template or textTemplate) - else - label.fontsize = widgetTable.size or 10 - end - - label._get = widgetTable.get - label.widget_type = "label" - label:ClearAllPoints() - label:SetPoint(currentXOffset, currentYOffset) - - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = label - end + amountLineWidgetAdded = amountLineWidgetAdded + 1 --dropdowns elseif (widgetTable.type:find("select")) then assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get() not found in the widget table for 'select'") local dropdown = getMenuWidgetVolative(parent, "dropdown", widgetIndexes) widgetCreated = dropdown + local defaultHeight = 18 - if (widgetTable.type == "selectfont") then - local func = detailsFramework:CreateFontListGenerator(widgetTable.set) - dropdown:SetFunction(func) + do + if (widgetTable.type == "selectfont") then + local func = detailsFramework:CreateFontListGenerator(widgetTable.set) + dropdown:SetFunction(func) - elseif (widgetTable.type == "selectcolor") then - local func = detailsFramework:CreateColorListGenerator(widgetTable.set) - dropdown:SetFunction(func) + elseif (widgetTable.type == "selectcolor") then + local func = detailsFramework:CreateColorListGenerator(widgetTable.set) + dropdown:SetFunction(func) - elseif (widgetTable.type == "selectanchor") then - local func = detailsFramework:CreateAnchorPointListGenerator(widgetTable.set) - dropdown:SetFunction(func) + elseif (widgetTable.type == "selectanchor") then + local func = detailsFramework:CreateAnchorPointListGenerator(widgetTable.set) + dropdown:SetFunction(func) - elseif (widgetTable.type == "selectoutline") then - local func = detailsFramework:CreateOutlineListGenerator(widgetTable.set) - dropdown:SetFunction(func) - else - dropdown:SetFunction(widgetTable.values) - end - - dropdown:Refresh() - dropdown:Select(widgetTable.get()) - dropdown:SetTemplate(dropdownTemplate) - - if (widgetWidth) then - dropdown:SetWidth(widgetWidth) - end - if (widgetHeight) then - dropdown:SetHeight(widgetHeight) + elseif (widgetTable.type == "selectoutline") then + local func = detailsFramework:CreateOutlineListGenerator(widgetTable.set) + dropdown:SetFunction(func) + else + dropdown:SetFunction(widgetTable.values) + end end local descPhrase = getDescPhraseText(languageTable, widgetTable) dropdown:SetTooltip(descPhrase) - dropdown._get = widgetTable.get - dropdown.widget_type = "select" local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) dropdown.hasLabel.text = namePhrase - dropdown.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) - --as these are reused widgets, clean the previous point - dropdown:ClearAllPoints() - dropdown.hasLabel:ClearAllPoints() - - if (bAlignAsPairs) then - dropdown.hasLabel:SetPoint(currentXOffset, currentYOffset) - dropdown:SetPoint("left", dropdown.hasLabel, "left", nAlignAsPairsLength, 0) - else - dropdown:SetPoint("left", dropdown.hasLabel, "right", 2, 0) - dropdown.hasLabel:SetPoint(currentXOffset, currentYOffset) - end - - --global callback - if (valueChangeHook) then - dropdown:SetHook("OnOptionSelected", valueChangeHook) - end - - --hook list (hook list is wiped when getting the widget) - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - dropdown:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = dropdown - end - - local widgetTotalSize = dropdown.hasLabel.widget:GetStringWidth() + 140 + 4 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + maxColumnWidth, maxWidgetWidth = setDropdownProperties(parent, dropdown, widgetTable, currentXOffset, currentYOffset, textTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth) + amountLineWidgetAdded = amountLineWidgetAdded + 1 --switchs elseif (widgetTable.type == "toggle" or widgetTable.type == "switch") then local switch = getMenuWidgetVolative(parent, "switch", widgetIndexes) widgetCreated = switch - switch:SetValue(widgetTable.get()) - switch:SetTemplate(switchTemplate) - switch:SetAsCheckBox() --it's always a checkbox on volatile menu - local descPhrase = getDescPhraseText(languageTable, widgetTable) switch:SetTooltip(descPhrase) - switch._get = widgetTable.get - switch.widget_type = "toggle" - switch.OnSwitch = widgetTable.set - - if (valueChangeHook) then - switch:SetHook("OnSwitch", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - switch:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.width) then - switch:SetWidth(widgetTable.width) - end - if (widgetTable.height) then - switch:SetHeight(widgetTable.height) - end local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) switch.hasLabel.text = namePhrase switch.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) - switch:ClearAllPoints() - switch.hasLabel:ClearAllPoints() - - if (bAlignAsPairs) then - switch.hasLabel:SetPoint(currentXOffset, currentYOffset) - switch:SetPoint("left", switch.hasLabel, "left", nAlignAsPairsLength, 0) - else - if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then - switch:SetPoint(currentXOffset, currentYOffset) - switch.hasLabel:SetPoint("left", switch, "right", 2) - - local nextWidgetTable = menuOptions[index+1] - if (nextWidgetTable) then - if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then - extraPaddingY = 4 - end - end - else - switch.hasLabel:SetPoint(currentXOffset, currentYOffset) - switch:SetPoint("left", switch.hasLabel, "right", 2) - end - end - - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = switch - end - - local widgetTotalSize = switch.hasLabel:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + maxColumnWidth, maxWidgetWidth, extraPaddingY = setToggleProperties(parent, switch, widgetTable, currentXOffset, currentYOffset, switchTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, true, bUseBoxFirstOnAllWidgets, menuOptions, index, maxWidgetWidth) + amountLineWidgetAdded = amountLineWidgetAdded + 1 --slider - elseif (widgetTable.type == "range" or widgetTable.type == "slider") then + elseif (widgetTable.type == "range") then local slider = getMenuWidgetVolative(parent, "slider", widgetIndexes) widgetCreated = slider - if (widgetTable.usedecimals) then - slider.slider:SetValueStep(0.01) - else - slider.slider:SetValueStep(widgetTable.step or 1) - end - slider.useDecimals = widgetTable.usedecimals - - slider.slider:SetMinMaxValues(widgetTable.min, widgetTable.max) - slider.slider:SetValue(widgetTable.get()) - slider.ivalue = slider.slider:GetValue() - - slider:SetTemplate(sliderTemplate) - - if (widgetWidth) then - slider:SetWidth(widgetWidth) - end - if (widgetHeight) then - slider:SetHeight(widgetHeight) - end - local descPhrase = getDescPhraseText(languageTable, widgetTable) slider:SetTooltip(descPhrase) - slider._get = widgetTable.get - slider.widget_type = "range" - slider:SetHook("OnValueChange", widgetTable.set) - - if (valueChangeHook) then - slider:SetHook("OnValueChange", valueChangeHook) - end - - if (widgetTable.thumbscale) then - slider:SetThumbSize (slider.thumb.originalWidth * widgetTable.thumbscale, nil) - else - slider:SetThumbSize (slider.thumb.originalWidth * 1.3, nil) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - slider:SetHook(hookName, hookFunc) - end - end local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) slider.hasLabel.text = namePhrase slider.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) - slider:ClearAllPoints() - slider.hasLabel:ClearAllPoints() - - if (bAlignAsPairs) then - slider.hasLabel:SetPoint(currentXOffset, currentYOffset) - slider:SetPoint("left", slider.hasLabel, "left", nAlignAsPairsLength, 0) - else - slider:SetPoint("left", slider.hasLabel, "right", 2) - slider.hasLabel:SetPoint(currentXOffset, currentYOffset) - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = slider - end - - local widgetTotalSize = slider.hasLabel:GetStringWidth() + 146 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + maxColumnWidth, maxWidgetWidth = setRangeProperties(parent, slider, widgetTable, currentXOffset, currentYOffset, sliderTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, widgetTable.usedecimals) + amountLineWidgetAdded = amountLineWidgetAdded + 1 --color - elseif (widgetTable.type == "color" or widgetTable.type == "color") then + elseif (widgetTable.type == "color") then local colorpick = getMenuWidgetVolative(parent, "color", widgetIndexes) widgetCreated = colorpick - colorpick.color_callback = widgetTable.set --callback - colorpick:SetTemplate(buttonTemplate) - colorpick:SetSize(18, 18) - local descPhrase = getDescPhraseText(languageTable, widgetTable) colorpick:SetTooltip(descPhrase) - colorpick._get = widgetTable.get - colorpick.widget_type = "color" - - local default_value, g, b, a = widgetTable.get() - if (type(default_value) == "table") then - colorpick:SetColor(unpack(default_value)) - else - colorpick:SetColor(default_value, g, b, a) - end - - if (valueChangeHook) then - colorpick:SetHook("OnColorChanged", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - colorpick:SetHook(hookName, hookFunc) - end - end local label = colorpick.hasLabel - local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) label.text = namePhrase label:SetTemplate(widgetTable.text_template or textTemplate) - label:ClearAllPoints() - colorpick:ClearAllPoints() - - if (bAlignAsPairs) then - label:SetPoint(currentXOffset, currentYOffset) - colorpick:SetPoint("left", label, "left", nAlignAsPairsLength, 0) - else - if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then - label:SetPoint("left", colorpick, "right", 2, 0) - colorpick:SetPoint(currentXOffset, currentYOffset) - extraPaddingY = 1 - else - colorpick:SetPoint("left", label, "right", 2, 0) - label:SetPoint(currentXOffset, currentYOffset) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = colorpick - end - - local widgetTotalSize = label:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + maxColumnWidth, maxWidgetWidth, extraPaddingY = setColorProperties(parent, colorpick, widgetTable, currentXOffset, currentYOffset, switchTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, bUseBoxFirstOnAllWidgets, extraPaddingY) + amountLineWidgetAdded = amountLineWidgetAdded + 1 --button elseif (widgetTable.type == "execute" or widgetTable.type == "button") then local button = getMenuWidgetVolative(parent, "button", widgetIndexes) + button.widget_type = "execute" widgetCreated = button - button:SetTemplate(buttonTemplate) - button:SetSize(widgetWidth or widgetTable.width or 120, widgetHeight or widgetTable.height or 18) - button:SetClickFunction(widgetTable.func, widgetTable.param1, widgetTable.param2) - - local textTemplate = widgetTable.text_template or textTemplate or detailsFramework.font_templates["ORANGE_FONT_TEMPLATE"] - button.textcolor = textTemplate.color - button.textfont = textTemplate.font - button.textsize = textTemplate.size + maxColumnWidth, maxWidgetWidth, latestInlineWidget = setExecuteProperties(parent, button, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate, latestInlineWidget) local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) button.text = namePhrase - button:ClearAllPoints() - - if (bAlignAsPairs) then - button:SetPoint(currentXOffset, currentYOffset) - else - if (widgetTable.inline) then - if (latestInlineWidget) then - button:SetPoint("left", latestInlineWidget, "right", 2, 0) - latestInlineWidget = button - else - button:SetPoint(currentXOffset, currentYOffset) - latestInlineWidget = button - end - else - button:SetPoint(currentXOffset, currentYOffset) - end - end - local descPhrase = getDescPhraseText(languageTable, widgetTable) button:SetTooltip(descPhrase) - button.widget_type = "execute" - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - button:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.width) then - button:SetWidth(widgetTable.width) - end - if (widgetTable.height) then - button:SetHeight(widgetTable.height) - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = button - end - - local widgetTotalSize = button:GetWidth() + 4 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + amountLineWidgetAdded = amountLineWidgetAdded + 1 --textentry elseif (widgetTable.type == "textentry") then local textentry = getMenuWidgetVolative(parent, "textentry", widgetIndexes) widgetCreated = textentry - textentry:SetCommitFunction(widgetTable.func or widgetTable.set) - textentry:SetTemplate(widgetTable.template or widgetTable.button_template or buttonTemplate) - textentry:SetSize(widgetWidth or widgetTable.width or 120, widgetHeight or widgetTable.height or 18) - local descPhrase = getDescPhraseText(languageTable, widgetTable) textentry:SetTooltip(descPhrase) - textentry.text = widgetTable.get() - textentry._get = widgetTable.get - textentry.widget_type = "textentry" - - textentry:SetHook("OnEnterPressed", function(...) - local upFunc = widgetTable.func or widgetTable.set - upFunc(...) - if (valueChangeHook) then - valueChangeHook() - end - end) - textentry:SetHook("OnEditFocusLost", function(...) - local upFunc = widgetTable.func or widgetTable.set - upFunc(...) - if (valueChangeHook) then - valueChangeHook() - end - end) local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) textentry.hasLabel.text = namePhrase textentry.hasLabel:SetTemplate(widgetTable.text_template or textTemplate) - textentry.hasLabel:ClearAllPoints() - textentry:ClearAllPoints() - - if (bAlignAsPairs) then - textentry.hasLabel:SetPoint(currentXOffset, currentYOffset) - textentry:SetPoint("left", textentry.hasLabel, "left", nAlignAsPairsLength, 0) - else - textentry:SetPoint("left", textentry.hasLabel, "right", 2) - textentry.hasLabel:SetPoint(currentXOffset, currentYOffset) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - textentry:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = textentry - end - - local widgetTotalSize = textentry.hasLabel:GetStringWidth() + 64 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - + maxColumnWidth, maxWidgetWidth = setTextEntryProperties(parent, textentry, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate) + amountLineWidgetAdded = amountLineWidgetAdded + 1 end --end loop if (widgetTable.nocombat) then @@ -847,10 +1081,28 @@ function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffse currentYOffset = currentYOffset - extraPaddingY end - if (widgetTable.type == "breakline" or currentYOffset < height) then - currentYOffset = yOffset - currentXOffset = currentXOffset + maxColumnWidth + 20 - maxColumnWidth = 0 + if (bUseScrollFrame) then + if (widgetTable.type == "breakline") then + biggestColumnHeight = math.min(currentYOffset, biggestColumnHeight) + currentYOffset = yOffset + + if (bAlignAsPairs) then + currentXOffset = currentXOffset + nAlignAsPairsLength + (widgetWidth or maxWidgetWidth) + nAlignAsPairsSpacing + else + currentXOffset = currentXOffset + maxColumnWidth + 20 + end + + amountLineWidgetAdded = 0 + maxColumnWidth = 0 + maxWidgetWidth = 0 + end + else + if (widgetTable.type == "breakline" or currentYOffset < height) then + currentYOffset = yOffset + currentXOffset = currentXOffset + maxColumnWidth + 20 + amountLineWidgetAdded = 0 + maxColumnWidth = 0 + end end if widgetCreated then @@ -880,668 +1132,348 @@ local getDescripttionPhraseID = function(widgetTable, languageAddonId, languageT return widgetTable.desc end - ---classes used by the menu builder on the menuOptions table on both functions BuildMenu and BuildMenuVolatile - ---the menuOptions consists of a table with several tables inside in array, each table is a widget to be created - ---class df_menu_label is used when the sub table of menuOptions has a key named "type" with the value "label" or "text" - function detailsFramework:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) - --how many widgets has been created on this line loop pass - local amountLineWidgetCreated = 0 - local latestInlineWidget - - --normalize format types - for index, widgetTable in ipairs(menuOptions) do - if (widgetTable.type == "space") then - widgetTable.type = "blank" - - elseif (widgetTable.type == "fontdropdown") then - widgetTable.type = "selectfont" - elseif (widgetTable.type == "colordropdown") then - widgetTable.type = "selectcolor" - elseif (widgetTable.type == "outlinedropdown") then - widgetTable.type = "selectoutline" - elseif (widgetTable.type == "anchordropdown") then - widgetTable.type = "selectanchor" - elseif (widgetTable.type == "dropdown") then - widgetTable.type = "select" - - elseif (widgetTable.type == "switch") then - widgetTable.type = "toggle" - - elseif (widgetTable.type == "slider") then - widgetTable.type = "range" - - elseif (widgetTable.type == "button") then - widgetTable.type = "execute" - end - end - - --catch some options added in the hash part of the menu table - local bUseBoxFirstOnAllWidgets = menuOptions.always_boxfirst - local widgetWidth = menuOptions.widget_width --a width to be used on all widgets - local widgetHeight = menuOptions.widget_height --a height to be used on all widgets - local bAlignAsPairs = menuOptions.align_as_pairs - local nAlignAsPairsLength = menuOptions.align_as_pairs_string_space or 160 - local nAlignAsPairsSpacing = menuOptions.align_as_pairs_spacing or 20 - - --if a scrollbox is passed, the height can be ignored - --the scrollBox child will be used as the parent, and the height of the child will be resized to fit the widgets - local bUseScrollFrame = menuOptions.use_scrollframe - local biggestColumnHeight = 0 --used to resize the scrollbox child when a scrollbox is passed - - if (not bUseScrollFrame) then - if (height and type(height) == "number") then - height = math.abs((height or parent:GetHeight()) - math.abs(yOffset) + 20) - height = height * -1 - else - height = parent:GetHeight() - end - else - local width, height = parent:GetSize() - parent = parent:GetScrollChild() - parent:SetSize(width, height) - end - - local languageAddonId = menuOptions.language_addonId - local languageTable - - if (languageAddonId) then - languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) - end - - if (not parent.widget_list) then - detailsFramework:SetAsOptionsPanel(parent) - end - - local currentXOffset = xOffset or 0 - local currentYOffset = yOffset or 0 - local maxColumnWidth = 0 --biggest width of widget + text size on the current column loop pass - local maxWidgetWidth = 0 --biggest widget width on the current column loop pass - local maxWidth = parent:GetWidth() --total width the buildmenu can use - not in use - - for index, widgetTable in ipairs(menuOptions) do - if (not widgetTable.hidden) then - local widgetCreated - if (latestInlineWidget) then - if (not widgetTable.inline) then - latestInlineWidget = nil - currentYOffset = currentYOffset - 28 - end - end +---classes used by the menu builder on the menuOptions table on both functions BuildMenu and BuildMenuVolatile +---the menuOptions consists of a table with several tables inside in array, each table is a widget to be created +---class df_menu_label is used when the sub table of menuOptions has a key named "type" with the value "label" or "text" +function detailsFramework:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) + --how many widgets has been created on this line loop pass + local amountLineWidgetAdded = 0 + local biggestColumnHeight = 0 --used to resize the scrollbox child when a scrollbox is passed + local latestInlineWidget + local currentXOffset = xOffset or 0 + local currentYOffset = yOffset or 0 + local maxColumnWidth = 0 --biggest width of widget + text size on the current column loop pass + local maxWidgetWidth = 0 --biggest widget width on the current column loop pass - local extraPaddingY = 0 + --parse settings and the options table + parseOptionsTypes(menuOptions) + local bUseBoxFirstOnAllWidgets, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, nAlignAsPairsSpacing, bUseScrollFrame, languageAddonId = parseOptionsTable(menuOptions) + parent, height = parseParent(bUseScrollFrame, parent, height, yOffset) + local languageTable = parseLanguageTable(languageAddonId) - if (widgetTable.type == "blank") then - --do nothing + if (not parent.widget_list) then + detailsFramework:SetAsOptionsPanel(parent) + end - elseif (widgetTable.type == "label" or widgetTable.type == "text") then - ---@cast widgetTable df_menu_label + for index, widgetTable in ipairs(menuOptions) do + if (not widgetTable.hidden) then + local widgetCreated + if (latestInlineWidget) then + if (not widgetTable.inline) then + latestInlineWidget = nil + currentYOffset = currentYOffset - 28 + end + end - local label = detailsFramework:CreateLabel(parent, "", widgetTable.text_template or textTemplate or widgetTable.size, widgetTable.color, widgetTable.font, nil, "$parentWidget" .. index, "overlay") - label._get = widgetTable.get - label.widget_type = "label" - label:SetPoint(currentXOffset, currentYOffset) + local extraPaddingY = 0 - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) - if (namePhraseId) then - DetailsFramework.Language.RegisterObject(languageAddonId, label.widget, namePhraseId) - label.languageAddonId = languageAddonId - else - local textToSet = (widgetTable.get and widgetTable.get()) or widgetTable.text or "" - label:SetText(textToSet) - end + if (widgetTable.type == "blank") then + --do nothing - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, label) - table.insert(parent.widget_list_by_type.label, label) + elseif (widgetTable.type == "label" or widgetTable.type == "text") then + ---@cast widgetTable df_menu_label - amountLineWidgetCreated = amountLineWidgetCreated + 1 + local label = detailsFramework:CreateLabel(parent, "", widgetTable.text_template or textTemplate or widgetTable.size, widgetTable.color, widgetTable.font, nil, "$parentWidget" .. index, "overlay") + setLabelProperties(parent, label, widgetTable, currentXOffset, currentYOffset) - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = label - end + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable) + if (namePhraseId) then + DetailsFramework.Language.RegisterObject(languageAddonId, label.widget, namePhraseId) + label.languageAddonId = languageAddonId + else + local textToSet = (widgetTable.get and widgetTable.get()) or widgetTable.text or "" + label:SetText(textToSet) + end - elseif (widgetTable.type:find("select")) then - ---@cast widgetTable df_menu_dropdown + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, label) + table.insert(parent.widget_list_by_type.label, label) - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") + amountLineWidgetAdded = amountLineWidgetAdded + 1 - local defaultHeight = 18 + elseif (widgetTable.type:find("select")) then + ---@cast widgetTable df_menu_dropdown + assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") + local defaultHeight = 18 - local dropdown - if (widgetTable.type == "selectfont") then - dropdown = detailsFramework:CreateFontDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) + local dropdown + do + if (widgetTable.type == "selectfont") then + dropdown = detailsFramework:CreateFontDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) - elseif (widgetTable.type == "selectcolor") then + elseif (widgetTable.type == "selectcolor") then dropdown = detailsFramework:CreateColorDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) - elseif (widgetTable.type == "selectanchor") then - dropdown = detailsFramework:CreateAnchorPointDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) - - elseif (widgetTable.type == "selectoutline") then - dropdown = detailsFramework:CreateOutlineDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) - else - dropdown = detailsFramework:NewDropDown(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 140, widgetHeight or defaultHeight, widgetTable.values, widgetTable.get(), dropdownTemplate) - end - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", descPhraseId, widgetTable.desc) - - dropdown._get = widgetTable.get - dropdown.widget_type = "select" - - local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - - dropdown.addonId = languageAddonId - if (languageAddonId) then - detailsFramework.Language.RegisterCallback(languageAddonId, function(addonId, languageId, ...) dropdown:Select(dropdown:GetValue()) end) - C_Timer.After(0.1, function() dropdown:Select(dropdown:GetValue()) end) - end + elseif (widgetTable.type == "selectanchor") then + dropdown = detailsFramework:CreateAnchorPointDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) - if (bAlignAsPairs) then - PixelUtil.SetPoint(label.widget, "topleft", dropdown:GetParent(), "topleft", currentXOffset, currentYOffset) - PixelUtil.SetPoint(dropdown.widget, "left", label.widget, "left", nAlignAsPairsLength, 0) - createOptionHighlightTexture(dropdown, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) + elseif (widgetTable.type == "selectoutline") then + dropdown = detailsFramework:CreateOutlineDropDown(parent, widgetTable.set, widgetTable.get(), widgetWidth or 140, widgetHeight or defaultHeight, nil, "$parentWidget" .. index, dropdownTemplate) else - dropdown:SetPoint("left", label, "right", 2, 0) - label:SetPoint(currentXOffset, currentYOffset) - end - - dropdown.hasLabel = label - - --global callback - if (valueChangeHook) then - dropdown:SetHook("OnOptionSelected", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - dropdown:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = dropdown - end - - local widgetTotalSize = label.widget:GetStringWidth() + 144 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - if (dropdown:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = dropdown:GetWidth() + dropdown = detailsFramework:NewDropDown(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 140, widgetHeight or defaultHeight, widgetTable.values, widgetTable.get(), dropdownTemplate) end + end - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, dropdown) - table.insert(parent.widget_list_by_type.dropdown, dropdown) - - widgetCreated = dropdown - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "toggle") then - ---@cast widgetTable df_menu_toggle - - local switch = detailsFramework:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate) - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, dropdown, "have_tooltip", descPhraseId, widgetTable.desc) + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + dropdown.hasLabel = label - switch._get = widgetTable.get - switch.widget_type = "toggle" - switch.OnSwitch = widgetTable.set + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + dropdown.addonId = languageAddonId - if (switchIsCheckbox) then - switch:SetAsCheckBox() - end + if (languageAddonId) then + detailsFramework.Language.RegisterCallback(languageAddonId, function(addonId, languageId, ...) dropdown:Select(dropdown:GetValue()) end) + C_Timer.After(0.1, function() dropdown:Select(dropdown:GetValue()) end) + end - if (valueChangeHook) then - switch:SetHook("OnSwitch", valueChangeHook) - end + maxColumnWidth, maxWidgetWidth = setDropdownProperties(parent, dropdown, widgetTable, currentXOffset, currentYOffset, textTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth) - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - switch:SetHook(hookName, hookFunc) - end - end + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, dropdown) + table.insert(parent.widget_list_by_type.dropdown, dropdown) - if (widgetTable.width) then - PixelUtil.SetWidth(switch.widget, widgetTable.width) - end - if (widgetTable.height) then - PixelUtil.SetHeight(switch.widget, widgetTable.height) - end + widgetCreated = dropdown + amountLineWidgetAdded = amountLineWidgetAdded + 1 - local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + elseif (widgetTable.type == "toggle") then + ---@cast widgetTable df_menu_toggle - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + local switch = detailsFramework:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate) - if (bAlignAsPairs) then - PixelUtil.SetPoint(label.widget, "topleft", switch:GetParent(), "topleft", currentXOffset, currentYOffset) - PixelUtil.SetPoint(switch.widget, "left", label.widget, "left", nAlignAsPairsLength, 0) - createOptionHighlightTexture(switch, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) - else - if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then - switch:SetPoint(currentXOffset, currentYOffset) - label:SetPoint("left", switch, "right", 2) - - local nextWidgetTable = menuOptions[index+1] - if (nextWidgetTable) then - if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then - extraPaddingY = 4 - end - end - else - label:SetPoint(currentXOffset, currentYOffset) - switch:SetPoint("left", label, "right", 2, 0) - end - end - switch.hasLabel = label + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", descPhraseId, widgetTable.desc) - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = switch - end + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + switch.hasLabel = label - local widgetTotalSize = label.widget:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - if (switch:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = switch:GetWidth() - end + maxColumnWidth, maxWidgetWidth, extraPaddingY = setToggleProperties(parent, switch, widgetTable, currentXOffset, currentYOffset, switchTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, switchIsCheckbox, bUseBoxFirstOnAllWidgets, menuOptions, index, maxWidgetWidth) - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, switch) - table.insert(parent.widget_list_by_type.switch, switch) + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, switch) + table.insert(parent.widget_list_by_type.switch, switch) - widgetCreated = switch - amountLineWidgetCreated = amountLineWidgetCreated + 1 + widgetCreated = switch + amountLineWidgetAdded = amountLineWidgetAdded + 1 - elseif (widgetTable.type == "range") then - ---@cast widgetTable df_menu_range + elseif (widgetTable.type == "range") then + ---@cast widgetTable df_menu_range - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'range'") - local bIsDecimals = widgetTable.usedecimals - local slider = detailsFramework:NewSlider(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 140, widgetHeight or 18, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), bIsDecimals, nil, nil, sliderTemplate) + assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'range'") + local bIsDecimals = widgetTable.usedecimals + local slider = detailsFramework:NewSlider(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 140, widgetHeight or 18, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), bIsDecimals, nil, nil, sliderTemplate) - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", descPhraseId, widgetTable.desc) + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, slider, "have_tooltip", descPhraseId, widgetTable.desc) - slider._get = widgetTable.get - slider.widget_type = "range" - slider:SetHook("OnValueChange", widgetTable.set) + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + slider.hasLabel = label - if (widgetTable.thumbscale) then - slider:SetThumbSize(slider.thumb:GetWidth() * widgetTable.thumbscale, nil) - else - slider:SetThumbSize(slider.thumb:GetWidth() * 1.3, nil) - end + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - if (valueChangeHook) then - slider:SetHook("OnValueChange", valueChangeHook) - end + maxColumnWidth, maxWidgetWidth = setRangeProperties(parent, slider, widgetTable, currentXOffset, currentYOffset, sliderTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, bIsDecimals) - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - slider:SetHook(hookName, hookFunc) - end - end + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, slider) + table.insert(parent.widget_list_by_type.slider, slider) - local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + widgetCreated = slider + amountLineWidgetAdded = amountLineWidgetAdded + 1 - if (bAlignAsPairs) then - PixelUtil.SetPoint(label.widget, "topleft", slider:GetParent(), "topleft", currentXOffset, currentYOffset) - PixelUtil.SetPoint(slider.widget, "left", label.widget, "left", nAlignAsPairsLength, 0) - createOptionHighlightTexture(slider, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) - else - slider:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - end - slider.hasLabel = label + elseif (widgetTable.type == "color") then + ---@cast widgetTable df_menu_color + assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'") + local colorpick = detailsFramework:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate) - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = slider - end + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", descPhraseId, widgetTable.desc) - local widgetTotalSize = label.widget:GetStringWidth() + 146 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + colorpick.hasLabel = label - if (slider:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = slider:GetWidth() - end + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, slider) - table.insert(parent.widget_list_by_type.slider, slider) + maxColumnWidth, maxWidgetWidth, extraPaddingY = setColorProperties(parent, colorpick, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, bUseBoxFirstOnAllWidgets, extraPaddingY) - widgetCreated = slider - amountLineWidgetCreated = amountLineWidgetCreated + 1 + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, colorpick) + table.insert(parent.widget_list_by_type.color, colorpick) - elseif (widgetTable.type == "color") then - ---@cast widgetTable df_menu_color - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'") - local colorpick = detailsFramework:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate) + widgetCreated = colorpick + amountLineWidgetAdded = amountLineWidgetAdded + 1 - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", descPhraseId, widgetTable.desc) + elseif (widgetTable.type == "execute") then + ---@cast widgetTable df_menu_button + local button = detailsFramework:NewButton(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 120, widgetHeight or 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate) + button.widget_type = "execute" - colorpick._get = widgetTable.get - colorpick.widget_type = "color" - colorpick:SetSize(18, 18) + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + button.hasLabel = label - local r, g, b, a = detailsFramework:ParseColors(widgetTable.get()) - colorpick:SetColor(r, g, b, a) + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, namePhraseId, widgetTable.name) - if (valueChangeHook) then - colorpick:SetHook("OnColorChanged", valueChangeHook) - end + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", descPhraseId, widgetTable.desc) - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - colorpick:SetHook(hookName, hookFunc) - end - end + maxColumnWidth, maxWidgetWidth, latestInlineWidget = setExecuteProperties(parent, button, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate, latestInlineWidget) - local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, button) + table.insert(parent.widget_list_by_type.button, button) - if (bAlignAsPairs) then - label:SetPoint(currentXOffset, currentYOffset) - colorpick:SetPoint("left", label, "left", nAlignAsPairsLength, 0) - createOptionHighlightTexture(colorpick, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) - else - if (widgetTable.boxfirst or bUseBoxFirstOnAllWidgets) then - label:SetPoint("left", colorpick, "right", 2) - colorpick:SetPoint(currentXOffset, currentYOffset) - extraPaddingY = 1 - else - colorpick:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - end - end + widgetCreated = button + amountLineWidgetAdded = amountLineWidgetAdded + 1 - colorpick.hasLabel = label + elseif (widgetTable.type == "textentry") then + ---@cast widgetTable df_menu_textentry + local textentry = detailsFramework:CreateTextEntry(parent, widgetTable.func or widgetTable.set, widgetWidth or 120, widgetHeight or 18, nil, "$parentWidget" .. index, nil, buttonTemplate) + local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) + textentry.hasLabel = label + textentry.align = widgetTable.align or "left" - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = colorpick - end + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", descPhraseId, widgetTable.desc) - local widgetTotalSize = label.widget:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - if (colorpick:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = colorpick:GetWidth() - end + maxColumnWidth, maxWidgetWidth = setTextEntryProperties(parent, textentry, widgetTable, currentXOffset, currentYOffset, buttonTemplate, widgetWidth, widgetHeight, bAlignAsPairs, nAlignAsPairsLength, valueChangeHook, maxColumnWidth, maxWidgetWidth, textTemplate) - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, colorpick) - table.insert(parent.widget_list_by_type.color, colorpick) + --store the widget created into the overall table and the widget by type + table.insert(parent.widget_list, textentry) + table.insert(parent.widget_list_by_type.textentry, textentry) - widgetCreated = colorpick - amountLineWidgetCreated = amountLineWidgetCreated + 1 + widgetCreated = textentry + amountLineWidgetAdded = amountLineWidgetAdded + 1 + end - elseif (widgetTable.type == "execute") then - ---@cast widgetTable df_menu_button + if (widgetTable.nocombat) then + table.insert(widgetsToDisableOnCombat, widgetCreated) + end - local button = detailsFramework:NewButton(parent, nil, "$parentWidget" .. index, nil, widgetWidth or 120, widgetHeight or 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate) + if (not widgetTable.inline) then + if (widgetTable.spacement) then + currentYOffset = currentYOffset - 30 + else + currentYOffset = currentYOffset - 20 + end + end - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, namePhraseId, widgetTable.name) + if (extraPaddingY > 0) then + currentYOffset = currentYOffset - extraPaddingY + end - if (not buttonTemplate) then - button:InstallCustomTexture() - end + if (bUseScrollFrame) then + if (widgetTable.type == "breakline") then + biggestColumnHeight = math.min(currentYOffset, biggestColumnHeight) + currentYOffset = yOffset - if (widgetTable.inline) then - if (latestInlineWidget) then - button:SetPoint("left", latestInlineWidget, "right", 2, 0) - latestInlineWidget = button - else - button:SetPoint(currentXOffset, currentYOffset) - latestInlineWidget = button - end + if (bAlignAsPairs) then + currentXOffset = currentXOffset + nAlignAsPairsLength + (widgetWidth or maxWidgetWidth) + nAlignAsPairsSpacing else - button:SetPoint(currentXOffset, currentYOffset) - end - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, button, "have_tooltip", descPhraseId, widgetTable.desc) - - button.widget_type = "execute" - - --button icon - if (widgetTable.icontexture) then - button:SetIcon(widgetTable.icontexture, nil, nil, nil, widgetTable.icontexcoords, nil, nil, 2) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - button:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = button - end - - if (widgetTable.width and not widgetWidth) then - button:SetWidth(widgetTable.width) - end - if (widgetTable.height and not widgetHeight) then - button:SetHeight(widgetTable.height) - end - - local widgetTotalSize = button:GetWidth() + 4 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - if (button:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = button:GetWidth() + currentXOffset = currentXOffset + maxColumnWidth + 20 end - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, button) - table.insert(parent.widget_list_by_type.button, button) - - widgetCreated = button - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "textentry") then - ---@cast widgetTable df_menu_textentry - - local textentry = detailsFramework:CreateTextEntry(parent, widgetTable.func or widgetTable.set, widgetWidth or 120, widgetHeight or 18, nil, "$parentWidget" .. index, nil, buttonTemplate) - textentry.align = widgetTable.align or "left" - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, textentry, "have_tooltip", descPhraseId, widgetTable.desc) - - textentry.text = widgetTable.get() - textentry._get = widgetTable.get - textentry.widget_type = "textentry" - textentry:SetHook("OnEnterPressed", widgetTable.func or widgetTable.set) - textentry:SetHook("OnEditFocusLost", widgetTable.func or widgetTable.set) - - local label = detailsFramework:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) + amountLineWidgetAdded = 0 + maxColumnWidth = 0 + maxWidgetWidth = 0 + end + else + if (widgetTable.type == "breakline" or currentYOffset < height) then + currentYOffset = yOffset + currentXOffset = currentXOffset + maxColumnWidth + 20 + amountLineWidgetAdded = 0 + maxColumnWidth = 0 + end + end + end --no widget.hidden + end --end loop - if (bAlignAsPairs) then - label:SetPoint(currentXOffset, currentYOffset) - textentry:SetPoint("left", label, "left", nAlignAsPairsLength, 0) - createOptionHighlightTexture(textentry, label, (widgetWidth or 140) + nAlignAsPairsLength + 5) - else - textentry:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - end + if (bUseScrollFrame) then + parent:SetHeight(biggestColumnHeight * -1) + end - textentry.hasLabel = label + detailsFramework.RefreshUnsafeOptionsWidgets() +end - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - textentry:SetHook(hookName, hookFunc) - end - end - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = textentry - end +local lockNotSafeWidgetsForCombat = function() + for _, widget in ipairs(widgetsToDisableOnCombat) do + widget:Disable() + end +end - local widgetTotalSize = label.widget:GetStringWidth() + 64 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end +local unlockNotSafeWidgetsForCombat = function() + for _, widget in ipairs(widgetsToDisableOnCombat) do + widget:Enable() + end +end - if (textentry:GetWidth() > maxWidgetWidth) then - maxWidgetWidth = textentry:GetWidth() - end +function detailsFramework.RefreshUnsafeOptionsWidgets() + if (detailsFramework.PlayerHasCombatFlag) then + lockNotSafeWidgetsForCombat() + else + unlockNotSafeWidgetsForCombat() + end +end - --store the widget created into the overall table and the widget by type - table.insert(parent.widget_list, textentry) - table.insert(parent.widget_list_by_type.textentry, textentry) +detailsFramework.PlayerHasCombatFlag = false +local ProtectCombatFrame = CreateFrame("frame") +ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_ENABLED") +ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_DISABLED") +ProtectCombatFrame:RegisterEvent("PLAYER_ENTERING_WORLD") +ProtectCombatFrame:SetScript("OnEvent", function(self, event) + if (event == "PLAYER_ENTERING_WORLD") then + if (InCombatLockdown()) then + detailsFramework.PlayerHasCombatFlag = true + else + detailsFramework.PlayerHasCombatFlag = false + end + detailsFramework.RefreshUnsafeOptionsWidgets() - widgetCreated = textentry - amountLineWidgetCreated = amountLineWidgetCreated + 1 - end + elseif (event == "PLAYER_REGEN_ENABLED") then + detailsFramework.PlayerHasCombatFlag = false + detailsFramework.RefreshUnsafeOptionsWidgets() - if (widgetTable.nocombat) then - table.insert(widgetsToDisableOnCombat, widgetCreated) - end + elseif (event == "PLAYER_REGEN_DISABLED") then + detailsFramework.PlayerHasCombatFlag = true + detailsFramework.RefreshUnsafeOptionsWidgets() + end +end) - if (not widgetTable.inline) then - if (widgetTable.spacement) then - currentYOffset = currentYOffset - 30 - else - currentYOffset = currentYOffset - 20 - end - end +function detailsFramework:CreateInCombatTexture(frame) + if (detailsFramework.debug and not frame) then + error("Details! Framework: CreateInCombatTexture invalid frame on parameter 1.") + end - if (extraPaddingY > 0) then - currentYOffset = currentYOffset - extraPaddingY - end + local inCombatBackgroundTexture = detailsFramework:CreateImage(frame) + inCombatBackgroundTexture:SetColorTexture(.6, 0, 0, .1) + inCombatBackgroundTexture:Hide() - if (bUseScrollFrame) then - if (widgetTable.type == "breakline") then - biggestColumnHeight = math.min(currentYOffset, biggestColumnHeight) - currentYOffset = yOffset + local inCombatLabel = detailsFramework:CreateLabel(frame, "you are in combat", 24, "silver") + inCombatLabel:SetPoint("right", inCombatBackgroundTexture, "right", -10, 0) + inCombatLabel:Hide() - if (bAlignAsPairs) then - currentXOffset = currentXOffset + nAlignAsPairsLength + (widgetWidth or maxWidgetWidth) + nAlignAsPairsSpacing - else - currentXOffset = currentXOffset + maxColumnWidth + 20 - end + frame:RegisterEvent("PLAYER_REGEN_DISABLED") + frame:RegisterEvent("PLAYER_REGEN_ENABLED") - amountLineWidgetCreated = 0 - maxColumnWidth = 0 - maxWidgetWidth = 0 - end - else - if (widgetTable.type == "breakline" or currentYOffset < height) then - currentYOffset = yOffset - currentXOffset = currentXOffset + maxColumnWidth + 20 - amountLineWidgetCreated = 0 - maxColumnWidth = 0 - end - end - end - end + frame:SetScript("OnEvent", function(self, event) + if (event == "PLAYER_REGEN_DISABLED") then + inCombatBackgroundTexture:Show() + inCombatLabel:Show() - if (bUseScrollFrame) then - parent:SetHeight(biggestColumnHeight * -1) + elseif (event == "PLAYER_REGEN_ENABLED") then + inCombatBackgroundTexture:Hide() + inCombatLabel:Hide() end + end) - detailsFramework.RefreshUnsafeOptionsWidgets() - end - - - local lockNotSafeWidgetsForCombat = function() - for _, widget in ipairs(widgetsToDisableOnCombat) do - widget:Disable() - end - end - - local unlockNotSafeWidgetsForCombat = function() - for _, widget in ipairs(widgetsToDisableOnCombat) do - widget:Enable() - end - end - - function detailsFramework.RefreshUnsafeOptionsWidgets() - if (detailsFramework.PlayerHasCombatFlag) then - lockNotSafeWidgetsForCombat() - else - unlockNotSafeWidgetsForCombat() - end - end - - detailsFramework.PlayerHasCombatFlag = false - local ProtectCombatFrame = CreateFrame("frame") - ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_ENABLED") - ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_DISABLED") - ProtectCombatFrame:RegisterEvent("PLAYER_ENTERING_WORLD") - ProtectCombatFrame:SetScript("OnEvent", function(self, event) - if (event == "PLAYER_ENTERING_WORLD") then - if (InCombatLockdown()) then - detailsFramework.PlayerHasCombatFlag = true - else - detailsFramework.PlayerHasCombatFlag = false - end - detailsFramework.RefreshUnsafeOptionsWidgets() - - elseif (event == "PLAYER_REGEN_ENABLED") then - detailsFramework.PlayerHasCombatFlag = false - detailsFramework.RefreshUnsafeOptionsWidgets() - - elseif (event == "PLAYER_REGEN_DISABLED") then - detailsFramework.PlayerHasCombatFlag = true - detailsFramework.RefreshUnsafeOptionsWidgets() - end - end) - - function detailsFramework:CreateInCombatTexture(frame) - if (detailsFramework.debug and not frame) then - error("Details! Framework: CreateInCombatTexture invalid frame on parameter 1.") - end - - local inCombatBackgroundTexture = detailsFramework:CreateImage(frame) - inCombatBackgroundTexture:SetColorTexture(.6, 0, 0, .1) - inCombatBackgroundTexture:Hide() - - local inCombatLabel = detailsFramework:CreateLabel(frame, "you are in combat", 24, "silver") - inCombatLabel:SetPoint("right", inCombatBackgroundTexture, "right", -10, 0) - inCombatLabel:Hide() - - frame:RegisterEvent("PLAYER_REGEN_DISABLED") - frame:RegisterEvent("PLAYER_REGEN_ENABLED") - - frame:SetScript("OnEvent", function(self, event) - if (event == "PLAYER_REGEN_DISABLED") then - inCombatBackgroundTexture:Show() - inCombatLabel:Show() - - elseif (event == "PLAYER_REGEN_ENABLED") then - inCombatBackgroundTexture:Hide() - inCombatLabel:Hide() - end - end) - - return inCombatBackgroundTexture - end \ No newline at end of file + return inCombatBackgroundTexture +end \ No newline at end of file diff --git a/Libs/DF/definitions.lua b/Libs/DF/definitions.lua index b31d5b33b..c20bd29d9 100644 --- a/Libs/DF/definitions.lua +++ b/Libs/DF/definitions.lua @@ -45,6 +45,7 @@ ---@class detailsframework ---@field dversion number +---@field internalFunctions table ---@field OptionsFunctions df_optionsmixin ---@field GlobalWidgetControlNames table ---@field RoundedCornerPanelMixin df_roundedcornermixin @@ -56,7 +57,7 @@ ---@field EditorMixin df_editormixin ---@field ClassCache {ID:number, Name:string, FileString:string, Texture:string, TexCoord:number[]}[] only available after calling GetClassList() ---@field Math df_math ----@field FontOutlineFlags {key1:outline, key2:string}[] +---@field FontOutlineFlags table ---@field table df_table_functions ---@field AnchorPoints string[] ---@field ClassFileNameToIndex table engClass -> classIndex @@ -102,7 +103,7 @@ ---@field CommaValue fun(self:table, value:number) : string convert a number to a string with commas, e.g. 1000000 -> 1,000,000 ---@field SplitTextInLines fun(self:table, text:string) : string[] split a text into lines ---@field UnitGroupRolesAssigned fun(unitId: unit, bUseSupport:boolean, specId: specializationid) : string there's no self here ----@field SetAnchor fun(self:table, widget:uiobject, anchorTable:df_anchor, anchorTo:uiobject) +---@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(text:string, textureInfo:table, bAddSpace:boolean?, bAddAfterText:boolean) : string textureInfo is a table with .texture .width .height .coords{left, right, top, bottom} ---@field CreateTextureInfo fun(texture:atlasname|texturepath|textureid, width:number?, height:number?, left:number?, right:number?, top:number?, bottom:number?, imageWidthnumber?, imageHeightnumber?) : table ---@field ApplyStandardBackdrop fun(self:table, frame:frame, bUseSolidColor:boolean?, alphaScale:number?) @@ -132,8 +133,10 @@ ---@field CreateAuraScrollBox fun(self:table, parent:frame, name:string?, data:table?, onRemoveCallback:function?, options:table?) : 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 ---@field + diff --git a/Libs/DF/dropdown.lua b/Libs/DF/dropdown.lua index 699284746..cff92a679 100644 --- a/Libs/DF/dropdown.lua +++ b/Libs/DF/dropdown.lua @@ -986,16 +986,13 @@ function DetailsFrameworkDropDownOnHide(self) object:Close() end +local iconSizeTable = {16, 16} function DF:BuildDropDownFontList(onClick, icon, iconTexcoord, iconSize) local fontTable = {} - if (type(iconSize) ~= "table") then - iconSize = {iconSize or 16, iconSize or 16} - end - local SharedMedia = LibStub:GetLibrary("LibSharedMedia-3.0") for name, fontPath in pairs(SharedMedia:HashTable("font")) do - fontTable[#fontTable+1] = {value = name, label = name, onclick = onClick, icon = icon, iconsize = iconSize, texcoord = iconTexcoord, font = fontPath, descfont = "abcdefg ABCDEFG"} + fontTable[#fontTable+1] = {value = name, label = name, onclick = onClick, icon = icon, iconsize = iconSizeTable, texcoord = iconTexcoord, font = fontPath, descfont = "abcdefg ABCDEFG"} end table.sort(fontTable, function(t1, t2) return t1.label < t2.label end) @@ -1141,11 +1138,12 @@ function DF:CreateOutlineListGenerator(callback) local newGenerator = function() local dropdownOptions = {} - for i, outlineInfo in ipairs(DF.FontOutlineFlags) do - local outlineName, outlineLoc = unpack(outlineInfo) + for index, outlineInfo in pairs(DF.FontOutlineFlags) do + local outlineValue = outlineInfo[1] + local outlineName = outlineInfo[2] table.insert(dropdownOptions, { - label = outlineLoc, - value = outlineName, + label = outlineName, + value = outlineValue, onclick = callback }) end diff --git a/Libs/DF/editor.lua b/Libs/DF/editor.lua index 43f1747cd..41583f98d 100644 --- a/Libs/DF/editor.lua +++ b/Libs/DF/editor.lua @@ -14,7 +14,7 @@ local _ --[=[ file description: this file has the code for the object editor the object editor itself is a frame and has a scrollframe as canvas showing another frame where there's the options for the editing object - + --]=] @@ -22,10 +22,10 @@ local _ --the mapTable is a table with the attribute name as a key, and the value is the profile key. For example, {["size"] = "text_size"} means profileTable["text_size"] = 10. ---@class df_editor_attribute ----@field name string ----@field label string +---@field name string? +---@field label string? ---@field widget string ----@field default any +---@field default any? ---@field minvalue number? ---@field maxvalue number? ---@field step number? @@ -55,12 +55,15 @@ local attributes = { name = "font", label = "Font", widget = "fontdropdown", - setter = function(widget, value) widget:SetFont(value, select(2, widget:GetFont())) end + setter = function(widget, value) + local font = LibStub:GetLibrary("LibSharedMedia-3.0"):Fetch("font", value) + widget:SetFont(font, select(2, widget:GetFont())) + end }, { name = "color", label = "Color", - widget = "colordropdown", + widget = "color", setter = function(widget, value) widget:SetTextColor(unpack(value)) end }, { @@ -69,6 +72,7 @@ local attributes = { widget = "range", setter = function(widget, value) widget:SetAlpha(value) end }, + {widget = "blank"}, { name = "shadow", label = "Draw Shadow", @@ -78,7 +82,7 @@ local attributes = { { name = "shadowcolor", label = "Shadow Color", - widget = "colordropdown", + widget = "color", setter = function(widget, value) widget:SetShadowColor(unpack(value)) end }, { @@ -103,6 +107,7 @@ local attributes = { widget = "outlinedropdown", setter = function(widget, value) widget:SetFont(widget:GetFont(), select(2, widget:GetFont()), value) end }, + {widget = "blank"}, { name = "anchor", label = "Anchor", @@ -113,16 +118,16 @@ local attributes = { name = "anchoroffsetx", label = "Anchor X Offset", widget = "range", - minvalue = -20, - maxvalue = 20, + minvalue = -100, + maxvalue = 100, setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end }, { name = "anchoroffsety", label = "Anchor Y Offset", widget = "range", - minvalue = -20, - maxvalue = 20, + minvalue = -100, + maxvalue = 100, setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end }, { @@ -134,56 +139,59 @@ local attributes = { maxvalue = math.pi*2, setter = function(widget, value) widget:SetRotation(value) end }, + { + name = "scale", + label = "Scale", + widget = "range", + usedecimals = true, + minvalue = 0.65, + maxvalue = 2.5, + setter = function(widget, value) widget:SetScale(value) end + }, } } -local profileTable = { - spellname_text_size = 10, - spellname_text_font = "Arial Narrow", - spellname_text_color = {1, 1, 1, 1}, - spellname_text_outline = "NONE", - spellname_text_shadow_color = {0, 0, 0, 1}, - spellname_text_shadow_color_offset = {1, -1}, - spellname_text_anchor = {side = 9, x = 0, y = 0}, -} - ---create a map table for the profile table -local mapTable = { - text = "text test", - size = "spellname_text_size", - font = "spellname_text_font", - color = "spellname_text_color", - outline = "spellname_text_outline", - shadowcolor = "spellname_text_shadow_color", - shadowoffsetx = "spellname_text_shadow_color_offset[1]", - shadowoffsety = "spellname_text_shadow_color_offset[2]", - anchor = "spellname_text_anchor.side", - anchoroffsetx = "spellname_text_anchor.x", - anchoroffsety = "spellname_text_anchor.y", -} - -local table_path = { - shadowWidth = "text_settings[1].width", - shadowHeight = "text_settings[1].height", - shadowEnabled = "text_settings.settings.enabled", - text = "text_settings.settings.text.current_text", -} - -local text_settings = { - shadowWidth = {{width = 100}}, - shadowHeight = {{height = 100}}, - shadowEnabled = {settings = {enabled = true}}, - text = {settings = {text = {current_text = "hellow world"}}}, -} - ---@class df_editormixin : table +---@field GetAllRegisteredObjects fun(self:df_editor):df_editor_objectinfo[] ---@field GetEditingObject fun(self:df_editor):uiobject +---@field GetEditingOptions fun(self:df_editor):df_editobjectoptions +---@field GetExtraOptions fun(self:df_editor):table ---@field GetEditingProfile fun(self:df_editor):table, table ---@field GetOnEditCallback fun(self:df_editor):function ---@field GetOptionsFrame fun(self:df_editor):frame ---@field GetCanvasScrollBox fun(self:df_editor):df_canvasscrollbox ----@field EditObject fun(self:df_editor, object:uiobject, profileTable:table, profileKeyMap:table, callback:function) +---@field GetObjectSelector fun(self:df_editor):df_scrollbox +---@field EditObject fun(self:df_editor, object:uiobject, profileTable:table, profileKeyMap:table, extraOptions:table?, callback:function?, options:df_editobjectoptions?) ---@field PrepareObjectForEditing fun(self:df_editor) +---@field CreateMoverGuideLines fun(self:df_editor) +---@field GetOverTheTopFrame fun(self:df_editor):frame +---@field GetMoverFrame fun(self:df_editor):frame +---@field StartObjectMovement fun(self:df_editor, anchorSettings:df_anchor) +---@field StopObjectMovement fun(self:df_editor) + +---@class df_editobjectoptions : table +---@field use_colon boolean if true a colon is shown after the option name +---@field can_move boolean if true the object can be moved +---@field use_guide_lines boolean if true guide lines are shown when the object is being moved + +---@type df_editobjectoptions +local editObjectDefaultOptions = { + use_colon = true, + can_move = true, + use_guide_lines = true, +} + +local getParentTable = function(profileTable, profileKey) + local parentPath + if (profileKey:match("%]$")) then + parentPath = profileKey:gsub("%s*%[.*%]%s*$", "") + else + parentPath = profileKey:gsub("%.[^.]*$", "") + end + + local parentTable = detailsFramework.table.getfrompath(profileTable, parentPath) + return parentTable +end detailsFramework.EditorMixin = { ---@param self df_editor @@ -191,6 +199,18 @@ detailsFramework.EditorMixin = { return self.editingObject end, + ---@param self df_editor + ---@return df_editobjectoptions + GetEditingOptions = function(self) + return self.editingOptions + end, + + ---@param self df_editor + ---@return table + GetExtraOptions = function(self) + return self.editingExtraOptions + end, + ---@param self df_editor ---@return table, table GetEditingProfile = function(self) @@ -207,28 +227,117 @@ detailsFramework.EditorMixin = { return self.optionsFrame end, + GetOverTheTopFrame = function(self) + return self.overTheTopFrame + end, + + GetMoverFrame = function(self) + return self.moverFrame + end, + GetCanvasScrollBox = function(self) return self.canvasScrollBox end, - ---@param self df_editor - ---@param object uiobject - ---@param profileTable table - ---@param profileKeyMap table - ---@param callback function calls when an attribute is changed with the payload: editingObject, optionName, newValue, profileTable, profileKey - EditObject = function(self, object, profileTable, profileKeyMap, callback) - assert(type(object) == "table", "EditObject(object) expects an UIObject on first parameter.") - assert(type(profileTable) == "table", "EditObject(object) expects a table on second parameter.") - assert(object.GetObjectType, "EditObject(object) expects an UIObject on first parameter.") + GetObjectSelector = function(self) + return self.objectSelector + end, + + EditObjectById = function(self, id) + ---@type df_editor_objectinfo + local objectRegistered = self:GetObjectById(id) + assert(type(objectRegistered) == "table", "EditObjectById() object not found.") + self:EditObject(objectRegistered) + end, + + EditObjectByIndex = function(self, index) + ---@type df_editor_objectinfo + local objectRegistered = self:GetObjectByIndex(index) + assert(type(objectRegistered) == "table", "EditObjectById() object not found.") + self:EditObject(objectRegistered) + end, + ---@param self df_editor + ---@param registeredObject df_editor_objectinfo + EditObject = function(self, registeredObject) + --clear previous values + self.editingObject = nil + self.editingProfileMap = nil + self.editingProfileTable = nil + self.editingOptions = nil + self.editingExtraOptions = nil + self.onEditCallback = nil + + local object = registeredObject.object + local profileTable = registeredObject.profiletable + local profileKeyMap = registeredObject.profilekeymap + local extraOptions = registeredObject.extraoptions + local callback = registeredObject.callback + local options = registeredObject.options + + --as there's no other place which this members are set, there is no need to create setter functions self.editingObject = object self.editingProfileMap = profileKeyMap self.editingProfileTable = profileTable - self.onEditCallback = callback + self.editingOptions = options + self.editingExtraOptions = extraOptions + + if (type(callback) == "function") then + self.onEditCallback = callback + end self:PrepareObjectForEditing() end, + ---@param self df_editor + CreateMoverGuideLines = function(self) + local overTheTopFrame = self:GetOverTheTopFrame() + local moverFrame = self:GetMoverFrame() + + self.moverGuideLines = { + left = overTheTopFrame:CreateTexture(nil, "overlay"), + right = overTheTopFrame:CreateTexture(nil, "overlay"), + top = overTheTopFrame:CreateTexture(nil, "overlay"), + bottom = overTheTopFrame:CreateTexture(nil, "overlay"), + } + + for side, texture in pairs(self.moverGuideLines) do + texture:SetColorTexture(.8, .8, .8, 0.1) + texture:SetSize(1, 1) + texture:SetDrawLayer("overlay", 7) + texture:Hide() + + if (side == "left" or side == "right") then + texture:SetHeight(1) + texture:SetWidth(GetScreenWidth()) + else + texture:SetWidth(1) + texture:SetHeight(GetScreenHeight()) + end + end + end, + + UpdateGuideLinesAnchors = function(self) + local object = self:GetEditingObject() + + for side, texture in pairs(self.moverGuideLines) do + texture:ClearAllPoints() + if (side == "left" or side == "right") then + if (side == "left") then + texture:SetPoint("right", object, "left", -2, 0) + else + texture:SetPoint("left", object, "right", 2, 0) + end + else + if (side == "top") then + texture:SetPoint("bottom", object, "top", 0, 2) + else + texture:SetPoint("top", object, "bottom", 0, -2) + end + end + end + end, + PrepareObjectForEditing = function(self) --get the object and its profile table with the current values local object = self:GetEditingObject() @@ -243,56 +352,135 @@ detailsFramework.EditorMixin = { local objectType = object:GetObjectType() local attributeList + --get options and extra options + local editingOptions = self:GetEditingOptions() + local extraOptions = self:GetExtraOptions() + + --get the attribute list for the object type if (objectType == "FontString") then + ---@cast object fontstring attributeList = attributes[objectType] end + --if there's extra options, add the attributeList to a new table and right after the extra options + if (extraOptions and #extraOptions > 0) then + local attributeListWithExtraOptions = {} + + for i = 1, #attributeList do + attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = attributeList[i] + end + + attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = {widget = "blank", default = true} + + for i = 1, #extraOptions do + attributeListWithExtraOptions[#attributeListWithExtraOptions+1] = extraOptions[i] + end + + attributeList = attributeListWithExtraOptions + end + + local anchorSettings + + --table to use on DF:BuildMenu() local menuOptions = {} for i = 1, #attributeList do local option = attributeList[i] - --get the key to be used on profile table - local profileKey = profileMap[option.name] - local value - - --if the key contains a dot or a bracket, it means it's a table path, example: "text_settings[1].width" - if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then - value = detailsFramework.table.getfrompath(profileTable, profileKey) + if (option.widget == "blank") then + menuOptions[#menuOptions+1] = {type = "blank"} else - value = profileTable[profileKey] - end - - --if no value is found, attempt to get a default - value = value or option.default - - if (value) then - menuOptions[#menuOptions+1] = { - type = option.widget, - name = option.label, - get = function() return value end, - set = function(widget, fixedValue, newValue) - if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then - detailsFramework.table.setfrompath(profileTable, profileKey, value) - else - profileTable[profileKey] = value - end - - if (self:GetOnEditCallback()) then - self:GetOnEditCallback()(object, option.name, newValue, profileTable, profileKey) - end - - if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then - local anchorTable = detailsFramework.table.getfrompath(profileTable, profileKey:gsub("%.[^.]*$", "")) - option.setter(object, anchorTable) - else - option.setter(object, newValue) - end - end, - min = option.minvalue, - max = option.maxvalue, - step = option.step, - usedecimals = option.usedecimals, - } + --get the key to be used on profile table + local profileKey = profileMap[option.name] + local value + + --if the key contains a dot or a bracket, it means it's a table path, example: "text_settings[1].width" + if (profileKey and (profileKey:match("%.") or profileKey:match("%["))) then + value = detailsFramework.table.getfrompath(profileTable, profileKey) + else + value = profileTable[profileKey] + end + + --if no value is found, attempt to get a default + if (type(value) == "nil") then + value = option.default + end + + local bHasValue = type(value) ~= "nil" + + local minValue = option.minvalue + local maxValue = option.maxvalue + + if (option.name == "anchoroffsetx") then + minValue = -object:GetParent():GetWidth()/2 + maxValue = object:GetParent():GetWidth()/2 + elseif (option.name == "anchoroffsety") then + minValue = -object:GetParent():GetHeight()/2 + maxValue = object:GetParent():GetHeight()/2 + end + + if (option.name == "classcolor") then print("", value) end + + if (bHasValue) then + if (option.name == "classcolor") then print("HERE", value) end + + local parentTable = getParentTable(profileTable, profileKey) + + if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then + anchorSettings = parentTable + end + + menuOptions[#menuOptions+1] = { + type = option.widget, + name = option.label, + get = function() return value end, + set = function(widget, fixedValue, newValue, ...) + --color is a table with 4 indexes for each color plus alpha + if (option.widget == "color") then + --calor callback sends the red color in the fixedParameter slot + local r, g, b, alpha = fixedValue, newValue, ... + --need to use the same table from the profile table + parentTable[1] = r + parentTable[2] = g + parentTable[3] = b + parentTable[4] = alpha + + newValue = parentTable + else + detailsFramework.table.setfrompath(profileTable, profileKey, newValue) + end + + if (self:GetOnEditCallback()) then + self:GetOnEditCallback()(object, option.name, newValue, profileTable, profileKey) + end + + --update the widget visual + --anchoring uses SetAnchor() which require the anchorTable to be passed + if (option.name == "anchor" or option.name == "anchoroffsetx" or option.name == "anchoroffsety") then + anchorSettings = parentTable + + if (option.name == "anchor") then + anchorSettings.x = 0 + anchorSettings.y = 0 + end + + self:StopObjectMovement() + + option.setter(object, parentTable) + + if (editingOptions.can_move) then + self:StartObjectMovement(anchorSettings) + end + else + option.setter(object, newValue) + end + end, + min = minValue, + max = maxValue, + step = option.step, + usedecimals = option.usedecimals, + id = option.name, + } + end end end @@ -303,7 +491,9 @@ detailsFramework.EditorMixin = { local optionsFrame = self:GetOptionsFrame() local canvasScrollBox = self:GetCanvasScrollBox() - local bUseColon = true + + local bUseColon = editingOptions.use_colon + local bSwitchIsCheckbox = true local maxHeight = 5000 @@ -318,25 +508,361 @@ detailsFramework.EditorMixin = { local options_slider_template = detailsFramework:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE") local options_button_template = detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE") - detailsFramework:BuildMenu(optionsFrame, menuOptions, 0, -2, maxHeight, bUseColon, options_text_template, options_dropdown_template, options_switch_template, bSwitchIsCheckbox, options_slider_template, options_button_template) + detailsFramework:BuildMenuVolatile(optionsFrame, menuOptions, 0, -2, maxHeight, bUseColon, options_text_template, options_dropdown_template, options_switch_template, bSwitchIsCheckbox, options_slider_template, options_button_template) + + if (editingOptions.can_move) then + self:StartObjectMovement(anchorSettings) + end end, + ---@param self df_editor + ---@param anchorSettings df_anchor + StartObjectMovement = function(self, anchorSettings) + local object = self:GetEditingObject() + local moverFrame = self:GetMoverFrame() + + --self:UpdateGuideLinesAnchors() + + --update guidelines + if (self:GetEditingOptions().use_guide_lines) then + --show all four guidelines + for side, texture in pairs(self.moverGuideLines) do + texture:Show() + end + end + + local optionsFrame = self:GetOptionsFrame() + + moverFrame:EnableMouse(true) + moverFrame:SetMovable(true) + moverFrame:ClearAllPoints() + moverFrame:Show() + + --update the mover frame size to match the object size + if (object:GetObjectType() == "FontString") then + ---@cast object fontstring + local width = object:GetStringWidth() + local height = object:GetStringHeight() + moverFrame:SetSize(width, height) + else + local width, height = object:GetSize() + moverFrame:SetSize(width, height) + end + + for i = 1, object:GetNumPoints() do + local point, relativeTo, relativePoint, x, y = object:GetPoint(i) + moverFrame:SetPoint(point, relativeTo, relativePoint, x, y) + end + + local currentPosX, currentPosY + + moverFrame:SetScript("OnMouseDown", function() + object:ClearAllPoints() + object:SetPoint("topleft", moverFrame, "topleft", 0, 0) + currentPosX, currentPosY = moverFrame:GetCenter() + moverFrame:SetBackdropBorderColor(1, 1, 0, 0) + + for i = 1, object:GetNumPoints() do + local point, relativeTo, relativePoint, x, y = object:GetPoint(i) + moverFrame:SetPoint(point, relativeTo, relativePoint, x, y) + end + + moverFrame:StartMoving() + moverFrame.bIsMoving = true + end) + + moverFrame:SetScript("OnMouseUp", function() + moverFrame:StopMovingOrSizing() + moverFrame.bIsMoving = false + + object:ClearAllPoints() + detailsFramework:SetAnchor(object, anchorSettings, object:GetParent()) + + moverFrame:ClearAllPoints() + for i = 1, object:GetNumPoints() do + local point, relativeTo, relativePoint, x, y = object:GetPoint(i) + moverFrame:SetPoint(point, relativeTo, relativePoint, x, y) + end + end) + + --detailsFramework:SetAnchor(moverFrame, anchorSettings) + --detailsFramework:SetAnchor(object, anchorSettings, moverFrame) + + moverFrame:SetScript("OnUpdate", function() + --if the object isn't moving, make the mover follow the object position + if (moverFrame.bIsMoving) then + object:ClearAllPoints() + object:SetPoint("topleft", moverFrame, "topleft", 0, 0) + + --if the object is moving, check if the moverFrame has moved + local newPosX, newPosY = moverFrame:GetCenter() + + --did the frame moved? + if (newPosX ~= currentPosX) then + local xOffset = newPosX - currentPosX + anchorSettings.x = anchorSettings.x + xOffset + local anchorXSlider = optionsFrame:GetWidgetById("anchoroffsetx") + anchorXSlider:SetValueNoCallback(anchorSettings.x) + end + + if (newPosY ~= currentPosY) then + local yOffset = newPosY - currentPosY + anchorSettings.y = anchorSettings.y + yOffset + local anchorYSlider = optionsFrame:GetWidgetById("anchoroffsety") + anchorYSlider:SetValueNoCallback(anchorSettings.y) + end + + currentPosX, currentPosY = newPosX, newPosY + end + + --[=[ + --update the mover frame size to match the object size + if (object:GetObjectType() == "FontString") then + ---@cast object fontstring + local width = object:GetStringWidth() + local height = object:GetStringHeight() + moverFrame:SetSize(width, height) + else + local width, height = object:GetSize() + moverFrame:SetSize(width, height) + end + --]=] + end) + end, + + ---@param self df_editor + StopObjectMovement = function(self) + local moverFrame = self:GetMoverFrame() + + moverFrame:EnableMouse(false) + moverFrame:SetScript("OnUpdate", nil) + + --hide all four guidelines + for side, texture in pairs(self.moverGuideLines) do + texture:Hide() + end + + moverFrame:Hide() + end, + + RegisterObject = function(self, object, localizedLabel, id, profileTable, profileKeyMap, extraOptions, callback, options) + assert(type(object) == "table", "RegisterObjectToEdit() expects an UIObject on #1 parameter.") + assert(object.GetObjectType, "RegisterObjectToEdit() expects an UIObject on #1 parameter.") + assert(type(profileTable) == "table", "RegisterObjectToEdit() expects a table on #4 parameter.") + assert(type(id) ~= "nil" and type(id) ~= "boolean", "RegisterObjectToEdit() expects an ID on parameter #3.") + assert(type(callback) == "function" or callback == nil, "RegisterObjectToEdit() expects a function or nil as the #7 parameter.") + + local registeredObjects = self:GetAllRegisteredObjects() + + --is object already registered? + for i = 1, #registeredObjects do + local objectRegistered = registeredObjects[i] + if (objectRegistered.object == object) then + error("RegisterObjectToEdit() object already registered.") + end + end + + --deploy the options table + options = type(options) == "table" and options or {} + detailsFramework.table.deploy(options, editObjectDefaultOptions) + + localizedLabel = type(localizedLabel) == "string" and localizedLabel or "invalid label" + + ---@type df_editor_objectinfo + local objectRegistered = { + object = object, + label = localizedLabel, + id = id, + profiletable = profileTable, + profilekeymap = profileKeyMap, + extraoptions = extraOptions or {}, + callback = callback, + options = options, + } + + registeredObjects[#registeredObjects+1] = objectRegistered + self.registeredObjectsByID[id] = objectRegistered + + local objectSelector = self:GetObjectSelector() + objectSelector:RefreshMe() + + --what to do after an object is registered? + return objectRegistered + end, + + UnregisterObject = function(self, object) + local registeredObjects = self:GetAllRegisteredObjects() + + for i = 1, #registeredObjects do + local objectRegistered = registeredObjects[i] + if (objectRegistered.object == object) then + self.registeredObjectsByID[objectRegistered.id] = nil + table.remove(registeredObjects, i) + break + end + end + + local objectSelector = self:GetObjectSelector() + objectSelector:RefreshMe() + + --stop editing the object + end, + + ---@param self df_editor + ---@return df_editor_objectinfo[] + GetAllRegisteredObjects = function(self) + return self.registeredObjects + end, + + ---@param self df_editor + ---@return df_editor_objectinfo? + GetObjectByRef = function(self, object) + local registeredObjects = self:GetAllRegisteredObjects() + for i = 1, #registeredObjects do + local objectRegistered = registeredObjects[i] + if (objectRegistered.object == object) then + return objectRegistered + end + end + end, + + GetObjectByIndex = function(self, index) + local registeredObjects = self:GetAllRegisteredObjects() + return registeredObjects[index] + end, + + GetObjectById = function(self, id) + return self.registeredObjectsByID[id] + end, + + CreateObjectSelectionList = function(self, scroll_width, scroll_height, scroll_lines, scroll_line_height) + local editorFrame = self + + local refreshFunc = function(self, data, offset, totalLines) --~refresh + for i = 1, totalLines do + local index = i + offset + ---@type df_editor_objectinfo + local objectRegistered = data[index] + + if (objectRegistered) then + local line = self:GetLine(i) + line.index = index + if (objectRegistered.object:GetObjectType() == "Texture") then + line.Icon:SetTexture([[Interface\AnimCreate\AnimCreateIcons]]) + line.Icon:SetTexCoord(1/4, 2/4, 1/4, 2/4) + + elseif (objectRegistered.object:GetObjectType() == "Texture") then + line.Icon:SetTexture([[Interface\AnimCreate\AnimCreateIcons]]) + line.Icon:SetTexCoord(2/4, 3/4, 0, 1/4) + end + + line.Label:SetText(objectRegistered.label) + end + end + end + + local createLineFunc = function(self, index) -- ~createline --~line + local line = CreateFrame("button", "$parentLine" .. index, self, "BackdropTemplate") + line:SetPoint("topleft", self, "topleft", 1, -((index-1)*(scroll_line_height+1)) - 1) + line:SetSize(scroll_width - 2, scroll_line_height) + + line:SetBackdrop({bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}) + if (index % 2 == 0) then + line:SetBackdropColor(.1, .1, .1, .1) + else + line:SetBackdropColor(.1, .1, .1, .4) + end + + detailsFramework:Mixin(line, detailsFramework.HeaderFunctions) + + --line:SetScript("OnEnter", lineOnEnter) + --line:SetScript("OnLeave", lineOnLeave) + + line:SetScript("OnClick", function(self) + local objectRegistered = editorFrame:GetObjectByIndex(self.index) + editorFrame:EditObject(objectRegistered) + end) + + --icon + local objectIcon = line:CreateTexture("$parentIcon", "overlay") + objectIcon:SetSize(scroll_line_height - 2, scroll_line_height - 2) + + --object label + local objectLabel = line:CreateFontString("$parentLabel", "overlay", "GameFontNormal") + + objectIcon:SetPoint("left", line, "left", 2, 0) + objectLabel:SetPoint("left", objectIcon, "right", 2, 0) + + line.Icon = objectIcon + line.Label = objectLabel + + return line + end + + local selectObjectScrollBox = detailsFramework:CreateScrollBox(self:GetParent(), "$parentSelectObjectScrollBox", refreshFunc, editorFrame:GetAllRegisteredObjects(), scroll_width, scroll_height, scroll_lines, scroll_line_height) + detailsFramework:ReskinSlider(selectObjectScrollBox) + + function selectObjectScrollBox:RefreshMe() + selectObjectScrollBox:SetData(editorFrame:GetAllRegisteredObjects()) + selectObjectScrollBox:Refresh() + end + + --create lines + for i = 1, scroll_lines do + selectObjectScrollBox:CreateLine(createLineFunc) + end + + return selectObjectScrollBox + end, } +---@class df_editor_defaultoptions : table +---@field width number +---@field height number +---@field create_object_list boolean +---@field object_list_width number +---@field object_list_height number +---@field object_list_lines number +---@field object_list_line_height number + +---@class df_editor_defaultoptions local editorDefaultOptions = { width = 400, height = 600, + create_object_list = true, + object_list_width = 200, + object_list_height = 420, + object_list_lines = 20, + object_list_line_height = 20, } ---@class df_editor : frame, df_optionsmixin, df_editormixin ---@field options table +---@field registeredObjects df_editor_objectinfo[] +---@field registeredObjectsByID table ---@field editingObject uiobject ---@field editingProfileTable table ---@field editingProfileMap table +---@field editingOptions df_editobjectoptions +---@field editingExtraOptions table +---@field moverGuideLines table ---@field onEditCallback function ---@field optionsFrame frame +---@field overTheTopFrame frame +---@field objectSelector df_scrollbox +---@field moverFrame frame ---@field canvasScrollBox df_canvasscrollbox +---@class df_editor_objectinfo : table +---@field object uiobject +---@field label string +---@field id any +---@field profiletable table +---@field profilekeymap table +---@field extraoptions table +---@field callback function +---@field options df_editobjectoptions + function detailsFramework:CreateEditor(parent, name, options) name = name or ("DetailsFrameworkEditor" .. math.random(100000, 10000000)) local editorFrame = CreateFrame("frame", name, parent, "BackdropTemplate") @@ -344,10 +870,25 @@ function detailsFramework:CreateEditor(parent, name, options) detailsFramework:Mixin(editorFrame, detailsFramework.EditorMixin) detailsFramework:Mixin(editorFrame, detailsFramework.OptionsFunctions) + editorFrame.registeredObjects = {} + editorFrame.registeredObjectsByID = {} + editorFrame:BuildOptionsTable(editorDefaultOptions, options) editorFrame:SetSize(editorFrame.options.width, editorFrame.options.height) + if (editorFrame.options.create_object_list) then + local scrollWidth = editorFrame.options.object_list_width + local scrollHeight = editorFrame.options.object_list_height + local scrollLinesAmount = editorFrame.options.object_list_lines + local scrollLineHeight = editorFrame.options.object_list_line_height + + local objectSelector = editorFrame:CreateObjectSelectionList(scrollWidth, scrollHeight, scrollLinesAmount, scrollLineHeight) + objectSelector:SetPoint("topleft", editorFrame, "topright", 2, 0) + editorFrame.objectSelector = objectSelector + objectSelector:RefreshMe() + end + --options frame is the frame that holds the options for the editing object, it is used as the parent frame for BuildMenuVolatile() local optionsFrame = CreateFrame("frame", name .. "OptionsFrame", editorFrame, "BackdropTemplate") optionsFrame:SetSize(editorFrame.options.width, 5000) @@ -355,6 +896,22 @@ function detailsFramework:CreateEditor(parent, name, options) local canvasFrame = detailsFramework:CreateCanvasScrollBox(editorFrame, optionsFrame, name .. "CanvasScrollBox") canvasFrame:SetAllPoints() + --over the top frame is a frame that is always on top of everything else + local OTTFrame = CreateFrame("frame", "$parentOTTFrame", UIParent) + OTTFrame:SetFrameStrata("TOOLTIP") + editorFrame.overTheTopFrame = OTTFrame + + --frame that is used to move the object + local moverFrame = CreateFrame("frame", "$parentMoverFrame", OTTFrame, "BackdropTemplate") + moverFrame:SetClampedToScreen(true) + moverFrame:SetBackdrop({ + edgeFile = "Interface\\Buttons\\WHITE8x8", + edgeSize = 1, + }) + editorFrame.moverFrame = moverFrame + + editorFrame:CreateMoverGuideLines() + editorFrame.optionsFrame = optionsFrame editorFrame.canvasScrollBox = canvasFrame diff --git a/Libs/DF/frames.lua b/Libs/DF/frames.lua new file mode 100644 index 000000000..4b5f698f0 --- /dev/null +++ b/Libs/DF/frames.lua @@ -0,0 +1,663 @@ + +---@class detailsframework +local detailsFramework = _G.DetailsFramework +if (not detailsFramework or not DetailsFrameworkCanLoad) then + return +end + +local CreateFrame = CreateFrame +local defaultRed, defaultGreen, defaultBlue = detailsFramework:GetDefaultBackdropColor() +--local defaultColorTable = {defaultRed, defaultGreen, defaultBlue, 1} +local defaultColorTable = {0.98, 0.98, 0.98, 1} +local defaultBorderColorTable = {0.1, 0.1, 0.1, 1} + +---@type edgenames[] +local cornerNames = {"TopLeft", "TopRight", "BottomLeft", "BottomRight"} + +---@class blz_backdrop : table +---@field TopLeftCorner texture +---@field TopRightCorner texture +---@field BottomLeftCorner texture +---@field BottomRightCorner texture +---@field TopEdge texture +---@field BottomEdge texture +---@field LeftEdge texture +---@field RightEdge texture +---@field Center texture + +---@class cornertextures : table +---@field TopLeft texture +---@field TopRight texture +---@field BottomLeft texture +---@field BottomRight texture + +---@class edgetextures : table +---@field Top texture +---@field Bottom texture +---@field Left texture +---@field Right texture + +---@class df_roundedpanel_options : table +---@field width number +---@field height number +---@field use_titlebar boolean +---@field use_scalebar boolean +---@field title string +---@field scale number +---@field roundness number +---@field color any +---@field border_color any +---@field corner_texture texturepath|textureid +---@field horizontal_border_size_offset number? + +---@class df_roundedpanel_preset : table, df_roundedpanel_options +---@field border_color any +---@field color any +---@field roundness number + +---@class df_roundedcornermixin : table +---@field RoundedCornerConstructor fun(self:df_roundedpanel) --called from CreateRoundedPanel +---@field SetColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil) +---@field SetBorderCornerColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil) +---@field SetRoundness fun(self:df_roundedpanel, slope: number) +---@field GetCornerSize fun(self:df_roundedpanel) : width, height +---@field OnSizeChanged fun(self:df_roundedpanel) --called when the frame size changes +---@field CreateBorder fun(self:df_roundedpanel) --called from SetBorderCornerColor if the border is not created yet +---@field CalculateBorderEdgeSize fun(self:df_roundedpanel, alignment: "vertical"|"horizontal"): number --calculate the size of the border edge texture +---@field SetTitleBarColor fun(self:df_roundedpanel, red: any, green: number|nil, blue: number|nil, alpha: number|nil) +---@field GetMaxFrameLevel fun(self:df_roundedpanel) : number --return the max frame level of the frame and its children + +---@class df_roundedpanel : frame, df_roundedcornermixin, df_optionsmixin, df_titlebar +---@field bHasBorder boolean +---@field bHasTitleBar boolean +---@field options df_roundedpanel_options +---@field cornerRoundness number +---@field CornerTextures cornertextures +---@field CenterTextures texture[] +---@field BorderCornerTextures cornertextures +---@field BorderEdgeTextures edgetextures +---@field TitleBar df_roundedpanel +---@field bIsTitleBar boolean +---@field TopLeft texture corner texture +---@field TopRight texture corner texture +---@field BottomLeft texture corner texture +---@field BottomRight texture corner texture +---@field TopEdgeBorder texture border edge +---@field BottomEdgeBorder texture border edge +---@field LeftEdgeBorder texture border edge +---@field RightEdgeBorder texture border edge +---@field TopLeftBorder texture border corner +---@field TopRightBorder texture border corner +---@field BottomLeftBorder texture border corner +---@field BottomRightBorder texture border corner +---@field TopHorizontalEdge texture texture connecting the top corners +---@field BottomHorizontalEdge texture texture connecting the bottom corners +---@field CenterBlock texture texture connecting the bottom left of the topleft corner with the top right of the bottom right corner + +---@param self df_roundedpanel +---@param textures cornertextures +---@param width number|nil +---@param height number|nil +---@param xOffset number|nil +---@param yOffset number|nil +---@param bIsBorder boolean|nil +local setCornerPoints = function(self, textures, width, height, xOffset, yOffset, bIsBorder) + for cornerName, thisTexture in pairs(textures) do + thisTexture:SetSize(width or 16, height or 16) + thisTexture:SetTexture(self.options.corner_texture) + + --set the mask + if (not thisTexture.MaskTexture and bIsBorder) then + thisTexture.MaskTexture = self:CreateMaskTexture(nil, "background") + thisTexture.MaskTexture:SetSize(74, 64) + thisTexture:AddMaskTexture(thisTexture.MaskTexture) + thisTexture.MaskTexture:SetTexture([[Interface\Azerite\AzeriteGoldRingRank2]]) --1940690 + --thisTexture.MaskTexture:Hide() + end + + xOffset = xOffset or 0 + yOffset = yOffset or 0 + + --todo: adjust the other corners setpoint offset + --todo (done): use mask when the alpha is below 0.98, disable the mask when the alpha is above 0.98 + + if (cornerName == "TopLeft") then + thisTexture:SetTexCoord(0, 0.5, 0, 0.5) + thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, yOffset) + if (thisTexture.MaskTexture) then + thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18-xOffset, 16+yOffset) + end + + elseif (cornerName == "TopRight") then + thisTexture:SetTexCoord(0.5, 1, 0, 0.5) + thisTexture:SetPoint(cornerName, self, cornerName, xOffset, yOffset) + if (thisTexture.MaskTexture) then + thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18+xOffset, 16+yOffset) + end + + elseif (cornerName == "BottomLeft") then + thisTexture:SetTexCoord(0, 0.5, 0.5, 1) + thisTexture:SetPoint(cornerName, self, cornerName, -xOffset, -yOffset) + if (thisTexture.MaskTexture) then + thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18-xOffset, 16-yOffset) + end + + elseif (cornerName == "BottomRight") then + thisTexture:SetTexCoord(0.5, 1, 0.5, 1) + thisTexture:SetPoint(cornerName, self, cornerName, xOffset, -yOffset) + if (thisTexture.MaskTexture) then + thisTexture.MaskTexture:SetPoint(cornerName, self, cornerName, -18+xOffset, 16-yOffset) + end + end + end +end + +detailsFramework.RoundedCornerPanelMixin = { + RoundedCornerConstructor = function(self) + self.CornerTextures = {} + self.CenterTextures = {} + self.BorderCornerTextures = {} + self.BorderEdgeTextures = {} + + self.cornerRoundness = 0 + + for i = 1, #cornerNames do + ---@type texture + local newCornerTexture = self:CreateTexture(nil, "border", nil, 0) + self.CornerTextures[cornerNames[i]] = newCornerTexture + self[cornerNames[i]] = newCornerTexture + end + + --create the top texture which connects the top corners with a horizontal line + ---@type texture + local topHorizontalEdge = self:CreateTexture(nil, "border", nil, 0) + topHorizontalEdge:SetPoint("topleft", self.CornerTextures["TopLeft"], "topright", 0, 0) + topHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["TopLeft"], "bottomright", 0, 0) + topHorizontalEdge:SetPoint("topright", self.CornerTextures["TopRight"], "topleft", 0, 0) + topHorizontalEdge:SetPoint("bottomright", self.CornerTextures["TopRight"], "bottomleft", 0, 0) + topHorizontalEdge:SetColorTexture(unpack(defaultColorTable)) + + --create the bottom texture which connects the bottom corners with a horizontal line + ---@type texture + local bottomHorizontalEdge = self:CreateTexture(nil, "border", nil, 0) + bottomHorizontalEdge:SetPoint("topleft", self.CornerTextures["BottomLeft"], "topright", 0, 0) + bottomHorizontalEdge:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "bottomright", 0, 0) + bottomHorizontalEdge:SetPoint("topright", self.CornerTextures["BottomRight"], "topleft", 0, 0) + bottomHorizontalEdge:SetPoint("bottomright", self.CornerTextures["BottomRight"], "bottomleft", 0, 0) + bottomHorizontalEdge:SetColorTexture(unpack(defaultColorTable)) + + --create the center block which connects the bottom left of the topleft corner with the top right of the bottom right corner + ---@type texture + local centerBlock = self:CreateTexture(nil, "border", nil, 0) + centerBlock:SetPoint("topleft", self.CornerTextures["TopLeft"], "bottomleft", 0, 0) + centerBlock:SetPoint("bottomleft", self.CornerTextures["BottomLeft"], "topleft", 0, 0) + centerBlock:SetPoint("topright", self.CornerTextures["BottomRight"], "topright", 0, 0) + centerBlock:SetPoint("bottomright", self.CornerTextures["BottomRight"], "topright", 0, 0) + centerBlock:SetColorTexture(unpack(defaultColorTable)) + + self.CenterTextures[#self.CenterTextures+1] = topHorizontalEdge + self.CenterTextures[#self.CenterTextures+1] = bottomHorizontalEdge + self.CenterTextures[#self.CenterTextures+1] = centerBlock + + self.TopHorizontalEdge = topHorizontalEdge + self.BottomHorizontalEdge = bottomHorizontalEdge + self.CenterBlock = centerBlock + + ---@type width + local width = self.options.width + ---@type height + local height = self.options.height + + self:SetSize(width, height) + + --fill the corner and edge textures table + setCornerPoints(self, self.CornerTextures) + end, + + ---get the highest frame level of the rounded panel and its children + ---@param self df_roundedpanel + ---@return framelevel + GetMaxFrameLevel = function(self) + ---@type framelevel + local maxFrameLevel = 0 + local children = {self:GetChildren()} + + for i = 1, #children do + local thisChild = children[i] + ---@cast thisChild frame + if (thisChild:GetFrameLevel() > maxFrameLevel) then + maxFrameLevel = thisChild:GetFrameLevel() + end + end + + return maxFrameLevel + end, + + ---create a frame placed at the top side of the rounded panel, this frame has a member called 'Text' which is a fontstring for the title + ---@param self df_roundedpanel + ---@return df_roundedpanel + CreateTitleBar = function(self) + ---@type df_roundedpanel + local titleBar = detailsFramework:CreateRoundedPanel(self, "$parentTitleBar", {width = self.options.width - 6, height = 16}) + titleBar:SetPoint("top", self, "top", 0, -4) + titleBar:SetRoundness(5) + titleBar:SetFrameLevel(9500) + titleBar.bIsTitleBar = true + self.TitleBar = titleBar + self.bHasTitleBar = true + + local textFontString = titleBar:CreateFontString("$parentText", "overlay", "GameFontNormal") + textFontString:SetPoint("center", titleBar, "center", 0, 0) + titleBar.Text = textFontString + + local closeButton = detailsFramework:CreateCloseButton(titleBar, "$parentCloseButton") + closeButton:SetPoint("right", titleBar, "right", -3, 0) + closeButton:SetSize(10, 10) + closeButton:SetAlpha(0.3) + closeButton:SetScript("OnClick", function(self) + self:GetParent():GetParent():Hide() + end) + detailsFramework:SetButtonTexture(closeButton, "common-search-clearbutton") + + return titleBar + end, + + ---return the width and height of the corner textures + ---@param self df_roundedpanel + ---@return number, number + GetCornerSize = function(self) + return self.CornerTextures["TopLeft"]:GetSize() + end, + + ---set how rounded the corners should be + ---@param self df_roundedpanel + ---@param roundness number + SetRoundness = function(self, roundness) + self.cornerRoundness = roundness + self:OnSizeChanged() + end, + + ---adjust the size of the corner textures and the border edge textures + ---@param self df_roundedpanel + OnSizeChanged = function(self) + --if the frame has a titlebar, need to adjust the size of the titlebar + if (self.bHasTitleBar) then + self.TitleBar:SetWidth(self:GetWidth() - 14) + end + + --if the frame height is below 32, need to recalculate the size of the corners + ---@type height + local frameHeight = self:GetHeight() + + if (frameHeight < 32) then + local newCornerSize = frameHeight / 2 + + --set the new size of the corners on all corner textures + for _, thisTexture in pairs(self.CornerTextures) do + thisTexture:SetSize(newCornerSize - (self.cornerRoundness - 2), newCornerSize) + end + + --check if the frame has border and set the size of the border corners as well + if (self.bHasBorder) then + for _, thisTexture in pairs(self.BorderCornerTextures) do + thisTexture:SetSize(newCornerSize-2, newCornerSize+2) + end + + --hide the left and right edges as the corner textures already is enough to fill the frame + self.BorderEdgeTextures["Left"]:Hide() + self.BorderEdgeTextures["Right"]:Hide() + + local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal") + self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1) + self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize + (self.options.horizontal_border_size_offset or 0), 1) + end + + self.CenterBlock:Hide() + else + if (self.bHasBorder) then + self.BorderEdgeTextures["Left"]:Show() + self.BorderEdgeTextures["Right"]:Show() + end + + ---@type width, height + local cornerWidth, cornerHeight = 16, 16 + + self.CenterBlock:Show() + + for _, thisTexture in pairs(self.CornerTextures) do + thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness) + end + + if (self.bHasBorder) then + for _, thisTexture in pairs(self.BorderCornerTextures) do + thisTexture:SetSize(cornerWidth-self.cornerRoundness, cornerHeight-self.cornerRoundness) + thisTexture.MaskTexture:SetSize(74-(self.cornerRoundness*0.75), 64-self.cornerRoundness) + end + + local horizontalEdgesNewSize = self:CalculateBorderEdgeSize("horizontal") + self.BorderEdgeTextures["Top"]:SetSize(horizontalEdgesNewSize, 1) + self.BorderEdgeTextures["Bottom"]:SetSize(horizontalEdgesNewSize, 1) + + local verticalEdgesNewSize = self:CalculateBorderEdgeSize("vertical") + self.BorderEdgeTextures["Left"]:SetSize(1, verticalEdgesNewSize) + self.BorderEdgeTextures["Right"]:SetSize(1, verticalEdgesNewSize) + end + end + end, + + ---get the size of the edge texture + ---@param self df_roundedpanel + ---@param alignment "vertical"|"horizontal" + ---@return number edgeSize + CalculateBorderEdgeSize = function(self, alignment) + ---@type string + local borderCornerName = next(self.BorderCornerTextures) + if (not borderCornerName) then + return 0 + end + + ---@type texture + local borderTexture = self.BorderCornerTextures[borderCornerName] + + alignment = alignment:lower() + + if (alignment == "vertical") then + return self:GetHeight() - (borderTexture:GetHeight() * 2) + 2 + + elseif (alignment == "horizontal") then + return self:GetWidth() - (borderTexture:GetHeight() * 2) + 2 + end + + error("df_roundedpanel:CalculateBorderEdgeSize(self, alignment) alignment must be 'vertical' or 'horizontal'") + end, + + ---@param self df_roundedpanel + CreateBorder = function(self) + local r, g, b, a = 0, 0, 0, 0.8 + + --create the corner edges + for i = 1, #cornerNames do + ---@type texture + local newBorderTexture = self:CreateTexture(nil, "background", nil, 0) + self.BorderCornerTextures[cornerNames[i]] = newBorderTexture + newBorderTexture:SetColorTexture(unpack(defaultColorTable)) + newBorderTexture:SetVertexColor(r, g, b, a) + self[cornerNames[i] .. "Border"] = newBorderTexture + end + + setCornerPoints(self, self.BorderCornerTextures, 16, 16, 1, 1, true) + + --create the top, left, bottom and right edges, the edge has 1pixel width and connects the corners + ---@type texture + local topEdge = self:CreateTexture(nil, "background", nil, 0) + topEdge:SetPoint("bottom", self, "top", 0, 0) + self.BorderEdgeTextures["Top"] = topEdge + + ---@type texture + local leftEdge = self:CreateTexture(nil, "background", nil, 0) + leftEdge:SetPoint("right", self, "left", 0, 0) + self.BorderEdgeTextures["Left"] = leftEdge + + ---@type texture + local bottomEdge = self:CreateTexture(nil, "background", nil, 0) + bottomEdge:SetPoint("top", self, "bottom", 0, 0) + self.BorderEdgeTextures["Bottom"] = bottomEdge + + ---@type texture + local rightEdge = self:CreateTexture(nil, "background", nil, 0) + rightEdge:SetPoint("left", self, "right", 0, 0) + self.BorderEdgeTextures["Right"] = rightEdge + + ---@type width + local horizontalEdgeSize = self:CalculateBorderEdgeSize("horizontal") + ---@type height + local verticalEdgeSize = self:CalculateBorderEdgeSize("vertical") + + --set the edges size + topEdge:SetSize(horizontalEdgeSize, 1) + leftEdge:SetSize(1, verticalEdgeSize) + bottomEdge:SetSize(horizontalEdgeSize, 1) + rightEdge:SetSize(1, verticalEdgeSize) + + for edgeName, thisTexture in pairs(self.BorderEdgeTextures) do + ---@cast thisTexture texture + thisTexture:SetColorTexture(unpack(defaultColorTable)) + thisTexture:SetVertexColor(r, g, b, a) + end + + self.TopEdgeBorder = topEdge + self.BottomEdgeBorder = bottomEdge + self.LeftEdgeBorder = leftEdge + self.RightEdgeBorder = rightEdge + + self.bHasBorder = true + end, + + ---@param self df_roundedpanel + ---@param red any + ---@param green number|nil + ---@param blue number|nil + ---@param alpha number|nil + SetTitleBarColor = function(self, red, green, blue, alpha) + if (self.bHasTitleBar) then + red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha) + self.TitleBar:SetColor(red, green, blue, alpha) + end + end, + + ---@param self df_roundedpanel + ---@param red any + ---@param green number|nil + ---@param blue number|nil + ---@param alpha number|nil + SetBorderCornerColor = function(self, red, green, blue, alpha) + if (not self.bHasBorder) then + self:CreateBorder() + end + + red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha) + + for _, thisTexture in pairs(self.BorderCornerTextures) do + thisTexture:SetVertexColor(red, green, blue, alpha) + end + + for _, thisTexture in pairs(self.BorderEdgeTextures) do + thisTexture:SetVertexColor(red, green, blue, alpha) + end + end, + + ---@param self df_roundedpanel + ---@param red any + ---@param green number|nil + ---@param blue number|nil + ---@param alpha number|nil + SetColor = function(self, red, green, blue, alpha) + red, green, blue, alpha = detailsFramework:ParseColors(red, green, blue, alpha) + + for _, thisTexture in pairs(self.CornerTextures) do + thisTexture:SetVertexColor(red, green, blue, alpha) + end + + for _, thisTexture in pairs(self.CenterTextures) do + thisTexture:SetVertexColor(red, green, blue, alpha) + end + + if (self.bHasBorder) then + if (alpha < 0.98) then + --if using borders, the two border textures overlaps making the alpha be darker than it should + for _, thisTexture in pairs(self.BorderCornerTextures) do + thisTexture.MaskTexture:Show() + end + else + for _, thisTexture in pairs(self.BorderCornerTextures) do + thisTexture.MaskTexture:Hide() + end + end + end + end, +} + +local defaultOptions = { + width = 200, + height = 200, + use_titlebar = false, + use_scalebar = false, + title = "", + scale = 1, + roundness = 0, + color = defaultColorTable, + border_color = defaultColorTable, + corner_texture = [[Interface\CHARACTERFRAME\TempPortraitAlphaMaskSmall]], +} + +local defaultPreset = { + border_color = {.1, .1, .1, 0.834}, + color = {defaultRed, defaultGreen, defaultBlue}, + roundness = 3, +} + +---create a regular panel with rounded corner +---@param parent frame +---@param name string|nil +---@param optionsTable table|nil +---@return df_roundedpanel +function detailsFramework:CreateRoundedPanel(parent, name, optionsTable) + ---@type df_roundedpanel + local newRoundedPanel = CreateFrame("frame", name, parent, "BackdropTemplate") + newRoundedPanel:EnableMouse(true) + newRoundedPanel.__dftype = "df_roundedpanel" + newRoundedPanel.__rcorners = true + + detailsFramework:Mixin(newRoundedPanel, detailsFramework.RoundedCornerPanelMixin) + detailsFramework:Mixin(newRoundedPanel, detailsFramework.OptionsFunctions) + newRoundedPanel:BuildOptionsTable(defaultOptions, optionsTable or {}) + newRoundedPanel:RoundedCornerConstructor() + newRoundedPanel:SetScript("OnSizeChanged", newRoundedPanel.OnSizeChanged) + + if (newRoundedPanel.options.use_titlebar) then + ---@type df_roundedpanel + local titleBar = detailsFramework:CreateRoundedPanel(newRoundedPanel, "$parentTitleBar", {height = 26}) + titleBar:SetPoint("top", newRoundedPanel, "top", 0, -7) + newRoundedPanel.TitleBar = titleBar + titleBar:SetRoundness(5) + newRoundedPanel.bHasTitleBar = true + end + + if (newRoundedPanel.options.use_scalebar) then + detailsFramework:CreateScaleBar(newRoundedPanel.TitleBar or newRoundedPanel, newRoundedPanel.options) + newRoundedPanel:SetScale(newRoundedPanel.options.scale) + end + + newRoundedPanel:SetRoundness(newRoundedPanel.options.roundness) + newRoundedPanel:SetColor(newRoundedPanel.options.color) + newRoundedPanel:SetBorderCornerColor(newRoundedPanel.options.border_color) + + return newRoundedPanel +end + +local applyPreset = function(frame, preset) + if (preset.border_color) then + frame:SetBorderCornerColor(preset.border_color) + end + + if (preset.color) then + frame:SetColor(preset.color) + end + + if (preset.roundness) then + frame:SetRoundness(preset.roundness) + else + frame:SetRoundness(1) + end + + if (preset.use_titlebar) then + frame:CreateTitleBar() + end +end + +---set a frame to have rounded corners following the settings passed by the preset table +---@param frame frame +---@param preset df_roundedpanel_preset? +function detailsFramework:AddRoundedCornersToFrame(frame, preset) + frame = frame and frame.widget or frame + assert(frame and frame.GetObjectType and frame.SetPoint, "AddRoundedCornersToFrame(frame): frame must be a frame object.") + + if (frame.__rcorners) then + return + end + + if (frame.GetBackdropBorderColor) then + local red, green, blue, alpha = frame:GetBackdropBorderColor() + if (alpha and alpha > 0) then + detailsFramework:MsgWarning("AddRoundedCornersToFrame() applyed to a frame with a backdrop border.") + detailsFramework:Msg(debugstack(2, 1, 0)) + end + end + + ---@cast frame +df_roundedcornermixin + detailsFramework:Mixin(frame, detailsFramework.RoundedCornerPanelMixin) + + if (not frame["BuildOptionsTable"]) then + ---@cast frame +df_optionsmixin + detailsFramework:Mixin(frame, detailsFramework.OptionsFunctions) + end + + frame:BuildOptionsTable(defaultOptions, {}) + + frame.options.width = frame:GetWidth() + frame.options.height = frame:GetHeight() + + frame:RoundedCornerConstructor() + frame:HookScript("OnSizeChanged", frame.OnSizeChanged) + + frame.__rcorners = true + + --handle preset + if (preset and type(preset) == "table") then + frame.options.horizontal_border_size_offset = preset.horizontal_border_size_offset + applyPreset(frame, preset) + else + applyPreset(frame, defaultPreset) + end +end + +---test case: +C_Timer.After(1, function() + + if true then return end + + local DF = DetailsFramework + + local parent = UIParent + local name = "NewRoundedCornerFrame" + local optionsTable = { + use_titlebar = true, + use_scalebar = true, + title = "Test", + scale = 1.0, + } + + ---@type df_roundedpanel + local frame = _G[name] or DF:CreateRoundedPanel(parent, name, optionsTable) + frame:SetSize(800, 600) + frame:SetPoint("center", parent, "center", 0, 0) + + frame:SetColor(.1, .1, .1, 1) + frame:SetTitleBarColor(.2, .2, .2, .5) + frame:SetBorderCornerColor(.2, .2, .2, .5) + frame:SetRoundness(0) + + local radiusSlider = DF:CreateSlider(frame, 120, 14, 0, 15, 1, frame.cornerRoundness, false, "RadiusBar", nil, nil, DF:GetTemplate("slider", "OPTIONS_SLIDER_TEMPLATE")) + radiusSlider:SetHook("OnValueChange", function(self, fixedValue, value) + value = floor(value) + if (frame.cornerRoundness == value) then + return + end + frame:SetRoundness(value) + end) + + local radiusText = frame:CreateFontString(nil, "overlay", "GameFontNormal") + radiusText:SetText("Radius:") + radiusText:SetPoint("bottomleft", radiusSlider.widget, "topleft", 0, 0) + radiusSlider:SetPoint(10, -100) +end) \ No newline at end of file diff --git a/Libs/DF/fw.lua b/Libs/DF/fw.lua index cae23f30d..55dea3e5f 100644 --- a/Libs/DF/fw.lua +++ b/Libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 482 +local dversion = 484 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) @@ -49,6 +49,8 @@ function DF:MsgWarning(msg, ...) print("|cFFFFFFAA" .. (self.__name or "Details!Framework") .. "|r |cFFFFAA00[Warning]|r", msg, ...) end +DF.internalFunctions = DF.internalFunctions or {} + local PixelUtil = PixelUtil or DFPixelUtil if (not PixelUtil) then --check if is in classic, TBC, or WotLK wow, if it is, build a replacement for PixelUtil @@ -514,6 +516,8 @@ function DF.table.getfrompath(t, path) end return value + else + return t[path] or t[tonumber(path)] end end @@ -539,7 +543,12 @@ function DF.table.setfrompath(t, path, value) lastTable[lastKey] = value return true end + else + t[path] = value + return true end + + return false end ---find the value inside the table, and it it's not found, add it @@ -1888,7 +1897,7 @@ local anchoringFunctions = { ---set the anchor point using a df_anchor table ---@param widget uiobject ---@param anchorTable df_anchor ----@param anchorTo uiobject +---@param anchorTo uiobject? function DF:SetAnchor(widget, anchorTable, anchorTo) anchorTo = anchorTo or widget:GetParent() anchoringFunctions[anchorTable.side](widget, anchorTo, anchorTable.x, anchorTable.y) @@ -2093,67 +2102,6 @@ end TutorialAlertFrame:Show() end - local refresh_options = function(self) - for _, widget in ipairs(self.widget_list) do - if (widget._get) then - if (widget.widget_type == "label") then - if (widget._get() and not widget.languageAddonId) then - widget:SetText(widget._get()) - end - - elseif (widget.widget_type == "select") then - widget:Select(widget._get()) - - elseif (widget.widget_type == "toggle" or widget.widget_type == "range") then - widget:SetValue(widget._get()) - - elseif (widget.widget_type == "textentry") then - widget:SetText(widget._get()) - - elseif (widget.widget_type == "color") then - local default_value, g, b, a = widget._get() - if (type(default_value) == "table") then - widget:SetColor (unpack(default_value)) - - else - widget:SetColor (default_value, g, b, a) - end - end - end - end - end - - local get_frame_by_id = function(self, id) - return self.widgetids [id] - end - - function DF:ClearOptionsPanel(frame) - for i = 1, #frame.widget_list do - frame.widget_list[i]:Hide() - if (frame.widget_list[i].hasLabel) then - frame.widget_list[i].hasLabel:SetText("") - end - end - - table.wipe(frame.widgetids) - end - - function DF:SetAsOptionsPanel(frame) - frame.RefreshOptions = refresh_options - frame.widget_list = {} - frame.widget_list_by_type = { - ["dropdown"] = {}, -- "select" - ["switch"] = {}, -- "toggle" - ["slider"] = {}, -- "range" - ["color"] = {}, -- - ["button"] = {}, -- "execute" - ["textentry"] = {}, -- - ["label"] = {}, --"text" - } - frame.widgetids = {} - frame.GetWidgetById = get_frame_by_id - end - function DF:CreateOptionsFrame(name, title, template) template = template or 1 @@ -2162,7 +2110,7 @@ end tinsert(UISpecialFrames, name) newOptionsFrame:SetSize(500, 200) - newOptionsFrame.RefreshOptions = refresh_options + newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel newOptionsFrame.widget_list = {} newOptionsFrame:SetScript("OnMouseDown", function(self, button) @@ -2200,7 +2148,7 @@ end tinsert(UISpecialFrames, name) newOptionsFrame:SetSize(500, 200) - newOptionsFrame.RefreshOptions = refresh_options + newOptionsFrame.RefreshOptions = DF.internalFunctions.RefreshOptionsPanel newOptionsFrame.widget_list = {} newOptionsFrame:SetScript("OnMouseDown", function(self, button) @@ -2703,6 +2651,7 @@ function DF:CreateAnimation(animation, animationType, order, duration, arg1, arg elseif (animationType == "ROTATION") then anim:SetDegrees(arg1) --degree + --print("SetOrigin", arg2, arg3, arg4) anim:SetOrigin(arg2 or "center", arg3 or 0, arg4 or 0) --point, x, y elseif (animationType == "TRANSLATION") then diff --git a/Libs/DF/scrollbox.lua b/Libs/DF/scrollbox.lua index fa4a49791..67d3bac69 100644 --- a/Libs/DF/scrollbox.lua +++ b/Libs/DF/scrollbox.lua @@ -706,6 +706,17 @@ local canvasScrollBoxDefaultOptions = { function detailsFramework:CreateCanvasScrollBox(parent, child, name, options) ---@type df_canvasscrollbox local canvasScrollBox = CreateFrame("scrollframe", name or ("DetailsFrameworkCanvasScroll" .. math.random(50000, 10000000)), parent, "BackdropTemplate, UIPanelScrollFrameTemplate") + canvasScrollBox.scrollStep = 20 + + canvasScrollBox:SetScript("OnMouseWheel", function(self, value) + local scrollBar = self + local scrollStep = scrollBar.scrollStep or scrollBar:GetHeight() / 2 + if ( value > 0 ) then + scrollBar:SetVerticalScroll(scrollBar:GetVerticalScroll() - scrollStep) + else + scrollBar:SetVerticalScroll(scrollBar:GetVerticalScroll() + scrollStep) + end + end) detailsFramework:Mixin(canvasScrollBox, detailsFramework.CanvasScrollBoxMixin) detailsFramework:Mixin(canvasScrollBox, detailsFramework.OptionsFunctions) diff --git a/Libs/DF/slider.lua b/Libs/DF/slider.lua index 28b0792cc..932a36e56 100644 --- a/Libs/DF/slider.lua +++ b/Libs/DF/slider.lua @@ -232,6 +232,11 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin) return self(value) end + function DFSliderMetaFunctions:SetValueNoCallback(value) + self.NoCallback = true + self.slider:SetValue(value) + end + -- thumb size function DFSliderMetaFunctions:SetThumbSize(width, height) if (not width) then @@ -697,6 +702,18 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin) table.insert(object.previous_value, 1, amt) table.remove(object.previous_value, 4) + if (object.useDecimals) then + slider.amt:SetText(string.format("%.2f", amt)) + else + slider.amt:SetText(math.floor(amt)) + end + object.ivalue = amt + + if (object.NoCallback) then + object.NoCallback = false + return + end + --some plugins registered OnValueChanged and others with OnValueChange local kill = object:RunHooksForWidget("OnValueChanged", slider, object.FixedValue, amt, object) if (kill) then @@ -711,17 +728,6 @@ DF:Mixin(DFSliderMetaFunctions, DF.ScriptHookMixin) if (object.OnValueChanged) then object.OnValueChanged(slider, object.FixedValue, amt) end - - if (amt < 10 and amt >= 1) then - amt = "0" .. amt - end - - if (object.useDecimals) then - slider.amt:SetText(string.format("%.2f", amt)) - else - slider.amt:SetText(math.floor(amt)) - end - object.ivalue = amt end ------------------------------------------------------------------------------------------------------------ diff --git a/Libs/DF/tabcontainer.lua b/Libs/DF/tabcontainer.lua index e058f2410..b8a04b2ba 100644 --- a/Libs/DF/tabcontainer.lua +++ b/Libs/DF/tabcontainer.lua @@ -271,6 +271,8 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, local buttonTextSize = optionsTable.button_text_size or 10 local containerWidthOffset = optionsTable.container_width_offset or 0 + local bFirstTabIsCreateOnDemand = false + --create the base frame ---@type df_tabcontainer local tabContainer = CreateFrame("frame", frameName, parent["widget"] or parent, "BackdropTemplate") @@ -318,10 +320,14 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, if (tabInfo.createOnDemandFunc) then tabFrame:SetScript("OnShow", function() if (tabInfo.createOnDemandFunc) then - detailsFramework:Dispatch(tabInfo.createOnDemandFunc, tabFrame, parent) + detailsFramework:Dispatch(tabInfo.createOnDemandFunc, tabFrame, tabContainer, parent) tabInfo.createOnDemandFunc = nil end end) + + if (tabIndex == 1) then + bFirstTabIsCreateOnDemand = true + end end --attempt to get the localized text from the language system using the addonId and the frameInfo.text @@ -405,7 +411,12 @@ function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, tabContainer:SetScript("OnShow", tabContainer.OnShow) --select the first frame local defaultTab = 1 - tabContainer:SelectTabByIndex(defaultTab) + + if (bFirstTabIsCreateOnDemand) then + C_Timer.After(0, function() tabContainer:SelectTabByIndex(defaultTab) end) + else + tabContainer:SelectTabByIndex(defaultTab) + end return tabContainer end diff --git a/Libs/DF/unitframe.lua b/Libs/DF/unitframe.lua index 04347c44f..23594fe81 100644 --- a/Libs/DF/unitframe.lua +++ b/Libs/DF/unitframe.lua @@ -1937,6 +1937,7 @@ function detailsFramework:CreateCastBar(parent, name, settingsOverride) detailsFramework:Mixin(castBar, detailsFramework.CastFrameFunctions) detailsFramework:Mixin(castBar, detailsFramework.StatusBarFunctions) + castBar:CreateTextureMask() castBar:AddMaskTexture(castBar.flashTexture) castBar:AddMaskTexture(castBar.background) diff --git a/Libs/LibOpenRaid/LibOpenRaid.lua b/Libs/LibOpenRaid/LibOpenRaid.lua index f6313d069..4e8f217e6 100644 --- a/Libs/LibOpenRaid/LibOpenRaid.lua +++ b/Libs/LibOpenRaid/LibOpenRaid.lua @@ -39,7 +39,7 @@ if (WOW_PROJECT_ID ~= WOW_PROJECT_MAINLINE and not isExpansion_Dragonflight()) t end local major = "LibOpenRaid-1.0" -local CONST_LIB_VERSION = 113 +local CONST_LIB_VERSION = 114 if (LIB_OPEN_RAID_MAX_VERSION) then if (CONST_LIB_VERSION <= LIB_OPEN_RAID_MAX_VERSION) then diff --git a/Libs/LibOpenRaid/ThingsToMantain_Dragonflight.lua b/Libs/LibOpenRaid/ThingsToMantain_Dragonflight.lua index 0d97f2009..eb9cd63ec 100644 --- a/Libs/LibOpenRaid/ThingsToMantain_Dragonflight.lua +++ b/Libs/LibOpenRaid/ThingsToMantain_Dragonflight.lua @@ -547,7 +547,10 @@ do [5384] = {cooldown = 30, duration = 0, specs = {}, talent = false, charges = 1, class = "HUNTER", type = 5}, --Feign Death [186387] = {cooldown = 30, duration = 6, specs = {}, talent = false, charges = 1, class = "HUNTER", type = 8}, --Bursting Shot [236776] = {cooldown = 40, duration = 0, specs = {253, 254, 255}, talent = false, charges = 1, class = "HUNTER", type = 8}, --High Explosive Trap - + [272682] = {cooldown = 45, duration = 4, specs = {253, 254, 255}, talent = false, charges = 1, class = "HUNTER", type = 7}, --Master's Call + --Boar nil 62305 Master's Call + --Boar Tiranaa 54216 Master's Call + --Tiranaa Tiranaa 272682 Master's Call --~druid -- 102 - Balance diff --git a/classes/class_combat.lua b/classes/class_combat.lua index 27d69461b..87a801cb2 100644 --- a/classes/class_combat.lua +++ b/classes/class_combat.lua @@ -416,6 +416,10 @@ end end + function classCombat:GetRunTime() + return self.run_time or self:GetCombatTime() + end + function classCombat:GetStartTime() return self.start_time end diff --git a/core/control.lua b/core/control.lua index a1a761d3b..49430ab46 100644 --- a/core/control.lua +++ b/core/control.lua @@ -377,7 +377,8 @@ --create a new combat object and preplace the current one local newCombatObject = Details.combate:NovaTabela(true, Details.tabela_overall, combatCounter, ...) - Details.tabela_vigente = newCombatObject + Details:SetCurrentCombat(newCombatObject) + --flag this combat as being created newCombatObject.IsBeingCreated = true @@ -514,14 +515,17 @@ Details:Msg("(debug) |cFFFFFF00ended a combat|r|cFFFF7700", Details.encounter_table and Details.encounter_table.name or "") end - if (Details.tabela_vigente.bIsClosed) then + ---@type combat + local currentCombat = Details:GetCurrentCombat() + + if (currentCombat.bIsClosed) then return end - Details.tabela_vigente.bIsClosed = true + currentCombat.bIsClosed = true - if (Details.tabela_vigente.__destroyed) then + if (currentCombat.__destroyed) then Details:Msg("a deleted combat was found during combat end, please report this bug on discord:") - Details:Msg("combat destroyed by:", Details.tabela_vigente.__destroyedBy) + Details:Msg("combat destroyed by:", currentCombat.__destroyedBy) end --flag the addon as 'leaving combat' @@ -538,13 +542,12 @@ --Details222.TimeCapture.StopCombat() --it did not start --check if this isn't a boss and try to find a boss in the segment - if (not Details.tabela_vigente.is_boss) then - + if (not currentCombat.is_boss) then --if this is a mythic+ dungeon, do not scan for encounter journal boss names in the actor list Details:FindBoss() --still didn't find the boss - if (not Details.tabela_vigente.is_boss) then + if (not currentCombat.is_boss) then local ZoneName, _, DifficultyID, _, _, _, _, ZoneMapID = GetInstanceInfo() local findboss = Details:GetRaidBossFindFunction (ZoneMapID) if (findboss) then @@ -558,34 +561,34 @@ Details:OnCombatPhaseChanged() --.PhaseData is nil here on alpha-32 - if (Details.tabela_vigente.bossFunction) then - Details:CancelTimer(Details.tabela_vigente.bossFunction) - Details.tabela_vigente.bossFunction = nil + if (currentCombat.bossFunction) then + Details:CancelTimer(currentCombat.bossFunction) + currentCombat.bossFunction = nil end --stop combat ticker Details:StopCombatTicker() --lock timers - Details.tabela_vigente:LockActivityTime() + currentCombat:LockActivityTime() --get waste shields if (Details.close_shields) then - Details:CloseShields (Details.tabela_vigente) + Details:CloseShields (currentCombat) end --salva hora, minuto, segundo do fim da luta - Details.tabela_vigente:seta_data (Details._detalhes_props.DATA_TYPE_END) - Details.tabela_vigente:seta_tempo_decorrido() + currentCombat:seta_data (Details._detalhes_props.DATA_TYPE_END) + currentCombat:seta_tempo_decorrido() --drop last events table to garbage collector - Details.tabela_vigente.player_last_events = {} + currentCombat.player_last_events = {} --flag instance type local _, InstanceType = GetInstanceInfo() - Details.tabela_vigente.instance_type = InstanceType + currentCombat.instance_type = InstanceType - if (not Details.tabela_vigente.is_boss and bIsFromEncounterEnd and type(bIsFromEncounterEnd) == "table") then + if (not currentCombat.is_boss and bIsFromEncounterEnd and type(bIsFromEncounterEnd) == "table") then local encounterID, encounterName, difficultyID, raidSize, endStatus = unpack(bIsFromEncounterEnd) if (encounterID) then local ZoneName, InstanceType, DifficultyID, DifficultyName, _, _, _, ZoneMapID = GetInstanceInfo() @@ -603,7 +606,7 @@ end local _, boss_index = Details:GetBossEncounterDetailsFromEncounterId (ZoneMapID, encounterID) - Details.tabela_vigente.is_boss = { + currentCombat.is_boss = { index = boss_index or 0, name = encounterName, encounter = encounterName, @@ -621,83 +624,68 @@ --tag as a mythic dungeon segment, can be any type of segment, this tag also avoid the segment to be tagged as trash local mythicLevel = C_ChallengeMode and C_ChallengeMode.GetActiveKeystoneInfo() if (mythicLevel and mythicLevel >= 2) then - Details.tabela_vigente.is_mythic_dungeon_segment = true - Details.tabela_vigente.is_mythic_dungeon_run_id = Details.mythic_dungeon_id + currentCombat.is_mythic_dungeon_segment = true + currentCombat.is_mythic_dungeon_run_id = Details.mythic_dungeon_id end --send item level after a combat if is in raid or party group C_Timer.After(1, Details.ScheduleSyncPlayerActorData) --if this segment isn't a boss fight - if (not Details.tabela_vigente.is_boss) then - - if (Details.tabela_vigente.is_pvp or Details.tabela_vigente.is_arena) then + if (not currentCombat.is_boss) then + if (currentCombat.is_pvp or currentCombat.is_arena) then Details:FlagActorsOnPvPCombat() end - if (Details.tabela_vigente.is_arena) then - Details.tabela_vigente.enemy = "[" .. ARENA .. "] " .. Details.tabela_vigente.is_arena.name + if (currentCombat.is_arena) then + currentCombat.enemy = "[" .. ARENA .. "] " .. currentCombat.is_arena.name end local in_instance = IsInInstance() --garrison returns party as instance type. if ((InstanceType == "party" or InstanceType == "raid") and in_instance) then if (InstanceType == "party") then - if (Details.tabela_vigente.is_mythic_dungeon_segment) then --setted just above + if (currentCombat.is_mythic_dungeon_segment) then --setted just above --is inside a mythic+ dungeon and this is not a boss segment, so tag it as a dungeon mythic+ trash segment local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() - Details.tabela_vigente.is_mythic_dungeon_trash = { + currentCombat.is_mythic_dungeon_trash = { ZoneName = zoneName, MapID = instanceMapID, Level = Details.MythicPlus.Level, EJID = Details.MythicPlus.ejID, } + Details:Msg("segment tagged as mythic+ trash.") else --tag the combat as trash clean up - Details.tabela_vigente.is_trash = true + currentCombat.is_trash = true end else - Details.tabela_vigente.is_trash = true + currentCombat.is_trash = true end else if (not in_instance) then if (Details.world_combat_is_trash) then - Details.tabela_vigente.is_world_trash_combat = true + currentCombat.is_world_trash_combat = true end end end - if (not Details.tabela_vigente.enemy) then + if (not currentCombat.enemy) then local enemy = Details:FindEnemy() if (enemy and Details.debug) then Details:Msg("(debug) enemy found", enemy) end - Details.tabela_vigente.enemy = enemy - end - - if (Details.debug) then - -- Details:Msg("(debug) forcing equalize actors behavior.") - -- Details:EqualizeActorsSchedule (Details.host_of) + currentCombat.enemy = enemy end Details:FlagActorsOnCommonFight() --fight_component else - - --this segment is a boss fight - if (not InCombatLockdown() and not UnitAffectingCombat("player")) then - - else - --Details.schedule_flag_boss_components = true - end - --calling here without checking for combat since the does not ran too long for scripts Details:FlagActorsOnBossFight() - local boss_id = Details.encounter_table.id - if (bossKilled) then - Details.tabela_vigente.is_boss.killed = true + currentCombat.is_boss.killed = true --add to storage if (not InCombatLockdown() and not UnitAffectingCombat("player") and not Details.logoff_saving_data) then @@ -709,11 +697,11 @@ Details.schedule_store_boss_encounter = true end - Details:SendEvent("COMBAT_BOSS_DEFEATED", nil, Details.tabela_vigente) + Details:SendEvent("COMBAT_BOSS_DEFEATED", nil, currentCombat) Details:CheckFor_TrashSuppressionOnEncounterEnd() else - Details:SendEvent("COMBAT_BOSS_WIPE", nil, Details.tabela_vigente) + Details:SendEvent("COMBAT_BOSS_WIPE", nil, currentCombat) --add to storage if (not InCombatLockdown() and not UnitAffectingCombat("player") and not Details.logoff_saving_data) then @@ -724,17 +712,15 @@ else Details.schedule_store_boss_encounter_wipe = true end - end - Details.tabela_vigente.is_boss.index = Details.tabela_vigente.is_boss.index or 1 - - Details.tabela_vigente.enemy = Details.tabela_vigente.is_boss.encounter + currentCombat.is_boss.index = currentCombat.is_boss.index or 1 - if (Details.tabela_vigente.instance_type == "raid") then + currentCombat.enemy = currentCombat.is_boss.encounter + if (currentCombat.instance_type == "raid") then Details.last_encounter2 = Details.last_encounter - Details.last_encounter = Details.tabela_vigente.is_boss.name + Details.last_encounter = currentCombat.is_boss.name if (Details.pre_pot_used) then Details.last_combat_pre_pot_used = Details.CopyTable(Details.pre_pot_used) @@ -748,17 +734,17 @@ if (bIsFromEncounterEnd) then if (Details.encounter_table.start) then - Details.tabela_vigente:SetStartTime (Details.encounter_table.start) + currentCombat:SetStartTime(Details.encounter_table.start) end - Details.tabela_vigente:SetEndTime (Details.encounter_table ["end"] or GetTime()) + currentCombat:SetEndTime(Details.encounter_table["end"] or GetTime()) end --encounter boss function - local bossFunction, bossFunctionType = Details:GetBossFunction (Details.tabela_vigente.is_boss.mapid or 0, Details.tabela_vigente.is_boss.index or 0) + local bossFunction, bossFunctionType = Details:GetBossFunction(currentCombat.is_boss.mapid or 0, currentCombat.is_boss.index or 0) if (bossFunction) then if (bitBand(bossFunctionType, 0x2) ~= 0) then --end of combat if (not Details.logoff_saving_data) then - local successful, errortext = pcall(bossFunction, Details.tabela_vigente) + local successful, errortext = pcall(bossFunction, currentCombat) if (not successful) then Details:Msg("error occurred on Encounter Boss Function:", errortext) end @@ -766,14 +752,12 @@ end end - if (Details.tabela_vigente.instance_type == "raid") then - --schedule captures off - - Details:CaptureSet (false, "damage", false, 15) - Details:CaptureSet (false, "energy", false, 15) - Details:CaptureSet (false, "aura", false, 15) - Details:CaptureSet (false, "energy", false, 15) - Details:CaptureSet (false, "spellcast", false, 15) + if (currentCombat.instance_type == "raid") then + Details:CaptureSet(false, "damage", false, 15) + Details:CaptureSet(false, "energy", false, 15) + Details:CaptureSet(false, "aura", false, 15) + Details:CaptureSet(false, "energy", false, 15) + Details:CaptureSet(false, "spellcast", false, 15) if (Details.debug) then Details:Msg("(debug) freezing parser for 15 seconds.") @@ -781,8 +765,8 @@ end --schedule sync - Details:EqualizeActorsSchedule (Details.host_of) - if (Details:GetEncounterEqualize (Details.tabela_vigente.is_boss.mapid, Details.tabela_vigente.is_boss.index)) then + Details:EqualizeActorsSchedule(Details.host_of) + if (Details:GetEncounterEqualize(currentCombat.is_boss.mapid, currentCombat.is_boss.index)) then Details:ScheduleTimer("DelayedSyncAlert", 3) end end @@ -792,7 +776,7 @@ Details.CloseSoloDebuffs() end - local tempo_do_combate = Details.tabela_vigente:GetCombatTime() + local tempo_do_combate = currentCombat:GetCombatTime() ---@type combat local invalidCombat @@ -805,11 +789,11 @@ local zoneName, zoneType = GetInstanceInfo() if (not bShouldForceDiscard and (zoneType == "none" or tempo_do_combate >= Details.minimum_combat_time or not segmentsTable[1])) then --combat accepted - Details.tabela_historico:AddCombat(Details.tabela_vigente) --move a tabela atual para dentro do hist�rico - if (Details.tabela_vigente.is_boss) then + Details.tabela_historico:AddCombat(currentCombat) --move a tabela atual para dentro do hist�rico + if (currentCombat.is_boss) then if (IsInRaid()) then - local cleuID = Details.tabela_vigente.is_boss.id - local diff = Details.tabela_vigente.is_boss.diff + local cleuID = currentCombat.is_boss.id + local diff = currentCombat.is_boss.diff if (cleuID and diff == 16) then -- 16 mythic local raidData = Details.raid_data @@ -821,60 +805,60 @@ end --get or build a table for this cleuID - mythicRaidData [cleuID] = mythicRaidData [cleuID] or {wipes = 0, kills = 0, best_try = 1, longest = 0, try_history = {}} - local cleuIDData = mythicRaidData [cleuID] + mythicRaidData[cleuID] = mythicRaidData[cleuID] or {wipes = 0, kills = 0, best_try = 1, longest = 0, try_history = {}} + local cleuIDData = mythicRaidData[cleuID] --store encounter data for plugins and weakauras - if (Details.tabela_vigente:GetCombatTime() > cleuIDData.longest) then - cleuIDData.longest = Details.tabela_vigente:GetCombatTime() + if (currentCombat:GetCombatTime() > cleuIDData.longest) then + cleuIDData.longest = currentCombat:GetCombatTime() end - if (Details.tabela_vigente.is_boss.killed) then + if (currentCombat.is_boss.killed) then cleuIDData.kills = cleuIDData.kills + 1 cleuIDData.best_try = 0 - table.insert(cleuIDData.try_history, {0, Details.tabela_vigente:GetCombatTime()}) + table.insert(cleuIDData.try_history, {0, currentCombat:GetCombatTime()}) --print("KILL", "best try", cleuIDData.best_try, "amt kills", cleuIDData.kills, "wipes", cleuIDData.wipes, "longest", cleuIDData.longest) else cleuIDData.wipes = cleuIDData.wipes + 1 if (Details.boss1_health_percent and Details.boss1_health_percent < cleuIDData.best_try) then cleuIDData.best_try = Details.boss1_health_percent - table.insert(cleuIDData.try_history, {Details.boss1_health_percent, Details.tabela_vigente:GetCombatTime()}) + table.insert(cleuIDData.try_history, {Details.boss1_health_percent, currentCombat:GetCombatTime()}) end --print("WIPE", "best try", cleuIDData.best_try, "amt kills", cleuIDData.kills, "wipes", cleuIDData.wipes, "longest", cleuIDData.longest) end end end - -- end --the combat is valid, see if the user is sharing data with somebody if (Details.shareData) then - local zipData = Details:CompressData (Details.tabela_vigente, "comm") + local zipData = Details:CompressData(currentCombat, "comm") if (zipData) then print("has zip data") end end - else --combat denied: combat did not pass the filter and cannot be added into the segment history --rewind the data set to the first slot in the segments table showTutorialForDiscardedSegment() --change the current combat to the latest combat available in the segment table - invalidCombat = Details.tabela_vigente - Details.tabela_vigente = segmentsTable[1] + invalidCombat = currentCombat + Details:SetCurrentCombat(segmentsTable[1]) + currentCombat = Details:GetCurrentCombat() --if it rewinds to an already erased combat, then create a new combat - if (Details.tabela_vigente.__destroyed) then - Details.tabela_vigente = Details.combate:NovaTabela(nil, Details.tabela_overall) + if (currentCombat.__destroyed) then + Details:SetCurrentCombat(Details.combate:NovaTabela(nil, Details.tabela_overall)) + currentCombat = Details:GetCurrentCombat() end - if (Details.tabela_vigente:GetStartTime() == 0) then - Details.tabela_vigente:SetStartTime(GetTime()) - Details.tabela_vigente:SetEndTime(GetTime()) + if (currentCombat:GetStartTime() == 0) then + currentCombat:SetStartTime(GetTime()) + currentCombat:SetEndTime(GetTime()) end - Details.tabela_vigente.resincked = true + currentCombat.resincked = true Details:InstanceCallDetailsFunc(Details.AtualizarJanela) if (Details.solo) then --code to update "solo" plugins, there's no solo plugins for details! at the moment @@ -905,8 +889,8 @@ Details.in_combat = false Details.leaving_combat = false - Details:Destroy(Details.tabela_vigente.PhaseData.damage_section) - Details:Destroy(Details.tabela_vigente.PhaseData.heal_section) + Details:Destroy(currentCombat.PhaseData.damage_section) + Details:Destroy(currentCombat.PhaseData.heal_section) Details:Destroy(Details.cache_damage_group) Details:Destroy(Details.cache_healing_group) @@ -938,7 +922,7 @@ Details:SendEvent("COMBAT_INVALID") Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, invalidCombat) else - Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, Details.tabela_vigente) + Details:SendEvent("COMBAT_PLAYER_LEAVE", nil, currentCombat) end Details:CheckForTextTimeCounter() @@ -948,7 +932,7 @@ --issue: invalidCombat will be just floating around in memory if not destroyed end --end of leaving combat function - function Details:GetPlayersInArena() + function Details:GetPlayersInArena() --ARENA_OPPONENT_UPDATE local aliados = GetNumGroupMembers() -- LE_PARTY_CATEGORY_HOME for i = 1, aliados-1 do local role = UnitGroupRolesAssigned and UnitGroupRolesAssigned("party" .. i) or "DAMAGER" diff --git a/core/meta.lua b/core/meta.lua index b24857aeb..29618091b 100644 --- a/core/meta.lua +++ b/core/meta.lua @@ -603,6 +603,23 @@ local classTypeUtility = Details.atributos.misc actorContainer:Cleanup() end end + else + if (combatObject.is_mythic_dungeon_segment) then + for i = 1, DETAILS_COMBAT_AMOUNT_CONTAINERS do + ---@type actorcontainer + local actorContainer = combatObject:GetContainer(i) + if (actorContainer) then + local actorTable = actorContainer:GetActorTable() + for o = #actorTable, 1, -1 do + ---@type actor + local actorObject = actorTable[o] + for funcName in pairs(Details222.Mixins.ActorMixin) do + actorObject[funcName] = nil + end + end + end + end + end end end diff --git a/core/parser.lua b/core/parser.lua index e2184d559..4346d534a 100755 --- a/core/parser.lua +++ b/core/parser.lua @@ -57,22 +57,22 @@ local _spell_energy_func = Details.habilidade_e_energy.Add local _spell_utility_func = Details.habilidade_misc.Add - --current combat and overall pointers - local _current_combat = Details.tabela_vigente or {} --placeholder table + ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- + --cache + --cache current combat + local _current_combat = Details.tabela_vigente or {} --placeholder table - --total container pointers - local _current_total = _current_combat.totals - local _current_gtotal = _current_combat.totals_grupo + --cache total table + local _current_total = _current_combat.totals + local _current_gtotal = _current_combat.totals_grupo - --actors container pointers - local _current_damage_container = _current_combat [1] - local _current_heal_container = _current_combat [2] - local _current_energy_container = _current_combat [3] - local _current_misc_container = _current_combat [4] + --cache actors containers + local _current_damage_container = _current_combat [1] + local _current_heal_container = _current_combat [2] + local _current_energy_container = _current_combat [3] + local _current_misc_container = _current_combat [4] ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ ---cache - local names_cache = {} + local names_cache = {} --damage local damage_cache = setmetatable({}, Details.weaktable) local damage_cache_pets = setmetatable({}, Details.weaktable) @@ -467,6 +467,9 @@ --Volatile Spark on razga'reth [194999] = true, + + --Ozumat - Throne of Tides + [44566] = true, } local ignored_npcids = {} @@ -5758,19 +5761,53 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 end end + function Details.parser_functions:CHALLENGE_MODE_END(...) --doesn't exists + Details:Msg("CHALLENGE_MODE_END", GetTime()) + end + + --WORLD_STATE_TIMER_START are a timer only used on scenarios + function Details.parser_functions:WORLD_STATE_TIMER_START(...) + local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() + if (difficultyID == 8) then + if (Details222.MythicPlus.CHALLENGE_MODE_START_AT + 10 > GetTime()) then + if (not Details222.MythicPlus.WorldStateTimerStartAt) then + local payload1, payload2, payload3 = ... + payload1 = payload1 or "" + payload2 = payload2 or "" + payload3 = payload3 or "" + Details222.MythicPlus.LogStep("Event: WORLD_STATE_TIMER_START | payload1: " .. payload1 .. " | payload2: " .. payload2 .. " | payload3: " .. payload3) + Details:SendEvent("COMBAT_MYTHICDUNGEON_START") + Details222.MythicPlus.WorldStateTimerStartAt = time() + end + end + end + end + function Details.parser_functions:CHALLENGE_MODE_START(...) --send mythic dungeon start event if (Details.debug) then - print("parser event", "CHALLENGE_MODE_START", ...) end local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() if (difficultyID == 8) then - Details:SendEvent("COMBAT_MYTHICDUNGEON_START") + Details222.MythicPlus.CHALLENGE_MODE_START_AT = GetTime() + Details222.MythicPlus.WorldStateTimerStartAt = nil + Details222.MythicPlus.WorldStateTimerEndAt = nil + Details222.MythicPlus.LogStep("Event: CHALLENGE_MODE_START") end end function Details.parser_functions:CHALLENGE_MODE_COMPLETED(...) + Details222.MythicPlus.WorldStateTimerEndAt = time() + + local mapChallengeModeID, level, time, onTime, keystoneUpgradeLevels, practiceRun, + oldOverallDungeonScore, newOverallDungeonScore, IsMapRecord, IsAffixRecord, + PrimaryAffix, isEligibleForScore, members + = C_ChallengeMode.GetCompletionInfo() + + Details222.MythicPlus.time = math.floor(time / 1000) + Details222.MythicPlus.bOnTime = onTime + --send mythic dungeon end event local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() if (difficultyID == 8) then @@ -5806,6 +5843,8 @@ local SPELL_POWER_PAIN = SPELL_POWER_PAIN or (PowerEnum and PowerEnum.Pain) or 1 if (not okay) then Details:Msg("something went wrong (0x7878):", errorText) end + + Details222.MythicPlus.LogStep("===== Mythic+ Finished =====") end function Details.parser_functions:PLAYER_REGEN_ENABLED(...) diff --git a/frames/window_main.lua b/frames/window_main.lua index 1c47a51c2..2b0e00c1b 100644 --- a/frames/window_main.lua +++ b/frames/window_main.lua @@ -9006,11 +9006,11 @@ end gameCooltip:AddLine("Remove Common Segments", nil, 1, "white", nil, Details.font_sizes.menus, Details.font_faces.menus) gameCooltip:AddIcon([[Interface\Buttons\UI-StopButton]], 1, 1, 14, 14, 0, 1, 0, 1, "orange") - gameCooltip:AddMenu(1, function() return Details.tabela_historico:ResetDataByCombatType("generic") end) + gameCooltip:AddMenu(1, function() Details.tabela_historico:ResetDataByCombatType("generic"); GameCooltip:Hide() end) gameCooltip:AddLine("Reset, but keep Mythic+ Overall Segments", nil, 1, "white", nil, Details.font_sizes.menus, Details.font_faces.menus) gameCooltip:AddIcon([[Interface\Buttons\UI-StopButton]], 1, 1, 14, 14, 0, 1, 0, 1, "orange") - gameCooltip:AddMenu(1, function() return Details.tabela_historico:ResetDataByCombatType("m+overall") end) + gameCooltip:AddMenu(1, function() Details.tabela_historico:ResetDataByCombatType("m+overall"); GameCooltip:Hide() end) gameCooltip:AddLine("$div", nil, 1, nil, -5, -11) diff --git a/frames/window_options2_sections.lua b/frames/window_options2_sections.lua index 7a4f83b60..1420cc76d 100644 --- a/frames/window_options2_sections.lua +++ b/frames/window_options2_sections.lua @@ -6401,7 +6401,8 @@ do return Details:Msg(Loc ["STRING_OPTIONS_SPELL_IDERROR"]) end - Details:UserCustomSpellAdd (id, name, icon) + local bAddedByUser = true + Details:UserCustomSpellAdd (id, name, icon, bAddedByUser) panel:Refresh() @@ -6910,16 +6911,6 @@ do desc = "When the run is done, make an overall segment.", }, - {--overall only with bosses - type = "toggle", - get = function() return Details.mythic_plus.make_overall_boss_only end, - set = function(self, fixedparam, value) - Details.mythic_plus.make_overall_boss_only = value - end, - name = "Overall Segment Boss Only", - desc = "Only add boss segments on the overall.", - }, - {--merge trash type = "toggle", get = function() return Details.mythic_plus.merge_boss_trash end, diff --git a/functions/mythicdungeon.lua b/functions/mythicdungeon.lua index efefc08bb..66ee779a1 100644 --- a/functions/mythicdungeon.lua +++ b/functions/mythicdungeon.lua @@ -49,10 +49,8 @@ end --]] ---precisa converter um wipe em um trash segment? provavel que sim - --- at the end of a mythic run, if enable on settings, merge all the segments from the mythic run into only one function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge + --at the end of a mythic run, if enable on settings, merge all the segments from the mythic run into only one if (DetailsMythicPlusFrame.DevelopmentDebug) then print("Details!", "MergeSegmentsOnEnd() > starting to merge mythic segments.", "InCombatLockdown():", InCombatLockdown()) end @@ -66,16 +64,18 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge local newCombat = Details:GetCurrentCombat() local segmentsTable = Details:GetCombatSegments() - local totalTime = 0 + local timeInCombat = 0 local startDate, endDate = "", "" local lastSegment local totalSegments = 0 + --copy deaths occured on all segments to the new segment, also sum the activity combat time if (Details.mythic_plus.reverse_death_log) then for i = 1, 40 do --copy the deaths from the first segment to the last one local thisCombat = segmentsTable[i] if (thisCombat and thisCombat.is_mythic_dungeon_run_id == Details.mythic_dungeon_id) then newCombat:CopyDeathsFrom(thisCombat, true) + timeInCombat = timeInCombat + thisCombat:GetCombatTime() end end else @@ -84,11 +84,30 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge if (thisCombat) then if (thisCombat.is_mythic_dungeon_run_id == Details.mythic_dungeon_id) then newCombat:CopyDeathsFrom(thisCombat, true) + timeInCombat = timeInCombat + thisCombat:GetCombatTime() end end end end + local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() + + --tag the segment as mythic overall segment + newCombat.is_mythic_dungeon = { + StartedAt = Details.MythicPlus.StartedAt, --the start of the run + EndedAt = Details.MythicPlus.EndedAt, --the end of the run + WorldStateTimerStart = Details222.MythicPlus.WorldStateTimerStartAt, + WorldStateTimerEnd = Details222.MythicPlus.WorldStateTimerEndAt, + TimeInCombat = timeInCombat, + SegmentID = "overall", --segment number within the dungeon + RunID = Details.mythic_dungeon_id, + OverallSegment = true, + ZoneName = Details.MythicPlus.DungeonName, + MapID = instanceMapID, + Level = Details.MythicPlus.Level, + EJID = Details.MythicPlus.ejID, + } + --add all boss segments from this run to this new segment for i = 1, 40 do --from the newer combat to the oldest local thisCombat = segmentsTable[i] @@ -96,15 +115,12 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge local canAddThisSegment = true if (Details.mythic_plus.make_overall_boss_only) then if (not thisCombat.is_boss) then - canAddThisSegment = false + --canAddThisSegment = false --disabled end end if (canAddThisSegment) then newCombat = newCombat + thisCombat - --newCombat:CopyDeathsFrom(thisCombat, true) - - totalTime = totalTime + thisCombat:GetCombatTime() totalSegments = totalSegments + 1 if (DetailsMythicPlusFrame.DevelopmentDebug) then @@ -113,7 +129,7 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge if (endDate == "") then local _, whenEnded = thisCombat:GetDate() - endDate =whenEnded + endDate = whenEnded end lastSegment = thisCombat end @@ -126,33 +142,33 @@ function DetailsMythicPlusFrame.MergeSegmentsOnEnd() --~merge end if (DetailsMythicPlusFrame.DevelopmentDebug) then - print("Details!", "MergeSegmentsOnEnd() > totalTime:", totalTime, "startDate:", startDate) + print("Details!", "MergeSegmentsOnEnd() > totalTime:", timeInCombat, "startDate:", startDate) end - local zoneName, instanceType, difficultyID, difficultyName, maxPlayers, dynamicDifficulty, isDynamic, instanceMapID, instanceGroupSize = GetInstanceInfo() - - --tag the segment as mythic overall segment - newCombat.is_mythic_dungeon = { - StartedAt = Details.MythicPlus.StartedAt, --the start of the run - EndedAt = Details.MythicPlus.EndedAt, --the end of the run - SegmentID = "overall", --segment number within the dungeon - RunID = Details.mythic_dungeon_id, - OverallSegment = true, - ZoneName = Details.MythicPlus.DungeonName, - MapID = instanceMapID, - Level = Details.MythicPlus.Level, - EJID = Details.MythicPlus.ejID, - } - newCombat.total_segments_added = totalSegments - newCombat.is_mythic_dungeon_segment = true newCombat.is_mythic_dungeon_run_id = Details.mythic_dungeon_id + --check if both values are valid, this can get invalid if the player leaves the dungeon before the timer ends or the game crashes + if (type(Details222.MythicPlus.time) == "number") then + newCombat.run_time = Details222.MythicPlus.time + Details222.MythicPlus.LogStep("GetCompletionInfo() Found, Time: " .. Details222.MythicPlus.time) + + elseif (newCombat.is_mythic_dungeon.WorldStateTimerEnd and newCombat.is_mythic_dungeon.WorldStateTimerStart) then + local runTime = newCombat.is_mythic_dungeon.WorldStateTimerEnd - newCombat.is_mythic_dungeon.WorldStateTimerStart + newCombat.run_time = Details222.MythicPlus.time + Details222.MythicPlus.LogStep("World State Timers is Available, Run Time: " .. runTime .. "| start:" .. newCombat.is_mythic_dungeon.WorldStateTimerStart .. "| end:" .. newCombat.is_mythic_dungeon.WorldStateTimerEnd) + else + newCombat.run_time = timeInCombat + Details222.MythicPlus.LogStep("GetCompletionInfo() and World State Timers not Found, Activity Time: " .. timeInCombat) + end + + newCombat:SetStartTime(GetTime() - timeInCombat) + newCombat:SetEndTime(GetTime()) + Details222.MythicPlus.LogStep("Activity Time: " .. timeInCombat) + --set the segment time and date - newCombat:SetStartTime (GetTime() - totalTime) - newCombat:SetEndTime (GetTime()) - newCombat:SetDate (startDate, endDate) + newCombat:SetDate(startDate, endDate) --immediatly finishes the segment just started Details:SairDoCombate() @@ -391,9 +407,9 @@ function DetailsMythicPlusFrame.MergeRemainingTrashAfterAllBossesDone() end end ---this function is called right after defeat a boss inside a mythic dungeon ---it comes from details! control leave combat function DetailsMythicPlusFrame.BossDefeated(this_is_end_end, encounterID, encounterName, difficultyID, raidSize, endStatus) --hold your breath and count to ten + --this function is called right after defeat a boss inside a mythic dungeon + --it comes from details! control leave combat if (DetailsMythicPlusFrame.DevelopmentDebug) then print("Details!", "BossDefeated() > boss defeated | SegmentID:", Details.MythicPlus.SegmentID, " | mapID:", Details.MythicPlus.DungeonID) end @@ -650,13 +666,13 @@ function DetailsMythicPlusFrame.MythicDungeonStarted() local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo() local zoneName, _, _, _, _, _, _, currentZoneID = GetInstanceInfo() - local mapID = C_Map.GetBestMapForUnit ("player") + local mapID = C_Map.GetBestMapForUnit("player") if (not mapID) then return end - local ejID = DF.EncounterJournal.EJ_GetInstanceForMap (mapID) + local ejID = DF.EncounterJournal.EJ_GetInstanceForMap(mapID) --setup the mythic run info Details.MythicPlus.Started = true @@ -669,12 +685,12 @@ function DetailsMythicPlusFrame.MythicDungeonStarted() Details.MythicPlus.ejID = ejID Details.MythicPlus.PreviousBossKilledAt = time() - Details:SaveState_CurrentMythicDungeonRun (Details.mythic_dungeon_id, zoneName, currentZoneID, time()+9.7, 1, mythicLevel, ejID, time()) + Details:SaveState_CurrentMythicDungeonRun(Details.mythic_dungeon_id, zoneName, currentZoneID, time()+9.7, 1, mythicLevel, ejID, time()) local name, groupType, difficultyID, difficult = GetInstanceInfo() if (groupType == "party" and Details.overall_clear_newchallenge) then Details.historico:ResetOverallData() - Details:Msg("overall data are now reset.") + Details:Msg("the overall data has been reset.") --localize-me if (Details.debug) then Details:Msg("(debug) timer is for a mythic+ dungeon, overall has been reseted.") @@ -684,19 +700,17 @@ function DetailsMythicPlusFrame.MythicDungeonStarted() if (DetailsMythicPlusFrame.DevelopmentDebug) then print("Details!", "MythicDungeonStarted() > State set to Mythic Dungeon, new combat starting in 10 seconds.") end - end function DetailsMythicPlusFrame.OnChallengeModeStart() --is this a mythic dungeon? - local _, _, difficulty, _, _, _, _, currentZoneID = GetInstanceInfo() + local _, _, difficultyID, _, _, _, _, currentZoneID = GetInstanceInfo() - if (difficulty == 8 and DetailsMythicPlusFrame.LastTimer and DetailsMythicPlusFrame.LastTimer+2 > GetTime()) then + if (difficultyID == 8) then --start the dungeon on Details! DetailsMythicPlusFrame.MythicDungeonStarted() - --print("D! mythic dungeon started!") + Details222.MythicPlus.LogStep("OnChallengeModeStart()") else - --print("D! mythic dungeon was already started!") --from zone changed local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo() @@ -794,10 +808,10 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even lowerInstance = Details:GetInstance(lowerInstance) if (lowerInstance) then C_Timer.After(3, function() - if (lowerInstance:IsEnabled()) then + --if (lowerInstance:IsEnabled()) then --todo, need localization - lowerInstance:InstanceAlert("Details!" .. " " .. "Damage" .. " " .. "Meter", {[[Interface\AddOns\Details\images\minimap]], 16, 16, false}, 3, {function() end}, false, true) - end + --lowerInstance:InstanceAlert("Details!" .. " " .. "Damage" .. " " .. "Meter", {[[Interface\AddOns\Details\images\minimap]], 16, 16, false}, 3, {function() end}, false, true) + --end end) end end @@ -812,7 +826,7 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even Details:Destroy(Details.cached_specs) end - C_Timer.After(0.5, DetailsMythicPlusFrame.OnChallengeModeStart) + C_Timer.After(0.25, DetailsMythicPlusFrame.OnChallengeModeStart) --debugging local mPlusSettings = Details.mythic_plus @@ -825,7 +839,7 @@ function DetailsMythicPlusFrame.EventListener.OnDetailsEvent(contextObject, even local mythicLevel = C_ChallengeMode.GetActiveKeystoneInfo() local zoneName, _, _, _, _, _, _, currentZoneID = GetInstanceInfo() - Details222.MythicPlus.LogStep("CHALLENGE_MODE_START | settings: " .. result .. " | level: " .. mythicLevel .. " | zone: " .. zoneName .. " | zoneId: " .. currentZoneID) + Details222.MythicPlus.LogStep("COMBAT_MYTHICDUNGEON_START | settings: " .. result .. " | level: " .. mythicLevel .. " | zone: " .. zoneName .. " | zoneId: " .. currentZoneID) elseif (event == "COMBAT_MYTHICDUNGEON_END") then --ignore the event if ignoring mythic dungeon special treatment @@ -842,7 +856,7 @@ end DetailsMythicPlusFrame:SetScript("OnEvent", function(_, event, ...) if (event == "START_TIMER") then - DetailsMythicPlusFrame.LastTimer = GetTime() + --DetailsMythicPlusFrame.LastTimer = GetTime() elseif (event == "ZONE_CHANGED_NEW_AREA") then if (DetailsMythicPlusFrame.IsDoingMythicDungeon) then diff --git a/functions/profiles.lua b/functions/profiles.lua index 1b32540c7..f96e4402f 100644 --- a/functions/profiles.lua +++ b/functions/profiles.lua @@ -495,6 +495,13 @@ function Details:ApplyProfile(profileName, bNoSave, bIsCopy) Details.time_type = 2 end + Details.capture_real["damage"] = true + Details.capture_real["heal"] = true + Details.capture_real["energy"] = true + Details.capture_real["miscdata"] = true + Details.capture_real["aura"] = true + Details.capture_real["spellcast"] = true + return true end @@ -1346,6 +1353,7 @@ local default_global_data = { custom = {}, savedStyles = {}, savedCustomSpells = {}, + userCustomSpells = {}, --spells modified by the user savedTimeCaptures = {}, lastUpdateWarning = 0, update_warning_timeout = 10, @@ -1594,7 +1602,9 @@ local default_global_data = { mythicrun_chart_frame = {}, mythicrun_chart_frame_minimized = {}, mythicrun_chart_frame_ready = {}, - }, + + mythicrun_time_type = 1, --1: combat time (the amount of time the player is in combat) 2: run time (the amount of time it took to finish the mythic+ run) + }, --implementar esse time_type quando estiver dando refresh na janela --plugin window positions plugin_window_pos = {}, diff --git a/functions/spellcache.lua b/functions/spellcache.lua index dd40059da..089798f96 100644 --- a/functions/spellcache.lua +++ b/functions/spellcache.lua @@ -336,12 +336,15 @@ do return customItemList end - function Details:UserCustomSpellUpdate(index, spellName, spellIcon) + function Details:UserCustomSpellUpdate(index, spellName, spellIcon) --called from the options panel > rename spells ---@type savedspelldata local savedSpellData = Details.savedCustomSpells[index] if (savedSpellData) then + local spellId = savedSpellData[1] savedSpellData[2], savedSpellData[3] = spellName or savedSpellData[2], spellIcon or savedSpellData[3] - return rawset(Details.spellcache, savedSpellData[1], {savedSpellData[2], 1, savedSpellData[3]}) + rawset(Details.spellcache, spellId, {savedSpellData[2], 1, savedSpellData[3]}) + Details.userCustomSpells[spellId] = true + return true else return false end @@ -416,7 +419,13 @@ do end end - function Details:UserCustomSpellAdd(spellId, spellName, spellIcon) + function Details:UserCustomSpellAdd(spellId, spellName, spellIcon, bAddedByUser) + if (Details.userCustomSpells[spellId]) then + if (not bAddedByUser) then + return + end + end + local isOverwrite = false for index, savedSpellData in ipairs(Details.savedCustomSpells) do if (savedSpellData[1] == spellId) then @@ -431,7 +440,11 @@ do tinsert(Details.savedCustomSpells, {spellId, spellName, spellIcon}) end - return rawset(Details.spellcache, spellId, {spellName, 1, spellIcon}) + rawset(Details.spellcache, spellId, {spellName, 1, spellIcon}) + + if (bAddedByUser) then + Details.userCustomSpells[spellId] = true + end end function Details:UserCustomSpellRemove(index) diff --git a/startup.lua b/startup.lua index dd5c0713e..e2c7dbc66 100644 --- a/startup.lua +++ b/startup.lua @@ -276,7 +276,10 @@ function Details:StartMeUp() Details.listener:RegisterEvent("PLAYER_SPECIALIZATION_CHANGED") Details.listener:RegisterEvent("PLAYER_TALENT_UPDATE") Details.listener:RegisterEvent("CHALLENGE_MODE_START") + --Details.listener:RegisterEvent("CHALLENGE_MODE_END") --doesn't exists ingame (only at cleu) Details.listener:RegisterEvent("CHALLENGE_MODE_COMPLETED") + Details.listener:RegisterEvent("WORLD_STATE_TIMER_START") + end Details.parser_frame:RegisterEvent("COMBAT_LOG_EVENT_UNFILTERED")