# LibHLYUI-1.0

A small, dependency-light, reusable **options-UI framework** for WoW addons.
Standalone movable window with a left **sidebar category nav**, flat
accent-themed widgets, auto-layout, and a module **registry**. Built for HLYqol
v2 but addon-agnostic.

File: `Libs/LibHLYUI-1.0/LibHLYUI-1.0.lua` (single file). Requires `LibStub`.
Optional: `LibSharedMedia-3.0` (font/sound dropdown helpers + previews).

Load in `.toc` **after LibStub**, before consumers:
```
Libs\LibStub\LibStub.lua
Libs\LibHLYUI-1.0\LibHLYUI-1.0.lua
```

---

## 1. Quick start

```lua
local LibHLYUI = LibStub("LibHLYUI-1.0")

local panel = LibHLYUI:CreatePanel({
    name   = "MyAddonOptions",   -- unique global frame name
    title  = "My Addon",         -- centered header text (name only, no version)
    accent = { 0.16, 0.90, 0.30 }, -- brand color; drives the whole theme
    width  = 760, height = 560,
})

local tab = panel:AddTab("General")   -- sidebar category, returns a layout
tab:Section("Display")
tab:Checkbox({ label = "Enable", get = ..., set = ... })
tab:Slider({ label = "Size", min = 8, max = 24, step = 1, get = ..., set = ... })

panel:Open()   -- or :Toggle() / :Close()
```

Widgets **auto-stack** vertically — no manual `SetPoint`. `panel:Refresh()`
re-reads every widget's getter (call after a setter changes related state).

---

## 2. Theme (single accent drives everything)

`lib.Theme` holds neutral colors + layout metrics. All **accent-tinted** entries
(header, nav, window edge, title/footer bar, slider/checkbox fill, hovers,
footer links, close X, section underline, info icon, speaker) are **derived**
from one accent via:

```lua
LibHLYUI:SetAccent(r, g, b)          -- recomputes all accent-tinted theme entries
-- or pass accent in CreatePanel opts (calls SetAccent for you)
```

Tables are mutated **in place**, so set the accent **before building** the panel
(or pass `opts.accent`). Default accent = HLYqol green `{0.16, 0.90, 0.30}`.

HLYqol sources its brand color from `addon.uiAccent` in `Initialize.lua` and
passes it as `opts.accent`. Change that one value → whole UI recolors.

Key neutral metrics: `rowHeight=24`, `pad=16`, `gap=10`, `sidebarW=150`.

---

## 3. Panel API (`PanelMixin`)

| Method | Purpose |
|---|---|
| `panel:AddTab(label[, hidden])` | Add a sidebar category; returns its **layout**. `hidden=true` → no sidebar entry (reachable only programmatically, e.g. via a footer link). Alias of `AddCategory`. |
| `panel:AddSidebarHeader(label)` | Non-clickable **group label** in the sidebar; categories added after it appear beneath it. The sidebar scrolls (flat scrollbar) when long. |
| `panel:SelectTab(index)` | Switch active category (1-based). |
| `panel:Refresh()` | Re-read every widget getter in every category. |
| `panel:Open()` / `:Close()` / `:Toggle()` / `:IsShown()` | Window visibility. |
| `panel:SetFooterRight(text)` | Right-aligned footer text (e.g. version). |
| `panel:AddFooterLink(label, onClick)` | Clickable footer link (left). `onClick(panel)`. Multiple links stack left-to-right. |

Window: movable (drag title bar), `ESC`/white **X** closes, registered in
`UISpecialFrames`. Layout = title bar (centered title + close) · left sidebar ·
content body · footer.

---

## 4. Layout API (`LayoutMixin`)

A layout is what `AddTab` / a `TabGroup` pane / a `Row` cell gives you. Methods
place a widget and auto-advance the vertical cursor.

### Structure
| Method | Renders |
|---|---|
| `:Section(text)` | Left title with an **accent underline** under the text. |
| `:Note(text)` | Description: **info icon** + dim wrapped text, extra spacing before the options that follow. |

### Inputs (all single-line: `label \| control`, fixed label column)
Common opts: `label`, `get`, `set`, `tooltip`, `onChange`, `labelWidth` (default 150).

| Method | Extra opts |
|---|---|
| `:Checkbox(opts)` | flat box: hollow (white border) off, solid accent on. `get/set` boolean. |
| `:Slider(opts)` | `min`, `max`, `step`, `format(v)`. Flat track/fill/thumb + **editable** value box. |
| `:Dropdown(opts)` | `values`, `order`, `fontPreview`, `soundPreview`. Scrollable popup. |
| `:Color(opts)` | `get` → `r,g,b`; `set(r,g,b)`. Opens Blizzard color picker. |
| `:EditBox(opts)` | flat text input; commit on Enter. |
| `:Button(opts)` | `label`, `func`, `width`. Accent-gradient flat button. |
| `:Bar(opts)` | Read-only flat bar: `label`, `value`, `max`, `valueText`, `color = {r,g,b}` or `{[1],[2],[3]}`. Proportional fill (value/max). For stats/visualizations. |

### `Dropdown` values
`opts.values` may be:
- array of `{ value=, text= }`
- array of plain strings/numbers (value = the entry)
- hash `{ [value] = displayText }` (+ optional `opts.order` = ordered value list)
- a **function** returning any of the above (re-evaluated on open)

`fontPreview = true` → each entry + the selected value render in their own LSM
typeface. `soundPreview = true` → a **speaker** button left of each entry and on
the selected value; click plays the LSM sound (without selecting).

### Multi-column rows
```lua
layout:Row({ builderFn1, builderFn2, builderFn3 })       -- N equal columns
layout:Row({ builderFn1, builderFn2 }, 3)                -- 2 entries on a 3-col grid (3rd empty)
```
Each builder gets a cell layout exposing the normal widget methods:
```lua
layout:Row({
    function(c) c:Checkbox({ label = "LFR",    get=..., set=... }) end,
    function(c) c:Checkbox({ label = "Normal", get=..., set=... }) end,
})
```
The optional `columns` arg fixes the grid so partial rows stay aligned.

### Inner sub-tabs (reiter) inside a category
```lua
layout:TabGroup({
    { name = "Death",   build = function(p) p:Checkbox{...} end },
    { name = "Trinket", build = function(p) ...               end },
})
```
Horizontal sub-tab bar + switchable panes. Sizes to the **active** pane (the
scrollbar only appears when that pane overflows). Alternative to listing
sub-modules vertically.

### LSM helper
```lua
LibHLYUI:GetMediaValues("font")   -- or "sound" -> array of { value=name, text=name }
```

---

## 5. Module registry (how HLYqol wires modules)

`UI/Options.lua` builds the panel from a registry. Each module **appends** to
`addon.optionsV2` and defines a build method `HLY:Build<X>Options(tab)`.

### Standalone category
```lua
function HLY:BuildFrenchConnectionOptions(tab)
    tab:Section("French Connection")
    tab:Note("…")
    tab:Row({
        function(c) c:Checkbox({ label="Enable", get=..., set=... }) end,
        function(c) c:Checkbox({ label="Sound",  get=..., set=... }) end,
    })
end

addon.optionsV2 = addon.optionsV2 or {}
addon.optionsV2[#addon.optionsV2 + 1] = {
    name = "French Connection", order = 50, build = "BuildFrenchConnectionOptions",
}
```
→ one sidebar entry, content from the build method.

### Grouped category (sidebar header + entries)
Add `group` + `groupOrder`; members of the same `group` render as a **sidebar
header** followed by one clickable entry per member. Each entry's content is
wrapped in a single-tab TabGroup so the tab bar shows consistently:
```lua
addon.optionsV2[#addon.optionsV2 + 1] = {
    group = "Alerts", groupOrder = 300,   -- sidebar header
    name = "Death Alert", order = 1,      -- entry beneath it
    build = "BuildDeathAlertOptions",
}
```
If a module builds its **own** `TabGroup` (multiple sub-features), set
`ownTabs = true` so it isn't double-wrapped:
```lua
{ group = "Group Finder", groupOrder = 250, name = "Déjà Vu", order = 2,
  ownTabs = true, build = "BuildNotAgainOptions" }
```

### Nested groups (parent header → sub-header → entries)
Add `parent` + `parentOrder` to nest groups under a top-level header. The group
becomes one clickable entry whose members are content sub-tabs (single member →
one tab, multiple → multiple tabs):
```lua
-- UI > Gear  (Upgrade Track + BoE/WuE as content tabs)
{ parent = "UI", parentOrder = 100, group = "Gear", groupOrder = 1,
  name = "Upgrade Track", order = 1, build = "BuildUpgradeTrackOptions" }
-- UI > Inspect  (single module -> one tab)
{ parent = "UI", parentOrder = 100, group = "Inspect", groupOrder = 2,
  name = "Compare Stats", order = 1, build = "BuildCompareStatsOptions" }
```

Build methods receive a layout/pane; the entry/tab name is the title (use `Note`
+ widgets, or `Section`s for sub-areas).

### Consolidate a top-level group into one entry
Set `groupAsEntry = true` on a top-level group's entries to render the whole
group as **one** clickable sidebar entry (styled like a section title) whose
members are content sub-tabs:
```lua
{ group = "Other", groupOrder = 800, name = "Tank Marker", order = 1,
  groupAsEntry = true, build = "BuildMarkerOptions",
  onShow = "ShowMarkerReminderPreview", onHide = "HideMarkerReminderPreview" }
```

### Live previews
A module may register `onShow`/`onHide` (method names) to drive an on-screen
preview while its page/sub-tab is active. Works for header+entries (per sidebar
entry) and for consolidated groups (per content sub-tab). Options also calls a
defensive hide on open/close so a preview never lingers.

`Options` always prepends a built-in **General** category and appends a
**hidden Changelog** category reached via a footer link.

### Conventions used in HLYqol
- Channels: pass `{ "Master","SFX","Music","Ambience","Dialog" }` (string array) as `values`.
- Sound dropdowns: `values = function() return self:GetSoundList() end, soundPreview = true`.
- Font dropdowns: `values = function() return self:GetFontList() end, fontPreview = true`.
- Group `order`/`groupOrder` rough scheme: General=1, Gear≈100, Group≈200, Alerts=300, Loot≈400, Display≈500.

---

## 6. Porting checklist (AceConfig → LibHLYUI)

For each module's AceConfig options in `Options/Options.lua`:
1. Add `HLY:Build<Module>Options(tab/pane)` in the module file.
2. `header` → `tab:Section(name)`; `description` → `tab:Note(text)` (strip `|cffffd100Info:|r`).
3. `toggle` → `Checkbox`; `range` → `Slider`; `select` → `Dropdown`; `color` → `Color`; `execute` → `Button`; `input` → `EditBox`.
4. Reuse the exact `get`/`set` bodies (call the same `Refresh*`/`Update*`/`Ensure*Defaults` methods).
5. Group small toggles/buttons with `Row({...})`.
6. Register in `addon.optionsV2` (standalone or grouped).
7. `luac -p` the changed files, deploy, test via `/hlyqol2`.

The old AceConfig UI (`/hlyqol`) stays in parallel until v2 reaches parity.

---

## 7. Status

- **Done:** framework, theme, all widgets, Row, TabGroup, footer, registry.
- **Ported:** General (minimap), French Connection, Alerts (DeathAlert, TrinketAlert, LustAlert, PvP GearCheck).
- **TODO:** Gear (UpgradeTrack, BoEWuE, CompareStats), Group (AutoAccept, JoinReminder, Marker), Loot (Loot, BonusRoll), Display (HLYstats, NotAgain), Statistics. Then switch `/hlyqol`, drop AceConfig, bump to 2.0.0.

Branch: `feature/options-v2-libhlyui`. Test command: `/hlyqol2`.
