- the beam is on screen: Chain Lightning arcs outward hop by hop and Drain Life ropes from hand to victim — a write-order edge was erasing the finished geometry every frame, and a tripwire now guards the whole class - the Controls page lands (sticky targeting, auto loot, invert mouse), UI scale waits for the era Apply button, and the terrain-distance slider retires - with auto-loot in effect a lootable corpse shows the triple-pouch cursor, the real client's tell - the character-screen freeze before a loading screen is gone: the shader warm-up hides behind a fully presented cover and tears down only its own roots Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1278 lines
82 KiB
XML
1278 lines
82 KiB
XML
<Ui>
|
||
<!--
|
||
The OPTIONS window (decision 0950) — the shell (slice A) and the real PAGES: Audio
|
||
(0957), Graphics (0959), Controls (0961). Opened from the game menu's Options button.
|
||
Setting rows ride the CVar store (0954): rows read GetCVar on show, write SetCVar on
|
||
interaction, and Defaults resets the visible page's CVars to their registered defaults.
|
||
Pages without rows are still empty and keep Defaults disabled. One row defers — uiScale
|
||
stages a pending value and commits through the era Apply button (0961), because a live
|
||
write would rescale the very slider being dragged.
|
||
|
||
This window is the one deliberate era-1.15.9 skin in a 1.12 shell (0950): the 1.12
|
||
OptionsFrame family (four separate panels) collapses into the era SettingsPanel look —
|
||
one window, a category list, one page area. Ground truth is the era dump
|
||
(wow-ui-source-era @ 1.15.9.68940), transcribed via the session's reference spec:
|
||
· **Root** — Blizzard_SettingsPanel.xml `SettingsPanel`: 920x724, CENTER, strata HIGH,
|
||
hidden. Skin per `SettingsFrameTemplate` (Classic/Blizzard_SettingsPanelTemplates.xml):
|
||
FlatPanelBackgroundTemplate at TOPLEFT (7,-18) → BOTTOMRIGHT (-3,3), the
|
||
`ButtonFrameTemplateNoPortrait` nine-slice (Classic/NineSliceLayouts.lua l.132 — right
|
||
edge x=-8/x1=8), the `_UI-Frame-TitleTileBg` title band (256x17, TexCoords
|
||
.2890625/.421875, TOPLEFT (8,-3) → TOPRIGHT (0,-3)), title GameFontNormal TOP (0,-5).
|
||
· **Category list** — Blizzard_CategoryList.xml + .lua: rows/headers STRETCH to the
|
||
scroll target's width (199, no scroll bar); header 30 tall (label 14px white at
|
||
LEFT (20,-1), Options_CategoryHeader_<n> wash at TOPLEFT at atlas size, n cycling
|
||
per header); row 20 tall (label GameFontNormal TOPLEFT (36,+1); bg CENTER —
|
||
options_list_active selected / options_list_hover hover, at atlas size); 2 between
|
||
elements, 18-tall spacer between groups (so 22 row-to-header).
|
||
· **Page area** — Blizzard_SettingsList.xml: title 20px white TOPLEFT (7,-22), the
|
||
options_horizontaldivider at TOP (0,-50), Defaults 96x22 at TOPRIGHT (-36,-16).
|
||
· **Tabs** — SharedXML Shared/Tabs/MinimalTab.{xml,lua}: a bottom-anchored three-slice
|
||
of Options_Tab[_Active]_{Left,Middle,Right}, GameTab TOPLEFT (32,-27) h37, AddOnsTab
|
||
5 right of it; width text+40; selected = white small text at BOTTOM+6, unselected =
|
||
gold small at BOTTOM+4.
|
||
· **Search box** — SharedXML SearchBoxTemplate → InputBoxVisualTemplate
|
||
(SecureUIPanelTemplates.xml l.42-67): the common-search-border three-slice (8x20 caps,
|
||
left hung 5 outside, middle stretched, all centered on the 22 box), 10x10 magnifier at
|
||
LEFT (1,-1), 17x17 clear button at RIGHT (-3), TextInsets 16/20, instructions label
|
||
GameFontDisableSmall tinted 0.35 at TOPLEFT (16,0) → BOTTOMRIGHT (-20,0).
|
||
Atlas members resolve by name through WoW-era/_extracted_ui/manifest.json (the 0950
|
||
seam); era-only FILE textures ride the same extraction as `era:textures/<fdid>.blp`.
|
||
|
||
SCOPE / divergences (noted, not bugs):
|
||
- **horizTile/vertTile are unsupported**, so every repeating tile is STRETCHED for now:
|
||
the `_UI-Frame-TitleTile` top band (a 256x28 repeat), the title-bg band under it, the
|
||
`!UI-Frame-LeftTile`/`!UI-Frame-RightTile` columns and the `_UI-Frame-Bot` strip. At
|
||
this window's spans the stretch reads as a soft smear of the tile — the gap to close
|
||
when tiling lands.
|
||
- **FlatPanelBackgroundTemplate is ONE solid fill here.** The era template is four
|
||
textures (two rounded-corner atlas pieces + two fills) all tinted the engine-side
|
||
global PANEL_BACKGROUND_COLOR = (0.122, 0.118, 0.129, 0.8) — the constant is defined in
|
||
the client binary, not the Lua dump; value read off the GlobalColor db2 mirror
|
||
(warcraft.wiki.gg/wiki/ColorMixin). One flat quad loses only the 16px rounded bottom
|
||
corners. The "lighter inner panel" on top of it is not a second fill — it is the real
|
||
`Options_InnerFrame` atlas member, placed below.
|
||
- **Close X wears the 1.12 UI-Panel-MinimizeButton art at the era seat** — which IS the
|
||
era's own close button: 1.15's UIPanelCloseButtonNoScripts names the same four
|
||
Interface\Buttons\UI-Panel-MinimizeButton-* files (Classic SharedUIPanelTemplates.xml
|
||
l.172-180), TOPRIGHT (+4,+4), 32x32. No divergence here at all.
|
||
- **Tabs are STATIC MinimalTab plates** (slice B): Game wears the selected state
|
||
permanently, AddOns the unselected gold — the era swaps these in code
|
||
(MinimalTabMixin), and with nothing behind AddOns the selection can never change, so
|
||
the two states are authored rather than driven. The mixin port comes with the addon
|
||
list. Width is era's text+40 law via an OnUpdate measure poll; the era's
|
||
measure-at-the-medium-font quirk is deliberately dropped (~6 units of padding).
|
||
- **Search filters nothing yet** — the box takes text, shows/hides its "Search"
|
||
placeholder and clear-X; wiring it to the category/row filter comes with the pages
|
||
slice.
|
||
- **No scroll bars**: the static category tree (9 rows, 2 headers) fits its 199px column
|
||
outright, and the deepest page (Audio, 7 rows) fits its area. MinimalScrollBar arrives
|
||
when a page outgrows the space.
|
||
- **Setting-page cuts (0957 Audio, 0959 Graphics)**: setting-row tooltips (the era
|
||
OPTION_TOOLTIP_* hovers) are not wired; Defaults resets the VISIBLE page only (the
|
||
era confirm dialog is not modeled); track clicks do nothing (thumb drag + steppers
|
||
only — the engine slider's scrollbar-faithful posture); era rows with no 1.12 CVar +
|
||
benilla knob behind them (output device, Dialog volume, loop music, channels, cache;
|
||
the whole era Graphics dropdown zoo) don't ship. A slider shows the 1.12 panel's
|
||
range even where the benilla knob's clamp is wider (uiScale's env clamp is 0.5..1.5):
|
||
a config value outside the page range displays clamped — thumb and readout agree on
|
||
the nearest expressible value, the engine keeps running the config's truth, and
|
||
nothing writes back until the player actually moves the row.
|
||
- **Apply-flow cuts (0961)**: the era's close-with-pending 3-button confirm dialog
|
||
(GAME_SETTINGS_CONFIRM_DISCARD) is not modeled — hiding the window silently discards
|
||
pending edits (the era's own hide path wipes without committing too); the 8-second
|
||
"keep these settings?" revert timer (CommitFlag.Revertable) is not modeled either —
|
||
Apply commits outright.
|
||
- Era rows this slice does not ship: the Accessibility group, and Languages/Network under
|
||
System — pages with no benilla knobs behind them yet (0950's "only rows backed by real
|
||
knobs" rule). Nameplates joined in slice B (real nameplate knobs exist to back its
|
||
page).
|
||
- **The window rides a scale knob** (slice B): fonts ride frame scale engine-wide now
|
||
(the 0219 §2 divergence closed), so ERA_WINDOW_SCALE × the era checkFit shrink
|
||
(UIPanelUpdateScaleForFit, transcribed in this file) sizes the whole window; 0.78 is
|
||
the director's-eye starting point, live-tunable. The knob is SHARED: it stands in for
|
||
the era client's UIParent scale (smaller than our 1.12 768-canvas on modern screens),
|
||
so every era-shaped window wears it — GameMenuFrame.xml reads it in its OnShow.
|
||
-->
|
||
|
||
<!-- The two era fonts this window needs beyond Fonts.xml's 1.12 set (the era
|
||
GameFontHighlightMedium / GameFontHighlightHuge — Blizzard_Fonts_Shared: FRIZQT 14 and
|
||
20, white). Named Options* rather than the era names on purpose: Fonts.xml is the
|
||
COMPLETE 1.12 set and stays that way; these are this window's own, defined with
|
||
MasterFont's exact shadow inlined (its only content — Fonts.xml l.62). -->
|
||
<Font name="OptionsFontHighlightMedium" font="Fonts\FRIZQT__.TTF" virtual="true">
|
||
<Shadow>
|
||
<Offset><AbsDimension x="1" y="-1"/></Offset>
|
||
<Color r="0" g="0" b="0"/>
|
||
</Shadow>
|
||
<FontHeight><AbsValue val="14"/></FontHeight>
|
||
<Color r="1.0" g="1.0" b="1.0"/>
|
||
</Font>
|
||
<Font name="OptionsFontHighlightHuge" font="Fonts\FRIZQT__.TTF" virtual="true">
|
||
<Shadow>
|
||
<Offset><AbsDimension x="1" y="-1"/></Offset>
|
||
<Color r="0" g="0" b="0"/>
|
||
</Shadow>
|
||
<FontHeight><AbsValue val="20"/></FontHeight>
|
||
<Color r="1.0" g="1.0" b="1.0"/>
|
||
</Font>
|
||
|
||
<Script><![CDATA[
|
||
-- The static category tree (era Game tab, Classic tocs), in list order. Keys are the row
|
||
-- name suffixes; the visible label doubles as the page title (the era does the same —
|
||
-- Settings.RegisterCategory names both from one string).
|
||
OPTIONS_CATEGORY_KEYS = {
|
||
"Controls", "Interface", "ActionBars", "Combat", "Social", "Keybindings", "Nameplates",
|
||
"Graphics", "Audio",
|
||
}
|
||
|
||
-- The era-window scale (shared, director-owned — the 0219 WORLDMAP_WINDOWED_SCALE
|
||
-- pattern): the era client's UIParent draws smaller against the screen than our 1.12
|
||
-- 768-canvas does on modern displays, and this knob is that mapping — every era-shaped
|
||
-- window wears it (this window here; GameMenuFrame in its OnShow — in the era both draw
|
||
-- at the SAME UIParent px-per-unit, the parity the director's eye checked). At 1.0 the
|
||
-- 724-tall window fills ~85% of the screen's height in our 768-virtual space, where the
|
||
-- reference at typical resolutions reads nearer two-thirds.
|
||
ERA_WINDOW_SCALE = 0.78
|
||
|
||
function OptionsFrame_UpdateScale()
|
||
-- The era panel is registered `checkFit = 1`: the panel manager also shrinks it to
|
||
-- fit a small screen (UIPanelUpdateScaleForFit: min over width/height ratios
|
||
-- against the panel + 20 units of air each way, capped at 1) — transcribed here.
|
||
local k = ERA_WINDOW_SCALE
|
||
local wFit = GetScreenWidth() / 940
|
||
local hFit = GetScreenHeight() / 744
|
||
if wFit < k then k = wFit end
|
||
if hFit < k then k = hFit end
|
||
OptionsFrame:SetScale(k)
|
||
end
|
||
|
||
-- The era tab width law (MinimalTab.lua l.7): label width + 40. Era measures at OnLoad;
|
||
-- our measure round-trip answers a frame late, so OnUpdate polls until the width is real
|
||
-- (the chat-header converge idiom), then latches. Deliberate divergence: the era
|
||
-- measures with the MEDIUM font it is about to swap away (an accident of its code
|
||
-- order, ~6 units of extra padding); we measure the small font the label actually
|
||
-- wears.
|
||
function OptionsTab_FitWidth(tab)
|
||
if tab.fitted then return end
|
||
local w = getglobal(tab:GetName() .. "Text"):GetStringWidth()
|
||
if w and w > 0 then
|
||
tab:SetWidth(w + 40)
|
||
tab.fitted = true
|
||
end
|
||
end
|
||
|
||
-- Each row's OnLoad: remember the key + title, paint the label. The whole row identity
|
||
-- lives here so adding a category is one XML row + one key in the table above.
|
||
function OptionsCategoryRow_OnLoad(row, key, label)
|
||
row.categoryKey = key
|
||
row.pageTitle = label
|
||
row:SetText(label)
|
||
end
|
||
|
||
-- The selection: exactly one row wears options_list_active + a white label; everyone
|
||
-- else drops back to bare + gold (the era CategoryList mixin's SetCategoryActive, cut to
|
||
-- our static list). Also writes the page title and swaps the page BODY: a category with
|
||
-- rows (OPTIONS_PAGE_ROWS) shows its body refreshed from the CVars and Defaults goes
|
||
-- live with it; every other page is still empty, Defaults disabled.
|
||
function OptionsFrame_SelectCategory(key)
|
||
OptionsFrame.selectedCategory = key
|
||
for _, k in ipairs(OPTIONS_CATEGORY_KEYS) do
|
||
local row = getglobal("OptionsFrameCategoryListRow" .. k)
|
||
local bg = getglobal("OptionsFrameCategoryListRow" .. k .. "Bg")
|
||
if k == key then
|
||
bg:SetAtlas("Options_List_Active", true)
|
||
row:SetTextFontObject("GameFontHighlight")
|
||
OptionsFrameContainerTitle:SetText(row.pageTitle)
|
||
else
|
||
bg:SetTexture(nil)
|
||
row:SetTextFontObject("GameFontNormal")
|
||
end
|
||
end
|
||
for k in pairs(OPTIONS_PAGE_ROWS) do
|
||
local body = getglobal("OptionsFrameContainerBody" .. k)
|
||
if k == key then body:Show() else body:Hide() end
|
||
end
|
||
if OPTIONS_PAGE_ROWS[key] then
|
||
OptionsPage_Refresh(key)
|
||
OptionsFrameContainerDefaults:Enable()
|
||
else
|
||
OptionsFrameContainerDefaults:Disable()
|
||
end
|
||
end
|
||
|
||
-- ── The setting pages: 1.12 rows in the era Settings grammar ────────────────────────
|
||
-- One machinery, every page (Audio 0957, the first). Semantics (rows, CVars, labels,
|
||
-- dependencies, the click-sound quirk) are the 1.12 client's own panels, quoted
|
||
-- verbatim (SoundOptionsFrame.lua / OptionsFrame.lua + GlobalStrings.lua); geometry is
|
||
-- the era settings list (Blizzard_SettingControls.xml/.lua + Blizzard_SettingsList.lua
|
||
-- @1.15.9 — 26-tall rows on a 9 spacing, label LEFT+37 → CENTER-85, control at
|
||
-- CENTER-80, child rows +15 indent in the small font). Era rows with no 1.12 CVar +
|
||
-- benilla knob behind them (Enable Sound=Sound_EnableAllSound, Dialog, output device,
|
||
-- channels…) do not ship — the 0950 honest-tree rule applied to settings.
|
||
OPTIONS_PAGE_ROWS = {
|
||
Controls = { "RowStickyTarget", "RowAutoLoot", "RowInvertMouse" },
|
||
Audio = {
|
||
"RowEnableAll", "RowMaster", "RowSound", "RowMusic", "RowAmbience",
|
||
"RowEnableMusic", "RowEnableAmbience",
|
||
},
|
||
Graphics = { "RowUiScale" },
|
||
}
|
||
|
||
function OptionsRow_OnLoad(row, cvar, label, child)
|
||
row.cvar = cvar
|
||
local l = getglobal(row:GetName() .. "Label")
|
||
l:SetText(label)
|
||
if child then
|
||
-- era child rows: +15 label indent and the small font
|
||
-- (SettingControls.lua l.319-322); the control itself does not indent.
|
||
l:SetFontObject("GameFontNormalSmall")
|
||
l:ClearAllPoints()
|
||
l:SetPoint("LEFT", row:GetName(), "LEFT", 52, 0)
|
||
l:SetPoint("RIGHT", row:GetName(), "CENTER", -85, 0)
|
||
end
|
||
end
|
||
|
||
-- A slider row's own range and readout, when the template's 0..1 / 5% / percent (the
|
||
-- volume shape) isn't it — called after OptionsRow_OnLoad in the row's OnLoad. The
|
||
-- refreshing guard: SetMinMaxValues re-clamps the held value and fires OnValueChanged,
|
||
-- and that clamp is wiring, not a player gesture — without the guard every load would
|
||
-- SetCVar the template's 0 clamped into the new range.
|
||
function OptionsSliderRow_Setup(row, min, max, step, fmt)
|
||
local slider = getglobal(row:GetName() .. "ControlSlider")
|
||
row.fmt = fmt
|
||
row.refreshing = 1
|
||
slider:SetMinMaxValues(min, max)
|
||
slider:SetValueStep(step)
|
||
row.refreshing = nil
|
||
end
|
||
|
||
-- The right-hand readout text (era MinimalSlider's RIGHT label seat): rounded percent
|
||
-- (era FormatPercentage, Audio.lua l.406-411) unless the row asked for the raw number.
|
||
function OptionsRow_FormatValue(row, value)
|
||
if row.fmt == "raw" then
|
||
return string.format("%d", math.floor(value + 0.5))
|
||
end
|
||
return string.format("%d%%", math.floor(value * 100 + 0.5))
|
||
end
|
||
|
||
-- The era row-wide hover wash (HoverBackgroundTemplate, white a=0.1): the row and every
|
||
-- control on it show the SAME texture, so crossing from label to slider doesn't blink.
|
||
function OptionsRow_Hover(row, on)
|
||
local hover = getglobal(row:GetName() .. "Hover")
|
||
if on then hover:Show() else hover:Hide() end
|
||
end
|
||
|
||
function OptionsCheckbox_OnClick(box)
|
||
local row = box:GetParent()
|
||
local checked = box:GetChecked()
|
||
-- Click kits: the 1.12 panels disagree with EACH OTHER — the interface panel's
|
||
-- PlayClickSound maps checked→CheckBoxOn (OptionsFrame.lua l.509), while the sound
|
||
-- panel's own OnClick maps checked→CheckBoxOff (SoundOptionsFrame.lua, the 0957
|
||
-- quirk) — so each page keeps its own panel's mapping (soundQuirk on Audio rows).
|
||
local kitOn = checked
|
||
if row.soundQuirk then kitOn = not kitOn end
|
||
if kitOn then
|
||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||
else
|
||
PlaySound("igMainMenuOptionCheckBoxOff")
|
||
end
|
||
-- The write: checked = 1 — unless the row is value-INVERTED (Sticky Targeting:
|
||
-- 1.12 checks it when deselectOnClick is "0", era wraps the same CVar in
|
||
-- NegateBoolean; both references agree).
|
||
local on = checked
|
||
if row.invert then on = not on end
|
||
if on then
|
||
SetCVar(row.cvar, 1)
|
||
else
|
||
SetCVar(row.cvar, 0)
|
||
end
|
||
OptionsPage_UpdateDependencies(OptionsFrame.selectedCategory)
|
||
end
|
||
|
||
function OptionsSlider_OnValueChanged(slider, value)
|
||
local control = slider:GetParent()
|
||
local row = control:GetParent()
|
||
if not row.refreshing then
|
||
-- era obeyStepOnDrag (MinimalSlider.xml l.5-7): user moves snap to the step
|
||
-- grid — anchored at the range MINIMUM, the reference slider's own anchor
|
||
-- (farclip's 1.12 grid is 177+n*60, not the multiples of 60). A refresh
|
||
-- writes the stored value raw (era SetValue does not snap), so an off-grid
|
||
-- config value still lands where it said.
|
||
local step = slider:GetValueStep()
|
||
if step and step > 0 then
|
||
local minv = slider:GetMinMaxValues()
|
||
local snapped = minv + math.floor((value - minv) / step + 0.5) * step
|
||
if math.abs(snapped - value) > 0.00001 then
|
||
slider:SetValue(snapped) -- re-enters once with the snapped value
|
||
return
|
||
end
|
||
value = snapped -- the clean grid number, not the f32-rounded echo
|
||
end
|
||
end
|
||
getglobal(control:GetName() .. "Value"):SetText(OptionsRow_FormatValue(row, value))
|
||
if row.cvar and not row.refreshing then
|
||
-- Two decimals cover every page's grid (the 5% volumes, uiScale's 0.01, whole
|
||
-- yards); trailing zeros stripped so a value at the registered default
|
||
-- compares equal to it as a STRING and stays out of the config file ("0.40"
|
||
-- would read as moved-off-"0.4").
|
||
local str = string.gsub(string.format("%.2f", value), "%.?0+$", "")
|
||
if row.deferred then
|
||
-- era CommitFlag.Apply, transcribed (Blizzard_Setting.lua's staged
|
||
-- SetValue half): the control shows the new value, the ENGINE keeps the
|
||
-- old one, and the CVar moves only when Apply commits — dragging a
|
||
-- slider that rescales itself mid-drag is exactly what this flag
|
||
-- exists to prevent. Dragging back onto the committed value clears the
|
||
-- pending (era's IsModified: a pending EQUAL to current isn't one).
|
||
if str == GetCVar(row.cvar) then
|
||
row.pending = nil
|
||
else
|
||
row.pending = str
|
||
end
|
||
OptionsApply_Update()
|
||
else
|
||
SetCVar(row.cvar, str)
|
||
end
|
||
end
|
||
end
|
||
|
||
function OptionsStepper_OnClick(btn, dir)
|
||
local slider = getglobal(btn:GetParent():GetName() .. "Slider")
|
||
-- era steppers: one step per click, the checkbox-on kit (MinimalSlider.lua l.53-57).
|
||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||
slider:SetValue(slider:GetValue() + dir * slider:GetValueStep())
|
||
end
|
||
|
||
function OptionsPage_Refresh(key)
|
||
local base = "OptionsFrameContainerBody" .. key
|
||
for _, rkey in ipairs(OPTIONS_PAGE_ROWS[key]) do
|
||
local row = getglobal(base .. rkey)
|
||
local box = getglobal(base .. rkey .. "Check")
|
||
if box then
|
||
-- SetChecked takes the CVar string exactly like the 1.12 panel: the numeric
|
||
-- read means "0" unchecks (button-check-and-state-texture.md). A
|
||
-- value-inverted row checks on "0" instead — UIOptionsFrame.lua's own
|
||
-- GAMEFIELD_DESELECT arm (`checked = 1` when the CVar reads "0").
|
||
if row.invert then
|
||
local checked
|
||
if GetCVar(row.cvar) == "0" then checked = 1 end
|
||
box:SetChecked(checked)
|
||
else
|
||
box:SetChecked(GetCVar(row.cvar))
|
||
end
|
||
else
|
||
local slider = getglobal(base .. rkey .. "ControlSlider")
|
||
row.refreshing = 1
|
||
-- Pending first (era GetValue returns the pendingValue while one is
|
||
-- staged): coming back to a page with an un-applied edit shows the edit.
|
||
slider:SetValue(tonumber(row.pending or GetCVar(row.cvar) or "0") or 0)
|
||
row.refreshing = nil
|
||
-- SetValue fires only on a CHANGE — write the readout unconditionally,
|
||
-- from the slider's OWN value: in range that IS the CVar (SetValue stores
|
||
-- raw), out of range the thumb and the number at least agree on the
|
||
-- nearest expressible value.
|
||
getglobal(base .. rkey .. "ControlValue"):SetText(
|
||
OptionsRow_FormatValue(row, slider:GetValue()))
|
||
end
|
||
end
|
||
OptionsPage_UpdateDependencies(key)
|
||
OptionsApply_Update()
|
||
end
|
||
|
||
-- Per-page dependency rules. Audio's is the 1.12 rule, verbatim: Enable All Sound off
|
||
-- greys Enable Ambience — and only it, of our rows
|
||
-- (SoundOptionsFrame_UpdateDependencies disables buttons 2 and 4, Ambience and Error
|
||
-- Speech; Enable Music stays live, the panel's own quirk).
|
||
function OptionsPage_UpdateDependencies(key)
|
||
if key ~= "Audio" then return end
|
||
local on = GetCVar("MasterSoundEffects") ~= "0"
|
||
local box = OptionsFrameContainerBodyAudioRowEnableAmbienceCheck
|
||
local label = OptionsFrameContainerBodyAudioRowEnableAmbienceLabel
|
||
if on then
|
||
box:Enable()
|
||
label:SetFontObject("GameFontNormal")
|
||
else
|
||
box:Disable()
|
||
label:SetFontObject("GameFontDisable")
|
||
end
|
||
end
|
||
|
||
-- Defaults, live on any page with rows: every row's CVar back to its registered
|
||
-- default (era resets per-setting the same way; the era all-pages confirm dialog is
|
||
-- not modeled — Defaults resets the VISIBLE page). A pending edit dies with it — the
|
||
-- default write supersedes what Apply would have committed.
|
||
function OptionsPage_Defaults(key)
|
||
local base = "OptionsFrameContainerBody" .. key
|
||
for _, rkey in ipairs(OPTIONS_PAGE_ROWS[key]) do
|
||
local row = getglobal(base .. rkey)
|
||
row.pending = nil
|
||
SetCVar(row.cvar, GetCVarDefault(row.cvar))
|
||
end
|
||
OptionsPage_Refresh(key)
|
||
end
|
||
|
||
-- ── The Apply flow (0961): era CommitFlag.Apply, cut to our rows ────────────────────
|
||
-- Pending edits are PANEL-wide like era's modified table: they survive a category
|
||
-- switch (the row redisplays its pending on return) and commit together; only hiding
|
||
-- the window discards them (era wipes on hide too — its close-time 3-button confirm
|
||
-- dialog and the 8-second revert timer are not modeled, SCOPE note).
|
||
|
||
-- The era Apply gate (HasUnappliedSettings + SetApplyButtonEnabled): the button is
|
||
-- enabled AND shown together — it EXISTS only while something is pending.
|
||
function OptionsApply_Update()
|
||
local pending
|
||
for key, rows in pairs(OPTIONS_PAGE_ROWS) do
|
||
local base = "OptionsFrameContainerBody" .. key
|
||
for _, rkey in ipairs(rows) do
|
||
if getglobal(base .. rkey).pending then pending = 1 end
|
||
end
|
||
end
|
||
if pending then
|
||
OptionsFrameApplyButton:Enable()
|
||
OptionsFrameApplyButton:Show()
|
||
else
|
||
OptionsFrameApplyButton:Disable()
|
||
OptionsFrameApplyButton:Hide()
|
||
end
|
||
end
|
||
|
||
-- Commit (era SettingsPanelMixin:CommitSettings, cut to our rows): every pending
|
||
-- value everywhere becomes a real SetCVar, and the button goes away.
|
||
function OptionsPage_Apply()
|
||
for key, rows in pairs(OPTIONS_PAGE_ROWS) do
|
||
local base = "OptionsFrameContainerBody" .. key
|
||
for _, rkey in ipairs(rows) do
|
||
local row = getglobal(base .. rkey)
|
||
if row.pending then
|
||
SetCVar(row.cvar, row.pending)
|
||
row.pending = nil
|
||
end
|
||
end
|
||
end
|
||
OptionsApply_Update()
|
||
end
|
||
|
||
-- Hide = discard (era's WipeModifiedTable-on-hide; the confirm dialog is cut): the
|
||
-- next open re-reads the committed CVars, so the sliders land back on the truth.
|
||
function OptionsPage_DiscardPending()
|
||
for key, rows in pairs(OPTIONS_PAGE_ROWS) do
|
||
local base = "OptionsFrameContainerBody" .. key
|
||
for _, rkey in ipairs(rows) do
|
||
getglobal(base .. rkey).pending = nil
|
||
end
|
||
end
|
||
OptionsApply_Update()
|
||
end
|
||
]]></Script>
|
||
|
||
<!-- The red panel button (era UIPanelButtonTemplate seat, 96x22): GameMenuButtonTemplate's
|
||
four-state UI-Panel-Button art + white/white/grey label trio, copied at this window's
|
||
size (GameMenuFrame.xml l.145 is the shared original; templates don't cross files). -->
|
||
<Button name="OptionsRedButtonTemplate" virtual="true">
|
||
<Size><AbsDimension x="96" y="22"/></Size>
|
||
<ButtonText name="$parentText"/>
|
||
<NormalTexture file="Interface\Buttons\UI-Panel-Button-Up"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></NormalTexture>
|
||
<PushedTexture file="Interface\Buttons\UI-Panel-Button-Down"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></PushedTexture>
|
||
<DisabledTexture file="Interface\Buttons\UI-Panel-Button-Disabled"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></DisabledTexture>
|
||
<HighlightTexture file="Interface\Buttons\UI-Panel-Button-Highlight" alphaMode="ADD"><TexCoords left="0" right="0.625" top="0" bottom="0.6875"/></HighlightTexture>
|
||
<NormalFont inherits="GameFontHighlight"/>
|
||
<HighlightFont inherits="GameFontHighlight"/>
|
||
<DisabledFont inherits="GameFontDisable"/>
|
||
</Button>
|
||
|
||
<!-- One category row (era Blizzard_CategoryList.xml's category button): the template's
|
||
175 width is never rendered in the era either — the ScrollBox stretches every row to
|
||
the scroll target's width, 199 with no scroll bar (CategoryList.lua l.176-197 +
|
||
ScrollBoxViewUtil), and ours is the no-bar case. Gold label centered in the row (the
|
||
full-row box below); a
|
||
CENTER bg that is EMPTY until the hover/selected atlas lands on it (options_list_hover
|
||
/ options_list_active at atlas size — 187x21, centered: 6 units in from either edge of
|
||
the 199 row, the alignment the first cut's 175-wide rows broke). Selection is
|
||
OptionsFrame_SelectCategory's; hover only ever paints a row that is NOT the selection. -->
|
||
<Button name="OptionsCategoryRowTemplate" virtual="true">
|
||
<Size><AbsDimension x="199" y="20"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentBg">
|
||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<!-- The era label is a box spanning the WHOLE row — TOPLEFT (36,+1) AND BOTTOMRIGHT
|
||
(0,+1), era CategoryList.xml l.72-77 — so the default MIDDLE vertical justify
|
||
CENTERS the text in the 20-tall row (1 unit high of true center, the era's own
|
||
nudge). The first cut had only the TOPLEFT corner: the auto-sized label hung from
|
||
the row TOP, ~4 units high of center ("text not vertically centered" — director,
|
||
2026-08-04). justifyH LEFT is the era's own attribute; a full-row box without it
|
||
would center horizontally. -->
|
||
<ButtonText name="$parentText" justifyH="LEFT">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="36" y="1"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="1"/></Offset></Anchor>
|
||
</Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontNormal"/>
|
||
<HighlightFont inherits="GameFontNormal"/>
|
||
<DisabledFont inherits="GameFontDisable"/>
|
||
<Scripts>
|
||
<OnClick>OptionsFrame_SelectCategory(this.categoryKey)</OnClick>
|
||
<OnEnter>
|
||
if OptionsFrame.selectedCategory ~= this.categoryKey then
|
||
getglobal(this:GetName() .. "Bg"):SetAtlas("Options_List_Hover", true)
|
||
end
|
||
</OnEnter>
|
||
<OnLeave>
|
||
if OptionsFrame.selectedCategory ~= this.categoryKey then
|
||
getglobal(this:GetName() .. "Bg"):SetTexture(nil)
|
||
end
|
||
</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- A category group header (era SettingsCategoryListHeaderTemplate, stretched to the 199
|
||
row width like the rows): a 14px white label at LEFT (20,-1) over the group's
|
||
BACKGROUND WASH — the Options_CategoryHeader_<n> atlas the era Init pins at the
|
||
header's TOPLEFT at atlas size (199x144: a header band whose gradient runs down over
|
||
the group's own rows; the rows' hover/selected plates draw above it, being later
|
||
siblings). The <n> cycles per header (CategoryList.lua l.378-384) — each instance's
|
||
OnLoad passes its own. -->
|
||
<Frame name="OptionsCategoryHeaderTemplate" virtual="true">
|
||
<Size><AbsDimension x="199" y="30"/></Size>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentBg">
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentLabel" inherits="OptionsFontHighlightMedium">
|
||
<Anchors><Anchor point="LEFT"><Offset><AbsDimension x="20" y="-1"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
</Frame>
|
||
|
||
<!-- One SETTING row, checkbox flavor (era SettingsCheckboxControlTemplate over
|
||
SettingsListElementTemplate, Blizzard_SettingControls.xml l.50-112): 26 tall, stretched
|
||
to the page width by its instance anchors; label GameFontNormal in a LEFT(37) →
|
||
CENTER(-85) box (the era mixin's runtime anchors, SettingControls.lua l.322-323); the
|
||
30x29 box at LEFT = row CENTER-80 (l.473). The checkbox art is the era minimal set at
|
||
its state seats (SettingControls.xml l.92-95): checkbox-minimal normal AND pushed (no
|
||
distinct pressed art), checkmark-minimal checked, checkmark-minimal-disabled
|
||
disabled+checked; no HighlightTexture — hover feedback is the row-wide Hover wash
|
||
(HoverBackgroundTemplate: white a=0.1, hung 10 left / 5 right of the row). -->
|
||
<Frame name="OptionsCheckboxRowTemplate" virtual="true" enableMouse="true">
|
||
<Size><AbsDimension y="26"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentHover" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-10" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="1" g="1" b="1" a="0.1"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentLabel" inherits="GameFontNormal" justifyH="LEFT">
|
||
<Anchors>
|
||
<Anchor point="LEFT"><Offset><AbsDimension x="37" y="0"/></Offset></Anchor>
|
||
<Anchor point="RIGHT" relativePoint="CENTER"><Offset><AbsDimension x="-85" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<CheckButton name="$parentCheck">
|
||
<Size><AbsDimension x="30" y="29"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativePoint="CENTER"><Offset><AbsDimension x="-80" y="0"/></Offset></Anchor></Anchors>
|
||
<NormalTexture atlas="checkbox-minimal"/>
|
||
<PushedTexture atlas="checkbox-minimal"/>
|
||
<CheckedTexture atlas="checkmark-minimal"/>
|
||
<DisabledCheckedTexture atlas="checkmark-minimal-disabled"/>
|
||
<Scripts>
|
||
<OnClick>OptionsCheckbox_OnClick(this)</OnClick>
|
||
<OnEnter>OptionsRow_Hover(this:GetParent(), 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this:GetParent(), nil)</OnLeave>
|
||
</Scripts>
|
||
</CheckButton>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnEnter>OptionsRow_Hover(this, 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this, nil)</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<!-- One SETTING row, slider flavor (era SettingsSliderControlTemplate →
|
||
MinimalSliderWithSteppersTemplate, SettingControls.lua l.550-552 + MinimalSlider.xml):
|
||
the same 26-tall row and label box; the 250x40 control at LEFT = row CENTER(-80,+3)
|
||
(its 40 height overrides the row symmetrically). Inside: the Slider inset 19 each side
|
||
(hit area the full height, the way era's is), wearing the minimal_sliderbar three-slice
|
||
at atlas size on the LEFT/RIGHT points (17-tall track centered) with the middle
|
||
stretched between the caps, and the 20x19 button thumb. Steppers hang 4 outside the
|
||
slider: 11x19 left, 9x18 right (the era's own asymmetry, MinimalSlider.xml l.43-64).
|
||
The readout is era's RIGHT label seat — 25 right of the slider edge = 6 right of the
|
||
control (MinimalSlider.xml l.73-77), rounded-percent formatted. -->
|
||
<Frame name="OptionsSliderRowTemplate" virtual="true" enableMouse="true">
|
||
<Size><AbsDimension y="26"/></Size>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentHover" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="-10" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="1" g="1" b="1" a="0.1"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentLabel" inherits="GameFontNormal" justifyH="LEFT">
|
||
<Anchors>
|
||
<Anchor point="LEFT"><Offset><AbsDimension x="37" y="0"/></Offset></Anchor>
|
||
<Anchor point="RIGHT" relativePoint="CENTER"><Offset><AbsDimension x="-85" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<Frame name="$parentControl">
|
||
<Size><AbsDimension x="250" y="40"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativePoint="CENTER"><Offset><AbsDimension x="-80" y="3"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentValue" inherits="GameFontNormal">
|
||
<Anchors><Anchor point="LEFT" relativePoint="RIGHT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<Slider name="$parentSlider" orientation="HORIZONTAL" minValue="0" maxValue="1" valueStep="0.05">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="19" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-19" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentTrackLeft" atlas="minimal_sliderbar_left" useAtlasSize="true">
|
||
<Anchors><Anchor point="LEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentTrackRight" atlas="minimal_sliderbar_right" useAtlasSize="true">
|
||
<Anchors><Anchor point="RIGHT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentTrackMiddle" atlas="_minimal_sliderbar_middle">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentTrackLeft" relativePoint="TOPRIGHT"/>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentTrackRight" relativePoint="BOTTOMLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<ThumbTexture atlas="minimal_sliderbar_button" useAtlasSize="true"/>
|
||
<Scripts>
|
||
<OnValueChanged>OptionsSlider_OnValueChanged(this, arg1)</OnValueChanged>
|
||
<OnEnter>OptionsRow_Hover(this:GetParent():GetParent(), 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this:GetParent():GetParent(), nil)</OnLeave>
|
||
</Scripts>
|
||
</Slider>
|
||
<Button name="$parentBack">
|
||
<Size><AbsDimension x="11" y="19"/></Size>
|
||
<Anchors><Anchor point="RIGHT" relativeTo="$parentSlider" relativePoint="LEFT"><Offset><AbsDimension x="-4" y="0"/></Offset></Anchor></Anchors>
|
||
<NormalTexture atlas="minimal_sliderbar_button_left"/>
|
||
<Scripts>
|
||
<OnClick>OptionsStepper_OnClick(this, -1)</OnClick>
|
||
<OnEnter>OptionsRow_Hover(this:GetParent():GetParent(), 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this:GetParent():GetParent(), nil)</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
<Button name="$parentForward">
|
||
<Size><AbsDimension x="9" y="18"/></Size>
|
||
<Anchors><Anchor point="LEFT" relativeTo="$parentSlider" relativePoint="RIGHT"><Offset><AbsDimension x="4" y="0"/></Offset></Anchor></Anchors>
|
||
<NormalTexture atlas="minimal_sliderbar_button_right"/>
|
||
<Scripts>
|
||
<OnClick>OptionsStepper_OnClick(this, 1)</OnClick>
|
||
<OnEnter>OptionsRow_Hover(this:GetParent():GetParent(), 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this:GetParent():GetParent(), nil)</OnLeave>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
</Frame>
|
||
</Frames>
|
||
<Scripts>
|
||
<OnEnter>OptionsRow_Hover(this, 1)</OnEnter>
|
||
<OnLeave>OptionsRow_Hover(this, nil)</OnLeave>
|
||
</Scripts>
|
||
</Frame>
|
||
|
||
<Frame name="OptionsFrame" frameStrata="HIGH" enableMouse="true" hidden="true">
|
||
<Size><AbsDimension x="920" y="724"/></Size>
|
||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<!-- FlatPanelBackgroundTemplate as one fill (header SCOPE note: the era tints
|
||
rounded-corner pieces with the binary-side PANEL_BACKGROUND_COLOR; this is
|
||
that color at the template's exact insets). -->
|
||
<Texture name="$parentFlatBg">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-18"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-3" y="3"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="0.122" g="0.118" b="0.129" a="0.8"/>
|
||
</Texture>
|
||
<!-- The title band behind the top tile (era `_UI-Frame-TitleTileBg`, SharedXML
|
||
Classic/SharedUIPanelTemplates.xml l.78: Interface\FrameGeneral\_UI-Frame —
|
||
era-only, manifest fdid 374157 — 256x17 horizTile). STRETCHED (SCOPE note). -->
|
||
<Texture name="$parentTitleBg" file="era:textures/374157.blp">
|
||
<Size><AbsDimension y="17"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="8" y="-3"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="0" y="-3"/></Offset></Anchor>
|
||
</Anchors>
|
||
<TexCoords left="0" right="1" top="0.2890625" bottom="0.421875"/>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="BORDER">
|
||
<!-- The ButtonFrameTemplateNoPortrait nine-slice, unrolled (NineSliceLayouts.lua
|
||
l.132 + NineSlice.lua's edge anchoring): corners at their corners at atlas
|
||
size; each edge pinned between its two corners on the anchored axis, its
|
||
other axis staying the atlas size. The three tile edges + the top band are
|
||
STRETCHED (SCOPE note). -->
|
||
<Texture name="$parentTopLeftCorner" atlas="UI-Frame-TopLeftCornerNoPortrait" useAtlasSize="true">
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentTopRightCorner" atlas="UI-Frame-TopCornerRight" useAtlasSize="true">
|
||
<Anchors><Anchor point="TOPRIGHT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentBotLeftCorner" atlas="UI-Frame-BotCornerLeft" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentBotRightCorner" atlas="UI-Frame-BotCornerRight" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentTopEdge" atlas="_UI-Frame-TitleTile" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentTopLeftCorner" relativePoint="TOPRIGHT"/>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentTopRightCorner" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
<Texture name="$parentBotEdge" atlas="_UI-Frame-Bot" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="BOTTOMLEFT" relativeTo="$parentBotLeftCorner" relativePoint="BOTTOMRIGHT"/>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentBotRightCorner" relativePoint="BOTTOMLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
<Texture name="$parentLeftEdge" atlas="!UI-Frame-LeftTile" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentTopLeftCorner" relativePoint="BOTTOMLEFT"/>
|
||
<Anchor point="BOTTOMLEFT" relativeTo="$parentBotLeftCorner" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
<!-- The right edge. The era layout's odd x=-8 / x1=8 pair (NineSliceLayouts
|
||
l.139) puts DIFFERENT x on the tile's two corner anchors — the reference
|
||
engine draws the quad through its anchor points, so the era tile is a
|
||
barely-visible lean (top 8 left, bottom 8 right; mean = flush at the frame
|
||
edge, which is exactly how the live window reads — director's capture,
|
||
2026-08-04). This engine's regions are axis-aligned rects, so we author the
|
||
MEAN: both corners flush (x=0). The first cut gave the whole tile to the
|
||
-8 anchor instead — the border floated 8 units into the panel with a dark
|
||
gap at the true edge, the director's "right border looks off". -->
|
||
<Texture name="$parentRightEdge" atlas="!UI-Frame-RightTile" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentTopRightCorner" relativePoint="BOTTOMRIGHT"/>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentBotRightCorner" relativePoint="TOPRIGHT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="ARTWORK">
|
||
<!-- The inner dark panel — the one big era piece, at its full atlas size. -->
|
||
<Texture name="$parentInnerFrame" atlas="Options_InnerFrame" useAtlasSize="true">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="17" y="-64"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<FontString name="$parentTitleText" inherits="GameFontNormal" text="Options">
|
||
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="-5"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- ── Tabs — the era MinimalTabTemplate plates (SharedXML Shared/Tabs/MinimalTab
|
||
l.3-46 + the mixin), authored STATIC: Game is the only tab with anything
|
||
behind it, so it wears the selected state permanently (Options_Tab_Active_*
|
||
plates, white small text lifted to BOTTOM+6) and AddOns wears the unselected
|
||
one (Options_Tab_* plates, gold small text at BOTTOM+4 — era's unselected
|
||
look, per the director's capture; the button is still dead). The three-slice:
|
||
Left/Right caps BOTTOM-anchored at atlas size (7x26 active / 7x23 not, so the
|
||
selected plate stands 3 taller), Middle (a 1px column) stretched between. The
|
||
era's width law is text + 40, measured at OnLoad with the MEDIUM font before
|
||
the small font lands (MinimalTab.lua l.7-9, the quirk kept verbatim) — our
|
||
measure round-trip answers a frame late, so OnUpdate polls until the width is
|
||
real, the chat-header idiom. -->
|
||
<Button name="$parentGameTab">
|
||
<Size><AbsDimension x="72" y="37"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="32" y="-27"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentLeft" atlas="Options_Tab_Active_Left" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentRight" atlas="Options_Tab_Active_Right" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentMiddle" atlas="Options_Tab_Active_Middle" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentLeft" relativePoint="TOPRIGHT"/>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRight" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<ButtonText name="$parentText" text="Game">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="6"/></Offset></Anchor></Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontHighlightSmall"/>
|
||
<HighlightFont inherits="GameFontHighlightSmall"/>
|
||
<DisabledFont inherits="GameFontHighlightSmall"/>
|
||
<Scripts>
|
||
<OnUpdate>OptionsTab_FitWidth(this)</OnUpdate>
|
||
</Scripts>
|
||
</Button>
|
||
<Button name="$parentAddOnsTab">
|
||
<Size><AbsDimension x="87" y="37"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentGameTab" relativePoint="TOPRIGHT"><Offset><AbsDimension x="5" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentLeft" atlas="Options_Tab_Left" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMLEFT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentRight" atlas="Options_Tab_Right" useAtlasSize="true">
|
||
<Anchors><Anchor point="BOTTOMRIGHT"/></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentMiddle" atlas="Options_Tab_Middle" useAtlasSize="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentLeft" relativePoint="TOPRIGHT"/>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRight" relativePoint="TOPLEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<ButtonText name="$parentText" text="AddOns">
|
||
<Anchors><Anchor point="BOTTOM"><Offset><AbsDimension x="0" y="4"/></Offset></Anchor></Anchors>
|
||
</ButtonText>
|
||
<NormalFont inherits="GameFontNormalSmall"/>
|
||
<HighlightFont inherits="GameFontNormalSmall"/>
|
||
<DisabledFont inherits="GameFontNormalSmall"/>
|
||
<!-- No addon list behind it: dead to clicks, but wearing the era's unselected
|
||
GOLD (not disabled grey) — the director's capture is the call. -->
|
||
<Scripts>
|
||
<OnLoad>self:Disable()</OnLoad>
|
||
<OnUpdate>OptionsTab_FitWidth(this)</OnUpdate>
|
||
</Scripts>
|
||
</Button>
|
||
|
||
<!-- ── The category list (era CategoryList, static — it fits) ──────────────────── -->
|
||
<Frame name="$parentCategoryList">
|
||
<Size><AbsDimension x="199"/></Size>
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="18" y="-76"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMLEFT"><Offset><AbsDimension x="18" y="46"/></Offset></Anchor>
|
||
</Anchors>
|
||
<!-- The era list rhythm (CreateScrollBoxListLinearView spacing=2 + the 18-tall
|
||
SettingsCategoryListSpacerTemplate between groups): 2 between every
|
||
element, 2+18+2 = 22 from a group's last row to the next header. -->
|
||
<Frames>
|
||
<Frame name="$parentHeaderGameplay" inherits="OptionsCategoryHeaderTemplate">
|
||
<Anchors><Anchor point="TOPLEFT"/></Anchors>
|
||
<Scripts>
|
||
<OnLoad>
|
||
getglobal(self:GetName() .. "Label"):SetText("Gameplay")
|
||
getglobal(self:GetName() .. "Bg"):SetAtlas("Options_CategoryHeader_1", true)
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
<Button name="$parentRowControls" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentHeaderGameplay" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Controls", "Controls")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowInterface" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowControls" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Interface", "Interface")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowActionBars" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowInterface" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "ActionBars", "Action Bars")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowCombat" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowActionBars" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Combat", "Combat")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowSocial" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowCombat" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Social", "Social")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowKeybindings" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowSocial" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Keybindings", "Keybindings")</OnLoad></Scripts>
|
||
</Button>
|
||
<!-- Nameplates — the era Gameplay group's own last row (NAMEPLATE_OPTIONS_LABEL,
|
||
order 15), backed by benilla's real nameplate knobs when the pages land. -->
|
||
<Button name="$parentRowNameplates" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowKeybindings" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Nameplates", "Nameplates")</OnLoad></Scripts>
|
||
</Button>
|
||
<Frame name="$parentHeaderSystem" inherits="OptionsCategoryHeaderTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowNameplates" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-22"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts>
|
||
<OnLoad>
|
||
getglobal(self:GetName() .. "Label"):SetText("System")
|
||
getglobal(self:GetName() .. "Bg"):SetAtlas("Options_CategoryHeader_2", true)
|
||
</OnLoad>
|
||
</Scripts>
|
||
</Frame>
|
||
<Button name="$parentRowGraphics" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentHeaderSystem" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Graphics", "Graphics")</OnLoad></Scripts>
|
||
</Button>
|
||
<Button name="$parentRowAudio" inherits="OptionsCategoryRowTemplate">
|
||
<Anchors><Anchor point="TOPLEFT" relativeTo="$parentRowGraphics" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-2"/></Offset></Anchor></Anchors>
|
||
<Scripts><OnLoad>OptionsCategoryRow_OnLoad(self, "Audio", "Audio")</OnLoad></Scripts>
|
||
</Button>
|
||
</Frames>
|
||
</Frame>
|
||
|
||
<!-- ── The page area (era SettingsList's header; the body is next slice's) ─────── -->
|
||
<Frame name="$parentContainer">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentCategoryList" relativePoint="TOPRIGHT"><Offset><AbsDimension x="16" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-22" y="47"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentDivider" atlas="Options_HorizontalDivider" useAtlasSize="true">
|
||
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="-50"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<!-- The page title — OptionsFrame_SelectCategory writes it. -->
|
||
<FontString name="$parentTitle" inherits="OptionsFontHighlightHuge">
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-22"/></Offset></Anchor></Anchors>
|
||
</FontString>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- Live on any page with rows (OPTIONS_PAGE_ROWS);
|
||
OptionsFrame_SelectCategory enables/disables it with the page swap. -->
|
||
<Button name="$parentDefaults" inherits="OptionsRedButtonTemplate" text="Defaults">
|
||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-36" y="-16"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:Disable()</OnLoad>
|
||
<OnClick>OptionsPage_Defaults(OptionsFrame.selectedCategory)</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- The page body: one child frame per category with rows, shown by
|
||
OptionsFrame_SelectCategory (Audio 0957, Graphics 0959). -->
|
||
<Frame name="$parentBody">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="0" y="-50"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<Frames>
|
||
<!-- The Controls page (0961): the gameplay checkboxes with benilla
|
||
knobs behind them, in the era Controls page's own row order
|
||
(Controls.lua @1.15.9: Sticky Targeting #1, Auto Loot #6,
|
||
Invert Mouse #12 — its Mouse section header is cut while one
|
||
row survives under it). Sticky Targeting is value-INVERTED in
|
||
BOTH references (1.12 UIOptionsFrame checks it on
|
||
deselectOnClick=="0"; era wraps the CVar in NegateBoolean);
|
||
labels are 1.12 GlobalStrings where the row exists there
|
||
(GAMEFIELD_DESELECT_TEXT, INVERT_MOUSE), era's for Auto Loot
|
||
(no 1.12 row exists). -->
|
||
<Frame name="$parentControls" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<Frames>
|
||
<Frame name="$parentRowStickyTarget" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-12"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-20" y="-12"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>
|
||
OptionsRow_OnLoad(self, "deselectOnClick", "Sticky Targeting")
|
||
self.invert = 1
|
||
</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowAutoLoot" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowStickyTarget" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowStickyTarget" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "autoLootDefault", "Auto Loot")</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowInvertMouse" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowAutoLoot" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowAutoLoot" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "mouseInvertPitch", "Invert Mouse")</OnLoad></Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
<!-- The Audio page. Row seats transcribe the era settings list
|
||
(Blizzard_SettingsList.lua l.43-47 + .xml: scroll box hung
|
||
-15/-20 off the header block with padLeft 25 / padRight 0 and
|
||
verticalPad 10 → first row at (10,-12); spacing 9 → 35 pitch).
|
||
Row ORDER is the era Audio page's grammar (volume block first,
|
||
then the enables) over the 1.12 row set: the three category
|
||
volumes ride as CHILDREN of Master Volume exactly like era's,
|
||
in the 1.12 panel's own slider order (Sound, Music, Ambience —
|
||
SoundOptionsFrame.lua's index order). -->
|
||
<Frame name="$parentAudio" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<Frames>
|
||
<Frame name="$parentRowEnableAll" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-12"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-20" y="-12"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>
|
||
OptionsRow_OnLoad(self, "MasterSoundEffects", "Enable All Sound")
|
||
self.soundQuirk = 1
|
||
</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowMaster" inherits="OptionsSliderRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowEnableAll" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowEnableAll" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "MasterVolume", "Master Volume")</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowSound" inherits="OptionsSliderRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowMaster" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowMaster" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "SoundVolume", "Sound Volume", 1)</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowMusic" inherits="OptionsSliderRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowSound" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowSound" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "MusicVolume", "Music Volume", 1)</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowAmbience" inherits="OptionsSliderRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowMusic" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowMusic" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>OptionsRow_OnLoad(self, "AmbienceVolume", "Ambience Volume", 1)</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowEnableMusic" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowAmbience" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowAmbience" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>
|
||
OptionsRow_OnLoad(self, "EnableMusic", "Enable Music")
|
||
self.soundQuirk = 1
|
||
</OnLoad></Scripts>
|
||
</Frame>
|
||
<Frame name="$parentRowEnableAmbience" inherits="OptionsCheckboxRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT" relativeTo="$parentRowEnableMusic" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT" relativeTo="$parentRowEnableMusic" relativePoint="BOTTOMRIGHT"><Offset><AbsDimension x="0" y="-9"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>
|
||
OptionsRow_OnLoad(self, "EnableAmbience", "Enable Ambience")
|
||
self.soundQuirk = 1
|
||
</OnLoad></Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
<!-- The Graphics page (0959; farclip row retired 0961 — the
|
||
director's call: the page holds UI Scale alone for now, the
|
||
farclip CVar/knob/env/debug-panel plumbing all stay). Range/
|
||
step/label are the 1.12 OptionsFrameSliders table verbatim
|
||
(OptionsFrame.lua l.25): uiscale 0.64..1.0 step 0.01 (UI_SCALE
|
||
"UI Scale", percent readout — era's own
|
||
FormatPercentageRounded on its uiscale row). 1.12's useUiScale
|
||
gate has no benilla knob (our scale always applies; off ≡ 1.0)
|
||
so the checkbox doesn't ship. -->
|
||
<Frame name="$parentGraphics" hidden="true">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"/>
|
||
<Anchor point="BOTTOMRIGHT"/>
|
||
</Anchors>
|
||
<Frames>
|
||
<Frame name="$parentRowUiScale" inherits="OptionsSliderRowTemplate">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="10" y="-12"/></Offset></Anchor>
|
||
<Anchor point="TOPRIGHT"><Offset><AbsDimension x="-20" y="-12"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Scripts><OnLoad>
|
||
OptionsRow_OnLoad(self, "uiScale", "UI Scale")
|
||
OptionsSliderRow_Setup(self, 0.64, 1.0, 0.01)
|
||
-- era registers its uiscale row CommitFlag.Apply
|
||
-- (Graphics.lua l.684): the one row whose live
|
||
-- write would rescale the very slider mid-drag —
|
||
-- it stages and commits through the Apply button.
|
||
self.deferred = 1
|
||
</OnLoad></Scripts>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
</Frames>
|
||
</Frame>
|
||
|
||
<!-- ── The search box (era SearchBoxTemplate → InputBoxVisualTemplate,
|
||
SecureUIPanelTemplates.xml l.42-67; filters NOTHING yet — SCOPE note): a 20-tall
|
||
THREE-slice of common-search-border atlases, vertically centered on the 22 box —
|
||
left cap 8x20 hung 5 outside, right cap 8x20 flush, middle stretched between —
|
||
plus the 10x10 magnifier at LEFT (1,-1) and a 17x17 clear button at RIGHT (-3)
|
||
that appears with text (ClearButtonMixin, cut to our placeholder machinery).
|
||
The first cut transcribed the LEGACY nine-piece Common-Input-Border instead —
|
||
its ink drew ~27 units tall against the reference's 18 and rode 4 units high:
|
||
the director's "search bar is too high" (capture-measured, 2026-08-04). -->
|
||
<EditBox name="$parentSearchBox" autoFocus="false" letters="64">
|
||
<Size><AbsDimension x="350" y="22"/></Size>
|
||
<Anchors>
|
||
<Anchor point="BOTTOMRIGHT" relativeTo="$parentContainer" relativePoint="TOPRIGHT"><Offset><AbsDimension x="4" y="20"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Layers>
|
||
<Layer level="BACKGROUND">
|
||
<Texture name="$parentBorderLeft" atlas="common-search-border-left">
|
||
<Size><AbsDimension x="8" y="20"/></Size>
|
||
<Anchors><Anchor point="LEFT"><Offset><AbsDimension x="-5" y="0"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentBorderRight" atlas="common-search-border-right">
|
||
<Size><AbsDimension x="8" y="20"/></Size>
|
||
<Anchors><Anchor point="RIGHT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
<Texture name="$parentBorderMiddle" atlas="common-search-border-middle">
|
||
<Size><AbsDimension y="20"/></Size>
|
||
<Anchors>
|
||
<Anchor point="LEFT" relativeTo="$parentBorderLeft" relativePoint="RIGHT"/>
|
||
<Anchor point="RIGHT" relativeTo="$parentBorderRight" relativePoint="LEFT"/>
|
||
</Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
<Layer level="ARTWORK">
|
||
<!-- The "Search" placeholder (era instructions label: GameFontDisableSmall
|
||
tinted 0.35). OnTextChanged below toggles it on first text. -->
|
||
<FontString name="$parentInstructions" inherits="GameFontDisableSmall" justifyH="LEFT" text="Search">
|
||
<Anchors>
|
||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="16" y="0"/></Offset></Anchor>
|
||
<Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-20" y="0"/></Offset></Anchor>
|
||
</Anchors>
|
||
<Color r="0.35" g="0.35" b="0.35"/>
|
||
</FontString>
|
||
</Layer>
|
||
<Layer level="OVERLAY">
|
||
<Texture name="$parentSearchIcon" atlas="common-search-magnifyingglass">
|
||
<Size><AbsDimension x="10" y="10"/></Size>
|
||
<Anchors><Anchor point="LEFT"><Offset><AbsDimension x="1" y="-1"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Frames>
|
||
<!-- The clear X (era ClearButtonMixin: shown while text exists, half-alpha
|
||
icon, click wipes). Shown/hidden by the box's OnTextChanged below. -->
|
||
<Button name="$parentClearButton" hidden="true">
|
||
<Size><AbsDimension x="17" y="17"/></Size>
|
||
<Anchors><Anchor point="RIGHT"><Offset><AbsDimension x="-3" y="0"/></Offset></Anchor></Anchors>
|
||
<Layers>
|
||
<Layer level="ARTWORK">
|
||
<Texture name="$parentIcon" atlas="common-search-clearbutton" alpha="0.5">
|
||
<Size><AbsDimension x="10" y="10"/></Size>
|
||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="3" y="-3"/></Offset></Anchor></Anchors>
|
||
</Texture>
|
||
</Layer>
|
||
</Layers>
|
||
<Scripts>
|
||
<OnClick>
|
||
OptionsFrameSearchBox:SetText("")
|
||
OptionsFrameSearchBox:ClearFocus()
|
||
</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<TextInsets><AbsInset left="16" right="20" top="0" bottom="0"/></TextInsets>
|
||
<Scripts>
|
||
<!-- Placeholder + clear-button toggle only — no filtering behind it yet
|
||
(SCOPE note). -->
|
||
<OnTextChanged>
|
||
if this:GetText() == "" then
|
||
OptionsFrameSearchBoxInstructions:Show()
|
||
OptionsFrameSearchBoxClearButton:Hide()
|
||
else
|
||
OptionsFrameSearchBoxInstructions:Hide()
|
||
OptionsFrameSearchBoxClearButton:Show()
|
||
end
|
||
</OnTextChanged>
|
||
<OnEscapePressed>this:ClearFocus()</OnEscapePressed>
|
||
</Scripts>
|
||
<FontString inherits="GameFontHighlightSmall"/>
|
||
</EditBox>
|
||
|
||
<!-- ── Close, both spellings ───────────────────────────────────────────────────── -->
|
||
<!-- The X — hung past the top-right corner like the era's ClosePanelButton, on the
|
||
1.12 minimize-ball art (header SCOPE note; the LootFrame close idiom). -->
|
||
<Button name="$parentClosePanelButton">
|
||
<Size><AbsDimension x="32" y="32"/></Size>
|
||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="4" y="4"/></Offset></Anchor></Anchors>
|
||
<NormalTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Up"/>
|
||
<PushedTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Down"/>
|
||
<HighlightTexture file="Interface\Buttons\UI-Panel-MinimizeButton-Highlight" alphaMode="ADD"/>
|
||
<Scripts>
|
||
<OnClick>
|
||
PlaySound("igMainMenuClose")
|
||
HideUIPanel(OptionsFrame)
|
||
</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- The red Close (era SettingsPanel.CloseButton). -->
|
||
<Button name="$parentCloseButton" inherits="OptionsRedButtonTemplate" text="Close">
|
||
<Anchors><Anchor point="BOTTOMRIGHT"><Offset><AbsDimension x="-16" y="16"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnClick>
|
||
PlaySound("igMainMenuClose")
|
||
HideUIPanel(OptionsFrame)
|
||
</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
<!-- The era Apply (SettingsPanel.ApplyButton: 96x22, RIGHT of Close's LEFT at -2,
|
||
Blizzard_SettingsPanel.xml l.52-57): exists only while a deferred row holds a
|
||
pending value — era enables and SHOWS it together (SetApplyButtonEnabled) —
|
||
and commits every pending edit on click. -->
|
||
<Button name="$parentApplyButton" inherits="OptionsRedButtonTemplate" text="Apply" hidden="true">
|
||
<Anchors><Anchor point="RIGHT" relativeTo="$parentCloseButton" relativePoint="LEFT"><Offset><AbsDimension x="-2" y="0"/></Offset></Anchor></Anchors>
|
||
<Scripts>
|
||
<OnLoad>self:Disable()</OnLoad>
|
||
<OnClick>OptionsPage_Apply()</OnClick>
|
||
</Scripts>
|
||
</Button>
|
||
</Frames>
|
||
<Scripts>
|
||
<!-- Controls is the default page; a re-opened window keeps its last selection (the
|
||
era remembers too — Settings.OpenToCategory's default seat). The scale re-fits
|
||
on every show — the era panel manager re-runs checkFit the same way. -->
|
||
<OnShow>
|
||
OptionsFrame_UpdateScale()
|
||
OptionsFrame_SelectCategory(this.selectedCategory or "Controls")
|
||
</OnShow>
|
||
<!-- Hiding discards pending edits (the era wipes its modified table on hide; its
|
||
close-time confirm dialog is cut — SCOPE note). -->
|
||
<OnHide>OptionsPage_DiscardPending()</OnHide>
|
||
</Scripts>
|
||
</Frame>
|
||
</Ui>
|