LibUIDropDownMenu
About
Standard UIDropDownMenu's global functions using protected frames and causing taints when used by third-party addons. But it is possible to avoid taints by using same functionality with that library.
What is it
Library is standard code from Blizzard's files EasyMenu.lua, UIDropDownMenu.lua, UIDropDownMenu.xml, UIDropDownMenuTemplates.lua and UIDropDownMenuTemplates.xml with frames, tables, variables and functions bounded to the library tables.
Global constants have been added with a prefix "L_", for example:
- L_UIDROPDOWNMENU_MINBUTTONS
Addon Dependancy and Configuration
- Embed LibUIDropDownMenu to your addon, you can specify to the subfolder of LibUIDropDownMenu if you feel this keep your addon's folder structure lighter.
- Add LibUIDropDownMenu.xml to your toc or your embeds.xml / libs.xml.
- Make sure your toc file has the following settings:
## Dependencies: LibStub, !LibUIDropDownMenu
- If your addon doesn't embed LibStub, you will need it.
How to use it (for addon developer)
Initial Preparation
Assuming your addon is using all the UIDropDownMenu functions from the WoW's built in function calls, then it is suggested that you have below preparation in your lua codes:
local LibDD = LibStub:GetLibrary("LibUIDropDownMenu-4.0")
Function Call Replacement
Depends on which UIDropDownMenu's function calls you have used in your addon, you will need below similar replacement:
UIDropDownMenu_Initialize => LibDD:UIDropDownMenu_Initialize
UIDropDownMenu_CreateInfo => LibDD:UIDropDownMenu_CreateInfo
UIDropDownMenu_AddButton => LibDD:UIDropDownMenu_AddButtonUIDropDownMenu_AddSeparator => LibDD:UIDropDownMenu_AddSeparator
UIDropDownMenu_AddSpace=> LibDD:UIDropDownMenu_AddSpaceUIDropDownMenu_SetSelectedValue => LibDD:UIDropDownMenu_SetSelectedValue
UIDropDownMenu_SetSelectedName=> LibDD:UIDropDownMenu_SetSelectedNameUIDropDownMenu_SetSelectedID => LibDD:UIDropDownMenu_SetSelectedID
UIDropDownMenu_SetWidth => LibDD:UIDropDownMenu_SetWidthCloseDropDownMenus => LibDD:CloseDropDownMenus
Creating new UIDropDownMenu
Traditionally you will either create a new frame in your lua codes or with XML by setting the frame to inherit from "UIDropDownMenuTemplate".
For example, if your lua code was something like below in the past:
local frame = CreateFrame("Frame", addon.Name.."DropDown", nil, "UIDropDownMenuTemplate")
By using this library, you will need to create your menu from like below:
local frame = LibDD:Create_UIDropDownMenu("MyDropDownMenu", parent_frame)
Constants
L_UIDROPDOWNMENU_MINBUTTONS- L_UIDROPDOWNMENU_MAXBUTTONS
- L_UIDROPDOWNMENU_MAXLEVELS
- L_UIDROPDOWNMENU_BUTTON_HEIGHT
- L_UIDROPDOWNMENU_BORDER_HEIGHT
- L_UIDROPDOWNMENU_OPEN_MENU
- L_UIDROPDOWNMENU_INIT_MENU
- L_UIDROPDOWNMENU_MENU_LEVEL
- L_UIDROPDOWNMENU_MENU_VALUE
- L_UIDROPDOWNMENU_SHOW_TIME
- L_UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT
- L_OPEN_DROPDOWNMENUS
- L_DropDownList1
- L_DropDownList2
Button Name
As you (the developers) might be aware that at some point you might need to manipulate the dropdowns by accessing the button names. For example, you have multiple levels of menus and you would like to hide or show some level's menu button. In that case, you need to make sure you also revise the button name used in your original codes when you are migrating to use LibUIDropDownMenu.
- "L_DropDownList"..i
Example:
for i = 1, L_UIDROPDOWNMENU_MAXLEVELS, 1 do dropDownList = _G["L_DropDownList"..i]; if ( i >= L_UIDROPDOWNMENU_MENU_LEVEL or frame ~= L_UIDROPDOWNMENU_OPEN_MENU ) then dropDownList.numButtons = 0; dropDownList.maxWidth = 0; for j=1, L_UIDROPDOWNMENU_MAXBUTTONS, 1 do button = _G["L_DropDownList"..i.."Button"..j]; button:Hide(); end dropDownList:Hide(); end end
List of button attributes
- info.text = [STRING] -- The text of the button
- info.value = [ANYTHING] -- The value that L_UIDROPDOWNMENU_MENU_VALUE is set to when the button is clicked
- info.func = [function()] -- The function that is called when you click the button
- info.checked = [nil, true, function] -- Check the button if true or function returns true
- info.isNotRadio = [nil, true] -- Check the button uses radial image if false check box image if true
- info.isTitle = [nil, true] -- If it's a title the button is disabled and the font color is set to yellow
- info.disabled = [nil, true] -- Disable the button and show an invisible button that still traps the mouseover event so menu doesn't time out
- info.tooltipWhileDisabled = [nil, 1] -- Show the tooltip, even when the button is disabled.
- info.hasArrow = [nil, true] -- Show the expand arrow for multilevel menus
- info.hasColorSwatch = [nil, true] -- Show color swatch or not, for color selection
- info.r = [1 - 255] -- Red color value of the color swatch
- info.g = [1 - 255] -- Green color value of the color swatch
- info.b = [1 - 255] -- Blue color value of the color swatch
- info.colorCode = [STRING] -- "|cAARRGGBB" embedded hex value of the button text color. Only used when button is enabled
- info.swatchFunc = [function()] -- Function called by the color picker on color change
- info.hasOpacity = [nil, 1] -- Show the opacity slider on the colorpicker frame
- info.opacity = [0.0 - 1.0] -- Percentatge of the opacity, 1.0 is fully shown, 0 is transparent
- info.opacityFunc = [function()] -- Function called by the opacity slider when you change its value
- info.cancelFunc = [function(previousValues)] -- Function called by the colorpicker when you click the cancel button (it takes the previous values as its argument)
- info.notClickable = [nil, 1] -- Disable the button and color the font white
- info.notCheckable = [nil, 1] -- Shrink the size of the buttons and don't display a check box
- info.owner = [Frame] -- Dropdown frame that "owns" the current dropdownlist
- info.keepShownOnClick = [nil, 1] -- Don't hide the dropdownlist after a button is clicked
- info.tooltipTitle = [nil, STRING] -- Title of the tooltip shown on mouseover
- info.tooltipText = [nil, STRING] -- Text of the tooltip shown on mouseover
- info.tooltipOnButton = [nil, 1] -- Show the tooltip attached to the button instead of as a Newbie tooltip.
- info.justifyH = [nil, "CENTER"] -- Justify button text
- info.arg1 = [ANYTHING] -- This is the first argument used by info.func
- info.arg2 = [ANYTHING] -- This is the second argument used by info.func
- info.fontObject = [FONT] -- font object replacement for Normal and Highlight
- info.menuTable = [TABLE] -- This contains an array of info tables to be displayed as a child menu
- info.noClickSound = [nil, 1] -- Set to 1 to suppress the sound when clicking the button. The sound only plays if .func is set.
- info.padding = [nil, NUMBER] -- Number of pixels to pad the text on the right side
- info.leftPadding = [nil, NUMBER] -- Number of pixels to pad the button on the left side
- info.minWidth = [nil, NUMBER] -- Minimum width for this line
- info.customFrame = frame -- Allows this button to be a completely custom frame, should inherit from UIDropDownCustomMenuEntryTemplate and override appropriate methods.
- info.icon = [TEXTURE] -- An icon for the button.
- info.mouseOverIcon = [TEXTURE] -- An override icon when a button is moused over.
- info.ignoreAsMenuSelection [nil, true] -- Never set the menu text/icon to this, even when this button is checked.
FAQ
Please go to FAQ Pages for more details.
Latest build v4.22 breaks addons that embed it on Classic versions (Era/Wrath)
Details issue #18
In reply to MrFIXlT:
Fixed now, thanks for reporting this.
Hello arithmandar! Thank you for this library! :)
I have an issue on the PTR 10.2.0.
When I have a mouseOverIcon in my dropdowns, the func is not called when I click on it. Instead it removes the line in the dropdown menu.
Do you have the same issue ?
[Deleted]
Edit: Not a good workaround... It disables info.iconTooltipTitle and info.iconTooltipText.
Still working on it.
A better workaround on line 244 in the LibUIDropDownMenu.lua file.
-- UIDropDownMenuButtonIcon Script BEGIN
local function icon_OnClick(self, button)
local buttonParent = self:GetParent()
if not buttonParent then
return
end
button_OnClick(buttonParent, button)
end
Got another way to contact you? This site is blocking me from sharing the code change I tried to request.
In reply to Teelo: changes? can you elaborate?
Hi-
9.5.1 got backdrop again...
On current PTR, the tooltips from right clicking on Titan (and plugins) have no background.
LibUIDropDownMenu.lua : 362
- BACKDROP_TOOLTIP_16_16_5555 no longer is in .../SharedXML/Backdrop.lua
For Titan, I made a new local backdrop (from BACKDROP_TOOLTIP_8_8_1111) and used it for :SetBackdrop of MenuBackdrop. This matched Titan but you may want something different.
local back_drop_info =
{ bgFile="Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile="Interface\\Tooltips\\UI-Tooltip-Border",
tile = true,
tileEdge = true,
insets = { left = 1, right = 1, top = 1, bottom = 1 },
tileSize = 8,
edgeSize = 8, }
...
fmb:SetBackdrop(back_drop_info)
In reply to urnati:
Thanks for pointing this out. I have made a local copy to define BACKDROP_TOOLTIP_16_16_5555 within LibUIDropDownMenu.lua
Hi-
Any tips or direction on the below would be appreciated.
Using the latest (v4.03.9000538556) for same addon on retail and BC. Same lib changes in both addon versions. Running ONLY that addon on retail and BC.
Retail works great.
BC almost works except drop down stays active / visible if you mouse away with no click or click that does not force drop down to close.
Mouse click elsewhere does not close the drop down. Appears there is no timeout to hide.
In reply to urnati:
See the original post at https://www.curseforge.com/wow/addons/libuidropdownmenu/issues/11
Without GLOBAL_MOUSE_DOWN like in Retail, it is necessary to periodically check if the mouse is no longer over the menu and then close it accordingly. This could be done with OnUpdate, but I would strongly suggest the use of C_Timer with L_UIDROPDOWNMENU_SHOW_TIME as in my example.
Note that the code was written in an older version of LibUiDropDownMenu so its possible line numbers and things have changed.
In reply to urnati:
This should be fixed / enhanced now. Let me know if any further issue.
In reply to arithmandar:
Initial testing is going well - thank you for the changes!
An observation - personally I do not see this as critical - if the user creates a drop down and mouses away *without* entering the drop down, the drop down stays active. Entering the drop down activates the timer and it will timeout on leave.
Since a user creates a drop down with the intent of using it, this should rarely happen. Famous last words :).
Hello Arith, the Titan Panel Development Team did some debugging and fixed the problem for The Burning Crusade Classic Beta.
Within LibUIDropDownMenu.lua at line 221, these changes will make it all work:
f.Backdrop = _G[name.."Backdrop"] or CreateFrame("Frame", name.."Backdrop", f, BackdropTemplateMixin and "BackdropTemplate")
f.Backdrop:SetAllPoints()
f.Backdrop:SetBackdrop({
bgFile = "Interface\\DialogFrame\\UI-DialogBox-Background-Dark",
edgeFile = "Interface\\DialogFrame\\UI-DialogBox-Border",
tile = true,
tileSize = 32,
edgeSize = 32,
insets = { left = 11, right = 12, top = 12, bottom = 9, },
})
f.MenuBackdrop= _G[name.."MenuBackdrop"] or CreateFrame("Frame", name.."MenuBackdrop", f, BackdropTemplateMixin and "BackdropTemplate")
f.MenuBackdrop:SetAllPoints()
f.MenuBackdrop:SetBackdrop({
bgFile = "Interface\\Tooltips\\UI-Tooltip-Background",
edgeFile = "Interface\\Tooltips\\UI-Tooltip-Border",
tile = true,
tileSize = 16,
edgeSize = 16,
insets = { left = 5, right = 4, top = 4, bottom = 4, },
})
Hello Arith, I'm seeing a new error in TBC beta which I believe it tied into the new backdrop for frames. Just an FYI.
Message: ...DropDownMenu\LibUIDropDownMenu\LibUIDropDownMenu.lua:223: attempt to call method 'SetBackdrop' (a nil value)
Time: Tue Apr 6 01:03:50 2021
Count: 1
Stack: ...DropDownMenu\LibUIDropDownMenu\LibUIDropDownMenu.lua:223: attempt to call method 'SetBackdrop' (a nil value)
[string "@Interface\AddOns\TitanClassic\libs\!LibUIDropDownMenu\LibUIDropDownMenu\LibUIDropDownMenu.lua"]:223: in function `creatre_UIDropDownList'
[string "@Interface\AddOns\TitanClassic\libs\!LibUIDropDownMenu\LibUIDropDownMenu\LibUIDropDownMenu.lua"]:386: in main chunk
Locals: name = "L_DropDownList1"
parent = nil
f = L_DropDownList1 {
0 = <userdata>
Backdrop = L_DropDownList1Backdrop {
}
}
(*temporary) = nil
(*temporary) = L_DropDownList1Backdrop {
0 = <userdata>
}
(*temporary) = <table> {
bgFile = "Interface\DialogFrame\UI-DialogBox-Background-Dark"
tileSize = 32
edgeFile = "Interface\DialogFrame\UI-DialogBox-Border"
tile = true
edgeSize = 32
insets = <table> {
}
}
(*temporary) = "attempt to call method 'SetBackdrop' (a nil value)"
_G = <table> {
UpdateOnBarHighlightMarksBySpell = <function> defined @Interface\FrameXML\ActionButton.lua:70
ERR_OUT_OF_CHI = "Not enough chi"
DH_HAVOC_CORE_ABILITY_2 = "Strong melee attack that consumes Fury. If it critical strikes, some Fury is refunded."
MerchantItem9ItemButtonStock = MerchantItem9ItemButtonStock {
}
GetTrainerServiceTypeFilter = <function> defined =[C]:-1
UNIT_NAMES_COMBATLOG_TOOLTIP = "Color unit names."
SetTrainerServiceTypeFilter = <function> defined =[C]:-1
LE_GAME_ERR_CHAT_RAID_RESTRICTED_TRIAL = 742
SPELL_FAILED_CUSTOM_ERROR_71 = "This partygoer wants to dance with you."
LE_GAME_ERR_PET_SPELL_TARGETS_DEAD = 399
ERROR_CLUB_TICKET_COUNT_AT_MAX_COMMUNITY = "Can't create any more invite links for this group."
CompactUnitFrameProfilesGeneralOptionsFrameHealthTextDropdownButtonNormalTexture = CompactUnitFrameProfilesGeneralOptionsFrameHealthTextDropdownButtonNormalTexture {
}
ERR_TRADE_EQUIPPED_BAG = "You can't trade equipped bags."
PVP_RANK_6_1 = "Corporal"
MultiBarLeftButton7 = MultiBarLeftButton7 {
}
InterfaceOptionsNamesPanelUnitNameplatesShowAll = InterfaceOptionsNamesPanelUnitNameplatesShowAll {
}
VideoOptionsFrameDefaults = VideoOptionsFrameDefaults {
}
MerchantItem2AltCurrencyFrameItem1Text = MerchantItem2AltCurrencyFrameItem1Text {
}
OPTION_TOOLTIP_ACTION_BUTTON_USE_KEY_DOWN = "Action button keybinds will respond on key down, rather than on key up."
BINDING_NAME_NAMEPLATES = "Show Enemy Name Plates"
CHAT_HEADER_SUFFIX = ": "
MultiBarBottomRightButton8Shine5 = MultiBarBottomRightButton8Shine5 {
}
IsReferAFriendLinked = <function> defined =[C]:-1
MAIL_LETTER_TOOLTIP = "Click to make a permanent
copy of this letter."
UnitFrameManaBar_UnregisterDefaultEvents = <function> defined @Interface\FrameXML\UnitFrame.lua:602
DUNGEON_FLOOR_UPPERBLACKROCKSPIRE3 = "Hall of Blackhand"
CHAT_CONFIG_OTHER_COMBAT = <table> {
}
FCFDockOverflowButton_OnClick = <function> defined @Interface\FrameXML\FloatingChatFrame.lua:2379
BOOST2_WARRIOR_COLOSSUSSMASH = "Use Colossus Smash.
Colossus Smash increases your damage."
BN_UNABLE_TO_RESOLVE_NAME = "Unable to whisper '%s'. Blizzard services may be unavailable."
AutoCompleteEditBox_OnKeyDown = <function> defined @Interface\FrameXML\AutoComplete.lua:368
CompactRaidFrameManagerDisplayFrameHiddenModeToggleTopRight = CompactRaidFrameManagerDisplayFrameHiddenModeToggleTopRight {
}
LE_GAME_ERR_ONLY_ONE_QUIVER = 32
SpellButton6Cooldown = SpellButton6Cooldown {
}
SLASH_LibQTip1 = "/qtip"
LOSS_OF_CONTROL_DISPLAY_FEAR = "Feared"
Graphics_QualityText = Graphics_QualityText {
}
VOICEMACRO_4_Sc_1_FEMALE = "Help me attack!"
Advanced_GraphicsAPIDropDownButtonHighlightTexture = Advanced_GraphicsAPIDropDownButtonHighlightTexture {
}
MultiBarRightButton7Shine9 = MultiBarRightButton7Shine9 {
}
BankFrameItem17SearchOverlay = BankFrameItem17SearchOverlay {
}
DMG_LCD = "DMG"
VideoOptionsPanel_Cancel = <function> defined @In
Also this one popped up too.
Message: Interface\AddOns\TitanClassic\TitanClassicUtils.lua:1208: attempt to call global 'L_Create_UIDropDownMenu' (a nil value)
Time: Tue Apr 6 01:03:51 2021
Count: 15
Stack: Interface\AddOns\TitanClassic\TitanClassicUtils.lua:1208: attempt to call global 'L_Create_UIDropDownMenu' (a nil value)
[string "@Interface\AddOns\TitanClassic\TitanClassicUtils.lua"]:1208: in function `TitanUtils_PluginToRegister'
[string "@Interface\AddOns\TitanClassic\TitanPanelClassicTemplate.lua"]:523: in function `TitanPanelButton_OnLoad'
[string "*:OnLoad"]:2: in function <[string "*:OnLoad"]:1>
Locals: self = TitanPanelXPButton {
0 = <userdata>
registry = <table> {
}
}
isChildButton = nil
cat = "Built-ins"
notes = ""
(*temporary) = nil
(*temporary) = "TitanPanelXPButtonRightClickMenu"
(*temporary) = TitanPanelXPButton {
0 = <userdata>
registry = <table> {
}
}
(*temporary) = "attempt to call global 'L_Create_UIDropDownMenu' (a nil value)"
In reply to Nnoggie:
Arithmandar, I put a fix for this in the issues tracker.
https://www.wowace.com/projects/libuidropdownmenu/issues/10
Developers, three are some major changes to the library.
I have now moving all the global functions into library so that they will now need to be call under the library's table.
Latest codes will be available under the branch below:
https://repos.wowace.com/wow/libuidropdownmenu/branches/v4.00
Please help to test it out and report any issue you found.
v3.02.9000136272 from yesterday has a typo in LibUIDropDownMenu.lua line 130:
should be
This was breaking DropDowns with a colorswatch.
In reply to Nnoggie:
Oops, I overlook that one. Thanks for catching this up.