diff --git a/Libs/DF/buildmenu.lua b/Libs/DF/buildmenu.lua new file mode 100644 index 000000000..882db5b89 --- /dev/null +++ b/Libs/DF/buildmenu.lua @@ -0,0 +1,1547 @@ + +local detailsFramework = _G["DetailsFramework"] +if (not detailsFramework or not DetailsFrameworkCanLoad) then + return +end + +local unpack = unpack +local C_Timer = C_Timer +local InCombatLockdown = InCombatLockdown +local CreateFrame = CreateFrame +local PixelUtil = PixelUtil +local _ + +---@class df_menu_table : table +---@field text_template table +---@field id string an unique string or number to identify the button, from parent.widgetids[id], parent is the first argument of BuildMenu and BuildMenuVolatile +---@field namePhraseId string the phrase id (from language localization) to use on the button + +---@class df_menu_label : df_menu_table +---@field get function +---@field color table +---@field font string +---@field size number +---@field text string + +---@class df_menu_dropdown : df_menu_table +---@field type string +---@field set function +---@field get function +---@field values table +---@field name string +---@field desc string +---@field descPhraseId string +---@field hooks table + +---@class df_menu_toggle : df_menu_table +---@field set function +---@field get function +---@field name string +---@field desc string +---@field descPhraseId string +---@field hooks table +---@field width number +---@field height number +---@field boxfirst boolean + +---@class df_menu_range : df_menu_table +---@field set function +---@field get function +---@field min number +---@field max number +---@field step number +---@field name string +---@field desc string +---@field descPhraseId string +---@field hooks table +---@field thumbscale number +---@field usedecimals boolean if true allow fraction values + +---@class df_menu_color : df_menu_table +---@field set function +---@field get function +---@field name string +---@field desc string +---@field descPhraseId string +---@field hooks table +---@field boxfirst boolean + +---@class df_menu_button : df_menu_table +---@field func function the function to execute when the button is pressed +---@field param1 any the first parameter to pass to the function +---@field param2 any the second parameter to pass to the function +---@field name string text to show on the button +---@field desc string text to show on the tooltip +---@field descPhraseId string the phrase id (from language localization) to use on the tooltip +---@field hooks table a table with hooks to add to the button +---@field width number +---@field height number +---@field inline boolean +---@field icontexture any +---@field icontexcoords table + +---@class df_menu_textentry : df_menu_table +---@field func function the function to execute when enter key is pressed +---@field set function same as above 'func' +---@field get function +---@field name string text to show on the button +---@field desc string text to show on the tooltip +---@field descPhraseId string the phrase id (from language localization) to use on the tooltip +---@field hooks table a table with hooks to add to the button +---@field inline boolean if true, the widget is placed in the rigt side of the previous one +---@field align string "left", "center" or "right" +---@field nocombat boolean can't edit when in combat +---@field spacement boolean gives a little of more space from the next widget + +local onEnterHighlight = function(self) + self.highlightTexture:Show() + if (self.parent:GetScript("OnEnter")) then + self.parent:GetScript("OnEnter")(self.parent) + end +end + +local onLeaveHighlight = function(self) + self.highlightTexture:Hide() + if (self.parent:GetScript("OnLeave")) then + self.parent:GetScript("OnLeave")(self.parent) + end +end + +local createOptionHighlightTexture = function(frame, label, widgetWidth) + frame = frame.widget or frame + label = label.widget or label + + local highlightFrame = CreateFrame("frame", nil, frame) + highlightFrame:EnableMouse(true) + highlightFrame:SetFrameLevel(frame:GetFrameLevel()-1) + + PixelUtil.SetSize(highlightFrame, widgetWidth, frame:GetHeight() + 1) + PixelUtil.SetPoint(highlightFrame, "topleft", label, "topleft", -2, 5) + + highlightFrame:SetScript("OnEnter", onEnterHighlight) + highlightFrame:SetScript("OnLeave", onLeaveHighlight) + + local highlightTexture = highlightFrame:CreateTexture(nil, "overlay") + highlightTexture:SetColorTexture(1, 1, 1, 0.1) + PixelUtil.SetPoint(highlightTexture, "topleft", highlightFrame, "topleft", 0, 0) + PixelUtil.SetPoint(highlightTexture, "bottomright", highlightFrame, "bottomright", 0, 0) + highlightTexture:Hide() + + local backgroundTexture = highlightFrame:CreateTexture(nil, "artwork") + backgroundTexture:SetColorTexture(1, 1, 1) + backgroundTexture:SetVertexColor(.25, .25, .25, 0.5) + PixelUtil.SetPoint(backgroundTexture, "topleft", highlightFrame, "topleft", 0, 0) + PixelUtil.SetPoint(backgroundTexture, "bottomright", highlightFrame, "bottomright", 0, 0) + + highlightFrame.highlightTexture = highlightTexture + highlightFrame.parent = frame + + return highlightTexture +end + + + +local formatOptionNameWithColon = function(text, useColon) + if (text) then + if (useColon) then + text = text .. ":" + return text + else + return text + end + end +end + +local widgetsToDisableOnCombat = {} + +local getMenuWidgetVolative = function(parent, widgetType, indexTable) + local widgetObject + + if (widgetType == "label") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType], "overlay") + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "dropdown") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateDropDown(parent, function() return {} end, nil, 120, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + + else + widgetObject:ClearHooks() + widgetObject.hasLabel.text = "" + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "switch") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateSwitch(parent, nil, true, 20, 20, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + else + widgetObject:ClearHooks() + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "slider") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateSlider(parent, 120, 20, 1, 2, 1, 1, false, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + else + widgetObject:ClearHooks() + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "color") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateColorPickButton(parent, "$parentWidget" .. widgetType .. indexTable[widgetType], nil, function()end, 1) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + else + widgetObject:ClearHooks() + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "button") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateButton(parent, function()end, 120, 18, "", nil, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + else + widgetObject:ClearHooks() + end + indexTable[widgetType] = indexTable[widgetType] + 1 + + elseif (widgetType == "textentry") then + widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] + if (not widgetObject) then + widgetObject = detailsFramework:CreateTextEntry(parent, function()end, 120, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) + widgetObject.hasLabel = detailsFramework:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") + + table.insert(parent.widget_list, widgetObject) + table.insert(parent.widget_list_by_type[widgetType], widgetObject) + else + widgetObject:ClearHooks() + end + indexTable[widgetType] = indexTable[widgetType] + 1 + end + + --if the widget is inside the no combat table, remove it + for i = 1, #widgetsToDisableOnCombat do + if (widgetsToDisableOnCombat[i] == widgetObject) then + table.remove(widgetsToDisableOnCombat, i) + break + end + end + + return widgetObject +end + +--get the description phrase from the language table or use the .desc or .deschraseid +local getDescPhraseText = function(languageTable, widgetTable) + local descPhraseId = languageTable and (languageTable[widgetTable.descPhraseId] or languageTable[widgetTable.desc]) + return descPhraseId or widgetTable.descPhraseId or widgetTable.desc or widgetTable.name or "-?-" +end + +local getNamePhraseID = function(widgetTable, languageAddonId, languageTable, bIgnoreEmbed) + if (widgetTable.namePhraseId) then + return widgetTable.namePhraseId + end + + if (not languageTable) then + return + end + + local keyName = widgetTable.name + + if (widgetTable.type == "label" and widgetTable.get) then + local key = widgetTable.get() + if (key and type(key) == "string") then + keyName = key + end + end + + --embed key is when the phraseId is inside a string surounded by @ + local embedPhraseId = keyName:match("@(.-)@") + + local hasValue = detailsFramework.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, embedPhraseId or keyName) + if (not hasValue) then + return + end + + if (embedPhraseId and not bIgnoreEmbed) then + return embedPhraseId, true + else + return keyName + end +end + +local getNamePhraseText = function(languageTable, widgetTable, useColon, languageAddonId) + local namePhraseId, bWasEmbed = getNamePhraseID(widgetTable, languageAddonId, languageTable) + local namePhrase = languageTable and (languageTable[namePhraseId] or languageTable[widgetTable.namePhraseId] or languageTable[widgetTable.name]) + + if (bWasEmbed and widgetTable.name) then + namePhrase = widgetTable.name:gsub("@" .. namePhraseId .. "@", namePhrase) + end + + return namePhrase or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or widgetTable.name or "-?-" +end + +--volatile menu can be called several times, each time all settings are reset and a new menu is built reusing the widgets +function detailsFramework:BuildMenuVolatile(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) + if (not parent.widget_list) then + detailsFramework:SetAsOptionsPanel(parent) + end + detailsFramework:ClearOptionsPanel(parent) + + local currentXOffset = xOffset or 0 + local currentYOffset = yOffset or 0 + local maxColumnWidth = 0 + + local latestInlineWidget + + local widgetIndexes = { + label = 1, + dropdown = 1, + switch = 1, + slider = 1, + color = 1, + button = 1, + 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 + + for index, widgetTable in ipairs(menuOptions) do + if (not widgetTable.hidden) then + local widgetCreated + if (latestInlineWidget) then + if (not widgetTable.inline) then + latestInlineWidget = nil + currentYOffset = currentYOffset - 20 + end + end + + local extraPaddingY = 0 + + if (not widgetTable.novolatile) then + --step a line + if (widgetTable.type == "blank" or widgetTable.type == "space") then + --do nothing + + elseif (widgetTable.type == "label" or widgetTable.type == "text") then + local label = getMenuWidgetVolative(parent, "label", widgetIndexes) + widgetCreated = label + + 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 + + --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 + + 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 == "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) + 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 + + --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 + + --slider + elseif (widgetTable.type == "range" or widgetTable.type == "slider") then + local slider = getMenuWidgetVolative(parent, "slider", widgetIndexes) + widgetCreated = slider + + if (widgetTable.usedecimals) then + slider.slider:SetValueStep(0.01) + else + slider.slider:SetValueStep(widgetTable.step 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 + + --color + elseif (widgetTable.type == "color" or widgetTable.type == "color") then + local colorpick = getMenuWidgetVolative(parent, "color", widgetIndexes) + widgetCreated = colorpick + + colorpick.color_callback = widgetTable.set --callback + colorpick:SetTemplate(buttonTemplate) + colorpick:SetSize(18, 18) + + 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 + + --button + elseif (widgetTable.type == "execute" or widgetTable.type == "button") then + local button = getMenuWidgetVolative(parent, "button", widgetIndexes) + 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 + + 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 + + --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 + + end --end loop + + if (widgetTable.nocombat) then + table.insert(widgetsToDisableOnCombat, widgetCreated) + end + + if (not widgetTable.inline) then + if (widgetTable.spacement) then + currentYOffset = currentYOffset - 30 + else + currentYOffset = currentYOffset - 20 + end + end + + if (extraPaddingY > 0) then + currentYOffset = currentYOffset - extraPaddingY + end + + if (widgetTable.type == "breakline" or currentYOffset < height) then + currentYOffset = yOffset + currentXOffset = currentXOffset + maxColumnWidth + 20 + maxColumnWidth = 0 + end + + if widgetCreated then + widgetCreated:Show() + end + end + end + end + + detailsFramework.RefreshUnsafeOptionsWidgets() +end + +local getDescripttionPhraseID = function(widgetTable, languageAddonId, languageTable) + if (widgetTable.descPhraseId) then + return widgetTable.descPhraseId + end + + if (not languageTable) then + return + end + + local hasValue = detailsFramework.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, widgetTable.desc) + if (not hasValue) then + return + end + + 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 + + local extraPaddingY = 0 + + if (widgetTable.type == "blank") then + --do nothing + + elseif (widgetTable.type == "label" or widgetTable.type == "text") then + ---@cast widgetTable df_menu_label + + 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 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 + + --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) + + amountLineWidgetCreated = amountLineWidgetCreated + 1 + + if (widgetTable.id) then + parent.widgetids[widgetTable.id] = label + end + + 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) + + 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 + + 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) + 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() + 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) + + switch._get = widgetTable.get + switch.widget_type = "toggle" + switch.OnSwitch = widgetTable.set + + if (switchIsCheckbox) then + switch:SetAsCheckBox() + end + + if (valueChangeHook) then + switch:SetHook("OnSwitch", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + switch:SetHook(hookName, hookFunc) + end + end + + if (widgetTable.width) then + PixelUtil.SetWidth(switch.widget, widgetTable.width) + end + if (widgetTable.height) then + PixelUtil.SetHeight(switch.widget, widgetTable.height) + end + + 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)) + + 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 + + if (widgetTable.id) then + parent.widgetids[widgetTable.id] = switch + end + + local widgetTotalSize = label.widget:GetStringWidth() + 32 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (switch:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = switch:GetWidth() + end + + --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 + + 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) + + 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) + + if (widgetTable.thumbscale) then + slider:SetThumbSize(slider.thumb:GetWidth() * widgetTable.thumbscale, nil) + else + slider:SetThumbSize(slider.thumb:GetWidth() * 1.3, nil) + end + + if (valueChangeHook) then + slider:SetHook("OnValueChange", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + slider:SetHook(hookName, hookFunc) + end + end + + local label = 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)) + + 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 + + if (widgetTable.id) then + parent.widgetids[widgetTable.id] = slider + end + + local widgetTotalSize = label.widget:GetStringWidth() + 146 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (slider:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = slider:GetWidth() + 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) + + widgetCreated = slider + amountLineWidgetCreated = amountLineWidgetCreated + 1 + + 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) + + local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) + DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", descPhraseId, widgetTable.desc) + + colorpick._get = widgetTable.get + colorpick.widget_type = "color" + colorpick:SetSize(18, 18) + + local r, g, b, a = detailsFramework:ParseColors(widgetTable.get()) + colorpick:SetColor(r, g, b, a) + + if (valueChangeHook) then + colorpick:SetHook("OnColorChanged", valueChangeHook) + end + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + colorpick:SetHook(hookName, hookFunc) + end + end + + local label = 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)) + + 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 + + colorpick.hasLabel = label + + if (widgetTable.id) then + parent.widgetids[widgetTable.id] = colorpick + end + + local widgetTotalSize = label.widget:GetStringWidth() + 32 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (colorpick:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = colorpick:GetWidth() + end + + --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) + + widgetCreated = colorpick + amountLineWidgetCreated = amountLineWidgetCreated + 1 + + 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) + + local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) + DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, namePhraseId, widgetTable.name) + + if (not buttonTemplate) then + button:InstallCustomTexture() + end + + if (widgetTable.inline) then + if (latestInlineWidget) then + button:SetPoint("left", latestInlineWidget, "right", 2, 0) + latestInlineWidget = button + else + button:SetPoint(currentXOffset, currentYOffset) + latestInlineWidget = button + end + else + button:SetPoint(currentXOffset, currentYOffset) + end + + 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() + 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)) + + 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 + + textentry.hasLabel = label + + --hook list + if (widgetTable.hooks) then + for hookName, hookFunc in pairs(widgetTable.hooks) do + textentry:SetHook(hookName, hookFunc) + end + end + + if (widgetTable.id) then + parent.widgetids [widgetTable.id] = textentry + end + + local widgetTotalSize = label.widget:GetStringWidth() + 64 + if (widgetTotalSize > maxColumnWidth) then + maxColumnWidth = widgetTotalSize + end + + if (textentry:GetWidth() > maxWidgetWidth) then + maxWidgetWidth = textentry:GetWidth() + 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) + + widgetCreated = textentry + amountLineWidgetCreated = amountLineWidgetCreated + 1 + end + + if (widgetTable.nocombat) then + table.insert(widgetsToDisableOnCombat, widgetCreated) + end + + if (not widgetTable.inline) then + if (widgetTable.spacement) then + currentYOffset = currentYOffset - 30 + else + currentYOffset = currentYOffset - 20 + end + end + + if (extraPaddingY > 0) then + currentYOffset = currentYOffset - extraPaddingY + end + + if (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 + + 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 + + if (bUseScrollFrame) then + parent:SetHeight(biggestColumnHeight * -1) + 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 diff --git a/Libs/DF/button.lua b/Libs/DF/button.lua index 33dc4850b..b292b1068 100644 --- a/Libs/DF/button.lua +++ b/Libs/DF/button.lua @@ -745,11 +745,11 @@ function ButtonMetaFunctions:SetTemplate(template) end if (template.width) then - self:SetWidth(template.width) + PixelUtil.SetWidth(self.button, template.width) end if (template.height) then - self:SetHeight(template.height) + PixelUtil.SetHeight(self.button, template.height) end if (template.backdrop) then @@ -928,7 +928,7 @@ end detailsFramework:Mixin(buttonObject.button, detailsFramework.WidgetFunctions) createButtonWidgets(buttonObject.button) - buttonObject.button:SetSize(width or 100, height or 20) + PixelUtil.SetSize(buttonObject.button, width or 100, height or 20) buttonObject.widget = buttonObject.button buttonObject.button.MyObject = buttonObject @@ -964,8 +964,8 @@ end if (shortMethod == false) then --if is false, do not use auto resize --do nothing elseif (not shortMethod) then --if the value is omitted, use the default resize - local new_width = textWidth + 15 - buttonObject.button:SetWidth(new_width) + local newWidth = textWidth + 15 + PixelUtil.SetWidth(buttonObject.button, newWidth) elseif (shortMethod == 1) then local loop = true diff --git a/Libs/DF/definitions.lua b/Libs/DF/definitions.lua index 51ee5a7a3..b31d5b33b 100644 --- a/Libs/DF/definitions.lua +++ b/Libs/DF/definitions.lua @@ -1,14 +1,16 @@ ---@class df_table_functions ---@field find fun(tbl:table, value:any) : number? find the index of a value in a array ----@field addunique fun(tbl:table, value:any) : boolean add a value to an array if it doesn't exist yet ----@field reverse fun(tbl:table) reverse the order of an array ----@field append fun(tbl1:table, tbl2:table) append the array of table2 to table1 ----@field duplicate fun(tblReceiving:table, tblGiving:table) copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, keys pointing to a UIObject are preserved ----@field copy fun(tblReceiving:table, tblGiving:table) copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, threat UIObjects as regular tables ----@field deploy fun(tblReceiving:table, tblGiving:table) copy keys/values that does exist on tblGiving but not in tblReceiving ----@field copytocompress fun(tblReceiving:table, tblGiving:table) copy the values from table2 to table1 overwriting existing values, ignores __index, functions and tables with a 'GetObjectType' key +---@field addunique fun(tbl:table, index:any, value:any) : boolean add a value to an array if it doesn't exist yet, if the index is omitted the value will be added to the end of the array +---@field reverse fun(tbl:table) : table reverse the order of an array +---@field append fun(tbl1:table, tbl2:table) : table append the array of table2 to table1 +---@field duplicate fun(tblReceiving:table, tblGiving:table) : table copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, keys pointing to a UIObject are preserved +---@field copy fun(tblReceiving:table, tblGiving:table) : table copy the values from table2 to table1 overwriting existing values, ignores __index and __newindex, threat UIObjects as regular tables +---@field deploy fun(tblReceiving:table, tblGiving:table) : table copy keys/values that does exist on tblGiving but not in tblReceiving +---@field copytocompress fun(tblReceiving:table, tblGiving:table) : table copy the values from table2 to table1 overwriting existing values, ignores __index, functions and tables with a 'GetObjectType' key ---@field removeduplicate fun(tbl1:table, tbl2:table) remove the keys from table1 which also exists in table2 with the same value +---@field getfrompath fun(tbl:table, path:string) : any get a value from a table using a path, e.g. getfrompath(tbl, "a.b.c") is the same as tbl.a.b.c +---@field setfrompath fun(tbl:table, path:string, value:any) : boolean set the value of a table using a path, e.g. setfrompath(tbl, "a.b.c", 10) is the same as tbl.a.b.c = 10 ---@field dump fun(tbl:table) : string dump a table to a string ---@class df_language : table @@ -51,9 +53,12 @@ ---@field Language df_language ---@field KeybindMixin df_keybindmixin ---@field ScriptHookMixin df_scripthookmixin +---@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 table df_table_functions +---@field AnchorPoints string[] ---@field ClassFileNameToIndex table engClass -> classIndex ---@field LoadSpellCache fun(self:table, hashMap:table, indexTable:table, allSpellsSameName:table) : hashMap:table, indexTable:table, allSpellsSameName:table load all spells in the game and add them into the passed tables ---@field UnloadSpellCache fun(self:table) wipe the table contents filled with LoadSpellCache() @@ -83,8 +88,10 @@ ---@field SetButtonTexture fun(self:table, button:button|df_button, texture:atlasname|texturepath|textureid) ---@field CreateFadeAnimation fun(self:table, UIObject:uiobject, fadeInTime:number?, fadeOutTime:number?, fadeInAlpha:number?, fadeOutAlpha:number?) ---@field SetFontSize fun(self:table, fontstring:fontstring, size:number) +---@field GetFontSize fun(self:table, fontstring:fontstring) : number return the font size of the fontstring ---@field SetFontColor fun(self:table, fontstring:fontstring, red:any, green:number?, blue:number?, alpha:number?) ---@field SetFontFace fun(self:table, fontstring:fontstring, font:string) +---@field GetFontFace fun(self:table, fontstring:fontstring) : string return the font face of the fontstring ---@field SetFontShadow fun(self:table, fontstring:fontstring, red:any, green:number?, blue:number?, alpha:number?, offsetX:number?, offsetY:number?) ---@field SetFontOutline fun(self:table, fontstring:fontstring, outline:fontflags) ---@field RemoveRealmName fun(self:table, name:string) : string, number remove the realm name from the player name, must be in the format of "name-realm" @@ -101,6 +108,9 @@ ---@field ApplyStandardBackdrop fun(self:table, frame:frame, bUseSolidColor:boolean?, alphaScale:number?) ---@field CreateLabel fun(self:table, parent:frame, text:string, size:number?, color:any?, font:string?, member:string?, name:string?, layer:drawlayer?) : df_label ---@field CreateDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateFontDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateColorDropDown fun(self:table, parent:frame, func:function, default:any, width:number?, height:number?, member:string?, name:string?, template:table?) : df_dropdown +---@field CreateFontListGenerator fun(self:table, callback:function) : function return a function which when called returns a table filled with all fonts available and ready to be used on dropdowns ---@field CreateTextEntry fun(self:table, parent:frame, textChangedCallback:function, width:number, height:number, member:string?, name:string?, labelText:string?, textentryTemplate:table?, labelTemplate:table?) : df_textentry ---@field ReskinSlider fun(self:table, slider:frame) ---@field GetAvailableSpells fun(self:table) : table @@ -121,6 +131,9 @@ ---@field CreateScrollBox fun(self:table, parent:frame, name:string, refreshFunc:function, data:table, width:number, height:number, lineAmount:number, lineHeight:number, createLineFunc:function?, autoAmount:boolean?, noScroll:boolean?, noBackdrop:boolean?) : df_scrollbox ---@field CreateAuraScrollBox fun(self:table, parent:frame, name:string?, data:table?, onRemoveCallback:function?, options:table?) : 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 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 4f02ff5fa..699284746 100644 --- a/Libs/DF/dropdown.lua +++ b/Libs/DF/dropdown.lua @@ -989,6 +989,10 @@ end 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"} @@ -1006,10 +1010,10 @@ function DropDownMetaFunctions:SetTemplate(template) self.template = template if (template.width) then - self:SetWidth(template.width) + PixelUtil.SetWidth(self.dropdown, template.width) end if (template.height) then - self:SetHeight(template.height) + PixelUtil.SetWidth(self.dropdown, template.height) end if (template.backdrop) then @@ -1092,7 +1096,116 @@ end ---@field ---@field +---return a function which when called returns a table filled with all fonts available and ready to be used on dropdowns +---@param callback function +---@return function +function DF:CreateFontListGenerator(callback) + return function() return DF:BuildDropDownFontList(callback, [[Interface\AnimCreate\AnimCreateIcons]], {0, 32/128, 64/128, 96/128}, 16) end +end + +local colorGeneratorStatusBarTexture = [[Interface\Tooltips\UI-Tooltip-Background]] +local colorGeneratorStatusBarColor = {.1, .1, .1, .8} +local colorGeneratorNoColor = {0, 0, 0, 0} + +function DF:CreateColorListGenerator(callback) + local newGenerator = function() + local dropdownOptions = {} + + for colorName, colorTable in pairs(DF:GetDefaultColorList()) do + table.insert(dropdownOptions, { + label = colorName, + value = colorTable, + color = colorTable, + statusbar = colorGeneratorStatusBarTexture, + statusbarcolor = colorGeneratorStatusBarColor, + onclick = callback + }) + end + + table.insert(dropdownOptions, 1, { + label = "no color", + value = "blank", + color = colorGeneratorNoColor, + statusbar = colorGeneratorStatusBarTexture, + statusbarcolor = colorGeneratorStatusBarColor, + onclick = callback + }) + + return dropdownOptions + end + + return newGenerator +end + +function DF:CreateOutlineListGenerator(callback) + local newGenerator = function() + local dropdownOptions = {} + + for i, outlineInfo in ipairs(DF.FontOutlineFlags) do + local outlineName, outlineLoc = unpack(outlineInfo) + table.insert(dropdownOptions, { + label = outlineLoc, + value = outlineName, + onclick = callback + }) + end + + return dropdownOptions + end + + return newGenerator +end + +function DF:CreateAnchorPointListGenerator(callback) + local newGenerator = function() + local dropdownOptions = {} + + for i, pointName in pairs(DF.AnchorPoints) do + table.insert(dropdownOptions, { + label = pointName, + value = i, + onclick = callback + }) + end + + return dropdownOptions + end + return newGenerator +end + +---create a dropdown object with a list of fonts +---@param parent frame +---@param callback function +---@param default any +---@param width number? +---@param height number? +---@param member string? +---@param name string? +---@param template table? +function DF:CreateFontDropDown(parent, callback, default, width, height, member, name, template) + local func = DF:CreateFontListGenerator(callback) + local dropDownObject = DF:NewDropDown(parent, parent, name, member, width, height, func, default, template) + return dropDownObject +end + +function DF:CreateColorDropDown(parent, callback, default, width, height, member, name, template) + local func = DF:CreateColorListGenerator(callback) + local dropDownObject = DF:NewDropDown(parent, parent, name, member, width, height, func, default, template) + return dropDownObject +end + +function DF:CreateOutlineDropDown(parent, callback, default, width, height, member, name, template) + local func = DF:CreateOutlineListGenerator(callback) + local dropDownObject = DF:NewDropDown(parent, parent, name, member, width, height, func, default, template) + return dropDownObject +end + +function DF:CreateAnchorPointDropDown(parent, callback, default, width, height, member, name, template) + local func = DF:CreateAnchorPointListGenerator(callback) + local dropDownObject = DF:NewDropDown(parent, parent, name, member, width, height, func, default, template) + return dropDownObject +end ---create a dropdown object ---@param parent frame @@ -1144,8 +1257,7 @@ function DF:NewDropDown(parent, container, name, member, width, height, func, de end dropDownObject.dropdown = DF:CreateNewDropdownFrame(parent, name) - dropDownObject.dropdown:SetWidth(width) - dropDownObject.dropdown:SetHeight(height) + PixelUtil.SetSize(dropDownObject.dropdown, width, height) dropDownObject.container = container dropDownObject.widget = dropDownObject.dropdown diff --git a/Libs/DF/editor.lua b/Libs/DF/editor.lua new file mode 100644 index 000000000..43f1747cd --- /dev/null +++ b/Libs/DF/editor.lua @@ -0,0 +1,362 @@ + +local detailsFramework = _G["DetailsFramework"] +if (not detailsFramework or not DetailsFrameworkCanLoad) then + return +end + +---@cast detailsFramework detailsframework + +local CreateFrame = CreateFrame +local unpack = unpack +local wipe = table.wipe +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 + +--]=] + + +--the editor doesn't know which key in the profileTable holds the current value for an attribute, so it uses a map table to find it. +--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 widget string +---@field default any +---@field minvalue number? +---@field maxvalue number? +---@field step number? +---@field usedecimals boolean? +---@field subkey string? + +--which object attributes are used to build the editor menu for each object type +local attributes = { + ---@type df_editor_attribute[] + FontString = { + { + name = "text", + label = "Text", + widget = "textentry", + default = "font string text", + setter = function(widget, value) widget:SetText(value) end, + }, + { + name = "size", + label = "Size", + widget = "range", + minvalue = 5, + maxvalue = 120, + setter = function(widget, value) widget:SetFont(widget:GetFont(), value, select(3, widget:GetFont())) end + }, + { + name = "font", + label = "Font", + widget = "fontdropdown", + setter = function(widget, value) widget:SetFont(value, select(2, widget:GetFont())) end + }, + { + name = "color", + label = "Color", + widget = "colordropdown", + setter = function(widget, value) widget:SetTextColor(unpack(value)) end + }, + { + name = "alpha", + label = "Alpha", + widget = "range", + setter = function(widget, value) widget:SetAlpha(value) end + }, + { + name = "shadow", + label = "Draw Shadow", + widget = "toggle", + setter = function(widget, value) widget:SetShadowColor(widget:GetShadowColor(), select(2, widget:GetShadowColor()), select(3, widget:GetShadowColor()), value and 0.5 or 0) end + }, + { + name = "shadowcolor", + label = "Shadow Color", + widget = "colordropdown", + setter = function(widget, value) widget:SetShadowColor(unpack(value)) end + }, + { + name = "shadowoffsetx", + label = "Shadow X Offset", + widget = "range", + minvalue = -10, + maxvalue = 10, + setter = function(widget, value) widget:SetShadowOffset(value, select(2, widget:GetShadowOffset())) end + }, + { + name = "shadowoffsety", + label = "Shadow Y Offset", + widget = "range", + minvalue = -10, + maxvalue = 10, + setter = function(widget, value) widget:SetShadowOffset(widget:GetShadowOffset(), value) end + }, + { + name = "outline", + label = "Outline", + widget = "outlinedropdown", + setter = function(widget, value) widget:SetFont(widget:GetFont(), select(2, widget:GetFont()), value) end + }, + { + name = "anchor", + label = "Anchor", + widget = "anchordropdown", + setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end + }, + { + name = "anchoroffsetx", + label = "Anchor X Offset", + widget = "range", + minvalue = -20, + maxvalue = 20, + setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end + }, + { + name = "anchoroffsety", + label = "Anchor Y Offset", + widget = "range", + minvalue = -20, + maxvalue = 20, + setter = function(widget, value) detailsFramework:SetAnchor(widget, value, widget:GetParent()) end + }, + { + name = "rotation", + label = "Rotation", + widget = "range", + usedecimals = true, + minvalue = 0, + maxvalue = math.pi*2, + setter = function(widget, value) widget:SetRotation(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 GetEditingObject fun(self:df_editor):uiobject +---@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 PrepareObjectForEditing fun(self:df_editor) + +detailsFramework.EditorMixin = { + ---@param self df_editor + GetEditingObject = function(self) + return self.editingObject + end, + + ---@param self df_editor + ---@return table, table + GetEditingProfile = function(self) + return self.editingProfileTable, self.editingProfileMap + end, + + ---@param self df_editor + ---@return function + GetOnEditCallback = function(self) + return self.onEditCallback + end, + + GetOptionsFrame = function(self) + return self.optionsFrame + 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.") + + self.editingObject = object + self.editingProfileMap = profileKeyMap + self.editingProfileTable = profileTable + self.onEditCallback = callback + + self:PrepareObjectForEditing() + end, + + PrepareObjectForEditing = function(self) + --get the object and its profile table with the current values + local object = self:GetEditingObject() + local profileTable, profileMap = self:GetEditingProfile() + profileMap = profileMap or {} + + if (not object or not profileTable) then + return + end + + --get the object type + local objectType = object:GetObjectType() + local attributeList + + if (objectType == "FontString") then + attributeList = attributes[objectType] + end + + 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) + 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, + } + end + end + + --at this point, the optionsTable is ready to be used on DF:BuildMenuVolatile() + menuOptions.align_as_pairs = true + menuOptions.align_as_pairs_length = 150 + menuOptions.widget_width = 180 + + local optionsFrame = self:GetOptionsFrame() + local canvasScrollBox = self:GetCanvasScrollBox() + local bUseColon = true + local bSwitchIsCheckbox = true + local maxHeight = 5000 + + local amountOfOptions = #menuOptions + local optionsFrameHeight = amountOfOptions * 20 + optionsFrame:SetHeight(optionsFrameHeight) + + --templates + local options_text_template = detailsFramework:GetTemplate("font", "OPTIONS_FONT_TEMPLATE") + local options_dropdown_template = detailsFramework:GetTemplate("dropdown", "OPTIONS_DROPDOWN_TEMPLATE") + local options_switch_template = detailsFramework:GetTemplate("switch", "OPTIONS_CHECKBOX_TEMPLATE") + 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) + end, + +} + +local editorDefaultOptions = { + width = 400, + height = 600, +} + +---@class df_editor : frame, df_optionsmixin, df_editormixin +---@field options table +---@field editingObject uiobject +---@field editingProfileTable table +---@field editingProfileMap table +---@field onEditCallback function +---@field optionsFrame frame +---@field canvasScrollBox df_canvasscrollbox + +function detailsFramework:CreateEditor(parent, name, options) + name = name or ("DetailsFrameworkEditor" .. math.random(100000, 10000000)) + local editorFrame = CreateFrame("frame", name, parent, "BackdropTemplate") + + detailsFramework:Mixin(editorFrame, detailsFramework.EditorMixin) + detailsFramework:Mixin(editorFrame, detailsFramework.OptionsFunctions) + + editorFrame:BuildOptionsTable(editorDefaultOptions, options) + + editorFrame:SetSize(editorFrame.options.width, editorFrame.options.height) + + --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) + + local canvasFrame = detailsFramework:CreateCanvasScrollBox(editorFrame, optionsFrame, name .. "CanvasScrollBox") + canvasFrame:SetAllPoints() + + editorFrame.optionsFrame = optionsFrame + editorFrame.canvasScrollBox = canvasFrame + + return editorFrame +end diff --git a/Libs/DF/fw.lua b/Libs/DF/fw.lua index ff12e26a8..cae23f30d 100644 --- a/Libs/DF/fw.lua +++ b/Libs/DF/fw.lua @@ -1,6 +1,6 @@ -local dversion = 479 +local dversion = 482 local major, minor = "DetailsFramework-1.0", dversion local DF, oldminor = LibStub:NewLibrary(major, minor) @@ -493,6 +493,55 @@ function DF.table.find(t, value) end end +---get a value from a table using a path, e.g. getfrompath(tbl, "a.b.c") is the same as tbl.a.b.c +---@param t table +---@param path string +---@return any +function DF.table.getfrompath(t, path) + if (path:match("%.") or path:match("%[")) then + local value + + for key in path:gmatch("[%w_]+") do + value = t[key] or t[tonumber(key)] + + --check if the value is nil, if it is, the key does not exists in the table + if (not value) then + return + end + + --update t for the next iteration + t = value + end + + return value + end +end + +---set the value of a table using a path, e.g. setfrompath(tbl, "a.b.c", 10) is the same as tbl.a.b.c = 10 +---@param t table +---@param path string +---@param value any +---@return boolean? +function DF.table.setfrompath(t, path, value) + if (path:match("%.") or path:match("%[")) then + local lastTable + local lastKey + + for key in path:gmatch("[%w_]+") do + lastTable = t + lastKey = key + + --update t for the next iteration + t = t[key] or t[tonumber(key)] + end + + if (lastTable and lastKey) then + lastTable[lastKey] = value + return true + end + end +end + ---find the value inside the table, and it it's not found, add it ---@param t table ---@param index integer|any @@ -691,10 +740,10 @@ local function tableToString(t, resultString, deep, seenTables) resultString = resultString .. space .. "[\"" .. key .. "\"] = \"|cFFfff1c1" .. value .. "|r\",\n" elseif (valueType == "number") then - resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFffc1f4" .. value .. "|r,\n" + resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF94CEA8" .. value .. "|r,\n" elseif (valueType == "function") then - resultString = resultString .. space .. "[\"" .. key .. "\"] = function()end,\n" + resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFFC586C0function|r,\n" elseif (valueType == "boolean") then resultString = resultString .. space .. "[\"" .. key .. "\"] = |cFF99d0ff" .. (value and "true" or "false") .. "|r,\n" @@ -709,7 +758,6 @@ local function tableToStringSafe(t) return tableToString(t, nil, 0, seenTables) end - ---get the contends of table 't' and return it as a string ---@param t table ---@param resultString string @@ -1050,7 +1098,7 @@ end ---@param self table ---@param fontString fontstring ---@param degrees number -function DF:SetFontRotation(fontString, degrees) --deprecated, use fontString:SetRotation(degrees) +function DF:SetFontRotation(fontString, degrees) --deprecated, use fontString:SetRotation(degrees) | retail use fontString:SetRotation(math.rad(degrees)) if (type(degrees) == "number") then if (not fontString.__rotationAnimation) then fontString.__rotationAnimation = DF:CreateAnimationHub(fontString) @@ -1246,6 +1294,15 @@ local ValidOutlines = { ["THICKOUTLINE"] = true, } +DF.FontOutlineFlags = { + {"NONE", "None"}, + {"MONOCHROME", "Monochrome"}, + {"OUTLINE", "Outline"}, + {"THICKOUTLINE", "Thick Outline"}, + {"OUTLINEMONOCHROME", "Outline & Monochrome"}, + {"THICKOUTLINEMONOCHROME", "Thick Outline & Monochrome"}, +} + ---set the outline of a fontstring, outline is a black border around the text, can be "NONE", "MONOCHROME", "OUTLINE" or "THICKOUTLINE" ---@param fontString table ---@param outline any @@ -1721,6 +1778,26 @@ end ---@field x number ---@field y number +DF.AnchorPoints = { + "Top Left", + "Left", + "Bottom Left", + "Bottom", + "Bottom Right", + "Right", + "Top Right", + "Top", + "Center", + "Inside Left", + "Inside Right", + "Inside Top", + "Inside Bottom", + "Inside Top Left", + "Inside Bottom Left", + "Inside Bottom Right", + "Inside Top Right", +} + local anchoringFunctions = { function(frame, anchorTo, offSetX, offSetY) --1 TOP LEFT frame:ClearAllPoints() @@ -1985,1184 +2062,6 @@ end return Saturate(red), Saturate(green), Saturate(blue), Saturate(alpha) end ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- ---menus - local formatOptionNameWithColon = function(text, useColon) - if (text) then - if (useColon) then - text = text .. ":" - return text - else - return text - end - end - end - - local widgetsToDisableOnCombat = {} - - local getMenuWidgetVolative = function(parent, widgetType, indexTable) - local widgetObject - - if (widgetType == "label") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType], "overlay") - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "dropdown") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateDropDown(parent, function() return {} end, nil, 140, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - - else - widgetObject:ClearHooks() - widgetObject.hasLabel.text = "" - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "switch") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateSwitch(parent, nil, true, 20, 20, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - else - widgetObject:ClearHooks() - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "slider") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateSlider(parent, 140, 20, 1, 2, 1, 1, false, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - else - widgetObject:ClearHooks() - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "color") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateColorPickButton(parent, "$parentWidget" .. widgetType .. indexTable[widgetType], nil, function()end, 1) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - else - widgetObject:ClearHooks() - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "button") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateButton(parent, function()end, 120, 18, "", nil, nil, nil, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - else - widgetObject:ClearHooks() - end - indexTable[widgetType] = indexTable[widgetType] + 1 - - elseif (widgetType == "textentry") then - widgetObject = parent.widget_list_by_type[widgetType][indexTable[widgetType]] - if (not widgetObject) then - widgetObject = DF:CreateTextEntry(parent, function()end, 120, 18, nil, "$parentWidget" .. widgetType .. indexTable[widgetType]) - widgetObject.hasLabel = DF:CreateLabel(parent, "", 10, "white", "", nil, "$parentWidget" .. widgetType .. indexTable[widgetType] .. "label", "overlay") - - tinsert(parent.widget_list, widgetObject) - tinsert(parent.widget_list_by_type[widgetType], widgetObject) - else - widgetObject:ClearHooks() - end - indexTable[widgetType] = indexTable[widgetType] + 1 - end - - --if the widget is inside the no combat table, remove it - for i = 1, #widgetsToDisableOnCombat do - if (widgetsToDisableOnCombat[i] == widgetObject) then - tremove(widgetsToDisableOnCombat, i) - break - end - end - - return widgetObject - end - - --get the description phrase from the language table or use the .desc or .deschraseid - local getDescPhraseText = function(languageTable, widgetTable) - local descPhraseId = languageTable and (languageTable[widgetTable.descPhraseId] or languageTable[widgetTable.desc]) - return descPhraseId or widgetTable.descPhraseId or widgetTable.desc or widgetTable.name or "-?-" - end - - local getNamePhraseID = function(widgetTable, languageAddonId, languageTable, bIgnoreEmbed) - if (widgetTable.namePhraseId) then - return widgetTable.namePhraseId - end - - if (not languageTable) then - return - end - - local keyName = widgetTable.name - - if (widgetTable.type == "label" and widgetTable.get) then - local key = widgetTable.get() - if (key and type(key) == "string") then - keyName = key - end - end - - --embed key is when the phraseId is inside a string surounded by @ - local embedPhraseId = keyName:match("@(.-)@") - - local hasValue = DF.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, embedPhraseId or keyName) - if (not hasValue) then - return - end - - if (embedPhraseId and not bIgnoreEmbed) then - return embedPhraseId, true - else - return keyName - end - end - - local getNamePhraseText = function(languageTable, widgetTable, useColon, languageAddonId) - local namePhraseId, bWasEmbed = getNamePhraseID(widgetTable, languageAddonId, languageTable) - local namePhrase = languageTable and (languageTable[namePhraseId] or languageTable[widgetTable.namePhraseId] or languageTable[widgetTable.name]) - - if (bWasEmbed and widgetTable.name) then - namePhrase = widgetTable.name:gsub("@" .. namePhraseId .. "@", namePhrase) - end - - return namePhrase or formatOptionNameWithColon(widgetTable.name, useColon) or widgetTable.namePhraseId or widgetTable.name or "-?-" - end - - --volatile menu can be called several times, each time all settings are reset and a new menu is built using the same widgets - function DF:BuildMenuVolatile(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) - if (not parent.widget_list) then - DF:SetAsOptionsPanel(parent) - end - DF:ClearOptionsPanel(parent) - - local currentXOffset = xOffset - local currentYOffset = yOffset - local maxColumnWidth = 0 - - local latestInlineWidget - - local widgetIndexes = { - label = 1, - dropdown = 1, - switch = 1, - slider = 1, - color = 1, - button = 1, - textentry = 1, - } - - height = abs((height or parent:GetHeight()) - abs(yOffset) + 20) - height = height * -1 - - --normalize format types - for index, widgetTable in ipairs(menuOptions) do - if (widgetTable.type == "space") then - widgetTable.type = "blank" - - elseif (widgetTable.type == "dropdown") then - widgetTable.type = "select" - - elseif (widgetTable.type == "switch") then - widgetTable.type = "toggle" - - elseif (widgetTable.type == "slider") then - widgetTable.type = "range" - - elseif (widgetTable.type == "button") then - widgetTable.type = "execute" - - end - end - - --catch some options added in the hash part of the menu table - local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst - local languageAddonId = menuOptions.language_addonId - local languageTable - - if (languageAddonId) then - languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) - end - - for index, widgetTable in ipairs(menuOptions) do - if (not widgetTable.hidden) then - - local widgetCreated - if (latestInlineWidget) then - if (not widgetTable.inline) then - latestInlineWidget = nil - currentYOffset = currentYOffset - 20 - end - end - - local extraPaddingY = 0 - - if (not widgetTable.novolatile) then - --step a line - if (widgetTable.type == "blank" or widgetTable.type == "space") then - --do nothing - - elseif (widgetTable.type == "label" or widgetTable.type == "text") then - local label = getMenuWidgetVolative(parent, "label", widgetIndexes) - widgetCreated = label - - 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 - - --dropdowns - elseif (widgetTable.type == "select" or widgetTable.type == "dropdown") then - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get() not found in the widget table for 'select'") - local dropdown = getMenuWidgetVolative(parent, "dropdown", widgetIndexes) - widgetCreated = dropdown - - dropdown:SetFunction(widgetTable.values) - dropdown:Refresh() - dropdown:Select(widgetTable.get()) - dropdown:SetTemplate(dropdownTemplate) - - 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) - dropdown:ClearAllPoints() - dropdown:SetPoint("left", dropdown.hasLabel, "right", 2) - dropdown.hasLabel:ClearAllPoints() - dropdown.hasLabel:SetPoint(currentXOffset, currentYOffset) - - --global callback - if (valueChangeHook) then - dropdown:SetHook("OnOptionSelected", valueChangeHook) - end - - --hook list (hook list is wiped when getting the widget) - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - dropdown:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = dropdown - end - - local widgetTotalSize = dropdown.hasLabel.widget:GetStringWidth() + 140 + 4 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --switchs - elseif (widgetTable.type == "toggle" or widgetTable.type == "switch") then - local switch = getMenuWidgetVolative(parent, "switch", widgetIndexes) - widgetCreated = switch - - switch:SetValue(widgetTable.get()) - switch:SetTemplate(switchTemplate) - switch:SetAsCheckBox() --it's always a checkbox on volatile menu - - 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 (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then - switch:SetPoint(currentXOffset, currentYOffset) - switch.hasLabel:SetPoint("left", switch, "right", 2) - - local nextWidgetTable = menuOptions[index+1] - if (nextWidgetTable) then - if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then - extraPaddingY = 4 - end - end - else - switch.hasLabel:SetPoint(currentXOffset, currentYOffset) - switch:SetPoint("left", switch.hasLabel, "right", 2) - end - - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = switch - end - - local widgetTotalSize = switch.hasLabel:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --slider - elseif (widgetTable.type == "range" or widgetTable.type == "slider") then - local slider = getMenuWidgetVolative(parent, "slider", widgetIndexes) - widgetCreated = slider - - if (widgetTable.usedecimals) then - slider.slider:SetValueStep(0.01) - else - slider.slider:SetValueStep(widgetTable.step) - end - slider.useDecimals = widgetTable.usedecimals - - slider.slider:SetMinMaxValues(widgetTable.min, widgetTable.max) - slider.slider:SetValue(widgetTable.get()) - slider.ivalue = slider.slider:GetValue() - - slider:SetTemplate(sliderTemplate) - - 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:SetPoint("left", slider.hasLabel, "right", 2) - slider.hasLabel:SetPoint(currentXOffset, currentYOffset) - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = slider - end - - local widgetTotalSize = slider.hasLabel:GetStringWidth() + 146 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --color - elseif (widgetTable.type == "color" or widgetTable.type == "color") then - local colorpick = getMenuWidgetVolative(parent, "color", widgetIndexes) - widgetCreated = colorpick - - colorpick.color_callback = widgetTable.set --callback - colorpick:SetTemplate(buttonTemplate) - colorpick:SetSize(18, 18) - - 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 (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then - label:SetPoint("left", colorpick, "right", 2) - colorpick:SetPoint(currentXOffset, currentYOffset) - extraPaddingY = 1 - else - colorpick:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = colorpick - end - - local widgetTotalSize = label:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --button - elseif (widgetTable.type == "execute" or widgetTable.type == "button") then - local button = getMenuWidgetVolative(parent, "button", widgetIndexes) - widgetCreated = button - - button:SetTemplate(buttonTemplate) - button:SetSize(widgetTable.width or 120, widgetTable.height or 18) - button:SetClickFunction(widgetTable.func, widgetTable.param1, widgetTable.param2) - - local textTemplate = widgetTable.text_template or textTemplate or DF.font_templates["ORANGE_FONT_TEMPLATE"] - button.textcolor = textTemplate.color - button.textfont = textTemplate.font - button.textsize = textTemplate.size - - local namePhrase = getNamePhraseText(languageTable, widgetTable, useColon, languageAddonId) - button.text = namePhrase - - 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 - - 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 - - --textentry - elseif (widgetTable.type == "textentry") then - local textentry = getMenuWidgetVolative(parent, "textentry", widgetIndexes) - widgetCreated = textentry - - textentry:SetCommitFunction(widgetTable.func or widgetTable.set) - textentry:SetTemplate(widgetTable.template or widgetTable.button_template or buttonTemplate) - textentry:SetSize(widgetTable.width or 120, widgetTable.height or 18) - - 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:SetPoint("left", textentry.hasLabel, "right", 2) - textentry.hasLabel:SetPoint(currentXOffset, currentYOffset) - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - textentry:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = textentry - end - - local widgetTotalSize = textentry.hasLabel:GetStringWidth() + 64 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - end --end loop - - if (widgetTable.nocombat) then - tinsert(widgetsToDisableOnCombat, widgetCreated) - end - - if (not widgetTable.inline) then - if (widgetTable.spacement) then - currentYOffset = currentYOffset - 30 - else - currentYOffset = currentYOffset - 20 - end - end - - if (extraPaddingY > 0) then - currentYOffset = currentYOffset - extraPaddingY - end - - if (widgetTable.type == "breakline" or currentYOffset < height) then - currentYOffset = yOffset - currentXOffset = currentXOffset + maxColumnWidth + 20 - maxColumnWidth = 0 - end - - if widgetCreated then - widgetCreated:Show() - end - end - end - end - - DF.RefreshUnsafeOptionsWidgets() - end - - local getDescripttionPhraseID = function(widgetTable, languageAddonId, languageTable) - if (widgetTable.descPhraseId) then - return widgetTable.descPhraseId - end - - if (not languageTable) then - return - end - - local hasValue = DF.Language.DoesPhraseIDExistsInDefaultLanguage(languageAddonId, widgetTable.desc) - if (not hasValue) then - return - end - - return widgetTable.desc - end - - function DF:BuildMenu(parent, menuOptions, xOffset, yOffset, height, useColon, textTemplate, dropdownTemplate, switchTemplate, switchIsCheckbox, sliderTemplate, buttonTemplate, valueChangeHook) - if (not parent.widget_list) then - DF:SetAsOptionsPanel(parent) - end - - local currentXOffset = xOffset - local currentYOffset = yOffset - local maxColumnWidth = 0 - - --how many widgets has been created on this line loop pass - local amountLineWidgetCreated = 0 - local latestInlineWidget - - height = abs((height or parent:GetHeight()) - abs(yOffset) + 20) - height = height * -1 - - --normalize format types - for index, widgetTable in ipairs(menuOptions) do - if (widgetTable.type == "space") then - widgetTable.type = "blank" - - elseif (widgetTable.type == "dropdown") then - widgetTable.type = "select" - - elseif (widgetTable.type == "switch") then - widgetTable.type = "toggle" - - elseif (widgetTable.type == "slider") then - widgetTable.type = "range" - - elseif (widgetTable.type == "button") then - widgetTable.type = "execute" - end - end - - --catch some options added in the hash part of the menu table - local useBoxFirstOnAllWidgets = menuOptions.always_boxfirst - local languageAddonId = menuOptions.language_addonId - local languageTable - - if (languageAddonId) then - languageTable = DetailsFramework.Language.GetLanguageTable(languageAddonId) - end - - for index, widgetTable in ipairs(menuOptions) do - if (not widgetTable.hidden) then - - local widgetCreated - if (latestInlineWidget) then - if (not widgetTable.inline) then - latestInlineWidget = nil - currentYOffset = currentYOffset - 28 - end - end - - local extraPaddingY = 0 - - if (widgetTable.type == "blank") then - --do nothing - - elseif (widgetTable.type == "label" or widgetTable.type == "text") then - local label = DF:CreateLabel(parent, "", widgetTable.text_template or textTemplate or widgetTable.size, widgetTable.color, widgetTable.font, nil, "$parentWidget" .. index, "overlay") - label._get = widgetTable.get - label.widget_type = "label" - label:SetPoint(currentXOffset, currentYOffset) - - 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 - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, label) - tinsert(parent.widget_list_by_type.label, label) - - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = label - end - - elseif (widgetTable.type == "select") then - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'select'") - local dropdown = DF:NewDropDown(parent, nil, "$parentWidget" .. index, nil, 140, 18, widgetTable.values, widgetTable.get(), dropdownTemplate) - - 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 = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - - dropdown.addonId = languageAddonId - if (languageAddonId) then - DF.Language.RegisterCallback(languageAddonId, function(addonId, languageId, ...) dropdown:Select(dropdown:GetValue()) end) - C_Timer.After(0.1, function() dropdown:Select(dropdown:GetValue()) end) - end - - dropdown:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - dropdown.hasLabel = label - - --global callback - if (valueChangeHook) then - dropdown:SetHook("OnOptionSelected", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - dropdown:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = dropdown - end - - local widgetTotalSize = label.widget:GetStringWidth() + 144 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, dropdown) - tinsert(parent.widget_list_by_type.dropdown, dropdown) - - widgetCreated = dropdown - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "toggle") then - local switch = DF:NewSwitch(parent, nil, "$parentWidget" .. index, nil, 60, 20, nil, nil, widgetTable.get(), nil, nil, nil, nil, switchTemplate) - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, switch, "have_tooltip", descPhraseId, widgetTable.desc) - - switch._get = widgetTable.get - switch.widget_type = "toggle" - switch.OnSwitch = widgetTable.set - - if (switchIsCheckbox) then - switch:SetAsCheckBox() - end - - if (valueChangeHook) then - switch:SetHook("OnSwitch", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - switch:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.width) then - switch:SetWidth(widgetTable.width) - end - if (widgetTable.height) then - switch:SetHeight(widgetTable.height) - end - - local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - - if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then - switch:SetPoint(currentXOffset, currentYOffset) - label:SetPoint("left", switch, "right", 2) - - local nextWidgetTable = menuOptions[index+1] - if (nextWidgetTable) then - if (nextWidgetTable.type ~= "blank" and nextWidgetTable.type ~= "breakline" and nextWidgetTable.type ~= "toggle" and nextWidgetTable.type ~= "color") then - extraPaddingY = 4 - end - end - else - label:SetPoint(currentXOffset, currentYOffset) - switch:SetPoint("left", label, "right", 2, 0) - end - switch.hasLabel = label - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = switch - end - - local widgetTotalSize = label.widget:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, switch) - tinsert(parent.widget_list_by_type.switch, switch) - - widgetCreated = switch - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "range") then - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'range'") - local isDecimanls = widgetTable.usedecimals - local slider = DF:NewSlider(parent, nil, "$parentWidget" .. index, nil, 140, 20, widgetTable.min, widgetTable.max, widgetTable.step, widgetTable.get(), isDecimanls, nil, nil, sliderTemplate) - - 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) - - if (widgetTable.thumbscale) then - slider:SetThumbSize(slider.thumb:GetWidth() * widgetTable.thumbscale, nil) - else - slider:SetThumbSize(slider.thumb:GetWidth() * 1.3, nil) - end - - if (valueChangeHook) then - slider:SetHook("OnValueChange", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - slider:SetHook(hookName, hookFunc) - end - end - - local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - - slider:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - slider.hasLabel = label - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = slider - end - - local widgetTotalSize = label.widget:GetStringWidth() + 146 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, slider) - tinsert(parent.widget_list_by_type.slider, slider) - - widgetCreated = slider - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "color") then - assert(widgetTable.get, "DetailsFramework:BuildMenu(): .get not found in the widget table for 'color'") - local colorpick = DF:NewColorPickButton(parent, "$parentWidget" .. index, nil, widgetTable.set, nil, buttonTemplate) - - local descPhraseId = getDescripttionPhraseID(widgetTable, languageAddonId, languageTable) - DetailsFramework.Language.RegisterTableKeyWithDefault(languageAddonId, colorpick, "have_tooltip", descPhraseId, widgetTable.desc) - - colorpick._get = widgetTable.get - colorpick.widget_type = "color" - colorpick:SetSize(18, 18) - - local r, g, b, a = DF:ParseColors(widgetTable.get()) - colorpick:SetColor(r, g, b, a) - - if (valueChangeHook) then - colorpick:SetHook("OnColorChanged", valueChangeHook) - end - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - colorpick:SetHook(hookName, hookFunc) - end - end - - local label = DF:NewLabel(parent, nil, "$parentLabel" .. index, nil, "", "GameFontNormal", widgetTable.text_template or textTemplate or 12) - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, label.widget, namePhraseId, formatOptionNameWithColon(widgetTable.name, useColon)) - - if (widgetTable.boxfirst or useBoxFirstOnAllWidgets) then - label:SetPoint("left", colorpick, "right", 2) - colorpick:SetPoint(currentXOffset, currentYOffset) - extraPaddingY = 1 - else - colorpick:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - end - - colorpick.hasLabel = label - - if (widgetTable.id) then - parent.widgetids[widgetTable.id] = colorpick - end - - local widgetTotalSize = label.widget:GetStringWidth() + 32 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, colorpick) - tinsert(parent.widget_list_by_type.color, colorpick) - - widgetCreated = colorpick - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "execute") then - local button = DF:NewButton(parent, nil, "$parentWidget" .. index, nil, 120, 18, widgetTable.func, widgetTable.param1, widgetTable.param2, nil, "", nil, buttonTemplate, textTemplate) - - local namePhraseId = getNamePhraseID(widgetTable, languageAddonId, languageTable, true) - DetailsFramework.Language.RegisterObjectWithDefault(languageAddonId, button.widget, namePhraseId, widgetTable.name) - - if (not buttonTemplate) then - button:InstallCustomTexture() - end - - if (widgetTable.inline) then - if (latestInlineWidget) then - button:SetPoint("left", latestInlineWidget, "right", 2, 0) - latestInlineWidget = button - else - button:SetPoint(currentXOffset, currentYOffset) - latestInlineWidget = button - end - else - button:SetPoint(currentXOffset, currentYOffset) - end - - 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) then - button:SetWidth(widgetTable.width) - end - if (widgetTable.height) then - button:SetHeight(widgetTable.height) - end - - local widgetTotalSize = button:GetWidth() + 4 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, button) - tinsert(parent.widget_list_by_type.button, button) - - widgetCreated = button - amountLineWidgetCreated = amountLineWidgetCreated + 1 - - elseif (widgetTable.type == "textentry") then - local textentry = DF:CreateTextEntry(parent, widgetTable.func or widgetTable.set, 120, 18, nil, "$parentWidget" .. index, nil, buttonTemplate) - 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 = DF: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)) - - textentry:SetPoint("left", label, "right", 2) - label:SetPoint(currentXOffset, currentYOffset) - textentry.hasLabel = label - - --hook list - if (widgetTable.hooks) then - for hookName, hookFunc in pairs(widgetTable.hooks) do - textentry:SetHook(hookName, hookFunc) - end - end - - if (widgetTable.id) then - parent.widgetids [widgetTable.id] = textentry - end - - local widgetTotalSize = label.widget:GetStringWidth() + 64 - if (widgetTotalSize > maxColumnWidth) then - maxColumnWidth = widgetTotalSize - end - - --store the widget created into the overall table and the widget by type - tinsert(parent.widget_list, textentry) - tinsert(parent.widget_list_by_type.textentry, textentry) - - widgetCreated = textentry - amountLineWidgetCreated = amountLineWidgetCreated + 1 - end - - if (widgetTable.nocombat) then - tinsert(widgetsToDisableOnCombat, widgetCreated) - end - - if (not widgetTable.inline) then - if (widgetTable.spacement) then - currentYOffset = currentYOffset - 30 - else - currentYOffset = currentYOffset - 20 - end - end - - if (extraPaddingY > 0) then - currentYOffset = currentYOffset - extraPaddingY - end - - if (widgetTable.type == "breakline" or currentYOffset < height) then - currentYOffset = yOffset - currentXOffset = currentXOffset + maxColumnWidth + 20 - amountLineWidgetCreated = 0 - maxColumnWidth = 0 - end - end - end - - DF.RefreshUnsafeOptionsWidgets() - end - - local lockNotSafeWidgetsForCombat = function() - for _, widget in ipairs(widgetsToDisableOnCombat) do - widget:Disable() - end - end - - local unlockNotSafeWidgetsForCombat = function() - for _, widget in ipairs(widgetsToDisableOnCombat) do - widget:Enable() - end - end - - function DF.RefreshUnsafeOptionsWidgets() - if (DF.PlayerHasCombatFlag) then - lockNotSafeWidgetsForCombat() - else - unlockNotSafeWidgetsForCombat() - end - end - - DF.PlayerHasCombatFlag = false - local ProtectCombatFrame = CreateFrame("frame") - ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_ENABLED") - ProtectCombatFrame:RegisterEvent("PLAYER_REGEN_DISABLED") - ProtectCombatFrame:RegisterEvent("PLAYER_ENTERING_WORLD") - ProtectCombatFrame:SetScript("OnEvent", function(self, event) - if (event == "PLAYER_ENTERING_WORLD") then - if (InCombatLockdown()) then - DF.PlayerHasCombatFlag = true - else - DF.PlayerHasCombatFlag = false - end - DF.RefreshUnsafeOptionsWidgets() - - elseif (event == "PLAYER_REGEN_ENABLED") then - DF.PlayerHasCombatFlag = false - DF.RefreshUnsafeOptionsWidgets() - - elseif (event == "PLAYER_REGEN_DISABLED") then - DF.PlayerHasCombatFlag = true - DF.RefreshUnsafeOptionsWidgets() - end - end) - - function DF:CreateInCombatTexture(frame) - if (DF.debug and not frame) then - error("Details! Framework: CreateInCombatTexture invalid frame on parameter 1.") - end - - local inCombatBackgroundTexture = DF:CreateImage(frame) - inCombatBackgroundTexture:SetColorTexture(.6, 0, 0, .1) - inCombatBackgroundTexture:Hide() - - local inCombatLabel = Plater:CreateLabel(frame, "you are in combat", 24, "silver") - inCombatLabel:SetPoint("right", inCombatBackgroundTexture, "right", -10, 0) - inCombatLabel:Hide() - - frame:RegisterEvent("PLAYER_REGEN_DISABLED") - frame:RegisterEvent("PLAYER_REGEN_ENABLED") - - frame:SetScript("OnEvent", function(self, event) - if (event == "PLAYER_REGEN_DISABLED") then - inCombatBackgroundTexture:Show() - inCombatLabel:Show() - - elseif (event == "PLAYER_REGEN_ENABLED") then - inCombatBackgroundTexture:Hide() - inCombatLabel:Hide() - end - end) - - return inCombatBackgroundTexture - end - ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --tutorials function DF:ShowTutorialAlertFrame(maintext, desctext, clickfunc) @@ -5963,6 +4862,7 @@ local bBenchmarkEnabled = false function _G.__benchmark(bNotPrintResult) if (not bBenchmarkEnabled) then bBenchmarkEnabled = true + debugprofilestop() benchmarkTime = debugprofilestop() else local elapsed = debugprofilestop() - benchmarkTime diff --git a/Libs/DF/label.lua b/Libs/DF/label.lua index 94a4d0ec6..8514562b4 100644 --- a/Libs/DF/label.lua +++ b/Libs/DF/label.lua @@ -284,6 +284,7 @@ detailsFramework:Mixin(LabelMetaFunctions, detailsFramework.ScriptHookMixin) ---@field shadow fontflags ---@field outline fontflags ---@field rotation number +---@field SetPoint fun(self: df_label, point: any, relativeTo: any, relativePoint: any, x: any, y: any) set the label position ---@field SetTemplate fun(self: df_label, template: table) set the fontstring visual by a template ---@field SetTextColor fun(self: df_label, red: any, green: number|nil, blue: number|nil, alpha: number|nil) set the button text color ---@field SetTextTruncated fun(self: df_label, text: string, maxWidth: width) diff --git a/Libs/DF/load.xml b/Libs/DF/load.xml index b01f71ee8..c0505fa83 100644 --- a/Libs/DF/load.xml +++ b/Libs/DF/load.xml @@ -35,6 +35,7 @@