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 ---@field public _separatorTable CustomDropDownOption[] ---@field public _hooked table ---@field public _Broadcast fun(self: LibDropDownExtension, event: LibDropDownExtensionEvent, dropdown: DropDownList) ---@field public Option table `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 ---@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 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) }) --]]