-
Notifications
You must be signed in to change notification settings - Fork 0
Troubleshooting
Contents
- Fast Triage
- Config or UI is nil
- Assets Are Missing
- Control Does Not Save
- Reset or Customized Count Looks Wrong
- Child Control Should Be Disabled or Hidden
- Dropdown Order Changes
- Dynamic Dropdown Does Not Refresh
- MultiDropdown Breaks While Clicking
- Search Does Not Find a Setting
- New Badge Does Not Show
- Open Target Does Not Focus the Control
- In-Game Smoke Test
When something is wrong, check these in order:
- Is
libs\\LibSettingsDesigner\\LibSettingsDesigner.xmlloaded before the file that registers settings? - Does
addon.LibSettingsDesigner.Configexist? - Does
addon.LibSettingsDesigner.UIexist? - Is the app registered once through
Config:RegisterAddOn(addonName, opts)? - Are category/page/control ids stable and unique?
- Does the control have a valid
type? - Does the direct control have either a simple
keyor explicit getter/setter? - Does the control have a
defaultwhen it should support reset/customized state? - If a wrapper exists, did the feature module use the wrapper instead of raw Config/UI calls?
Symptom:
addon.LibSettingsDesigner == nil
addon.LibSettingsDesigner.Config == nil
addon.LibSettingsDesigner.UI == nilLikely causes:
- The XML file is not included in the TOC.
- The XML file loads after the settings registration file.
- The library was copied to a different path, but the TOC still points to the old path.
- The host addon table is not the same table used by the library.
Expected TOC include:
libs\LibSettingsDesigner\LibSettingsDesigner.xmlExpected XML order:
<Ui xmlns="http://www.blizzard.com/wow/ui/">
<Script file="LibSettingsDesignerConfig.lua" />
<Script file="LibSettingsDesignerUI.lua" />
</Ui>Symptoms:
- Border/close/dropdown/collapse art is blank.
- Icons render inconsistently.
- The frame works, but the skin looks broken.
Check assetRoot:
assetRoot = "Interface\\AddOns\\MyAddon\\libs\\LibSettingsDesigner\\Assets\\"Rules:
- Keep reusable LibSettingsDesigner UI assets under the vendored library folder.
- Keep addon-specific feature icons in the host addon's media folder.
- Use
iconKeywhen you want the app icon map to resolve the texture later. - Avoid depending on another addon's asset path.
For simple DB values, make sure opts.db() returns the table you expect:
db = function() return MyAddonDB.profile endThen the control should have a direct key:
app:RegisterControl("general.core", {
id = "enabled",
key = "enabled",
type = "toggle",
label = "Enabled",
default = true,
})For nested values, use explicit getter/setter:
app:RegisterControl("bars.layout", {
id = "scale",
type = "slider",
label = "Scale",
min = 0.5,
max = 2,
step = 0.05,
default = 1,
getValue = function()
return MyAddonDB.profile.bars and MyAddonDB.profile.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,
})Do not rely on key = "bars.scale" unless the host wrapper explicitly supports
dotted paths. LibSettingsDesigner's documented simple DB behavior is direct
opts.db()[control.key] access.
Check the default source:
default = trueor:
dbDefault = function()
return MyAddon.Defaults.profile.someValue
endCommon causes:
- Missing default.
- Default has a different type than the stored value, such as
truevs"true". - A getter returns a derived value but the default describes the raw stored value.
- A table default is mutated and reused instead of copied by the host addon.
- The control should not count as customized and needs
trackCustomized = false.
For action-only buttons, customized tracking is usually not meaningful.
Use disabled state when the user should still see the option:
parentCheck = function()
return MyAddonDB.profile.enabled == true
endUse visibility when the option should disappear completely:
visibleWhen = function()
return MyAddonDB.profile.advanced == true
endRule of thumb:
| Wanted behavior | Use |
|---|---|
| Show dependency but prevent editing |
isEnabled or parentCheck
|
| Remove advanced/irrelevant row |
visibleWhen or hiddenWhen
|
| Hide a whole feature page | page-level visibleWhen or hiddenWhen
|
Lua table iteration order is not stable. Always provide an order list for user-facing dropdowns:
list = {
LEFT = "Left",
CENTER = "Center",
RIGHT = "Right",
},
orderList = { "LEFT", "CENTER", "RIGHT" },For dynamic lists, return both the map and deterministic order:
listFunc = function()
return MyAddon.GetProfileLabels()
end,
optionfunc = function()
return MyAddon.GetProfileOrder()
end,If the source data changes outside the dropdown's own interaction, refresh the current page after the data source has changed:
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 refresh while a dropdown menu is actively being clicked. That can destroy the currently open menu and produce awkward UI behavior.
MultiDropdown selections should update the selection map only. Avoid rebuilding the settings center from the option click callback.
Good:
setSelectedFunc = function(value, selected)
MyAddonDB.profile.roles = MyAddonDB.profile.roles or {}
MyAddonDB.profile.roles[value] = selected and true or nil
MyAddon.RefreshRolePreview()
endRisky:
setSelectedFunc = function(value, selected)
MyAddonDB.profile.roles[value] = selected
ConfigUI:Open(app) -- do not rebuild from here
endSearch text is built from label/title/id, description, key, page/category/group text, notes, and explicit aliases.
Add aliases when users may search for a different word:
keywords = { "alias", "display name", "nickname" }Use user-facing terms, common abbreviations, and old option names when a setting was renamed.
A badge needs both sides:
local app = Config:RegisterAddOn(addonName, {
isNewTag = function(tagID)
return MyAddon.NewTags and MyAddon.NewTags[tagID] == true
end,
})and:
newTagID = "FreshFeature"If the page has a badge but the control does not, check whether newTagID is set
on the page, the control, or both depending on the intended display.
Use the exact registered page and control ids:
ConfigUI:Open(app, "interface.action-bars", "barScale")Do not use labels. These are wrong because they can be localized or renamed:
ConfigUI:Open(app, "Action Bars", "Scale")If using a combined target like page.control, make sure the page id is the
longest matching registered page id and the remaining part is the control id.
After documentation or runtime changes, validate the expected user path:
- Open the settings center from the slash command or addon button.
- Confirm dashboard/sidebar render without missing art.
- Open every category with changed docs or changed registration metadata.
- Change one toggle, one slider, one dropdown, one MultiDropdown, and one input.
- Reload UI and verify persisted values.
- Reset a page and verify defaults.
- Search for a label, a keyword alias, and an old renamed setting name.
- Hover controls with notes and check panel spacing.
- Open a page directly with
ConfigUI:Open(app, pageID, controlID). - Check BugSack/console for Lua errors.
Wiki
• 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