Dropdowns - RGXMods/RGX-Framework GitHub Wiki
RGX Dropdowns System
Complete documentation for RGXDropdowns — nested UIDropDownMenu wrappers, auto-width, inline buttons, and dual-schema item normalization.
Overview
RGXDropdowns wraps WoW's native UIDropDownMenu system with:
- Nested submenus — arbitrary depth via
childrenarrays - Auto-width — measures rendered button labels and sizes the list frame to fit
- Inline buttons — per-item action widgets (e.g. preview buttons) pinned to dropdown buttons
- Item normalization —
CopyItem/NormalizeItemstransforms various item schemas into the canonical UIDropDownMenu format - MenuUtil compatibility — dual
func/onClickfields for both legacy and modern WoW dropdown paths
No custom scroll frames, no custom menu rendering — all built on WoW's native UIDropDownMenu_* API.
CreateNestedDropdown
local dd = Drops:CreateNestedDropdown(parent, {
label = "Choose Font", -- label text above the dropdown
width = 200, -- dropdown width in pixels
value = "Inter-Regular", -- initial selected value
items = {
{ text = "Sans", isHeader = true }, -- section header
{ text = "Inter", value = "Inter-Regular", -- leaf item
children = { -- OR submenu
{ text = "Regular", value = "Inter-Regular" },
{ text = "Bold", value = "Inter-Bold" },
}},
{ isSeparator = true }, -- horizontal divider
{ text = "None", value = "" }, -- leaf with empty value
{ text = "Icon Item", value = "x", icon = "Interface\\Icons\\INV_Misc_QuestionMark" },
{ text = "Colored", value = "y", colorCode = "|cffFF8000" },
{ text = "Sticky", value = "z", keepOpen = true },
},
onChange = function(value, item) end, -- fires on leaf selection
autoWidth = { minWidth = 200, leftInset = 10 }, -- auto-size the list frame
-- Optional BLU-derived retail button face; the same nested menu remains.
-- Omit for the client-default dropdown presentation.
triggerStyle = "retail",
justifyH = "LEFT", -- retail trigger text alignment: LEFT (default), CENTER, or RIGHT
onButtonCreated = function(buttonFrame, item) end, -- after each button is rendered
})
Fonts:CreateFontDropdown and Fonts:CreateFontSettingControl pass
triggerStyle through to this factory. The visible retail trigger is styled
with the current RGXDesign theme; the existing native menu handles selection.
justifyH only affects the closed retail trigger face, not menu rows, and
CENTER/RIGHT move the arrow inset so the label keeps symmetric (or
right-anchored) room.
Item schema
| Field | Type | Description |
|---|---|---|
text |
string | Display label |
value |
any | Selection value (passed to onChange) |
children |
array | Submenu items (creates hasArrow = true) |
menuList |
array | Alias for children (BPU compat) |
isHeader |
boolean | Non-selectable section header |
isSeparator |
boolean | Horizontal divider |
icon |
string | Texture path, shown left of label |
colorCode |
string | ` |
keepOpen |
boolean | Don't close menu on select |
func |
function | Legacy callback (BPU / UIDropDownMenu path) |
onClick |
function | RGX MenuUtil callback |
checked |
boolean | Radio/checkbox check state |
isNotRadio |
boolean | Checkbox style (not radio dot) |
keepShownOnClick |
boolean | Menu stays open after click |
notCheckable |
boolean | No check indicator at all |
hasArrow |
boolean | Submenu arrow (auto-set if children present) |
Item Normalization
CopyItem
local normalized = Drops:CopyItem(item)
Transforms various input schemas into canonical UIDropDownMenu format:
| Input field | Output field | Rule |
|---|---|---|
menuList |
children |
menuList → children (BPU compat) |
arg1 |
value |
arg1 → value |
font |
value |
font → value |
name |
value |
name → value if path is also present |
func |
onClick |
func → onClick if onClick is absent |
Both children and menuList reference the same table after normalization — consumers using either field get the same data.
NormalizeItems
Drops:NormalizeItems(items) -- in-place normalization of an item array
Calls CopyItem on each entry. Used internally by CreateNestedDropdown before rendering.
Auto-Width
Dropdown list frames in WoW are sized before their button content is rendered. RGX solves this with a deferred two-pass measurement:
Drops:ForceWidth(level, minWidth, leftInset, opts)
level— 1 = root menu, 2 = first submenu, etc.minWidth— minimum list frame widthleftInset— left padding offsetopts.inlineKeys— inline button keys to account foropts.compactRight— reduce right marginopts.countKey— item count key for sizing
Implementation: ForceWidth is deferred via RGX:After(0). On the first pass, it measures all button FontString:GetWidth() values. On the second pass, it applies the resolved width to the list frame and all buttons.
Inline Buttons
Inline buttons are small action widgets attached to individual dropdown button frames. They persist across re-renders by key.
AddInlineButton
Drops:AddInlineButton(buttonFrame, {
key = "preview", -- reuse key (default "inline")
text = "▶", -- button label
width = 20, -- button width
height = 16, -- button height
tooltip = "Preview", -- tooltip text on hover
onClick = function(inlineBtn, buttonFrame) end,
})
The inline button is positioned to the right of the dropdown button's text, inset from the right edge.
HideInlineButtons
Drops:HideInlineButtons(level, key) -- hide all inline buttons with key at level
Call before re-populating a menu level to clear stale inline buttons.
Helpers
Drops:GetListFrame(level) -- returns _G["DropDownList"..level]
Drops:ShortenLabel(text, maxChars) -- truncate with "..." ellipsis
Drops:NormalizeItems(items) -- normalize item list in place
Font Dropdown Integration
CreateFontDropdown (in the Fonts module) delegates to Drops:CreateNestedDropdown with font-specific item data from BuildGroupedFontItems:
-- library/fonts/dropdowns.lua
function Fonts:CreateFontDropdown(parent, opts)
local function buildItems()
return Fonts:BuildGroupedFontItems({ keepShownOnClick = true })
end
return Drops:CreateNestedDropdown(parent, {
label = opts.label,
width = opts.width or 200,
value = opts.value,
items = buildItems(),
onChange = function(value, item)
-- resolve font name from value, call opts.onChange
end,
})
end
Selection is handled by the onChange callback on the CreateNestedDropdown holder — not by passing onSelect through buildItems. This keeps the font dropdown's callback path consistent with all other RGX dropdowns.
Dual-Schema Design
RGX dropdown items carry both legacy and modern callback fields:
func— called by WoW'sUIDropDownMenudispatch when a button is clicked (legacy path, used by BPU)onClick— called by WoW'sMenuUtilmodular system (modern path, used by RGX)
Both are set to the same closure so either dispatch path produces the same behavior.
Similarly, submenu data is carried in both:
children— read by RGX'sNormalizeItemsandCreateNestedDropdownmenuList— read by BPU'sCreateMenuInfosystem
After CopyItem / NormalizeItems, both fields reference the same table.
File Layout
library/dropdowns/
└── dropdowns.lua — CreateNestedDropdown, CopyItem, NormalizeItems, ForceWidth, AddInlineButton, HideInlineButtons, GetListFrame, ShortenLabel