Wowpedia

We have moved to Warcraft Wiki. Click here for information and the new URL.

READ MORE

Wowpedia
Register
Advertisement

UIDropDownMenuTemplate is a FrameXML Frame template that can be used to create contextual menus and dropdown boxes in World of Warcraft. This tutorial explains how to use it in your addon.

Summary: implement a function describing the contents of your drop-down menu, and possibly another function to respond to the user selecting a particular menu item.

Provided functionality[]

The template can be used to create two UI elements: drop-down lists and context menus, both of which can be used to present a multi-level menu to the user. The menu's items may be disabled, checked, show a color picker swatch, or be styled as a title. The template automatically creates a drop-down box, as well as any list buttons as necessary.

Drop-down list Context menu
UIDropDownMenu-NoMenu UIDropDownMenu-Menu

The differences between the two display modes are inconsequential from a coding perspective -- if you're creating a context menu, you'll need to provide an appropriate argument to the UIDropDownMenu_Initialize call slightly and call ToggleDropDownMenu yourself when you want the context menu to appear.

Menu initialization functions[]

In order to display a menu, you'll need to create an initialization function which will describe what items your menu contains. The function will be passed these three arguments:

frame
Frame - A reference to your UIDropDownMenuTemplate-inheriting frame.
level
number - Nesting depth of the dropdown menu your function should describe; 1 corresponds to the outermost level, with 2 and 3 being accessible if the user hovers over a menu item that is described as having a nested menu.
menuList
any type - A value from the item description of the parent menu item, which can be used to identify which nested menu should be described.

Your function must, rather than returning any values, call UIDropDownMenu_AddButton with a description of each menu item you wish to create. Menu items are described via a table argument, with a large number of named keys providing customization options. The most important ones to set are:

info.text
string - text to display for this menu item.
info.checked
boolean - if true, a checkmark/depressed radio button is displayed next to the item.

In practice, your initialization function may be as simple as the one in the following example.

 function WPDropDownDemo_Menu(frame, level, menuList)
  local info = UIDropDownMenu_CreateInfo()
  info.text, info.checked = "Blue Pill", true
  UIDropDownMenu_AddButton(info)
  info.text, info.checked = "Red Pill", false
  UIDropDownMenu_AddButton(info)
 end

Handling user interaction[]

Typically, your addon will want to respond to the user making a dropdown selection in some fashion. The easiest way to accomplish this is to use the func and arg1/arg2 keys of the info table:

info.func
function - if set, info.func(self, info.arg1, info.arg2, checked) will be called when this menu item is selected (clicked).
info.arg1, info.arg2
any type - these arguments will be passed to func when this menu item is pressed.

This means that you'll frequently need to write two functions: one to respond to the user making selections in the menu, and the other to describe the items in your menu.

The following snippet illustrates the pattern. The info.func value is set to the function handling the clicks, and the menu initializer is modified to set the info.arg1 values in addition to info.text, which allows the _OnClick function to determine which option was selected easily.

 local function WPDropDownDemo_OnClick(self, arg1, arg2, checked)
  if arg1 == 1 then
   print("You can continue to believe whatever you want to believe.")
  elseif arg1 == 2 then
   print("Let's see how deep the rabbit hole goes.")
  end
 end
 function WPDropDownDemo_Menu(frame, level, menuList)
  local info = UIDropDownMenu_CreateInfo()
  info.func = WPDropDownDemo_OnClick
  info.text, info.arg1 = "Blue Pill", 1
  UIDropDownMenu_AddButton(info)
  info.text, info.arg1 = "Red Pill", 2
  UIDropDownMenu_AddButton(info)
 end

Nesting menus[]

Dropdown and context menus may be nested -- menu items may be marked such that they open an additional level of menus when hovered over. The following two info table keys are relevant to this purpose:

info.hasArrow
boolean - if true, this menu item will open a sub-menu when hovered over.
info.menuList
any type - will be passed to the initializer function when a sub-menu is opened from this item.

Your initializer function will therefore need to be aware of which level of the menu its being asked to describe. For instance:

 function WPDropDownDemo_Menu(frame, level, menuList)
  local info = UIDropDownMenu_CreateInfo()
 
  if level == 1 then
   -- Outermost menu level
   info.text, info.hasArrow, info.menuList = "Play a game", true, "Games"
   UIDropDownMenu_AddButton(info)
   info.text, info.hasArrow = "Plain old option", nil
   UIDropDownMenu_AddButton(info)
   info.text, info.hasArrow = "Some other list", true, "Other"
   UIDropDownMenu_AddButton(info)
 
  elseif menuList == "Games" then
   -- Show the "Games" sub-menu
   for s in ("Tic-tac-toe; Checkers; Chess; Global Thermonuclear War"):gmatch("[^;%s][^;]*") do
    info.text = s
    UIDropDownMenu_AddButton(info, level)
   end
 
  elseif menuList == "Other" then
   -- Show the "Some other list" sub-menu
   for s in ("Something old; Something new; Something borrowed; Something blue"):gmatch("[^;%s][^;]*") do
    info.text = s
    UIDropDownMenu_AddButton(info, level)
   end
   info.text, info.hasArrow, info.menuList = "Infinite menus", true, menuList
   UIDropDownMenu_AddButton(info, level)
  end
 end

Warning Warning: UIDropDownMenu_AddButton's second argument, level, specifies to which level of the currently open menu the menu item should be appended. If you fail to specify it for sub-menus, your "nested" menu items will be added at the bottom of the outermost menu level.

Creating a dropdown widget[]

In order to use your menu initializer function, you'll need to create a frame inheriting from UIDropDownMenuTemplate and call UIDropDownMenu_Initialize to bind your menu function to your frame. Your frame must have a name.

The following code creates, positions, and initializes a dropdown menu widget:

 local dropDown = CreateFrame("Frame", "WPDemoDropDown", UIParent, "UIDropDownMenuTemplate")
 dropDown:SetPoint("CENTER")
 UIDropDownMenu_SetWidth(dropDown, 200) -- Use in place of dropDown:SetWidth
 -- Bind an initializer function to the dropdown; see previous sections for initializer function examples.
 UIDropDownMenu_Initialize(dropDown, WPDropDownDemo_Menu)

Generally, you'll want to have your dropdown display text indicating the current value of whatever choice the user can make using the dropdown. To adjust the text displayed by the dropdown, you can use the following function:

 UIDropDownMenu_SetText(dropDown, "Exciting goes here text")

The text displayed by the menu will be automatically set to the newly selected menu item's info.text value whenever the user makes a selection, so you'll usually only need to explicitly alter the text when your dropdown is first presented to the user, or when its value is changed without user interaction.

Creating context menus[]

The following code creates and initializes a context menu widget.

 local dropDown = CreateFrame("Frame", "WPDemoContextMenu", UIParent, "UIDropDownMenuTemplate")
 -- Bind an initializer function to the dropdown; see previous sections for initializer function examples.
 UIDropDownMenu_Initialize(dropDown, WPDropDownDemo_Menu, "MENU")

To show a context menu, you'll need to call ToggleDropDownMenu function when you wish to show the context menu. Generally, the call to show a menu would look like this:

 ToggleDropDownMenu(1, nil, dropDown, "cursor", 3, -3)

A complete example[]

Dropdown demo

The example dropdown menu.

The following example illustrates how UIDropDownMenuTemplate can be used in a typical addon, allowing the user to customize the value of some variable in the addon. In this example, a multi-level dropdown menu is used to let the user pick a favorite number from 0 to 49.

 local favoriteNumber = 42 -- A user-configurable setting
 
 -- Create the dropdown, and configure its appearance
 local dropDown = CreateFrame("FRAME", "WPDemoDropDown", UIParent, "UIDropDownMenuTemplate")
 dropDown:SetPoint("CENTER")
 UIDropDownMenu_SetWidth(dropDown, 200)
 UIDropDownMenu_SetText(dropDown, "Favorite number: " .. favoriteNumber)
 
 -- Create and bind the initialization function to the dropdown menu
 UIDropDownMenu_Initialize(dropDown, function(self, level, menuList)
  local info = UIDropDownMenu_CreateInfo()
  if (level or 1) == 1 then
   -- Display the 0-9, 10-19, ... groups
   for i=0,4 do
    info.text, info.checked = i*10 .. " - " .. (i*10+9), favoriteNumber >= i*10 and favoriteNumber <= (i*10+9)
    info.menuList, info.hasArrow = i, true
    UIDropDownMenu_AddButton(info)
   end
 
  else
   -- Display a nested group of 10 favorite number options
   info.func = self.SetValue
   for i=menuList*10, menuList*10+9 do
    info.text, info.arg1, info.checked = i, i, i == favoriteNumber
    UIDropDownMenu_AddButton(info, level)
   end
  end
 end)
 
 -- Implement the function to change the favoriteNumber
 function dropDown:SetValue(newValue)
  favoriteNumber = newValue
  -- Update the text; if we merely wanted it to display newValue, we would not need to do this
  UIDropDownMenu_SetText(dropDown, "Favorite number: " .. favoriteNumber)
  -- Because this is called from a sub-menu, only that menu level is closed by default.
  -- Close the entire menu with this next call
  CloseDropDownMenus()
 end

General advice[]

  • The default UIDropDownMenu implementation does not provide scrolling -- if you attempt to display too many menu items in a single level, some of them will be inaccessible.
  • UIDropDownMenu provides more customization options than are discussed in this HOWTO. You can, among other things, divide menus into multiple sections with non-selectable titles, control whether selection is reflected by a checkmark or a radio button, display icons alongside text and much more. See the full list of info table keys.
  • Avoid using dropdown menus to encompass the entirety of your addon's configuration -- interacting with menu structures several levels deep does not make for a pleasant user experience.
  • Avoid using the UIDropDownMenu_SetSelected* and UIDropDownMenu_GetSelected* families of functions, since some of the former generally only work correctly while your menu is open. You can achieve the same effect using info.checked, info.func and UIDropDownMenu_SetText, as described in this HOWTO.

See also[]

  • UI Object UIDropDownMenu: template documentation, including additional info table keys.
  • API EasyMenu: allows you to specify the entirety of your context menu dropdown as a table, removes the need to write initializer functions.
Advertisement