Go Back   WowAce Forums > Tips, FAQs, and Guides

Reply
 
Thread Tools
Old 02-03-2009   #1
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Guide: Using UIDropDownMenu in your addon

Introduction

The purpose of this guide is to aid authors in creating simple dropdown menus that are using the default UI's built in UIDropDownMenu without requiring any library.


Limitations of UIDropDownMenu
  • No sliders.
  • Cannot refresh a menu. You need to close the current menu level and get the user to reopen it to redraw it.
  • No editboxes. You can however use a staticpopup to achieve it.
  • No confirmations. You can however use a staticpopup to achieve it.
  • No tooltips if "Beginner Tooltips" cvar is turned off.
  • Only .checked menu item attribute is allowed to be of type function, which means it gets called to get a return value of true/false. The other attributes cannot be assigned functions to obtain their values.


Advantages of UIDropDownMenu
  • Comes with default UI, so you do not need to include large libraries like DewdropLib.
  • Programmatic creation of menu items, so they are dynamic. AceOption tables are fairly static in nature unless you have code to modify the aceopt tables directly.
  • Very low overhead. You only need to create one frame and one menu initialization function.


Some Terminology

Look at the following dropdown menus that are created by using UIDropDownMenu:



Level
In the 2 left menus (Omen3 and GemHelper), these are simple menus that are 1 level deep.
In the menu on the right (Postal), this menu has several levels, Postal being the level 1 menu, and the OpenAll submenu being the level 2 menu. The level 3 menu (submenu of OpenAll options) isn't shown.

Display Mode
UIDropDownMenu provides for 2 types of menu display templates.
The GemHelper menu does not have a displayMode set and is the default setting.
The Omen3 and Postal menus have displayMode = "MENU" set.
This is purely for looks, it does not affect functionality.


UIDropDownMenu functions you will use

UIDropDownMenu_AddButton(info, level)

This tells UIDropDownMenu to add a menuitem to the current open menu at level level.
The info argument is a table containing parameters for the menuitem you are adding.

ToggleDropDownMenu(level, value, dropDownFrame, anchorName, xOffset, yOffset, menuList, button)

This tells the default UI to open your Dropdown defined by dropDownFrame at level level with submenuID value.
The menu that is displayed will be :SetPoint("TOPLEFT", _G[anchorName], "BOTTOMLEFT", xOffset, yOffset)
If anchorName is the string "cursor", it will be anchored to your cursor instead.
The arguments from anchorName onwards (including anchorName) are all optional.

For the purposes of this guide, menuList and button will not be used or explained.

CloseDropDownMenus([level])

Hides the currently open UIDropDown menu at level level. If level is not specified, it defaults to 1, which means it closes the entire menu.

UIDROPDOWNMENU_OPEN_MENU
This is a global variable in the _G namespace. It will contain the frame reference to the currently open menu (or the last open one).
This variable is rarely used, but can be useful for checking if a certain menu is open to close it, or if you are reusing the same menu frame.

UIDROPDOWNMENU_MENU_VALUE
This is a global variable in the _G namespace. It will contain the submenuID of the deepest level that is currently open/to be opened.
This variable is used to open submenus.


Creating our DropDown

For the purposes of this guide, we will reproduce the Omen menu in the screenshot above.
First we need to create a dropdown frame:

Code:
local Omen_TitleDropDownMenu = CreateFrame("Frame", "Omen_TitleDropDownMenu")
Omen_TitleDropDownMenu.displayMode = "MENU"
Omen_TitleDropDownMenu.initialize = function(self, level) end
Yes, it is that simple. Note:
  1. The frame must be named. In this case, it is named "Omen_TitleDropDownMenu" (2nd argument to CreateFrame())
  2. The frame does NOT inherit from any template. It is just a frame. No tricks.
  3. If the .displayMode = "MENU" attribute isn't set, the menu will look like the GemHelper one in the screenshot.
  4. The .initialize attribute is the function that ToggleDropDownMenu() will call when you open the menu.
Attached Images
File Type: jpg dropdowns.jpg (39.9 KB, 2247 views)
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #2
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

Writing our initialize menu function

The .initialize function is THE function that controls the entire menu and its menuitem generation. It takes in 3 arguments, self, level and menuList.

self: This will be the menu frame itself. In our example, the frame Omen_TitleDropDownMenu will be passed in to self. The global UIDROPDOWNMENU_OPEN_MENU will also contain this frame.
level: The level of menu that is to be generated/displayed.
menuList: We aren't using it in our guide. It will contain menuList passed in to ToggleDropDownMenu()

In order to encourage table reuse, we use a local info table upvalue that we use over and over again to pass information to UIDropDownMenu_AddButton().
Code:
local info = {}
Omen_TitleDropDownMenu.initialize = function(self, level)
    if not level then return end
    wipe(info)
    if level == 1 then
        -- Create the title of the menu
        info.isTitle = 1
        info.text = "Omen Quick Menu"
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)
    end
end
When the menu is first called, level will contain the value of 1, since you want to display the level 1 menu. Here, we instruct UIDropDownMenu to add a menuitem with the text "Omen Quick Menu", and that this menuitem is a title.

Here is a little explanation of the menuitem attributes:

info.isTitle = [nil, true] -- If it's a title the button is disabled and the font color is set to yellow
info.text = [STRING] -- The text of the button
info.notCheckable = [nil, 1] -- Shrink the size of the buttons and don't display a check box

Now lets add the remainder menu items.

Code:
local info = {}
Omen_TitleDropDownMenu.initialize = function(self, level)
    if not level then return end
    wipe(info)
    if level == 1 then
        -- Create the title of the menu
        info.isTitle = 1
        info.text = "Omen Quick Menu"
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)

        info.disabled     = nil
        info.isTitle      = nil
        info.notCheckable = nil

        info.text = "Lock Omen"
        info.func = function()
            db.Locked = not db.Locked
            Omen:UpdateGrips()
            LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
        end
        info.checked = db.Locked
        UIDropDownMenu_AddButton(info, level)

        info.text = "Use Focus Target"
        info.func = function() Omen:ToggleFocus() end
        info.checked = db.UseFocus
        UIDropDownMenu_AddButton(info, level)

        info.text = "Test Mode"
        info.func = function()
            testMode = not testMode
            Omen:UpdateBars()
            LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
        end
        info.checked = testMode
        UIDropDownMenu_AddButton(info, level)

        info.text = "Open Config"
        info.func = function() Omen:ShowConfig() end
        info.checked = nil
        UIDropDownMenu_AddButton(info, level)

        info.text = "Hide Omen"
        info.func = function() Omen:Toggle() end
        UIDropDownMenu_AddButton(info, level)

        -- Close menu item
        info.text         = CLOSE
        info.func         = function() CloseDropDownMenus() end
        info.checked      = nil
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)
    end
end
We do this by setting the .isTitle and .notCheckable options to nil, overwriting the .text attribute, and assign .checked to either true or false depending on whether the menu should display a tick next to it. Note that we also set .disabled to nil, because the function UIDropDownMenu_AddButton() sets .disabled = 1 when .isTitle is true. This is the only case where UIDropDownMenu_AddButton() modifies the info table passed in.

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.checked = [nil, true, function] -- Check the button if true or function returns true
info.func = [function()] -- The function that is called when you click the button

Looking back at the screenshot above, you will notice that all the menu items are indented except for the title menuitem and the Close menuitem. The indentation is caused by the attribute .notCheckable. If this attribute is nil/false, then it is indented (the button allocates space for the checkmark which is shown or hidden depending on .checked). If it is true/non-nil, then it is not indented. .disabled will cause a button's text to be gray unless .isTitle is also true, which makes it yellow. .colorCode attribute can be used to override this (see Button Attributes at the end of this guide). If .func is nil, then no function is run when the button is clicked.

Opening the menu

Now that we have the menu function done, we want to show the menu. In our case, we want the menu to be shown when we right-click Omen's title bar:

Code:
Omen.Title = CreateFrame("Button", "OmenTitle", Omen.Anchor)
-- Other code for the title's width/height/dragging to move Omen etc
Omen.Title:SetScript("OnClick", function(self, button, down)
    if button == "RightButton" then
        ToggleDropDownMenu(1, nil, Omen_TitleDropDownMenu, self:GetName(), 0, 0)
    end
end)
Omen.Title:RegisterForClicks("RightButtonUp")
This tells WoW that "When I right-click OmenTitle, toggle the menu called Omen_TitleDropDownMenu, at level 1, anchored to myself at (0, 0) offset". ToggleDropDownMenu() will then call Omen_TitleDropDownMenu:initialize(1) and your function then adds the menuitems at level 1. This also means that if Omen_TitleDropDownMenu is already open, a second right click on OmenTitle will close the menu.


Common "Mistakes"

You will notice that the menu function above creates new function closures when assigning info.func every time the initialization function is called (when the menu is opened). This isn't really good. You are recommended to upvalue them instead.

Note that the initialization function declaration can also be
Code:
function Omen_TitleDropDownMenu:initialize(level)
end
and it will work fine, as long as you realize that the self variable refers to the menu frame, and NOT your addon object. Another mistake is that the function name is also initialize with a small letter i.
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.

Last edited by Xinhuan; 02-03-2009 at 02:01 PM.
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #3
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

Passing arguments to functions

So far, the menu items above run functions when clicked on and these functions take in no input arguments. They can however take in arguments. Let's look at how the first part of Postal's menu is created. It is a list of Postal's modules, and the checkmarks indicate whether the modules are currently enabled or disabled.

Yes, the menu items CAN have submenus (see the arrow for Express/Select/OpenAll?) and yet itself be a clickable button that does stuff (in this case, enable/disable the module). This is something DewdropLib can't do.

Code:
-- Our Postal menu frame
local Postal_DropDownMenu = CreateFrame("Frame", "Postal_DropDownMenu")
Postal_DropDownMenu.displayMode = "MENU"
Postal_DropDownMenu.info = {}

-- Button that when clicked on shows Postal's menu
-- In Postal, I reuse Postal_DropDownMenu in many places to show different menus
-- So I reassign .initialize to the appropriate menu function before toggling to show it
Postal_ModuleMenuButton:SetScript("OnClick", function(self, button, down)
    if Postal_DropDownMenu.initialize ~= Postal.Menu then
        CloseDropDownMenus()
        Postal_DropDownMenu.initialize = Postal.Menu
    end
    ToggleDropDownMenu(1, nil, Postal_DropDownMenu, self:GetName(), 0, 0)
end)

function Postal.Menu(self, level)
    if not level then return end
    local info = self.info
    wipe(info)
    if level == 1 then
        info.isTitle = 1
        info.text = "Postal"
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)

        info.disabled = nil
        info.isTitle = nil
        info.notCheckable = nil

        info.keepShownOnClick = 1
        for name, module in Postal:IterateModules() do 
            info.text = L[name]
            info.func = Postal.ToggleModule
            info.arg1 = name
            info.arg2 = module
            info.checked = module:IsEnabled()
            info.hasArrow = module.ModuleMenu ~= nil
            info.value = module
            UIDropDownMenu_AddButton(info, level)
        end

        -- Add a blank separator
        wipe(info)
        info.disabled = 1
        UIDropDownMenu_AddButton(info, level)
        info.disabled = nil
    end
end
In the code above, we learn how to add a blank separator. Its just a disabled button with no text. For now, ignore the .hasArrow and .value attributes. We also see that we created menu items dynamically, using a for-loop that iterates over all modules.

What we are interested in here is these 2 attributes:
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.keepShownOnClick = [nil, 1] -- Don't hide the dropdownlist after a button is clicked

Postal.ToggleModule is defined as follows:
Code:
function Postal.ToggleModule(dropdownbutton, arg1, arg2, checked)
    Postal.db.profile.ModuleEnabledState[arg1] = checked
    if checked then arg2:Enable() else arg2:Disable() end
end
The function info.func() is called with the following arguments:
dropdownbutton: The button frame that is clicked on. This is something like DropDownList1Button3 which indicates menu level 1 and menu item 3. For the most part, you will never use this, other than to hack checkmark display (see end of guide for UncheckHack).
arg1: The value in info.arg1
arg2: The value in info.arg2
checked: Boolean containing true or false. This is the new value that is in effect.

In our example code, we stored the module reference directly in info.arg2, so that we could call arg2:Enable() and arg2:Disable() directly.
The first line saves the new state in the savedvariables.
The second line enables or disables the module.

Creating multiple level menus

This is where we introduce .hasArrow and .value attributes.

info.hasArrow = [nil, true] -- Show the expand arrow for multilevel menus
info.value = [ANYTHING] -- The value that UIDROPDOWNMENU_MENU_VALUE is set to when the button is clicked

When .hasArrow is true, it displays the arrow. When the mouse is hovered over such an entry that has an arrow, the menu initialization function is called to show the submenu with UIDROPDOWNMENU_MENU_VALUE equal to the parent's .value. Here's an example code:

Code:
local ExampleDropDownMenu = CreateFrame("Frame", "ExampleDropDownMenu")
ExampleDropDownMenu.displayMode = "MENU"
ExampleDropDownMenu.info = {}
ExampleDropDownMenu.UncheckHack = function(dropdownbutton)
    _G[dropdownbutton:GetName().."Check"]:Hide()
end
ExampleDropDownMenu.HideMenu = function()
    if UIDROPDOWNMENU_OPEN_MENU == ExampleDropDownMenu then
        CloseDropDownMenus()
    end
end

ExampleDropDownMenu.initialize = function(self, level)
    if not level then return end
    local info = self.info
    wipe(info)
    if level == 1 then
        info.isTitle = 1
        info.text = "Example"
        info.notCheckable = 1
        UIDropDownMenu_AddButton(info, level)

        info.keepShownOnClick = 1
        info.disabled = nil
        info.isTitle = nil
        info.notCheckable = nil

        info.text = "Abcd"
        info.func = self.UncheckHack
        info.hasArrow = 1
        info.value = "submenu1"
        UIDropDownMenu_AddButton(info, level)

        info.text = "Wxyz" -- Note .hasArrow and .func fallthrough from prev item.
        info.value = "submenu2"
        UIDropDownMenu_AddButton(info, level)

        -- Close menu item
        info.hasArrow     = nil
        info.value        = nil
        info.notCheckable = 1
        info.text         = CLOSE
        info.func         = self.HideMenu
        UIDropDownMenu_AddButton(info, level)

    elseif level == 2 then
        if UIDROPDOWNMENU_MENU_VALUE == "submenu1" then
            info.text = "Foo"
            UIDropDownMenu_AddButton(info, level)

            info.text = "Bar"
            UIDropDownMenu_AddButton(info, level)

        elseif UIDROPDOWNMENU_MENU_VALUE == "submenu2" then
            info.text = "Moo"
            UIDropDownMenu_AddButton(info, level)

            info.text = "Lar"
            UIDropDownMenu_AddButton(info, level)

        end
    end
end
The above will result in a menu that looks like this screenshot:



.keepShownOnClick = 1 will cause the checkmark to toggle whether you like it or not if you happen to click on it. You can "hack" around it by assigning the UncheckHack as above to the .func which essentally just hides it. If you instead tried to use .notClickable = 1, it will instead disable mouse events on the button, which means that hovering your mouse on it will not open the submenu, only if you hovered your mouse on the arrow itself. So we avoid using it.

Essentially, UIDropDownMenu doesn't care about what your menu parent is, you could have many menu items at level 1 have info.value = "submenu2", and it would open the same "submenu2" at level 2. In particular, info.value can be any value of any type, this means it can be a table, or a number, or a function. You can abuse this by assigning info.value to a table at a higher menu level, so that UIDROPDOWNMENU_MENU_VALUE will be that table at the lower menu level, and iterate over UIDROPDOWNMENU_MENU_VALUE to build the submenu, and assign a subtable to info.value for further submenus.
Attached Images
File Type: jpg dropdowns2.jpg (4.9 KB, 2136 views)
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #4
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

Using EasyMenu

EasyMenu is meant to be a wrapper provided by Blizzard to help you create menus using UIDropDownMenu. Essentially, instead of writing code to set all the info.blahs and then calling UIDropDownMenu_AddButton(info, level), EasyMenu takes in an array of info[] and does it for you.

The following shows how Omen's menu (in the first screenshot) can be rewritten using EasyMenu. First we have to define a table, lets call it OmenMenuTable. In it, is an array of info[]. So the first menuitem is OmenMenuTable[1], the second menu item is OmenMenuTable[2] and so on.

Simply put, EasyMenu will iterate over the OmenMenuTable and call UIDropDownMenu_AddButton(OmenMenuTable[ i ], 1).

Code:
local Omen_TitleDropDownMenu = CreateFrame("Frame", "Omen_TitleDropDownMenu")
local OmenMenuTable = {
    {
        text = L["Omen Quick Menu"],
        isTitle = 1,
        notCheckable = 1,
    },
    {
        text = L["Lock Omen"],
        func = function()
            db.Locked = not db.Locked
            Omen:UpdateGrips()
            LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
        end,
        checked = function() return db.Locked end,
    },
    {
        text = L["Use Focus Target"],
        func = function() Omen:ToggleFocus() end,
        checked = function() return db.UseFocus end,
    },
    {
        text = L["Test Mode"],
        func = function()
            testMode = not testMode
            Omen:UpdateBars()
            LibStub("AceConfigRegistry-3.0"):NotifyChange("Omen")
        end,
        checked = function() return testMode end,
    },
    {
       text = L["Open Config"],
        func = function() Omen:ShowConfig() end,
    },
    {
        text = L["Hide Omen"],
        func = function() Omen:Toggle() end,
    },
    {
        text = CLOSE,
        func = function() CloseDropDownMenus() end,
        notCheckable = 1,
    },
}
Notice that the .checked entries are now functions that return true/false values. That is because by prepulating the OmenMenuTable, they need to be functions that EasyMenu can call to determine if that item should be checked or not at the time the menu is displayed.

The code to show the menu is simply replacing the ToggleDropDownMenu() to the following EasyMenu() call:
Code:
Omen.Title:SetScript("OnClick", function(self, button, down)
    if button == "RightButton" then
        EasyMenu(OmenMenuTable, Omen_TitleDropDownMenu, self:GetName(), 0, 0, nil)
    end
end)
This says, use OmenMenuTable to generate my menu, using the frame Omen_TitleDropDownMenu, and parent it to myself at 0, 0 offsets. The last parameter (nil here) is to indicate the displayMode. nil means to use the GemHelper style displayMode (see first screenshot in first post). "MENU" would be to use the Omen/Postal display style.

There is a catch!
If you use "MENU" display style, your frame MUST inherit from UIDropDownMenuTemplate because EasyMenu() calls UIDropDownMenu_Initialize() which hides certain elements of the inherited frame in "MENU" mode.

That is, the above code has to be changed from
Code:
local Omen_TitleDropDownMenu = CreateFrame("Frame", "Omen_TitleDropDownMenu")
and
Code:
EasyMenu(OmenMenuTable, Omen_TitleDropDownMenu, self:GetName(), 0, 0, nil)
to
Code:
local Omen_TitleDropDownMenu = CreateFrame("Frame", "Omen_TitleDropDownMenu", nil, "UIDropDownMenuTemplate")
and
Code:
EasyMenu(OmenMenuTable, Omen_TitleDropDownMenu, self:GetName(), 0, 0, "MENU")
Obviously, inheriting from the template will generate a lot of useless inherited frames that get hidden.

Submenus in EasyMenu are defined in

info.menuTable = [TABLE] -- This contains an array of info tables to be displayed as a child menu

So you just embed subtables in your tables as necessary. Obviously .menuTable only has meaning when used with .hasArrow

Advantages of EasyMenu
  • Easy to code by predefining your info[] tables. No need to write an initialization function.
  • Good for small static menus.
Disadvantages of EasyMenu
  • The predefined table is static, meaning you can't dynamically generate menu items to add to your menus, unless you have code that actively modifies the predefined table. This is usually more trouble than it is worth. If you need dynamic menus, code it directly and don't use EasyMenu.
  • Each .checked entry needs to be a function that returns true/false. This can add up to a lot of functions.
  • You cannot use displayMode = "MENU" without inheriting from UIDropDownMenuTemplate.

Button attributes
The following is cut and pasted from FrameXML\UIDropDownMenu.lua
  • info.text = [STRING] -- The text of the button
  • info.value = [ANYTHING] -- The value that 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.minWidth = [nil, NUMBER] -- Minimum width for this line

Notes:
Most of the attributes above are copied to the button frame object representing the menu item. i.e, the dropdownbutton arg that is passed to the first argument of info.func.

.tooltipTitle and .tooltipText are only used when "Beginner Tooltips" cvar is turned on by the user. Sorry, there is no way to provide tooltips otherwise, which is a reason why DewdropLib is popular.

.keepShownOnClick will cause the checkmark to toggle whether you like it or not. You can "hack" around it by assigning the following .func:

Code:
ExampleDropDownMenu.UncheckHack = function(dropdownbutton)
    _G[dropdownbutton:GetName().."Check"]:Hide()
end

-- In the code for the menu items...
    info.keepShownOnClick = 1
    info.func = self.UncheckHack
.owner isn't used by the default UI, or by any code for the matter.

Read FrameXML\UIDropDownMenu.lua code to see how the remainer attributes are used. The only ones I haven't covered is really just the color swatch (example where it is used is in the right click menu on your chatframe tab to set the background color).

Conclusion

Using UIDropDownMenu is relatively simple. All it requires is to create a named frame, and a menu initialization function. It is well suited for small menus that have very few items and/or very few levels. If you need something more, it is probably better to use DewdropLib or some form of UIDropDownMenu wrapper that can do most of these tasks automatically rather than tediously set info.blah everytime.
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.

Last edited by Phanx; 1 Week Ago at 03:10 AM. Reason: fixed bad formatting
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #5
HunterZ
Legendary Member
 
HunterZ's Avatar
 
Join Date: Dec 2005
Location: Seattle
Posts: 3,368
Default Re: Guide: Using UIDropDownMenu in your addon

Should mention the taint issues with UIDropDownMenu.

Also, never ever use it for SharedMedia textures or fonts
HunterZ is offline   Reply With Quote
Old 02-03-2009   #6
Mera_LaCroisadeEcarlate
Full Member
 
Join Date: May 2007
Location: France
Posts: 110
Send a message via MSN to Mera_LaCroisadeEcarlate
Default Re: Guide: Using UIDropDownMenu in your addon

good job Xinhuan will help for sure but yet im not enough convinced to do the jump from dew which does not need lots of tweaks to run along ace3config, I'm rather waiting at xinhuan's dewdrop3 ;p
Mera_LaCroisadeEcarlate is offline   Reply With Quote
Old 02-03-2009   #7
Dridzt
Hero Member
 
Dridzt's Avatar
 
Join Date: Nov 2005
Posts: 866
Default Re: Guide: Using UIDropDownMenu in your addon

This should be on wowwiki and pretty much any addon hosting site that has a developer forum

Great job.
Dridzt is offline   Reply With Quote
Old 02-03-2009   #8
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

Quote:
Originally Posted by HunterZ View Post
Should mention the taint issues with UIDropDownMenu.
That is a different issue and has been longstanding. As long as you don't use "Set Focus" from the menu in unit frames, taint is irrelevant. So many addons use UIDropDownMenu that this isn't really a valid reason for your addon not to use one.
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #9
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

Quote:
Originally Posted by Mera_LaCroisadeEcarlate View Post
good job Xinhuan will help for sure but yet im not enough convinced to do the jump from dew which does not need lots of tweaks to run along ace3config, I'm rather waiting at xinhuan's dewdrop3 ;p
UIDropDownMenu is meant for simple menus, usually menus that are just 1 or 2 levels deep, for small simple options or executing simple functions.

If you look at Omen's menu, it doesn't configure stuff, it is just a quick menu that offers quick toggles to commonly used functions. Omen still uses AceGUI/AceConfig for the heavyweight configuration.

Recount is another addon that is using UIDropDownMenu in exactly the same way as described above as Omen.
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.
Xinhuan is offline   Reply With Quote
Old 02-03-2009   #10
Xinhuan
Asian Sheep Lover
 
Xinhuan's Avatar
 
Join Date: Aug 2007
Location: Singapore
Posts: 4,033
Default Re: Guide: Using UIDropDownMenu in your addon

I have updated the last post in the guide with how to use EasyMenu.
__________________
Author/Maintainer of Postal, Omen3, GemHelper, BankItems, WoWEquip, GatherMate, Routes, HandyNotes and some others.
Xinhuan is offline   Reply With Quote
Reply


Currently Active Users Viewing This Thread: 1 (0 members and 1 guests)
 
Thread Tools

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is On
HTML code is Off

Forum Jump


All times are GMT. The time now is 12:03 PM.