Description
A lightweight menu library for World of Warcraft addon development that simplifies creating dropdown menus with support for titles, separators, nested items, and custom styling.
Features
Menu System (Krowi_Menu)
- Simple API: Easy-to-use methods for building menus programmatically
- Flexible Menu Items: Support for titles, separators, checkable items, and disabled states
- Nested Menus: Create hierarchical menu structures with children
- Smart Positioning: Automatic submenu repositioning to keep menus on-screen (Classic)
- Custom Styling: Control menu positioning, frame strata, and frame levels
- Selection State: Track and refresh selected menu items
- Krowi_LibMan Integration: Modern library management with Krowi_LibMan
Menu Item Builder (Krowi_MenuItem)
- Item Creation: Build menu items with custom properties
- External Links: Integration with popup dialogs for displaying external links
- Child Items: Support for nested menu structures
- Flexible Options: Checkable, disabled, and title items
Menu Utilities (Krowi_MenuUtil)
- Cross-Version Compatibility: Unified API for both modern (Mainline) and Classic WoW versions
- Automatic Detection: Detects game version and uses appropriate menu system
- Simplified Interface: Consistent methods across all WoW versions
Menu Builder (Krowi_MenuBuilder)
- High-Level Abstraction: Simplified API for building complex menus with checkboxes, radio buttons, and filters
- Callback-Based Architecture: Flexible callback system for handling menu interactions and state management
- Smart Defaults: Automatic integration with
Krowi_Util_2for common operations (KeyIsTrue, KeyEqualsText) - Callback Helper:
BindCallbacksutility eliminates boilerplate when binding object methods to callbacks - Build Version Filters: Built-in support for creating hierarchical version filter menus with Select/Deselect All
- Cross-Version Support: Unified API that works seamlessly on both Modern (Mainline) and Classic WoW
- Instance-Based: Create multiple independent menu builders with isolated state and configurations
- Type Safety: Full parameter validation with helpful error messages
Usage Examples
Basic Menu Setup
local menu = KROWI_LIBMAN:GetLibrary('Krowi_Menu_2')
local pages = {} -- Table with data
menu:Clear() -- Reset menu
menu:AddFull({Text = "View Pages", IsTitle = true})
for i, _ in next, pages do
menu:AddFull({
Text = (pages[i].IsViewed and "" or "|T132049:0|t") .. pages[i].SubTitle,
Func = function()
-- Some function here
end
})
end
menu:Open()
Advanced Example with Nested Menus
local menu = KROWI_LIBMAN:GetLibrary('Krowi_Menu_2')
local menuItem = KROWI_LIBMAN:GetLibrary('Krowi_MenuItem_2')
menu:Clear()
-- Create a parent item with children
local parent = menuItem:New({Text = "Settings"})
parent:AddFull({
Text = "Enable Feature",
Checked = true,
Func = function() print("Feature toggled") end
})
parent:AddSeparator()
parent:AddFull({
Text = "Reset to Defaults",
Func = function() print("Reset") end
})
menu:Add(parent)
menu:Open("cursor")
MenuBuilder Example
local MenuBuilder = KROWI_LIBMAN:GetLibrary('Krowi_MenuBuilder_2')
-- Configure MenuBuilder with callbacks using BindCallbacks helper
local config = {
callbacks = MenuBuilder.BindCallbacks(self, {
GetCheckBoxStateText = "GetCheckBoxStateText",
KeyIsTrue = "KeyIsTrue",
OnCheckboxSelect = "OnCheckboxSelect",
KeyEqualsText = "KeyEqualsText",
OnRadioSelect = "OnRadioSelect"
}),
translations = {
["Select All"] = "Select All",
["Deselect All"] = "Deselect All",
["Version"] = "Version"
}
}
local builder = MenuBuilder:New(config)
-- Modern WoW: Setup menu on a dropdown button
if WOW_PROJECT_ID == WOW_PROJECT_MAINLINE then
builder:SetupMenuForModern(myDropdownButton)
-- Define the menu structure
function builder:CreateMenu()
local menu = self:GetMenu()
self:CreateTitle(menu, "Filter Options")
self:CreateCheckbox(menu, "Show Completed", filters, {"Completed"})
self:CreateCheckbox(menu, "Show In Progress", filters, {"InProgress"})
self:CreateDivider(menu)
local sortMenu = self:CreateSubmenuButton(menu, "Sort By")
self:CreateRadio(sortMenu, "Name", filters, {"SortBy"}, "name")
self:CreateRadio(sortMenu, "Date", filters, {"SortBy"}, "date")
end
else
-- Classic WoW: Show menu manually
function builder:CreateMenu()
local menu = self:GetMenu()
self:CreateTitle(menu, "Filter Options")
self:CreateCheckbox(menu, "Show Completed", filters, {"Completed"})
self:CreateCheckbox(menu, "Show In Progress", filters, {"InProgress"})
self:CreateDivider(menu)
local sortMenu = self:CreateSubmenuButton(menu, "Sort By")
self:CreateRadio(sortMenu, "Name", filters, {"SortBy"}, "name")
self:CreateRadio(sortMenu, "Date", filters, {"SortBy"}, "date")
self:AddChildMenu(menu, sortMenu)
end
-- Show the menu when needed
builder:Show(frameAnchor, 0, 0)
end
Callback Implementation Example
-- Example object that implements menu callbacks
local MyAddon = {}
function MyAddon:GetCheckBoxStateText(text, filters, keys)
-- Add visual indicators to checkbox text
local isChecked = self:KeyIsTrue(filters, keys)
return (isChecked and "|cFF00FF00✓ |r" or "|cFF808080○ |r") .. text
end
function MyAddon:KeyIsTrue(filters, keys)
-- Read nested keys from filters table (auto-provided if Krowi_Util-1.0 is available)
local value = filters
for _, key in ipairs(keys) do
value = value[key]
if value == nil then return false end
end
return value == true
end
function MyAddon:OnCheckboxSelect(filters, keys)
-- Toggle the filter value
local value = filters
for i = 1, #keys - 1 do
value = value[keys[i]]
end
local finalKey = keys[#keys]
value[finalKey] = not value[finalKey]
-- Refresh UI
self:UpdateDisplay()
end
function MyAddon:KeyEqualsText(filters, keys, text)
-- Check if filter equals specific value
local value = filters
for _, key in ipairs(keys) do
value = value[key]
if value == nil then return false end
end
return value == text
end
function MyAddon:OnRadioSelect(filters, keys, value)
-- Set the filter to selected value
local filterTable = filters
for i = 1, #keys - 1 do
filterTable = filterTable[keys[i]]
end
filterTable[keys[#keys]] = value
-- Refresh UI
self:UpdateDisplay()
end
API Reference
Krowi_Menu
Creating a Menu
local menu = KROWI_LIBMAN:GetLibrary('Krowi_Menu_2')
Menu Functions
| Function | Parameters | Description |
|---|---|---|
Clear() |
- | Resets the menu, removing all items |
Add(item) |
item (MenuItem) |
Adds a converted menu item to the menu |
AddFull(info) |
info (table) |
Creates and adds a menu item from info table |
AddTitle(text) |
text (string) |
Adds a title item to the menu |
AddSeparator() |
- | Adds a separator line to the menu |
Open(anchor, offsetX, offsetY, point, relativePoint, frameStrata, frameLevel) |
See below | Opens and displays the menu |
Toggle(...) |
Same as Open | Toggles menu visibility (opens if closed, closes if open) |
Close() |
- | Closes the menu if it's currently open |
SetSelectedName(name) |
name (string) |
Sets the selected menu item by name and refreshes display |
GetSelectedName(frame) |
frame (frame) |
Returns the currently selected item name |
Open() Parameters
| Parameter | Type | Default | Description |
|---|---|---|---|
anchor |
string/frame | "cursor" | Anchor point for menu positioning |
offsetX |
number | 0 | Horizontal offset |
offsetY |
number | 0 | Vertical offset |
point |
string | nil | Point on menu frame |
relativePoint |
string | nil | Point on anchor frame |
frameStrata |
string | "FULLSCREEN_DIALOG" | Frame strata level |
frameLevel |
number | nil | Frame level (auto-raised if strata set without level) |
Menu Item Info Table
| Field | Type | Default | Description |
|---|---|---|---|
Text |
string | "INFO TEXT" | Display text for the menu item |
Checked |
boolean | nil | Whether the item is checked |
Func |
function | nil | Function to call when item is clicked |
IsTitle |
boolean | nil | Whether this item is a title (non-clickable) |
Disabled |
boolean | nil | Whether the item is disabled |
IsNotRadio |
boolean | nil | Use checkbox instead of radio button |
NotCheckable |
boolean | true | Whether the item can be checked/unchecked |
KeepShownOnClick |
boolean | nil | Keep menu open after clicking this item |
IgnoreAsMenuSelection |
boolean | nil | Don't update selection state when checked |
Children |
table | nil | Array of child menu items for nested menus |
IsSeparator |
boolean | nil | Whether this item is a separator line |
Krowi_MenuItem
Creating Menu Items
local menuItem = KROWI_LIBMAN:GetLibrary('Krowi_MenuItem_2')
MenuItem Functions
| Function | Parameters | Returns | Description |
|---|---|---|---|
New(info, hideOnClick) |
info (table/string), hideOnClick (boolean) |
MenuItem | Creates a new menu item. If info is string, uses it as Text |
Add(item) |
item (MenuItem) |
MenuItem | Adds a child item to this menu item |
AddFull(info) |
info (table) |
MenuItem | Creates and adds a child from info table |
AddTitle(text) |
text (string) |
- | Adds a title child to this menu item |
AddSeparator() |
- | MenuItem | Adds a separator child to this menu item |
Krowi_MenuUtil
Creating Utilities Instance
local menuUtil = KROWI_LIBMAN:GetLibrary('Krowi_MenuUtil_2')
MenuUtil Functions
| Function | Parameters | Description |
|---|---|---|
CreateTitle(menu, text) |
menu (menu), text (string) |
Creates a title in the menu (adapts to game version) |
CreateButton(menu, text, func, isEnabled) |
menu (menu), text (string), func (function), isEnabled (boolean) |
Creates a menu button (adapts to game version) |
CreateDivider(menu) |
menu (menu) |
Creates a divider/separator (adapts to game version) |
AddChildMenu(menu, child) |
menu (menu), child (MenuItem) |
Adds a child to the menu (Classic only) |
CreateButtonAndAdd(menu, text, func, isEnabled) |
menu (menu), text (string), func (function), isEnabled (boolean) |
Creates and adds a button in one call |
Note: MenuUtil automatically detects whether the game is running Mainline or Classic and uses the appropriate menu system. On Mainline, it uses the modern menu API. On Classic versions, it uses the Krowi_MenuItem library.
Krowi_MenuBuilder
Creating a MenuBuilder Instance
local MenuBuilder = KROWI_LIBMAN:GetLibrary('Krowi_MenuBuilder_2')
local builder = MenuBuilder:New(config)
Configuration Object
The configuration object passed to MenuBuilder:New(config) supports the following fields:
| Field | Type | Required | Description |
|---|---|---|---|
callbacks |
table | Yes | Table of callback functions for menu interactions (see Callback Reference below) |
translations |
table | No | Table of localized strings. Defaults: ["Select All"], ["Deselect All"], ["Version"] |
uniqueTag |
string | No | Unique identifier for this instance. Auto-generated from instance address if not provided |
Example Configuration:
local config = {
callbacks = {
GetCheckBoxStateText = function(text, filters, keys) return text end,
KeyIsTrue = function(filters, keys) return false end,
OnCheckboxSelect = function(filters, keys, ...) end,
KeyEqualsText = function(filters, keys, value) return false end,
OnRadioSelect = function(filters, keys, value, ...) end,
-- Optional version filter callbacks
IsMinorVersionChecked = function(filters, minor) return false end,
OnMinorVersionSelect = function(filters, minor) end,
IsMajorVersionChecked = function(filters, major) return false end,
OnMajorVersionSelect = function(filters, major) end,
OnAllVersionsSelect = function(filters, value) end,
CreateBuildVersionFilterGroups = function(version, filters, menuBuilder) end
},
translations = {
["Select All"] = "Select All",
["Deselect All"] = "Deselect All",
["Version"] = "Version"
},
uniqueTag = "MyAddonMenu"
}
Callback Reference
MenuBuilder uses callbacks to interact with your addon's data and respond to user interactions. Callbacks are optional unless specified as required.
Checkbox Callbacks:
| Callback | Parameters | Returns | Required | Description |
|---|---|---|---|---|
GetCheckBoxStateText |
text, filters, keys |
string | No | Modifies checkbox text based on state. Default: returns text unchanged |
KeyIsTrue |
filters, keys |
boolean | No* | Checks if a nested key is true. Default: uses Krowi_Util_2.ReadNestedKeys if available |
OnCheckboxSelect |
filters, keys, ... |
void | Yes | Called when checkbox is clicked. Varargs are custom data passed to CreateCheckbox |
Radio Button Callbacks:
| Callback | Parameters | Returns | Required | Description |
|---|---|---|---|---|
KeyEqualsText |
filters, keys, value |
boolean | No* | Checks if nested key equals value. Default: uses Krowi_Util_2.ReadNestedKeys if available |
OnRadioSelect |
filters, keys, value, ... |
void | Yes | Called when radio button is selected. Varargs are custom data passed to CreateRadio |
Build Version Filter Callbacks (Optional - only needed if using CreateBuildVersionFilter):
| Callback | Parameters | Returns | Description |
|---|---|---|---|
IsMinorVersionChecked |
filters, minor |
boolean | Returns whether all patches in minor version are checked |
OnMinorVersionSelect |
filters, minor |
void | Called when minor version checkbox is toggled |
IsMajorVersionChecked |
filters, major |
boolean | Returns whether all versions in major version are checked |
OnMajorVersionSelect |
filters, major |
void | Called when major version checkbox is toggled |
OnAllVersionsSelect |
filters, value |
void | Called when Select All / Deselect All is clicked |
CreateBuildVersionFilterGroups |
version, filters, menuBuilder |
void | Builds the version filter menu structure. Called by CreateBuildVersionFilter |
General Callbacks:
| Callback | Parameters | Returns | Description |
|---|---|---|---|
OnAllSelect |
filters, keys, value |
void | Called by CreateSelectDeselectAll for batch operations. Used by CreateSelectDeselectAllButtons |
Notes:
- *
KeyIsTrueandKeyEqualsTexthave smart defaults ifKrowi_Util_2library is available keysparameter is always an array representing nested path, e.g.,{"Filters", "ShowCompleted"}filtersis your addon's filter data structure- Varargs (
...) allow passing custom context to callbacks
MenuBuilder Utility Functions
| Function | Parameters | Returns | Description |
|---|---|---|---|
BindCallbacks(obj, methodNames) |
obj (table), methodNames (table) |
table | Creates callback table by binding object methods. methodNames maps callback names to method names on the object |
Example:
-- Instead of manually creating callbacks like this:
local config = {
callbacks = {
OnCheckboxSelect = function(filters, keys, ...)
return MyAddon:OnCheckboxSelect(filters, keys, ...)
end,
OnRadioSelect = function(filters, keys, value, ...)
return MyAddon:OnRadioSelect(filters, keys, value, ...)
end
}
}
-- Use BindCallbacks to eliminate boilerplate:
local config = {
callbacks = MenuBuilder.BindCallbacks(MyAddon, {
OnCheckboxSelect = "OnCheckboxSelect",
OnRadioSelect = "OnRadioSelect"
})
}
MenuBuilder Methods
MenuBuilder Methods
Menu Management:
| Function | Parameters | Description |
|---|---|---|
GetMenu() |
- | Returns the current menu object (Modern: MenuProxy, Classic: Krowi_Menu) |
Show(anchor, offsetX, offsetY) |
anchor (frame), offsetX (number), offsetY (number) |
Classic only: Shows menu at anchor. Modern: No-op (use SetupMenuForModern) |
ShowPopup(createFunc, anchor, offsetX, offsetY) |
createFunc (function), anchor (frame), offsetX (number), offsetY (number) |
Shows standalone popup menu. Calls createFunc(builder) to build menu structure |
Close() |
- | Classic only: Closes the menu. Modern: No-op (auto-managed) |
SetupMenuForModern(button) |
button (frame with SetupMenu) |
Modern only: Configures dropdown button. Requires button to have SetupMenu method (WowStyle1FilterDropdownMixin) |
SetElementEnabled(element, isEnabled) |
element (button/checkbox/radio), isEnabled (boolean) |
Enables or disables a menu element dynamically. Modern: Uses element:SetEnabled(). Classic: Sets element.Disabled property |
Menu Building:
All methods support optional menu parameter. If omitted, uses GetMenu().
| Function | Parameters | Description |
|---|---|---|
CreateTitle(menu, text) |
menu, text (string) |
Creates non-clickable title header |
CreateDivider(menu) |
menu |
Creates separator line |
CreateCheckbox(menu, text, filters, keys, ...) |
menu, text (string), filters (table), keys (array), varargs |
Creates checkbox. keys is path to value in filters. Varargs passed to OnCheckboxSelect callback |
CreateCustomCheckbox(menu, text, isCheckedFunc, onClickFunc) |
menu, text (string), isCheckedFunc (function), onClickFunc (function) |
Creates checkbox with direct callback functions, bypassing standard callback system |
CreateRadio(menu, text, filters, keys, value, ...) |
menu, text (string), filters (table), keys (array), value (any), varargs |
Creates radio button. value is stored when selected (defaults to text). Varargs passed to OnRadioSelect |
CreateCustomRadio(menu, text, isSelectedFunc, onClickFunc) |
menu, text (string), isSelectedFunc (function), onClickFunc (function) |
Creates radio button with direct callbacks, bypassing standard callback system |
CreateSubmenuButton(menu, text, func, isEnabled) |
menu, text (string), func (function), isEnabled (boolean) |
Creates button that opens submenu. Modern: Returns submenu. Classic: Returns MenuItem |
CreateSubmenuRadio(menu, text, isSelectedFunc, onClickFunc, isEnabled) |
menu, text (string), isSelectedFunc (function), onClickFunc (function), isEnabled (boolean) |
Creates radio button in submenu with custom callbacks |
AddChildMenu(menu, child) |
menu, child (menu/MenuItem) |
Classic only: Adds child menu to parent. Modern: No-op (auto-managed) |
CreateButtonAndAdd(menu, text, func, isEnabled) |
menu, text (string), func (function), isEnabled (boolean) |
Modern: Same as CreateSubmenuButton. Classic: Creates and adds button in one call |
Specialized Menu Builders:
| Function | Parameters | Description |
|---|---|---|
CreateBuildVersionFilter(filters, menu) |
filters (table), menu |
Creates build/patch version filter submenu. Requires version filter callbacks |
CreateSelectDeselectAllVersions(version, filters) |
version (menu), filters (table) |
Adds Select All / Deselect All buttons for version filters |
CreateSelectDeselectAll(menu, text, filters, keys, value, callback) |
menu, text (string), filters (table), keys (array), value (boolean), callback (function) |
Creates button for batch select/deselect. callback defaults to OnAllSelect |
CreateSelectDeselectAllButtons(menu, filters, keys, callback) |
menu, filters (table), keys (array), callback (function) |
Creates both Select All and Deselect All buttons |
CreateMinorVersionGroup(majorGroup, filters, major, minor) |
majorGroup (menu), filters (table), major (table), minor (table) |
Creates minor version checkbox (e.g., "11.0.x"). Used internally by version filters |
CreateMajorVersionGroup(version, filters, major) |
version (menu), filters (table), major (table) |
Creates major version checkbox (e.g., "11.x.x"). Used internally by version filters |
Important Notes:
- Modern vs Classic: Some methods behave differently or are no-ops depending on
WOW_PROJECT_ID - Menu Parameter: When
menuisnil, methods automatically useGetMenu() - Varargs: Extra parameters passed to
CreateCheckboxandCreateRadioare forwarded to callbacks for custom context
Use Cases
- Right-click context menus
- Dropdown selection menus
- Settings and options menus
- Action selection menus
- Navigation menus
- Custom frame menus
- Any scenario requiring dropdown menu functionality
Requirements
- Krowi_LibMan
- Krowi_Util_2 (optional, for default MenuBuilder callbacks)


