You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
875 lines
34 KiB
875 lines
34 KiB
local MAJOR, MINOR = "LibDropDownExtension-1.0", 3
|
|
assert(LibStub, MAJOR .. " requires LibStub")
|
|
|
|
---@class DropDownList : Button
|
|
---@field public dropdown CustomDropDown
|
|
---@field public GetID fun(): number
|
|
---@field public maxWidth number
|
|
|
|
---@class LibDropDownExtension
|
|
---@field public _callbacks CustomDropDownCallback[]
|
|
---@field public _cdropdowns table<DropDownList, CustomDropDown>
|
|
---@field public _separatorTable CustomDropDownOption[]
|
|
---@field public _hooked table<DropDownList, boolean>
|
|
---@field public _Broadcast fun(self: LibDropDownExtension, event: LibDropDownExtensionEvent, dropdown: DropDownList)
|
|
---@field public Option table<string, CustomDropDownOption> `LibDropDownExtension.Option.Separator` `LibDropDownExtension.Option.Space`
|
|
---@field public RegisterEvent fun(self: LibDropDownExtension, events: string, func: LibDropDownExtensionCallback, levels?: number|boolean, data?: table): boolean `LibDropDownExtension:RegisterEvent(events, func[, levels[, data]])` where func is later called as `func(dropdown, event, options, level, data)` and the return boolean if true will append the options to the dropdown, otherwise false will ignore appending our options to the dropdown.
|
|
---@field public UnregisterEvent fun(self: LibDropDownExtension, events: string, func: LibDropDownExtensionCallback, levels?: number|boolean): boolean `LibDropDownExtension:UnregisterEvent(events, func[, levels])`
|
|
|
|
---@alias LibDropDownExtensionEvent "OnShow"|"OnHide"
|
|
|
|
---@alias LibDropDownExtensionCallback fun(dropdown: CustomDropDown, event: LibDropDownExtensionEvent, options: CustomDropDownOption[], level: number, data?: table): CustomDropDownOption[]?
|
|
|
|
---@type LibDropDownExtension?, number?
|
|
local Lib, LibPrevMinor = LibStub:NewLibrary(MAJOR, MINOR) ---@diagnostic disable-line: assign-type-mismatch
|
|
if not Lib then return end
|
|
|
|
---@class CustomDropDownOptionIconInfo
|
|
---@field public tCoordLeft number
|
|
---@field public tCoordRight number
|
|
---@field public tCoordTop number
|
|
---@field public tCoordBottom number
|
|
---@field public tSizeX number
|
|
---@field public tSizeY number
|
|
---@field public tFitDropDownSizeX boolean
|
|
---@field public disablecolor? string
|
|
|
|
-- copy pasta more or less from UIDropDownMenu.lua about UIDropDownMenu_CreateInfo()
|
|
---@class CustomDropDownOption : CustomDropDownOptionIconInfo
|
|
---@field public text string @The text of the button
|
|
---@field public value any @The value that UIDROPDOWNMENU_MENU_VALUE is set to when the button is clicked
|
|
---@field public func fun(button: CustomDropDownButton, arg1: any, arg2: any, checked: boolean) @The function that is called when you click the button. Called as `func(button, option.arg1, option.arg2, option.checkedEval)`
|
|
---@field public checked boolean|fun(self: CustomDropDownOption): boolean? @Check the button if true or function returns true
|
|
---@field public checkedEval boolean @The final value of the checked state (in case a function and such, use this in your click handler)
|
|
---@field public isNotRadio boolean @Check the button uses radial image if false check box image if true
|
|
---@field public isTitle boolean @If it's a title the button is disabled and the font color is set to yellow
|
|
---@field public disabled boolean @Disable the button and show an invisible button that still traps the mouseover event so menu doesn't time out
|
|
---@field public tooltipWhileDisabled boolean @Show the tooltip, even when the button is disabled.
|
|
---@field public hasArrow boolean @Show the expand arrow for multilevel menus
|
|
---@field public hasColorSwatch boolean @Show color swatch or not, for color selection
|
|
---@field public r number @Red color value of the color swatch [1-255]
|
|
---@field public g number @Green color value of the color swatch [1-255]
|
|
---@field public b number @Blue color value of the color swatch [1-255]
|
|
---@field public colorCode string @"|cAARRGGBB" embedded hex value of the button text color. Only used when button is enabled
|
|
---@field public swatchFunc function @Function called by the color picker on color change
|
|
---@field public hasOpacity boolean @Show the opacity slider on the colorpicker frame
|
|
---@field public opacity number @Percentatge of the opacity, 1.0 is fully shown, 0 is transparent [0.0-1.0]
|
|
---@field public opacityFunc function @Function called by the opacity slider when you change its value
|
|
---@field public cancelFunc function @Function called by the colorpicker when you click the cancel button (it takes the previous values as its argument)
|
|
---@field public notClickable boolean @Disable the button and color the font white
|
|
---@field public notCheckable boolean @Shrink the size of the buttons and don't display a check box
|
|
---@field public owner table @Dropdown frame that "owns" the current dropdownlist
|
|
---@field public keepShownOnClick boolean @Don't hide the dropdownlist after a button is clicked
|
|
---@field public tooltipTitle string @Title of the tooltip shown on mouseover
|
|
---@field public tooltipText string @Text of the tooltip shown on mouseover
|
|
---@field public tooltipOnButton boolean @Show the tooltip attached to the button instead of as a Newbie tooltip.
|
|
---@field public noTooltipWhileEnabled boolean
|
|
---@field public justifyH string @Justify button text like "LEFT", "CENTER", "RIGHT"
|
|
---@field public arg1 any @This is the first argument used by .func
|
|
---@field public arg2 any @This is the second argument used by .func
|
|
---@field public fontObject table @font object replacement for Normal and Highlight
|
|
---@field public menuTable table @This contains an array of info tables to be displayed as a child menu
|
|
---@field public noClickSound boolean @Set to 1 to suppress the sound when clicking the button. The sound only plays if .func is set.
|
|
---@field public padding number @Number of pixels to pad the text on the right side
|
|
---@field public leftPadding number @Number of pixels to pad the button on the left side
|
|
---@field public minWidth number @Minimum width for this line
|
|
---@field public customFrame table @Allows this button to be a completely custom frame, should inherit from UIDropDownCustomMenuEntryTemplate and override appropriate methods.
|
|
---@field public icon string|number @An icon for the button.
|
|
---@field public mouseOverIcon string|number @An override icon when a button is moused over.
|
|
---@field public dist number
|
|
---@field public isUninteractable boolean
|
|
---@field public iconOnly boolean
|
|
---@field public iconInfo CustomDropDownOptionIconInfo
|
|
---@field public customCheckIconAtlas string
|
|
---@field public customCheckIconTexture string|number
|
|
---@field public customUncheckIconAtlas string
|
|
---@field public customUncheckIconTexture string|number
|
|
---@field public show? fun()
|
|
|
|
---@class CustomDropDownCallback
|
|
---@field public events table<LibDropDownExtensionEvent, number|boolean>
|
|
---@field public func LibDropDownExtensionCallback
|
|
---@field public options CustomDropDownOption[]
|
|
---@field public data table
|
|
|
|
Lib._callbacks = Lib._callbacks or {}
|
|
local callbacks = Lib._callbacks
|
|
|
|
---@class CustomDropDownButtonIconTexture : Texture
|
|
---@field public tFitDropDownSizeX? number
|
|
|
|
---@class CustomDropDownButton : Button
|
|
---@field public option CustomDropDownOption
|
|
---@field public order number
|
|
---@field public invisibleButton Button
|
|
---@field public highlight Texture
|
|
---@field public normalText FontString
|
|
---@field public iconTexture CustomDropDownButtonIconTexture
|
|
---@field public expandArrow Button
|
|
---@field public check Texture
|
|
---@field public uncheck Texture
|
|
---@field public colorSwatch Button
|
|
---@field public colorSwatchNormalTexture Texture
|
|
---@field public mouseOverIcon string?
|
|
---@field public tooltipOnButton boolean?
|
|
---@field public tooltipTitle string?
|
|
---@field public tooltipWhileDisabled boolean?
|
|
---@field public tooltipInstruction string?
|
|
---@field public tooltipText string?
|
|
---@field public tooltipWarning string?
|
|
---@field public colorSwatchBg Texture?
|
|
|
|
---@class CustomDropDown : DropDownList
|
|
---@field public options CustomDropDownOption[]
|
|
---@field public buttons CustomDropDownButton[]
|
|
|
|
Lib._cdropdowns = Lib._cdropdowns or {}
|
|
local cdropdowns = Lib._cdropdowns
|
|
|
|
---@param self CustomDropDownButton
|
|
local function CustomDropDownButton_OnClick(self)
|
|
local option = self.option
|
|
if not option then
|
|
return
|
|
end
|
|
local cdropdown = self:GetParent() ---@type Frame
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local checked = option.checked ---@type boolean
|
|
if type(checked) == "function" then
|
|
checked = checked(self)
|
|
end
|
|
if option.keepShownOnClick and not option.notCheckable then
|
|
if checked then
|
|
checked = false
|
|
self.check:Hide()
|
|
self.uncheck:Show()
|
|
else
|
|
checked = true
|
|
self.check:Show()
|
|
self.uncheck:Hide()
|
|
end
|
|
end
|
|
if type(option.checkedEval) ~= "function" then
|
|
option.checkedEval = checked
|
|
end
|
|
if type(option.func) == "function" then
|
|
option.func(self, option.arg1, option.arg2, checked)
|
|
end
|
|
if not option.noClickSound then
|
|
PlaySound(SOUNDKIT.U_CHAT_SCROLL_BUTTON)
|
|
end
|
|
if not option.keepShownOnClick then
|
|
cdropdown:GetParent():Hide() -- CloseDropDownMenus()
|
|
end
|
|
end
|
|
|
|
---@param self CustomDropDownButton
|
|
local function CustomDropDownButton_OnEnter(self)
|
|
local option = self.option
|
|
if not option then
|
|
return
|
|
end
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local cdropdown = self:GetParent() ---@type CustomDropDown
|
|
if option.hasArrow then
|
|
-- open next dropdown level and we don't support that
|
|
else
|
|
CloseDropDownMenus(cdropdown:GetID() + 1)
|
|
end
|
|
self.highlight:Show()
|
|
local shownTooltip
|
|
if self.normalText:IsTruncated() then
|
|
if not shownTooltip then
|
|
shownTooltip = true
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
|
GameTooltip_SetTitle(GameTooltip, self.normalText:GetText())
|
|
else
|
|
GameTooltip:AddLine(self.normalText:GetText(), 1, 1, 1, false)
|
|
end
|
|
GameTooltip:Show()
|
|
end
|
|
if option.tooltipTitle and not option.noTooltipWhileEnabled then
|
|
if option.tooltipOnButton then
|
|
if not shownTooltip then
|
|
shownTooltip = true
|
|
GameTooltip:SetOwner(self, "ANCHOR_RIGHT")
|
|
GameTooltip_SetTitle(GameTooltip, option.tooltipTitle)
|
|
else
|
|
GameTooltip:AddLine(option.tooltipTitle, 1, 1, 1, false)
|
|
end
|
|
if option.tooltipText then
|
|
GameTooltip_AddNormalLine(GameTooltip, option.tooltipText, true)
|
|
end
|
|
GameTooltip:Show()
|
|
end
|
|
end
|
|
if option.mouseOverIcon then
|
|
self.iconTexture:SetTexture(self.mouseOverIcon)
|
|
self.iconTexture:Show()
|
|
end
|
|
end
|
|
|
|
---@param self CustomDropDownButton
|
|
local function CustomDropDownButton_OnLeave(self)
|
|
self.highlight:Hide()
|
|
GameTooltip:Hide()
|
|
local option = self.option
|
|
if option.mouseOverIcon then
|
|
if option.icon then
|
|
self.iconTexture:SetTexture(option.icon)
|
|
else
|
|
self.iconTexture:Hide()
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param self CustomDropDownButton
|
|
local function CustomDropDownButton_OnEnable(self)
|
|
self.invisibleButton:Hide()
|
|
end
|
|
|
|
---@param self CustomDropDownButton
|
|
local function CustomDropDownButton_OnDisable(self)
|
|
self.invisibleButton:Show()
|
|
end
|
|
|
|
local function CustomDropDownButton_InvisibleButton_OnEnter(self)
|
|
local button = self:GetParent() ---@type CustomDropDownButton
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local cdropdown = button:GetParent() ---@type CustomDropDown
|
|
CloseDropDownMenus(cdropdown:GetID() + 1)
|
|
if not button.tooltipOnButton or (not button.tooltipTitle and not button.tooltipWhileDisabled) then
|
|
return
|
|
end
|
|
GameTooltip:SetOwner(button, "ANCHOR_RIGHT")
|
|
GameTooltip_SetTitle(GameTooltip, button.tooltipTitle)
|
|
if button.tooltipInstruction then
|
|
GameTooltip_AddInstructionLine(GameTooltip, button.tooltipInstruction)
|
|
end
|
|
if button.tooltipText then
|
|
GameTooltip_AddNormalLine(GameTooltip, button.tooltipText, true)
|
|
end
|
|
if button.tooltipWarning then
|
|
GameTooltip_AddColoredLine(GameTooltip, button.tooltipWarning, RED_FONT_COLOR, true)
|
|
end
|
|
GameTooltip:Show()
|
|
end
|
|
|
|
local function CustomDropDownButton_InvisibleButton_OnLeave(self)
|
|
GameTooltip:Hide()
|
|
end
|
|
|
|
local function CustomDropDownButton_ColorSwatch_OnClick(self)
|
|
local button = self:GetParent() ---@type CustomDropDownButton
|
|
CloseMenus()
|
|
UIDropDownMenuButton_OpenColorPicker(button)
|
|
end
|
|
|
|
local function CustomDropDownButton_ColorSwatch_OnEnter(self)
|
|
local button = self:GetParent() ---@type CustomDropDownButton
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local cdropdown = button:GetParent() ---@type CustomDropDown
|
|
CloseDropDownMenus(cdropdown:GetID() + 1)
|
|
button.colorSwatchBg:SetVertexColor(NORMAL_FONT_COLOR.r, NORMAL_FONT_COLOR.g, NORMAL_FONT_COLOR.b)
|
|
end
|
|
|
|
local function CustomDropDownButton_ColorSwatch_OnLeave(self)
|
|
local button = self:GetParent() ---@type CustomDropDownButton
|
|
button.colorSwatchBg:SetVertexColor(HIGHLIGHT_FONT_COLOR.r, HIGHLIGHT_FONT_COLOR.g, HIGHLIGHT_FONT_COLOR.b)
|
|
end
|
|
|
|
---@param cdropdown CustomDropDown
|
|
---@param button CustomDropDownButton?
|
|
---@return CustomDropDownButton button
|
|
local function NewCustomDropDownButton(cdropdown, button)
|
|
local index = #cdropdown.buttons + 1
|
|
---@diagnostic disable-next-line: cast-local-type
|
|
button = button or CreateFrame("Button", cdropdown:GetName() .. "Button" .. index, cdropdown, "UIDropDownMenuButtonTemplate")
|
|
button.order = nil
|
|
button.option = nil
|
|
button:SetID(index)
|
|
button:SetFrameLevel(cdropdown:GetFrameLevel() + 2)
|
|
button:SetScript("OnClick", CustomDropDownButton_OnClick)
|
|
button:SetScript("OnEnter", CustomDropDownButton_OnEnter)
|
|
button:SetScript("OnLeave", CustomDropDownButton_OnLeave)
|
|
button:SetScript("OnEnable", CustomDropDownButton_OnEnable)
|
|
button:SetScript("OnDisable", CustomDropDownButton_OnDisable)
|
|
local buttonName = button:GetName()
|
|
button.invisibleButton = _G[buttonName .. "InvisibleButton"]
|
|
button.invisibleButton:SetScript("OnEnter", CustomDropDownButton_InvisibleButton_OnEnter)
|
|
button.invisibleButton:SetScript("OnLeave", CustomDropDownButton_InvisibleButton_OnLeave)
|
|
button.highlight = _G[buttonName .. "Highlight"]
|
|
button.normalText = _G[buttonName .. "NormalText"]
|
|
button.normalText:ClearAllPoints()
|
|
button.normalText:SetPoint("LEFT")
|
|
button.normalText:SetPoint("RIGHT")
|
|
button.normalText:SetWordWrap(false) ---@diagnostic disable-line: redundant-parameter
|
|
button.normalText:SetNonSpaceWrap(false)
|
|
button.iconTexture = _G[buttonName .. "Icon"]
|
|
button.expandArrow = _G[buttonName .. "ExpandArrow"]
|
|
button.expandArrow:SetScript("OnMouseDown", nil) ---@diagnostic disable-line: param-type-mismatch
|
|
button.expandArrow:SetScript("OnEnter", nil) ---@diagnostic disable-line: param-type-mismatch
|
|
button.check = _G[buttonName .. "Check"]
|
|
button.uncheck = _G[buttonName .. "UnCheck"]
|
|
button.colorSwatch = _G[buttonName .. "ColorSwatch"]
|
|
button.colorSwatchBg = _G[buttonName .. "ColorSwatchSwatchBg"]
|
|
button.colorSwatchNormalTexture = _G[buttonName .. "ColorSwatchNormalTexture"]
|
|
button.colorSwatch:SetScript("OnClick", CustomDropDownButton_ColorSwatch_OnClick)
|
|
button.colorSwatch:SetScript("OnEnter", CustomDropDownButton_ColorSwatch_OnEnter)
|
|
button.colorSwatch:SetScript("OnLeave", CustomDropDownButton_ColorSwatch_OnLeave)
|
|
---@diagnostic disable-next-line: return-type-mismatch
|
|
return button
|
|
end
|
|
|
|
---@param cdropdown CustomDropDown
|
|
local function CustomDropDown_OnShow(cdropdown)
|
|
---@type DropDownList
|
|
local parent = cdropdown:GetParent() ---@diagnostic disable-line: assign-type-mismatch
|
|
local maxWidth = parent.maxWidth
|
|
local width, height = parent:GetWidth(), 32
|
|
for i = 1, #cdropdown.buttons do
|
|
local button = cdropdown.buttons[i]
|
|
if button:IsShown() then
|
|
button:SetWidth(maxWidth)
|
|
height = height + button:GetHeight()
|
|
end
|
|
end
|
|
cdropdown:SetHeight(height)
|
|
end
|
|
|
|
---@param widget Region
|
|
local function Hide(widget)
|
|
widget:SetAlpha(0)
|
|
widget:Hide()
|
|
widget.Show = widget.Hide
|
|
end
|
|
|
|
---@param dropdown DropDownList
|
|
local function NewCustomDropDown(dropdown)
|
|
---@type CustomDropDown
|
|
local cdropdown = CreateFrame("Button", "LibDropDownExtensionCustomDropDown_" .. tostring(dropdown), dropdown, "UIDropDownListTemplate") ---@diagnostic disable-line: assign-type-mismatch
|
|
cdropdown:SetID(dropdown:GetID())
|
|
cdropdown.options = {}
|
|
cdropdown.buttons = {}
|
|
do
|
|
local cdropdownName = cdropdown:GetName()
|
|
Hide(_G[cdropdownName .. "Backdrop"])
|
|
Hide(_G[cdropdownName .. "MenuBackdrop"])
|
|
cdropdown:SetFrameStrata(dropdown:GetFrameStrata())
|
|
cdropdown:SetFrameLevel(dropdown:GetFrameLevel() + 1)
|
|
cdropdown:SetScript("OnClick", nil) ---@diagnostic disable-line: param-type-mismatch
|
|
cdropdown:SetScript("OnUpdate", nil) ---@diagnostic disable-line: param-type-mismatch
|
|
cdropdown:SetScript("OnShow", CustomDropDown_OnShow)
|
|
cdropdown:SetScript("OnHide", nil) ---@diagnostic disable-line: param-type-mismatch
|
|
for i = 1, UIDROPDOWNMENU_MAXBUTTONS do
|
|
---@type CustomDropDownButton
|
|
local button = _G[cdropdown:GetName() .. "Button" .. i]
|
|
if not button then
|
|
break
|
|
end
|
|
button = NewCustomDropDownButton(cdropdown, button)
|
|
cdropdown.buttons[i] = button
|
|
end
|
|
end
|
|
return cdropdown
|
|
end
|
|
|
|
---@param a CustomDropDownButton
|
|
---@param b CustomDropDownButton
|
|
local function SortDropDownButtons(a, b)
|
|
return a.order < b.order
|
|
end
|
|
|
|
---@param cdropdown CustomDropDown
|
|
local function ClearDropDown(cdropdown)
|
|
for i = 1, #cdropdown.buttons do
|
|
local button = cdropdown.buttons[i]
|
|
button.option = nil
|
|
end
|
|
table.wipe(cdropdown.options)
|
|
end
|
|
|
|
---@param cdropdown CustomDropDown
|
|
---@param options CustomDropDownOption[]
|
|
---@param orderOffset number
|
|
local function AppendDropDown(cdropdown, options, orderOffset)
|
|
---@type table<CustomDropDownButton, boolean?>
|
|
local available = {}
|
|
for i = 1, #cdropdown.buttons do
|
|
local button = cdropdown.buttons[i]
|
|
if not button.option then
|
|
available[button] = true
|
|
end
|
|
end
|
|
for i = 1, #options do
|
|
local option = options[i]
|
|
---@type CustomDropDownButton
|
|
local button = next(available)
|
|
if not button then
|
|
button = NewCustomDropDownButton(cdropdown)
|
|
cdropdown.buttons[#cdropdown.buttons + 1] = button
|
|
else
|
|
available[button] = nil
|
|
end
|
|
button.order = orderOffset + i
|
|
button.option = option
|
|
cdropdown.options[#cdropdown.options + 1] = option
|
|
end
|
|
end
|
|
|
|
---@param button CustomDropDownButton
|
|
local function RefreshButton(button)
|
|
local option = button.option
|
|
|
|
local icon = button.iconTexture
|
|
local invisibleButton = button.invisibleButton
|
|
|
|
button:SetDisabledFontObject(GameFontDisableSmallLeft)
|
|
button:Enable()
|
|
invisibleButton:Hide()
|
|
|
|
if option.notClickable then
|
|
option.disabled = true
|
|
button:SetDisabledFontObject(GameFontHighlightSmallLeft)
|
|
end
|
|
|
|
if option.isTitle then
|
|
option.disabled = true
|
|
button:SetDisabledFontObject(GameFontNormalSmallLeft)
|
|
end
|
|
|
|
if option.disabled then
|
|
button:Disable()
|
|
invisibleButton:Show()
|
|
option.colorCode = nil
|
|
end
|
|
|
|
if option.disablecolor then
|
|
option.colorCode = option.disablecolor
|
|
end
|
|
|
|
if option.text then
|
|
|
|
if option.colorCode then
|
|
button:SetText(option.colorCode .. option.text .. "|r")
|
|
else
|
|
button:SetText(option.text)
|
|
end
|
|
|
|
if option.icon or option.mouseOverIcon then
|
|
icon:ClearAllPoints()
|
|
icon:SetPoint("RIGHT")
|
|
icon:SetSize(16, 16)
|
|
icon:SetTexture(option.icon or option.mouseOverIcon)
|
|
if option.tCoordLeft then
|
|
icon:SetTexCoord(option.tCoordLeft, option.tCoordRight, option.tCoordTop, option.tCoordBottom)
|
|
else
|
|
icon:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
icon:Show()
|
|
else
|
|
icon:Hide()
|
|
end
|
|
|
|
if option.fontObject then
|
|
button:SetNormalFontObject(option.fontObject)
|
|
button:SetHighlightFontObject(option.fontObject)
|
|
else
|
|
button:SetNormalFontObject(GameFontHighlightSmallLeft)
|
|
button:SetHighlightFontObject(GameFontHighlightSmallLeft)
|
|
end
|
|
|
|
else
|
|
button:SetText("")
|
|
icon:Hide()
|
|
end
|
|
|
|
if option.iconInfo then
|
|
icon.tFitDropDownSizeX = option.iconInfo.tFitDropDownSizeX
|
|
else
|
|
icon.tFitDropDownSizeX = nil
|
|
end
|
|
|
|
if option.iconOnly and option.icon then
|
|
icon:ClearAllPoints()
|
|
icon:SetPoint("LEFT")
|
|
icon:SetWidth(option.iconInfo and option.iconInfo.tSizeX or 16)
|
|
icon:SetHeight(option.iconInfo and option.iconInfo.tSizeY or 16)
|
|
icon:SetTexture(option.icon)
|
|
if option.iconInfo and option.iconInfo.tCoordLeft then
|
|
icon:SetTexCoord(option.iconInfo.tCoordLeft, option.iconInfo.tCoordRight, option.iconInfo.tCoordTop, option.iconInfo.tCoordBottom)
|
|
else
|
|
icon:SetTexCoord(0, 1, 0, 1)
|
|
end
|
|
icon:Show()
|
|
end
|
|
|
|
local expandArrow = button.expandArrow
|
|
expandArrow:SetShown(option.hasArrow)
|
|
expandArrow:SetEnabled(not option.disabled)
|
|
|
|
if option.iconOnly then
|
|
icon:SetPoint("LEFT")
|
|
icon:SetPoint("RIGHT", -5, 0)
|
|
end
|
|
|
|
--[=[
|
|
local xPos = 5
|
|
local displayInfo = button.normalText ---@type FontString|Texture
|
|
if option.iconOnly then
|
|
displayInfo = icon
|
|
end
|
|
|
|
displayInfo:ClearAllPoints()
|
|
|
|
if option.notCheckable then
|
|
if option.justifyH and option.justifyH == "CENTER" then
|
|
displayInfo:SetPoint("CENTER", button, "CENTER", -7, 0)
|
|
else
|
|
displayInfo:SetPoint("LEFT", button, "LEFT", 0, 0)
|
|
end
|
|
xPos = xPos + 10
|
|
else
|
|
displayInfo:SetPoint("LEFT", button, "LEFT", 20, 0)
|
|
xPos = xPos + 12
|
|
end
|
|
|
|
local frame = UIDROPDOWNMENU_OPEN_MENU
|
|
if frame and frame.displayMode == "MENU" then
|
|
if not option.notCheckable then
|
|
xPos = xPos - 6
|
|
end
|
|
end
|
|
|
|
frame = frame or UIDROPDOWNMENU_INIT_MENU
|
|
if option.leftPadding then
|
|
xPos = xPos + option.leftPadding
|
|
end
|
|
|
|
displayInfo:SetPoint("TOPLEFT", button, "TOPLEFT", xPos, 0)
|
|
--]=]
|
|
|
|
if not option.notCheckable then
|
|
|
|
local check = button.check
|
|
local uncheck = button.uncheck
|
|
|
|
if option.disabled then
|
|
check:SetDesaturated(true)
|
|
check:SetAlpha(0.5)
|
|
uncheck:SetDesaturated(true)
|
|
uncheck:SetAlpha(0.5)
|
|
else
|
|
check:SetDesaturated(false)
|
|
check:SetAlpha(1)
|
|
uncheck:SetDesaturated(false)
|
|
uncheck:SetAlpha(1)
|
|
end
|
|
|
|
if option.customCheckIconAtlas or option.customCheckIconTexture then
|
|
check:SetTexCoord(0, 1, 0, 1)
|
|
uncheck:SetTexCoord(0, 1, 0, 1)
|
|
if option.customCheckIconAtlas then
|
|
check:SetAtlas(option.customCheckIconAtlas)
|
|
uncheck:SetAtlas(option.customUncheckIconAtlas or option.customCheckIconAtlas)
|
|
else
|
|
check:SetTexture(option.customCheckIconTexture)
|
|
uncheck:SetTexture(option.customUncheckIconTexture or option.customCheckIconTexture)
|
|
end
|
|
elseif option.isNotRadio then
|
|
check:SetTexCoord(0, 0.5, 0, 0.5)
|
|
check:SetTexture("Interface\\Common\\UI-DropDownRadioChecks")
|
|
uncheck:SetTexCoord(0.5, 1, 0, 0.5)
|
|
uncheck:SetTexture("Interface\\Common\\UI-DropDownRadioChecks")
|
|
else
|
|
check:SetTexCoord(0, 0.5, 0.5, 1)
|
|
check:SetTexture("Interface\\Common\\UI-DropDownRadioChecks")
|
|
uncheck:SetTexCoord(0.5, 1, 0.5, 1)
|
|
uncheck:SetTexture("Interface\\Common\\UI-DropDownRadioChecks")
|
|
end
|
|
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local checked = option.checked ---@type boolean
|
|
if type(checked) == "function" then
|
|
checked = checked(button)
|
|
end
|
|
|
|
if checked then
|
|
button:LockHighlight()
|
|
check:Show()
|
|
uncheck:Hide()
|
|
else
|
|
button:UnlockHighlight()
|
|
check:Hide()
|
|
uncheck:Show()
|
|
end
|
|
|
|
else
|
|
button.check:Hide()
|
|
button.uncheck:Hide()
|
|
end
|
|
|
|
local colorSwatch = button.colorSwatch
|
|
if option.hasColorSwatch then
|
|
button.colorSwatchNormalTexture:SetVertexColor(option.r, option.g, option.b)
|
|
colorSwatch:Show()
|
|
else
|
|
colorSwatch:Hide()
|
|
end
|
|
end
|
|
|
|
---@param cdropdown CustomDropDown
|
|
local function RefreshButtons(cdropdown)
|
|
local lastButton ---@type CustomDropDownButton?
|
|
for i = 1, #cdropdown.buttons do
|
|
local button = cdropdown.buttons[i]
|
|
if not button.option then
|
|
button.order = 1000000
|
|
end
|
|
end
|
|
table.sort(cdropdown.buttons, SortDropDownButtons)
|
|
for i = 1, #cdropdown.buttons do
|
|
local button = cdropdown.buttons[i]
|
|
if button.option then
|
|
button:ClearAllPoints()
|
|
if lastButton then
|
|
button:SetPoint("TOPLEFT", lastButton, "BOTTOMLEFT", 0, 0)
|
|
else
|
|
button:SetPoint("TOPLEFT", cdropdown, "TOPLEFT", 15, -17)
|
|
end
|
|
RefreshButton(button)
|
|
button:Show()
|
|
lastButton = button
|
|
else
|
|
button:Hide()
|
|
end
|
|
end
|
|
local numOptions = #cdropdown.options
|
|
if numOptions > 0 then
|
|
---@diagnostic disable-next-line: assign-type-mismatch
|
|
local parent = cdropdown:GetParent() ---@type Frame
|
|
parent:SetHeight(parent:GetHeight() + 16 * numOptions)
|
|
cdropdown:ClearAllPoints()
|
|
cdropdown:SetPoint("BOTTOMLEFT", parent, "BOTTOMLEFT", 0, 0)
|
|
cdropdown:SetPoint("BOTTOMRIGHT", parent, "BOTTOMRIGHT", 0, 0)
|
|
cdropdown:Show()
|
|
else
|
|
cdropdown:Hide()
|
|
end
|
|
end
|
|
|
|
---@param dropdown DropDownList
|
|
local function GetCustomDropDown(dropdown)
|
|
local cdropdown = cdropdowns[dropdown]
|
|
if not cdropdown then
|
|
cdropdown = NewCustomDropDown(dropdown)
|
|
cdropdowns[dropdown] = cdropdown
|
|
end
|
|
return cdropdown
|
|
end
|
|
|
|
---@param option CustomDropDownOption
|
|
local function IsOptionValid(option)
|
|
return type(option) == "table"
|
|
end
|
|
|
|
---@param options1 CustomDropDownOption[]
|
|
---@param options2 CustomDropDownOption[]
|
|
local function CopyOptions(options1, options2)
|
|
table.wipe(options2)
|
|
local index = 0
|
|
for i = 1, #options1 do
|
|
local option = options1[i]
|
|
if IsOptionValid(option) then
|
|
index = index + 1
|
|
options2[index] = option
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param options CustomDropDownOption[]
|
|
local function RemoveInvalidOptions(options)
|
|
for i = #options, 1, -1 do
|
|
local option = options[i]
|
|
if not IsOptionValid(option) then
|
|
table.remove(options, i)
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param event LibDropDownExtensionEvent
|
|
---@param dropdown DropDownList
|
|
function Lib:_Broadcast(event, dropdown)
|
|
local level = dropdown:GetID()
|
|
local cdropdown = GetCustomDropDown(dropdown)
|
|
local shownSeparator
|
|
ClearDropDown(cdropdown)
|
|
for i = 1, #callbacks do
|
|
local callback = callbacks[i]
|
|
local callbackLevel = callback.events[event]
|
|
if callbackLevel == true or callbackLevel == level then
|
|
local status, retval = pcall(callback.func, dropdown.dropdown, event, callback.options, level, callback.data)
|
|
if status and retval then
|
|
if not shownSeparator and callback.options[1] then
|
|
shownSeparator = true
|
|
AppendDropDown(cdropdown, Lib._separatorTable, 0)
|
|
end
|
|
if type(retval) == "table" and retval ~= callback.options then
|
|
CopyOptions(retval, callback.options)
|
|
else
|
|
RemoveInvalidOptions(callback.options)
|
|
end
|
|
AppendDropDown(cdropdown, callback.options, i * 100)
|
|
end
|
|
end
|
|
end
|
|
RefreshButtons(cdropdown)
|
|
end
|
|
|
|
---@param func LibDropDownExtensionCallback
|
|
---@return CustomDropDownCallback?, number?
|
|
local function GetCallbackForFunc(func)
|
|
for i = 1, #callbacks do
|
|
local callback = callbacks[i]
|
|
if callback.func == func then
|
|
return callback, i
|
|
end
|
|
end
|
|
end
|
|
|
|
---@param self DropDownList
|
|
local function DropDown_OnShow(self)
|
|
Lib:_Broadcast("OnShow", self)
|
|
end
|
|
|
|
---@param self DropDownList
|
|
local function DropDown_OnHide(self)
|
|
Lib:_Broadcast("OnHide", self)
|
|
end
|
|
|
|
Lib._hooked = Lib._hooked or {}
|
|
|
|
for i = 1, 3 do
|
|
local dropDownList = _G[format("DropDownList%d", i)] ---@type DropDownList?
|
|
if dropDownList and not Lib._hooked[dropDownList] then
|
|
Lib._hooked[dropDownList] = true
|
|
dropDownList:HookScript("OnShow", DropDown_OnShow)
|
|
dropDownList:HookScript("OnHide", DropDown_OnHide)
|
|
end
|
|
end
|
|
|
|
Lib.Option = Lib.Option or {}
|
|
|
|
Lib.Option.Separator = Lib.Option.Separator or {
|
|
hasArrow = false,
|
|
dist = 0,
|
|
isTitle = true,
|
|
isUninteractable = true,
|
|
notCheckable = true,
|
|
iconOnly = true,
|
|
icon = "Interface\\Common\\UI-TooltipDivider-Transparent",
|
|
tCoordLeft = 0,
|
|
tCoordRight = 1,
|
|
tCoordTop = 0,
|
|
tCoordBottom = 1,
|
|
tSizeX = 0,
|
|
tSizeY = 8,
|
|
tFitDropDownSizeX = true,
|
|
iconInfo = {
|
|
tCoordLeft = 0,
|
|
tCoordRight = 1,
|
|
tCoordTop = 0,
|
|
tCoordBottom = 1,
|
|
tSizeX = 0,
|
|
tSizeY = 8,
|
|
tFitDropDownSizeX = true
|
|
}
|
|
}
|
|
|
|
Lib.Option.Space = Lib.Option.Space or {
|
|
hasArrow = false,
|
|
dist = 0,
|
|
isTitle = true,
|
|
isUninteractable = true,
|
|
notCheckable = true
|
|
}
|
|
|
|
Lib._separatorTable = Lib._separatorTable or { Lib.Option.Separator }
|
|
|
|
---@param events string
|
|
---@param func LibDropDownExtensionCallback
|
|
---@param levels (number|boolean)?
|
|
---@param data table?
|
|
---@return boolean success
|
|
function Lib:RegisterEvent(events, func, levels, data)
|
|
assert(type(events) == "string" and type(func) == "function", "LibDropDownExtension:RegisterEvent(events, func[, levels][, data]) requires events to be a string and func a function. levels is an optional number 1, 2 or nil for any level.")
|
|
local callback = GetCallbackForFunc(func)
|
|
for _, event in ipairs({strsplit(" ", events)}) do
|
|
if not callback then
|
|
---@type CustomDropDownCallback
|
|
callback = {
|
|
events = {},
|
|
func = func,
|
|
options = {},
|
|
data = type(data) == "table" and data or {},
|
|
}
|
|
callbacks[#callbacks + 1] = callback
|
|
end
|
|
callback.events[event] = levels or true
|
|
end
|
|
return callback ~= nil
|
|
end
|
|
|
|
---@param events string
|
|
---@param func LibDropDownExtensionCallback
|
|
---@param levels (number|boolean)?
|
|
---@return boolean success
|
|
function Lib:UnregisterEvent(events, func, levels)
|
|
assert(type(events) == "string" and type(func) == "function", "LibDropDownExtension:UnregisterEvent(events, func) requires events to be a string and func a function.")
|
|
local callback, index = GetCallbackForFunc(func)
|
|
if not callback then
|
|
return false
|
|
end
|
|
for _, event in ipairs({strsplit(" ", events)}) do
|
|
callback.events[event] = levels
|
|
end
|
|
if not next(callback.events) then
|
|
table.remove(callbacks, index)
|
|
end
|
|
return true
|
|
end
|
|
|
|
-- DEBUG:
|
|
--[[
|
|
print(..., MAJOR, LibPrevMinor, "->", MINOR)
|
|
Lib:RegisterEvent("OnShow", function(dropdown, event, options, level, data)
|
|
if event == "OnShow" then
|
|
options[1] = { text = "A1 | Level " .. level .. " | Random " .. data.test .. " | A1", tooltipOnButton = true, tooltipTitle = "Custom Tooltip Title", tooltipText = "Custom Tooltip Text" }
|
|
options[2] = { text = "A2 | Level " .. level .. " | Random " .. data.test .. " | A2" }
|
|
options[3] = { text = "A3 | Level " .. level .. " | Random " .. data.test .. " | A3" }
|
|
return true
|
|
else
|
|
wipe(options)
|
|
end
|
|
end, true, { test = random(100000000, 999999999) })
|
|
Lib:RegisterEvent("OnShow", function(dropdown, event, options, level, data)
|
|
if event == "OnShow" then
|
|
options[1] = { text = "B1 | Level " .. level .. " | Random " .. data.test .. " | B1" }
|
|
options[2] = { text = "B2 | Level " .. level .. " | Random " .. data.test .. " | B2" }
|
|
options[3] = { text = "B3 | Level " .. level .. " | Random " .. data.test .. " | B3" }
|
|
return true
|
|
else
|
|
wipe(options)
|
|
end
|
|
end, true, { test = random(100000000, 999999999) })
|
|
Lib:RegisterEvent("OnShow", function(dropdown, event, options, level, data)
|
|
if event == "OnShow" then
|
|
options[1] = { text = "C1 | Level " .. level .. " | Random " .. data.test .. " | C1" }
|
|
options[2] = { text = "C2 | Level " .. level .. " | Random " .. data.test .. " | C2" }
|
|
options[3] = { text = "C3 | Level " .. level .. " | Random " .. data.test .. " | C3" }
|
|
return true
|
|
else
|
|
wipe(options)
|
|
end
|
|
end, true, { test = random(100000000, 999999999) })
|
|
--]]
|
|
|