Settings UI Layer
The UI layer renders settings pages from declarative definitions and handles user interaction. It consists of Registry, PageBuilder, DefinitionRenderer, Controls, and Dialogs.
Architecture
graph TB
Page[Page Definition] --> Registry[Registry]
Registry --> PageBuilder[PageBuilder]
PageBuilder --> Section[Section Widget]
Section --> DefinitionRenderer[DefinitionRenderer]
DefinitionRenderer --> Controls[Control Factory]
Controls --> Checkbox[Checkbox]
Controls --> Slider[Slider]
Controls --> Dropdown[Dropdown]
Controls --> Button[Button]
Controls --> More[... 9 more types]
Checkbox --> Store[Store.Set/Get]
Slider --> Store
Dropdown --> Store
Button --> Dialogs[Dialogs]
style Registry fill:#fff4e1
style PageBuilder fill:#fff4e1
style DefinitionRenderer fill:#fff4e1
style Controls fill:#fff4e1
Registry Module
Location: SpectrumFederation/modules/UI/Settings/Registry.lua
The Registry manages page lifecycle and integrates with the Blizzard Settings API.
Core Methods
RegisterPage
Parameters:
pageId(string) - Unique identifier for the pagetitle(string) - Display titledefinition(table) - Page structure (sections and controls)options(table, optional) - Configuration optionsparent(string) - Parent page ID for sub-pagesorder(number) - Display order
Example:
SF.Settings.Registry:RegisterPage(
"main",
"Spectrum Federation",
{
{ type = "section", title = "General", controls = { ... } }
}
)
GetPage
Parameters:
pageId(string) - The page identifier
Returns:
page(table|nil) - Page metadata or nil if not found
Page Lifecycle
- Registration -
RegisterPage()stores page definition - First Open - Panel created,
PageBuilder:Build()called,__sfBuiltflag set - Refresh - Existing panel reuses controls, only updates values
- Re-layout - Window resize triggers section reflow
Integration with Blizzard Settings
-- Register with WoW Settings UI
local category = Settings.RegisterCanvasLayoutCategory(panel, title)
Settings.RegisterAddOnCategory(category)
Pages appear in the native Escape menu under "AddOns".
PageBuilder Module
Location: SpectrumFederation/modules/UI/Settings/PageBuilder.lua
The PageBuilder creates scrollable layouts with sections.
Building a Page
Parameters:
panel(Frame) - The container framedefinition(table) - Array of section definitions
Process:
- Creates scroll frame with child content frame
- Iterates through sections
- For each section:
- Creates Section widget
- Passes control definitions to DefinitionRenderer
- Anchors section below previous
- Sets up scroll bar and mouse wheel handler
- Registers resize handler for reflow
Example Definition:
{
{
type = "section",
title = "General Settings",
intro = "Configure basic addon behavior",
controls = {
{ type = "checkbox", label = "Enable", path = "global.enabled" },
{ type = "slider", label = "Scale", path = "global.scale", min = 0.5, max = 2.0 }
}
},
{
type = "section",
title = "Advanced",
condition = function() return IsAdvancedMode() end,
controls = { ... }
}
}
Section Widget
Location: SpectrumFederation/modules/UI/Settings/Widgets/Section.lua
Sections group related controls with optional titles, intro text, and reset buttons.
Features:
- Optional title with horizontal line
- Intro text with word wrapping
- Reset button with confirmation dialog
- Error/warning/success message display
- Conditional visibility
- Collapsible (future feature)
Creating a Section:
local section = SF.Settings.Widgets.Section.Create(parent, {
title = "Profile Settings",
intro = "Manage your loot profiles",
resetPath = "lootHelper.profiles",
condition = function()
return SF.Settings.Store:Get("lootHelper.enabled")
end
})
Layout and Reflow
The PageBuilder handles dynamic layout:
-- Initial layout
PageBuilder:Build(panel, definition)
-- Reflow on resize
panel:SetScript("OnSizeChanged", function(self)
PageBuilder:Reflow(self)
end)
DefinitionRenderer Module
Location: SpectrumFederation/modules/UI/Settings/DefinitionRenderer.lua
The DefinitionRenderer converts declarative control definitions into WoW UI controls.
Rendering Controls
Parameters:
parent(Frame) - Container framecontrolDefinitions(table) - Array of control specs
Returns:
controls(table) - Array of created control frames
Process:
- Iterates through control definitions
- For each definition:
- Validates type
- Calls appropriate factory function from Controls module
- Sets up state binding (get/set)
- Applies conditional visibility
- Anchors control below previous
- Returns array of controls for section management
Control Definition Format
{
type = "checkbox", -- Control type (required)
label = "Enable Feature", -- Display label
path = "global.enabled", -- Store path for auto-binding
tooltip = "Enables the feature",
adminOnly = true, -- Optional UI-only admin gating
help = "Additional help text",
-- OR custom binding
get = function() return GetValue() end,
set = function(value) SetValue(value) end,
-- Optional visibility
visible = function() return IsEnabled() end,
enabled = function() return CanChange() end
}
Section condition controls section visibility. Use item-level adminOnly = true when a setting should remain visible to all users but become greyed out and non-interactable for non-admins.
State Binding
The renderer automatically binds controls to the Store:
Path-based (automatic):
Custom (manual):
{
type = "dropdown",
label = "Active Profile",
get = function()
return SF.Settings.Store:GetActiveLootHelperProfileData()
end,
set = function(profileId)
SF.Settings.Store:SetActiveLootHelperProfileId(profileId)
end,
items = function()
return GetProfileList()
end
}
Conditional Visibility
Controls can be shown/hidden dynamically:
{
type = "slider",
label = "Advanced Setting",
path = "global.advancedValue",
visible = function()
-- Only show if advanced mode is enabled
return SF.Settings.Store:Get("global.advancedMode") == true
end
}
The visibility function is re-evaluated:
- On section refresh
- When dependent settings change
- When
RefreshPage()is called
Controls Module
Location: SpectrumFederation/modules/UI/Settings/Control/Controls.lua
The Controls module provides factory functions for 13+ control types.
Available Control Types
| Type | Description | Key Options |
|---|---|---|
checkbox |
Boolean toggle | label, tooltip |
slider |
Numeric range | min, max, step, format |
dropdown |
Selection list | items, width |
editbox |
Text input | placeholder, maxLetters |
button |
Click action | label, onClick |
display |
Read-only value | label, format |
help |
Formatted help text | text, color |
text |
Simple label | text, fontSize |
spacer |
Vertical spacing | height |
dropdownIconButton |
Dropdown with icon button | items, iconButton |
editboxButton |
Editbox with button | buttonLabel, onButtonClick |
scrollList |
Scrollable item list | items, height, itemTemplate |
See the Controls documentation for detailed information on each type.
Factory Pattern
Each control type has a factory function:
Controls.CreateCheckbox(parent, opts)
Controls.CreateSlider(parent, opts)
Controls.CreateDropdown(parent, opts)
-- ... etc
Common Options:
get(function) - Value getterset(function) - Value setterpath(string) - Auto-bind to Store pathenabled(function) - Enable statevisible(function) - Visibility statetooltip(string) - Hover tooltiphelp(string) - Help text below control
Example:
local checkbox = SF.Settings.Controls.CreateCheckbox(parent, {
label = "Enable Feature",
path = "global.featureEnabled",
tooltip = "Enables the cool new feature",
help = "This feature requires restart"
})
Auto-save Binding
Controls automatically save changes to the Store:
checkbox:SetScript("OnClick", function(self)
local newValue = self:GetChecked()
opts.set(newValue) -- Calls Store:Set() or custom setter
end)
No manual "Save" button needed - changes are immediate.
Dialogs Module
Location: SpectrumFederation/modules/UI/Settings/Dialogs.lua
The Dialogs module provides modal dialogs for user confirmation and input.
Confirm Dialog
Parameters:
message(string) - Confirmation messageacceptText(string, optional) - Accept button label (default: "Confirm")onAccept(function) - Callback when confirmed
Example:
SF.Settings.Dialogs.Confirm(
"Delete this profile? This action cannot be undone.",
"Delete",
function()
DeleteProfile(profileId)
end
)
Prompt Dialog
Parameters:
message(string) - Prompt messagetext(string) - Input labeldefaultText(string, optional) - Pre-filled textonAccept(function) - Callback with entered text:function(enteredText)
Example:
SF.Settings.Dialogs.Prompt(
"Create New Profile",
"Enter profile name:",
"",
function(name)
CreateProfile(name)
end
)
Dialog Implementation
Dialogs use WoW's StaticPopupDialogs system:
StaticPopupDialogs["SF_SETTINGS_CONFIRM"] = {
text = "%s",
button1 = "Confirm",
button2 = "Cancel",
OnAccept = function(self, data)
data.onAccept()
end,
timeout = 0,
whileDead = true,
hideOnEscape = true
}
Page Refresh
Pages can be refreshed to update control values and visibility:
Triggers:
- Profile switch
- Setting change that affects visibility
- Manual refresh request
Process:
- Calls
visible()for each control - Calls
get()to update values - Re-evaluates section conditions
- Triggers reflow if layout changed
UI Style Constants
Location: SpectrumFederation/modules/UI/Settings/Style.lua
Defines consistent styling:
SF.Settings.Style = {
COLORS = {
TEXT = {1, 1, 1},
SUBTEXT = {0.7, 0.7, 0.7},
ACCENT = {0.3, 0.6, 1.0}
},
FONTS = {
NORMAL = "Fonts\\FRIZQT__.TTF",
BOLD = "Fonts\\ARIALN.TTF"
},
SPACING = {
SECTION = 20,
CONTROL = 8,
INDENT = 20
}
}
Best Practices
Page Organization
DO:
- Group related settings in sections
- Use clear, descriptive titles
- Provide intro text for complex sections
- Order settings logically (simple → advanced)
DON'T:
- Create flat lists of 50+ controls
- Mix unrelated settings in one section
- Use technical jargon in labels
Control Selection
DO:
- Use checkboxes for boolean toggles
- Use sliders for numeric ranges
- Use dropdowns for 3+ options
- Use editboxes for free-form text
DON'T:
- Use editbox for boolean (use checkbox)
- Use dropdown for yes/no (use checkbox)
- Use slider for 2-3 discrete values (use dropdown)
Help Text
DO:
- Explain what the setting does
- Mention if restart required
- Link to related documentation
- Use
helptype for formatted guidance
DON'T:
- Repeat the label in help text
- Use technical implementation details
- Write essay-length help text
Performance
DO:
- Use
conditionfunctions for expensive checks - Debounce rapidly-changing controls (sliders)
- Lazy-load complex dropdown items
- Cache visibility results where possible
DON'T:
- Call expensive functions in
get()on every refresh - Fetch data from server in control functions
- Create controls for hidden sections
Debugging
Enable debug logging to trace UI operations:
Log Categories:
SETTINGS:REGISTRY- Page registration and lifecycleSETTINGS:PAGEBUILDER- Page building and reflowSETTINGS:RENDERER- Control creationSETTINGS:CONTROLS- Control interactions
Example Output:
[SETTINGS:REGISTRY] Registering page 'main'
[SETTINGS:PAGEBUILDER] Building page with 3 sections
[SETTINGS:RENDERER] Rendering 12 controls
[SETTINGS:CONTROLS] Checkbox 'global.enabled' changed: true
Next Steps
- Learn about Controls in detail
- Follow the Creating Pages guide
- Review Best Practices