-
Notifications
You must be signed in to change notification settings - Fork 0
Architecture
Contents
LibSettingsDesigner is a vendored settings-center renderer for World of Warcraft addons.
It has two responsibilities:
- Store settings metadata in a predictable data model.
- Render that metadata as a modern settings center.
Feature modules should not manually build every settings row. They should register categories, pages, groups, controls, notes, and dashboard metadata. The UI renderer then turns that metadata into the actual frame.
The library is intentionally vendored. Every addon ships its own copy and loads it into the host addon's namespace:
addon.LibSettingsDesigner.Config
addon.LibSettingsDesigner.UIIt is not a global LibStub library and should not be shared between addons.
In this repository, the runtime source is isolated under
runtime/LibSettingsDesigner/. A consuming addon should copy that folder to its
own libs/LibSettingsDesigner/ path. Documentation under docs/ is not part of
the in-game runtime.
Expected runtime files inside a host addon:
MyAddon/
MyAddon.toc
libs/
LibSettingsDesigner/
LibSettingsDesigner.xml
LibSettingsDesignerConfig.lua
LibSettingsDesignerUI.lua
Assets/
| Piece | Owns | Should not own |
|---|---|---|
LibSettingsDesignerConfig.lua |
Apps, categories, pages, groups, controls, defaults, value reads/writes, visibility, search metadata, customized counts, legacy bridge mapping. | Rendering details, textures, frame layout, addon-specific feature behavior. |
LibSettingsDesignerUI.lua |
Main frame, dashboard, sidebar, page cards, detail pages, widgets, notes, search box, density, frame size/lock, open/toggle helpers. | Addon-specific feature strings, feature business logic, global shared-lib registration. |
Assets/ |
Reusable library UI art such as borders, arrows, close buttons, and generic UI skin pieces. | Host-addon feature icons unless they are intended to be reusable library art. |
| Host addon wrappers | Compatibility with existing settings helpers, localization, module-specific callbacks, defaults, runtime refresh. | Core renderer behavior that belongs in the library. |
The normal flow looks like this:
Host addon loads LibSettingsDesigner.xml
-> LibSettingsDesignerConfig.lua creates addon.LibSettingsDesigner.Config
-> LibSettingsDesignerUI.lua creates addon.LibSettingsDesigner.UI
-> host addon calls Config:RegisterAddOn(addonName, opts)
-> host addon registers categories/pages/groups/controls
-> user opens ConfigUI:Open(app)
-> UI reads the Config model and renders the settings center
-> user changes a control
-> Config writes value through setting/setValue/db key
-> optional callback refreshes runtime addon feature
-> UI updates row/customized/search/dashboard state as needed
Keep this separation clean. The metadata should describe the settings. The setter should persist the setting and refresh the affected addon feature. The UI should render and interact; it should not know feature-specific rules.
Register structure in this order:
Config:RegisterAddOn(addonID, opts)app:RegisterCategory(data)app:RegisterPage(data)-
app:RegisterGroup(pageID, data)when the page needs headings/sections -
app:RegisterPageNote(pageID, data)when the page needs explanatory notes app:RegisterControl(pageID, data)-
app:RegisterControlNote(controlID, data)when a control needs extra notes
Minimal shape:
local app = Config:RegisterAddOn(addonName, {
title = "My Addon",
settingsTitle = "My Addon Settings",
addonFolder = addonName,
assetRoot = "Interface\\AddOns\\MyAddon\\libs\\LibSettingsDesigner\\Assets\\",
db = function() return MyAddonDB.profile end,
locale = L,
})
app:RegisterCategory({
id = "general",
title = GENERAL or "General",
order = 100,
})
app:RegisterPage({
id = "general.core",
category = "general",
title = "Core",
description = "Main addon behavior.",
order = 100,
})
app:RegisterControl("general.core", {
id = "enabled",
key = "enabled",
type = "toggle",
label = ENABLE or "Enable",
description = "Enable the addon.",
default = true,
})Use stable ids for everything:
- category ids:
interface,profiles,help - page ids:
interface.action-bars,profiles.import-export - group ids:
visibility,layout,advanced - control ids:
actionBarMouseover,profileName
Do not use display text as an id when the text can be localized or renamed. Renaming a label should not break saved UI state, open targets, search focus, or wrapper mappings.
A control value can come from several sources. Prefer the simplest source that matches the setting.
Use key when the value lives directly under opts.db(). var is a
legacy/wrapper alias and is not mapped to key by direct RegisterControl:
app:RegisterControl("general.core", {
id = "enabled",
key = "enabled",
type = "toggle",
label = "Enabled",
default = true,
})This reads/writes:
MyAddonDB.profile.enabledUse explicit getValue/setValue for nested tables, private DBs,
per-character values, derived values, or compatibility fallbacks:
app:RegisterControl("bars.layout", {
id = "barScale",
type = "slider",
label = "Bar scale",
min = 0.5,
max = 2,
step = 0.05,
default = 1,
getValue = function()
local bars = MyAddonDB.profile.bars
return bars and bars.scale or 1
end,
setValue = function(value)
MyAddonDB.profile.bars = MyAddonDB.profile.bars or {}
MyAddonDB.profile.bars.scale = tonumber(value) or 1
MyAddon.RefreshBars()
end,
})Documented default resolution order:
control.defaultcontrol.dbDefault-
control.setting:GetDefaultValue()orcontrol.setting:GetDefault()
Use default for most controls. Use dbDefault when the stored DB default is
computed or shared with an existing settings system.
Documented value read order:
control.setting:GetValue()-
control.getSelection(control)orcontrol.getSelection() control.getValue()opts.db()[control.key]control.default
Documented value write order:
control.setting:SetValue(value)-
control.setSelection(selection)for MultiDropdown map writes control.setValue(value)opts.db()[control.key] = value
When behavior matters, prefer explicit getters/setters instead of relying on fallbacks.
The UI API opens a rendered settings center:
ConfigUI:Open(app)
ConfigUI:Open(app, "interface.action-bars")
ConfigUI:Open(app, "interface.action-bars", "barScale")
ConfigUI:Toggle(app)Open and Toggle accept either the app object or the registered addon id:
ConfigUI:Open("MyAddon", "general.core")The rendered frame stores internal state at:
frame._LibSettingsDesignerStateTreat that state as an internal integration point. Use it only for narrow cases, such as refreshing the currently rendered page after an external editor changed metadata or runtime dropdown options:
local frame = addon.ConfigCenterFrame
local state = frame and frame._LibSettingsDesignerState
if frame and frame:IsShown() and state and state.RenderContent then
state:RenderContent()
endDo not use frame-state access as a general replacement for proper setters, control callbacks, or wrapper helper updates.
| Extension point | Use it for | Documentation |
|---|---|---|
opts.dashboard |
Dashboard hero/cards/status/features/new entries. | Dashboard |
layout = "info" and content
|
Static help/reference pages. | InfoPage |
note, notes, richNote, richNotes
|
Hover help and rich explanations. | Notes |
keywords, searchtags
|
Search aliases and discoverability. | Search and New Badges |
newTagID + opts.isNewTag
|
New-feature badges. | Search and New Badges |
| legacy registration methods | Bridge existing addon settings wrappers. | Wrapper Bridge Pattern |
getSize/setSize, getLocked/setLocked
|
Persist settings window state. | UI API |
Do:
- Keep the library generic.
- Keep addon strings in the host addon's locale table.
- Keep feature-specific runtime changes inside host addon callbacks.
- Keep wrapper helpers thin and predictable.
- Keep ids stable even when labels change.
- Keep assets for the library under the vendored library folder.
Do not:
- Register LibSettingsDesigner with
LibStub:NewLibrary. - Depend on another addon's copy of the library.
- Add host-addon feature logic to
LibSettingsDesignerUI.lua. - Hard-code host-addon feature names in reusable library docs or code.
- Rebuild the entire settings frame from dropdown or MultiDropdown click callbacks.
- Store booleans as strings.
- Use localized text as ids.
Use the current names everywhere:
LibSettingsDesignerConfig.lua
LibSettingsDesignerUI.lua
addon.LibSettingsDesigner.Config
addon.LibSettingsDesigner.UI
frame._LibSettingsDesignerStateWiki
• Home
• Architecture
• Vendoring
• Quick Start
• Field Glossary
• Troubleshooting
• Validation
Reference
⚬ Config API
⚬ UI API
⚬ Elements
⚬ Examples
Elements
Structure
• Category
• Page
• Group
• Dashboard
• InfoPage
• Custom
Controls
• Toggle
• CheckboxDropdown
• Dropdown
• MultiDropdown
• SoundDropdown
• Input
• Slider
• Button
Advanced
• ColorPicker
• ColorPalette
• ColorOverrides
• ReorderList
• Expandable
• Notes
Examples
Start
• Minimal Addon
• Complete Settings Center
• Wrapper Bridge Pattern
Data and Behavior
• Dependent Controls
• Nested Database Values
• Dynamic Dropdowns
• Runtime Refresh
• Search and New Badges
• Custom Hosted Editors
Polish
• Support Links
• Theme Colors
• Theme Borders