chore: hoist plugins to root and move main into Details/
Each Details_* plugin and the main Details addon now lives in its own repo-root folder, matching the Exiles fork-layout convention.
This commit is contained in:
@@ -0,0 +1,464 @@
|
||||
|
||||
local detailsFramework = DetailsFramework
|
||||
|
||||
if (not detailsFramework or not DetailsFrameworkCanLoad) then
|
||||
return
|
||||
end
|
||||
|
||||
local unpack = unpack
|
||||
local CreateFrame = CreateFrame
|
||||
local PixelUtil = PixelUtil
|
||||
|
||||
---@class df_tabinfotable : table
|
||||
---@field name string
|
||||
---@field text string
|
||||
---@field createOnDemandFunc function?
|
||||
|
||||
---@class df_tabcontainer : frame
|
||||
---@field AllFrames df_tabcontainerframe[]
|
||||
---@field AllButtons df_tabcontainerbutton[]
|
||||
---@field AllFramesByName table<string, df_tabcontainerframe>
|
||||
---@field AllButtonsByName table<string, df_tabcontainerbutton>
|
||||
---@field hookList table
|
||||
---@field CurrentIndex number
|
||||
---@field IsContainer boolean
|
||||
---@field ButtonSelectedBorderColor table
|
||||
---@field ButtonNotSelectedBorderColor table
|
||||
---@field CanCloseWithRightClick boolean
|
||||
---@field SetIndex fun(self: df_tabcontainer, index: number)
|
||||
---@field SelectTabByIndex fun(self: df_tabcontainer, menuIndex: number)
|
||||
---@field SelectTabByName fun(self: df_tabcontainer, name: string)
|
||||
---@field CreateUnderlineGlow fun(button: button)
|
||||
---@field OnShow fun(self: df_tabcontainer)
|
||||
---@field GetTabFrameByName fun(self: df_tabcontainer, name: string): df_tabcontainerframe
|
||||
---@field GetTabFrameByIndex fun(self: df_tabcontainer, index: number): df_tabcontainerframe
|
||||
---@field GetTabButtonByName fun(self: df_tabcontainer, name: string): df_tabcontainerbutton
|
||||
---@field GetTabButtonByIndex fun(self: df_tabcontainer, index: number): df_tabcontainerbutton
|
||||
|
||||
---@class df_tabcontainerframe : frame
|
||||
---@field bIsFrontPage boolean
|
||||
---@field titleText fontstring
|
||||
---@field tabIndex number
|
||||
---@field OnMouseDown fun(self: df_tabcontainerframe, button: string)
|
||||
---@field OnMouseUp fun(self: df_tabcontainerframe, button: string)
|
||||
---@field RefreshOptions fun(self: df_tabcontainerframe)|nil
|
||||
|
||||
---@class df_tabcontainerbutton : button
|
||||
---@field selectedUnderlineGlow texture
|
||||
---@field textsize number
|
||||
---@field mainFrame df_tabcontainer
|
||||
---@field leftSelectionIndicator texture
|
||||
|
||||
--create a template for the tab buttons
|
||||
local tabTemplate = detailsFramework.table.copy({}, detailsFramework:GetTemplate("button", "OPTIONS_BUTTON_TEMPLATE"))
|
||||
tabTemplate.backdropbordercolor = nil
|
||||
|
||||
detailsFramework.TabContainerMixin = {
|
||||
---@param self df_tabcontainer
|
||||
---@param tabIndex number
|
||||
---@return df_tabcontainerframe
|
||||
GetTabFrameByIndex = function(self, tabIndex)
|
||||
return self.AllFrames[tabIndex]
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
---@param name string
|
||||
---@return df_tabcontainerframe
|
||||
GetTabFrameByName = function(self, name)
|
||||
return self.AllFramesByName[name]
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
---@param tabIndex number
|
||||
---@return df_tabcontainerbutton
|
||||
GetTabButtonByIndex = function(self, tabIndex)
|
||||
return self.AllButtons[tabIndex]
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
---@param name string
|
||||
---@return df_tabcontainerbutton
|
||||
GetTabButtonByName = function(self, name)
|
||||
return self.AllButtonsByName[name]
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
---@param backdropTable backdrop|nil
|
||||
---@param backdropColorTable table|string|nil
|
||||
---@param backdropBorderColorTable table|string|nil
|
||||
SetTabFramesBackdrop = function(self, backdropTable, backdropColorTable, backdropBorderColorTable)
|
||||
for tabIndex, tabFrame in ipairs(self.AllFrames) do
|
||||
if (backdropTable) then
|
||||
tabFrame:SetBackdrop(backdropTable)
|
||||
end
|
||||
if (backdropColorTable) then
|
||||
local r, g, b, a = detailsFramework:ParseColors(backdropColorTable)
|
||||
tabFrame:SetBackdropColor(r, g, b, a)
|
||||
end
|
||||
if (backdropBorderColorTable) then
|
||||
local r, g, b, a = detailsFramework:ParseColors(backdropColorTable)
|
||||
tabFrame:SetBackdropBorderColor(r, g, b, a)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
---create a underglow texture for the selected tab, this texture is a small yellow bright gradient below the button
|
||||
---@param self df_tabcontainerbutton
|
||||
CreateUnderlineGlow = function(self)
|
||||
local selectedGlow = self:CreateTexture(nil, "background", nil, -4)
|
||||
selectedGlow:SetPoint("topleft", self["widget"], "bottomleft", -7, 0)
|
||||
selectedGlow:SetPoint("topright", self["widget"], "bottomright", 7, 0)
|
||||
selectedGlow:SetTexture([[Interface\BUTTONS\UI-Panel-Button-Glow]])
|
||||
selectedGlow:SetTexCoord(0, 95/128, 30/64, 38/64)
|
||||
selectedGlow:SetBlendMode("ADD")
|
||||
selectedGlow:SetHeight(8)
|
||||
selectedGlow:SetAlpha(.75)
|
||||
selectedGlow:Hide()
|
||||
self.selectedUnderlineGlow = selectedGlow
|
||||
end,
|
||||
|
||||
---@param tabContainer df_tabcontainer
|
||||
---@param menuIndex number
|
||||
SelectTabByIndex = function(tabContainer, menuIndex)
|
||||
---@type df_tabcontainerbutton
|
||||
local tabButton = tabContainer.AllButtons[menuIndex]
|
||||
---@type df_tabcontainerframe
|
||||
local tabFrame = tabContainer.AllFrames[menuIndex]
|
||||
|
||||
--hide all tab frame and hide the selection glow from tab buttons
|
||||
for i = 1, #tabContainer.AllFrames do
|
||||
---@type df_tabcontainerframe
|
||||
local thisTabFrame = tabContainer.AllFrames[i]
|
||||
thisTabFrame:Hide()
|
||||
|
||||
---@type df_tabcontainerbutton
|
||||
local thisTabButton = tabContainer.AllButtons[i]
|
||||
if (tabContainer.ButtonNotSelectedBorderColor) then
|
||||
thisTabButton:SetBackdropBorderColor(unpack(tabContainer.ButtonNotSelectedBorderColor))
|
||||
end
|
||||
if (thisTabButton.selectedUnderlineGlow) then
|
||||
thisTabButton.selectedUnderlineGlow:Hide()
|
||||
end
|
||||
end
|
||||
|
||||
tabFrame:Show()
|
||||
if (tabFrame.RefreshOptions) then
|
||||
tabFrame:RefreshOptions()
|
||||
end
|
||||
|
||||
if (tabContainer.ButtonSelectedBorderColor) then
|
||||
tabButton:SetBackdropBorderColor(unpack(tabContainer.ButtonSelectedBorderColor))
|
||||
end
|
||||
|
||||
if (tabButton.selectedUnderlineGlow) then
|
||||
tabButton.selectedUnderlineGlow:Show()
|
||||
end
|
||||
|
||||
tabContainer.CurrentIndex = menuIndex
|
||||
|
||||
if (tabContainer.hookList.OnSelectIndex) then
|
||||
detailsFramework:QuickDispatch(tabContainer.hookList.OnSelectIndex, tabContainer, tabButton)
|
||||
end
|
||||
end,
|
||||
|
||||
---@param tabContainer df_tabcontainer
|
||||
---@param name string
|
||||
SelectTabByName = function(tabContainer, name)
|
||||
---@type df_tabcontainerframe
|
||||
local tabFrame = tabContainer.AllFramesByName[name]
|
||||
if (tabFrame) then
|
||||
local tabIndex = tabFrame.tabIndex
|
||||
tabContainer:SelectTabByIndex(tabIndex)
|
||||
else
|
||||
error("df_tabcontainer:SelectTabByName(name): param #2 'name' not found within 'tabContainer.AllFramesByName'.")
|
||||
end
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
---@param index number
|
||||
SetIndex = function(self, index)
|
||||
self.CurrentIndex = index
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainer
|
||||
OnShow = function(self)
|
||||
local index = self.CurrentIndex
|
||||
self:SelectTabByIndex(index)
|
||||
end
|
||||
}
|
||||
|
||||
detailsFramework.TabContainerFrameMixin = {
|
||||
---@param self df_tabcontainerframe
|
||||
---@param button string
|
||||
OnMouseDown = function(self, button)
|
||||
--search for UIParent
|
||||
---@type frame
|
||||
local highestParent = detailsFramework:FindHighestParent(self)
|
||||
local tabContainer = self:GetParent()
|
||||
---@cast tabContainer df_tabcontainer
|
||||
|
||||
if (button == "LeftButton") then
|
||||
if (not highestParent.IsMoving and highestParent:IsMovable()) then
|
||||
highestParent:StartMoving()
|
||||
highestParent.IsMoving = true
|
||||
end
|
||||
|
||||
elseif (button == "RightButton") then
|
||||
if (not highestParent.IsMoving and tabContainer.IsContainer) then
|
||||
if (self.bIsFrontPage) then
|
||||
if (tabContainer.CanCloseWithRightClick) then
|
||||
if (highestParent["CloseFunction"]) then
|
||||
highestParent["CloseFunction"](highestParent)
|
||||
else
|
||||
highestParent:Hide()
|
||||
end
|
||||
end
|
||||
else
|
||||
--goes back to front page
|
||||
tabContainer:SelectTabByIndex(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
---@param self df_tabcontainerframe
|
||||
---@param button string
|
||||
OnMouseUp = function(self, button)
|
||||
local frame = detailsFramework:FindHighestParent(self)
|
||||
if (frame.IsMoving) then
|
||||
frame:StopMovingOrSizing()
|
||||
frame.IsMoving = false
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
---@class df_tabcontaineroptions : table
|
||||
---@field width number?
|
||||
---@field height number?
|
||||
---@field button_border_color table?
|
||||
---@field button_selected_border_color table?
|
||||
---@field right_click_y number?
|
||||
---@field hide_click_label boolean?
|
||||
---@field close_text_alpha number?
|
||||
---@field rightbutton_always_close boolean?
|
||||
---@field right_click_interact boolean?
|
||||
---@field y_offset number?
|
||||
---@field button_width number?
|
||||
---@field button_height number?
|
||||
---@field button_x number?
|
||||
---@field button_y number?
|
||||
---@field button_text_size number?
|
||||
---@field container_width_offset number?
|
||||
|
||||
---creates a frame called tabContainer which is used as base for the tab container object
|
||||
---the function receives a table called tabList which contains sub tables with two keys 'name' and 'text', name is the frame name and text is the text displayed on the button
|
||||
---then the function iterate amongst the tabList and create a frame and a button for each entry using the value of the 'text' key as the text for the button and 'name' for the name of the frame
|
||||
---when the user click on a button, the tabContainer hide all frames and show the frame which was created together with that button
|
||||
---@param parent frame the parent frame
|
||||
---@param title string a string to use as the title of the tab container, the title is always shown
|
||||
---@param frameName string the frame name to pass into the CreateFrame function
|
||||
---@param tabList df_tabinfotable[] the list of tabs to create, each entry has a 'name' and 'text' keys
|
||||
---@param optionsTable df_tabcontaineroptions?
|
||||
---@param hookList table<string, function>?
|
||||
---@param languageInfo any
|
||||
---@return df_tabcontainer
|
||||
function detailsFramework:CreateTabContainer(parent, title, frameName, tabList, optionsTable, hookList, languageInfo)
|
||||
optionsTable = optionsTable or {}
|
||||
|
||||
local parentFrameWidth = parent:GetWidth()
|
||||
local yOffset = optionsTable.y_offset or 0
|
||||
local buttonWidth = optionsTable.button_width or 160
|
||||
local buttonHeight = optionsTable.button_height or 20
|
||||
local buttonAnchorX = optionsTable.button_x or 230
|
||||
local buttonAnchorY = optionsTable.button_y or 0
|
||||
local buttonTextSize = optionsTable.button_text_size or 10
|
||||
local containerWidthOffset = optionsTable.container_width_offset or 0
|
||||
|
||||
local bFirstTabIsCreateOnDemand = false
|
||||
|
||||
--create the base frame
|
||||
---@type df_tabcontainer
|
||||
local tabContainer = CreateFrame("frame", frameName, parent["widget"] or parent, "BackdropTemplate")
|
||||
tabContainer.hookList = hookList or {}
|
||||
tabContainer:SetSize(optionsTable.width or 750, optionsTable.height or 450)
|
||||
|
||||
detailsFramework:Mixin(tabContainer, detailsFramework.TabContainerMixin)
|
||||
|
||||
--create the fontstring which show the title
|
||||
---@type fontstring
|
||||
local mainTitle = detailsFramework:CreateLabel(tabContainer, title, 24, "white")
|
||||
mainTitle:SetPoint("topleft", tabContainer, "topleft", 10, -30 + yOffset)
|
||||
|
||||
tabContainer.AllFrames = {}
|
||||
tabContainer.AllButtons = {}
|
||||
tabContainer.AllFramesByName = {}
|
||||
tabContainer.AllButtonsByName = {}
|
||||
tabContainer.CurrentIndex = 1
|
||||
tabContainer.IsContainer = true
|
||||
tabContainer.ButtonSelectedBorderColor = optionsTable.button_selected_border_color or {1, 1, 0, 1}
|
||||
tabContainer.ButtonNotSelectedBorderColor = optionsTable.button_border_color or {0, 0, 0, 0}
|
||||
|
||||
if (optionsTable.right_click_interact ~= nil) then
|
||||
tabContainer.CanCloseWithRightClick = optionsTable.right_click_interact
|
||||
else
|
||||
tabContainer.CanCloseWithRightClick = true
|
||||
end
|
||||
|
||||
--languageInfo
|
||||
local addonId = languageInfo and languageInfo.language_addonId or "none"
|
||||
|
||||
for tabIndex, tabInfo in ipairs(tabList) do
|
||||
--create a frame which will be shown when the tabButton is clicked
|
||||
--when this tab isn't selected, this frame is hidden
|
||||
---@type df_tabcontainerframe
|
||||
local tabFrame = CreateFrame("frame", "$parent" .. tabInfo.name, tabContainer, "BackdropTemplate")
|
||||
detailsFramework:Mixin(tabFrame, detailsFramework.TabContainerFrameMixin)
|
||||
tabFrame:SetAllPoints()
|
||||
tabFrame:SetFrameLevel(210)
|
||||
tabFrame:SetScript("OnMouseDown", tabFrame.OnMouseDown)
|
||||
tabFrame:SetScript("OnMouseUp", tabFrame.OnMouseUp)
|
||||
tabFrame.tabIndex = tabIndex
|
||||
tabFrame:Hide()
|
||||
|
||||
if (tabInfo.createOnDemandFunc) then
|
||||
tabFrame:SetScript("OnShow", function()
|
||||
if (tabInfo.createOnDemandFunc) then
|
||||
detailsFramework:Dispatch(tabInfo.createOnDemandFunc, tabFrame, tabContainer, parent)
|
||||
tabInfo.createOnDemandFunc = nil
|
||||
end
|
||||
end)
|
||||
|
||||
if (tabIndex == 1) then
|
||||
bFirstTabIsCreateOnDemand = true
|
||||
end
|
||||
end
|
||||
|
||||
--attempt to get the localized text from the language system using the addonId and the frameInfo.text
|
||||
local phraseId = tabInfo.text
|
||||
local bIsLanguagePrahseID = detailsFramework.Language.DoesPhraseIDExistsInDefaultLanguage(addonId, phraseId)
|
||||
|
||||
--create the fontstring which show this tab text, this text is only shown when the tab is shown
|
||||
local titleLabel = detailsFramework:CreateLabel(tabFrame, "", 16, "silver")
|
||||
if (bIsLanguagePrahseID) then
|
||||
DetailsFramework.Language.RegisterObjectWithDefault(addonId, titleLabel, tabInfo.text, tabInfo.text)
|
||||
else
|
||||
titleLabel:SetText(tabInfo.text)
|
||||
end
|
||||
titleLabel:SetPoint("topleft", mainTitle, "bottomleft", 0, 0)
|
||||
tabFrame.titleText = titleLabel
|
||||
|
||||
---@type df_tabcontainerbutton
|
||||
local tabButton = detailsFramework:CreateButton(tabContainer, function() tabContainer:SelectTabByIndex(tabIndex) end, buttonWidth, buttonHeight, tabInfo.text, tabIndex, nil, nil, nil, "$parentTabButton" .. tabInfo.name, false, tabTemplate)
|
||||
PixelUtil.SetSize(tabButton, buttonWidth, buttonHeight)
|
||||
tabButton:SetFrameLevel(220)
|
||||
tabButton.textsize = buttonTextSize
|
||||
tabButton.mainFrame = tabContainer
|
||||
tabContainer.CreateUnderlineGlow(tabButton)
|
||||
|
||||
--register the fontstring with the language system
|
||||
if (bIsLanguagePrahseID) then
|
||||
DetailsFramework.Language.RegisterObjectWithDefault(addonId, tabButton["widget"], tabInfo.text, tabInfo.text)
|
||||
end
|
||||
|
||||
local rightClickToBack
|
||||
if (tabIndex == 1 or optionsTable.rightbutton_always_close) then
|
||||
rightClickToBack = detailsFramework:CreateLabel(tabFrame, "right click to close", 10, "gray")
|
||||
rightClickToBack:SetPoint("bottomright", tabFrame, "bottomright", -1, optionsTable.right_click_y or 0)
|
||||
if (optionsTable.close_text_alpha) then
|
||||
rightClickToBack:SetAlpha(optionsTable.close_text_alpha)
|
||||
end
|
||||
tabFrame.bIsFrontPage = true
|
||||
else
|
||||
rightClickToBack = detailsFramework:CreateLabel(tabFrame, "right click to go back to main menu", 10, "gray")
|
||||
rightClickToBack:SetPoint("bottomright", tabFrame, "bottomright", -1, optionsTable.right_click_y or 0)
|
||||
if (optionsTable.close_text_alpha) then
|
||||
rightClickToBack:SetAlpha(optionsTable.close_text_alpha)
|
||||
end
|
||||
end
|
||||
|
||||
if (optionsTable.hide_click_label) then
|
||||
rightClickToBack:Hide()
|
||||
end
|
||||
|
||||
table.insert(tabContainer.AllFrames, tabFrame)
|
||||
table.insert(tabContainer.AllButtons, tabButton)
|
||||
tabContainer.AllFramesByName[tabInfo.name] = tabFrame
|
||||
tabContainer.AllFramesByName[tabInfo.text] = tabFrame
|
||||
tabContainer.AllButtonsByName[tabInfo.name] = tabButton
|
||||
tabContainer.AllButtonsByName[tabInfo.text] = tabButton
|
||||
end
|
||||
|
||||
--order buttons
|
||||
local x = buttonAnchorX
|
||||
local y = buttonAnchorY
|
||||
local spaceBetweenButtons = 3
|
||||
|
||||
local allocatedSpaceForButtons = parentFrameWidth - ((#tabList - 2) * spaceBetweenButtons) - buttonAnchorX + containerWidthOffset
|
||||
local amountButtonsPerRow = math.floor(allocatedSpaceForButtons / buttonWidth)
|
||||
|
||||
tabContainer.AllButtons[1]:SetPoint("topleft", mainTitle, "topleft", x, y)
|
||||
x = x + buttonWidth + 2
|
||||
|
||||
for i = 2, #tabContainer.AllButtons do
|
||||
local button = tabContainer.AllButtons[i]
|
||||
PixelUtil.SetPoint(button, "topleft", mainTitle, "topleft", x, y)
|
||||
x = x + buttonWidth + 2
|
||||
|
||||
if (i % amountButtonsPerRow == 0) then
|
||||
x = buttonAnchorX
|
||||
y = y - buttonHeight - 1
|
||||
end
|
||||
end
|
||||
|
||||
--when show the frame, reset to the current internal index
|
||||
tabContainer:SetScript("OnShow", tabContainer.OnShow)
|
||||
--select the first frame
|
||||
local defaultTab = 1
|
||||
|
||||
if (bFirstTabIsCreateOnDemand) then
|
||||
C_Timer.After(0, function() tabContainer:SelectTabByIndex(defaultTab) end)
|
||||
else
|
||||
tabContainer:SelectTabByIndex(defaultTab)
|
||||
end
|
||||
|
||||
return tabContainer
|
||||
end
|
||||
|
||||
|
||||
--[=[example:
|
||||
|
||||
local parent = UIParent
|
||||
local title = "My AddOn Options"
|
||||
local frameName = "MyAddOnOptionsFrame"
|
||||
local tabList = {
|
||||
{name = "GeneralSettings", text = "General Settings"},
|
||||
{name = "AdvancedSettings", text = "Advanced Settings"},
|
||||
{name = "AboutTheAddon", text = "Addon Info"},
|
||||
}
|
||||
local optionsTable = {}
|
||||
local hookList = {}
|
||||
local languageInfo = {language_addonId = "MyAddOnTocName"}
|
||||
|
||||
local tabContainer = DetailsFramework:CreateTabContainer(parent, title, frameName, tabList, optionsTable, hookList, languageInfo)
|
||||
tabContainer:SetPoint("center", UIParent, "center", 0, 0)
|
||||
tabContainer:SetSize(750, 450)
|
||||
tabContainer:Show()
|
||||
|
||||
--ways for getting a tab frame and start to create widgets inside it
|
||||
local tabIndex = 1
|
||||
local generalSettingsTabFrame = tabContainer:GetTabFrameByIndex(tabIndex) --using a tabIndex
|
||||
local advancedSettingsTabFrame = tabContainer:GetTabFrameByName("Advanced Settings") --using the tab text
|
||||
local aboutTabFrame = tabContainer:GetTabFrameByName("AboutTheAddon") --using the tab name
|
||||
|
||||
--clicking on tab buttons will automatically show the tab frame, to select a tab frame without clicking on the button, use:
|
||||
tabContainer:SelectTabByIndex(tabIndex) --using a tabIndex
|
||||
tabContainer:SelectTabByName("Advanced Settings") --using the tab text
|
||||
tabContainer:SelectTabByName("AdvancedSettings") --using the tab name
|
||||
|
||||
--modify the background color by applying a backdrop
|
||||
local backdropTable = {edgeFile = [[Interface\Buttons\WHITE8X8]], edgeSize = 1, bgFile = [[Interface\Tooltips\UI-Tooltip-Background]], tileSize = 64, tile = true}
|
||||
local backdropColor = {DetailsFramework:GetDefaultBackdropColor()}
|
||||
local backdropBorderColor = {0, 0, 0, 1}
|
||||
tabContainer:SetTabFramesBackdrop(backdropTable, backdropColor, backdropBorderColor)
|
||||
|
||||
--]=]
|
||||
Reference in New Issue
Block a user