comments: six interface files say what each frame is and what it mirrors
A frame's comment names the stock file or 1.12 default its shape follows; a Lua comment stays where the code would otherwise be "fixed". Notes the code contradicts are gone. With XML and Lua comments stripped every file is identical, and each is well-formed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1,65 +1,19 @@
|
||||
<!--
|
||||
OUR adapters over the reference's own `Interface\FrameXML\ContainerFrame.xml`/`.lua`.
|
||||
|
||||
**This client does not transcribe those files — it EXECUTES them, off the player's own patch
|
||||
chain** (decision 1751; `ui_script::reference_ui` carries the rule, and `benilla.toc` carries
|
||||
the position). The bag windows on screen ARE `ContainerFrame1..12`. What is in THIS file is
|
||||
only benilla's, and only where a reference body reaches for something this client genuinely
|
||||
does not have.
|
||||
|
||||
The rule, restated in one line so it can be applied without re-reading the module: the sourced
|
||||
file's globals stand unmodified; ours, loading after, win where both define a name; and where a
|
||||
reference body needs a benilla answer, it is WRAPPED here — never silently stubbed.
|
||||
|
||||
TWO THINGS THIS FILE USED TO DO AND NO LONGER DOES, both retired by 1751's swap:
|
||||
· It declared `ContainerFrameItemButtonTemplate`, transcribed from ContainerFrame.xml, because
|
||||
the Lua could be sourced and the XML could not. The XML is sourced now, so the reference's
|
||||
own declaration is the live one and the copy is deleted — the whole point of the migration.
|
||||
· It defined `updateContainerFrameAnchors()` as a deliberate no-op, because the reference's
|
||||
body walks twelve `ContainerFrame` windows this client did not build. It builds them, so the
|
||||
reference's own body is correct here and the stub is deleted. Five corpus addons (Bongos,
|
||||
Bongos_ActionBar, the CT_BarMod family) hook that global and call through; they now reach the
|
||||
real thing.
|
||||
|
||||
── The adapters, and why neither is a stub ──────────────────────────────────────────────────
|
||||
`ContainerFrameItemButton_OnEnter` opens with a keyring fork (ref l.615-620):
|
||||
|
||||
if this:GetParent():GetID() == KEYRING_CONTAINER then
|
||||
GameTooltip:SetInventoryItem("player", KeyRingButtonIDToInvSlotID(this:GetID()))
|
||||
CursorUpdate()
|
||||
return
|
||||
end
|
||||
|
||||
That fork exists because the REAL client's keyring items are inventory slots, not container
|
||||
slots, so it has to detour out of the container API to describe one. benilla's keyring is an
|
||||
ordinary container (id -2, decision 0765): its slots come through `GetContainerItemInfo` and
|
||||
`GameTooltip:SetBagItem` like every other bag's — which is exactly why the reference's keyring
|
||||
WINDOW, its verbs and its sounds all take our keyring unchanged, and why this one line is the
|
||||
only thing in that file the swap did not simply inherit. The detour is not merely unavailable
|
||||
here (`KeyRingButtonIDToInvSlotID` is not a binding here) — it is *wrong* here, and supplying
|
||||
the verb would buy an empty tooltip, because our keyring items were never fed into the
|
||||
inventory-slot space it reads.
|
||||
|
||||
So the wrapper below answers the keyring case the way benilla answers every other bag, and
|
||||
hands EVERY other slot straight to the reference's own body. The path the player is actually
|
||||
on — hovering an ordinary bag slot — runs the real client's code, unedited.
|
||||
|
||||
── Named, not hidden: what still does not work through the sourced files ────────────────────
|
||||
· `REPAIR_COST` is read by the reference's repair-mode arm. It is a `GlobalStrings.lua` key and
|
||||
this client sources that file too, so it resolves at runtime — and the arm is unreachable
|
||||
today regardless, since `GameTooltip:SetBagItem`'s repairCost is an INTERIM 0.
|
||||
· `NO_EMPTY_KEYRING_SLOTS` is read by the reference's `PutKeyInKeyRing` when the ring is full,
|
||||
and no such GlobalString exists — 1.12 ships only `NO_EMPTY_KEYRING_SLOTS_ERROR`. That is a
|
||||
REFERENCE BUG, and this client used to un-quirk it. It does not any more: the reference's own
|
||||
file runs, so a full keyring prints the reference's own empty error line. Faithful, and
|
||||
deliberately so (1751: a divergence becomes faithful or becomes a record, never silent).
|
||||
Our adapters over the stock `Interface\FrameXML\ContainerFrame.xml` and `.lua`, which run off
|
||||
the player's patch chain: the bag windows are the stock `ContainerFrame1..12`. This file loads
|
||||
after them, so its definitions win; a stock body that needs a benilla answer is wrapped here.
|
||||
Deviation: benilla's keyring is a container (id -2) where the reference's is inventory slots,
|
||||
so a keyring slot's tooltip comes from `GameTooltip:SetBagItem` like any bag's; the stock
|
||||
keyring fork in `ContainerFrameItemButton_OnEnter` (ContainerFrame.lua l.615-620) reads the
|
||||
inventory-slot space, which our keyring items are not in.
|
||||
A full keyring prints the reference's empty error line: its `PutKeyInKeyRing` reads
|
||||
`NO_EMPTY_KEYRING_SLOTS`, which 1.12 does not ship (it has `NO_EMPTY_KEYRING_SLOTS_ERROR`).
|
||||
That is the reference's behaviour, kept.
|
||||
-->
|
||||
<Ui xmlns="http://www.blizzard.com/wow/ui/">
|
||||
<Script><![CDATA[
|
||||
-- The reference's own body, captured before we shadow the name. Nil on a machine with no
|
||||
-- client data (nothing was sourced) — which must be LOUD at the call, not a quiet no-op:
|
||||
-- a bag hover that silently does nothing is precisely the failure mode that let Bagnon
|
||||
-- ship "working" through four green survey columns.
|
||||
-- The stock body, captured before the name is shadowed. It is nil when no client data was
|
||||
-- sourced, and the call then says so rather than doing nothing.
|
||||
local refItemButtonOnEnter = ContainerFrameItemButton_OnEnter
|
||||
|
||||
function ContainerFrameItemButton_OnEnter(button)
|
||||
@@ -68,9 +22,8 @@
|
||||
end
|
||||
if not button then return end
|
||||
|
||||
-- benilla's keyring is a CONTAINER (0765), so the reference's inventory-slot detour is
|
||||
-- both unavailable and wrong here; this is the same pair of calls its general path
|
||||
-- makes for every other bag, which is all a keyring slot ever needed.
|
||||
-- Our keyring is a container, so its slot takes the stock general path's calls for a
|
||||
-- bag slot instead of the inventory-slot detour.
|
||||
if button:GetParent():GetID() == KEYRING_CONTAINER then
|
||||
if (button:GetRight() or 0) >= (GetScreenWidth() / 2) then
|
||||
GameTooltip:SetOwner(button, "ANCHOR_LEFT")
|
||||
@@ -90,50 +43,30 @@
|
||||
refItemButtonOnEnter(button)
|
||||
end
|
||||
|
||||
-- ══ The three bag verbs benilla answers differently (decisions 0561 + 1751) ═════════
|
||||
--
|
||||
-- `CloseAllBags`, `OpenBackpack` and `CloseBackpack` all exist in the sourced
|
||||
-- `ContainerFrame.lua` too, and benilla's differ for one reason, not three: **0561, the
|
||||
-- director's call that a window which opens your bags opens EVERY equipped bag**, not just
|
||||
-- the backpack. The vendor/bank/mail/trade windows call `OpenBackpack` from their OnShow so
|
||||
-- your bags come up with them and you can sell or deposit from one; the reference's own
|
||||
-- body touches bag 0 alone. `CloseBackpack` is its inverse and has to match it, and
|
||||
-- `CloseAllBags` is the same family's unconditional form.
|
||||
--
|
||||
-- **The `Benilla*` names, and the publish under them, are load ORDER** (1751). Order
|
||||
-- decides every collision and the manifest is the order: this file loads after the
|
||||
-- reference's `ContainerFrame.xml`, so the three publishes at the foot are the ones that
|
||||
-- stand. The bodies lived in our UIParent.lua counterpart until 1988 retired it — a
|
||||
-- harness with no bag stack now meets the loader's stand-ins for the three names
|
||||
-- (`test_ui::UIPARENT_STAND_INS`), not a nil.
|
||||
--
|
||||
-- What CHANGED with the swap is only the plumbing: these used to walk five benilla windows
|
||||
-- by name and stash `wasOpen` on each frame. The windows are the reference's recycled
|
||||
-- `ContainerFrame1..12` now — frame N is a different bag every time it opens — so the
|
||||
-- was-open memory is keyed by BAG ID, and every open and close goes through the
|
||||
-- reference's own `OpenBag`/`CloseBag`/`IsBagOpen` rather than a raw Show/Hide. That buys
|
||||
-- the reference's own guards for free (OpenBag's CanOpenPanels + NotWhileDeadError,
|
||||
-- ToggleBag's `size > 0`) and means an addon replacing those verbs is honoured here too.
|
||||
-- ══ The three bag verbs benilla answers differently ═══════════════════════════════════
|
||||
-- Deviation: `OpenBackpack` opens every equipped bag, not only the backpack, so the
|
||||
-- vendor, bank, mail and trade windows that call it from OnShow bring up every bag to sell
|
||||
-- or deposit from; `CloseBackpack` is its inverse, `CloseAllBags` the unconditional form.
|
||||
-- The was-open memory is keyed by bag id because `ContainerFrame1..12` are recycled across
|
||||
-- bags, and every open and close goes through the stock `OpenBag`/`CloseBag`/`IsBagOpen`,
|
||||
-- so their guards and any addon that replaces them apply.
|
||||
BENILLA_BAG_WAS_OPEN = {}
|
||||
|
||||
-- The UNCONDITIONAL close, called by ShowUIPanel's native-center branch above: a centre
|
||||
-- frame takes the whole screen, so nothing is being "restored afterwards" the way a vendor
|
||||
-- window restores the bags it opened, and the was-open memory is deliberately ignored.
|
||||
-- Range 0..NUM_CONTAINER_FRAMES rather than the old five names — the reference's own
|
||||
-- CloseAllBags covers the bank bags too (l.702-707), and now so does this.
|
||||
-- The unconditional close, from the stock ShowUIPanel's centre branch: it ignores the
|
||||
-- was-open memory and covers the bank bags, as the reference's CloseAllBags does
|
||||
-- (l.702-707).
|
||||
function BenillaCloseAllBags()
|
||||
for bagId = 0, (NUM_CONTAINER_FRAMES or -1) do
|
||||
CloseBag(bagId)
|
||||
end
|
||||
end
|
||||
|
||||
-- OpenBackpack — 0561's all-bags form. A bag already open is REMEMBERED and left alone, so
|
||||
-- the paired CloseBackpack does not close a window the player opened themselves; that
|
||||
-- memory is the reference's own `backpackWasOpen`, one entry per bag instead of one flag.
|
||||
-- A bag already open is remembered and left alone, so CloseBackpack leaves open a window
|
||||
-- the player opened; the reference's `backpackWasOpen`, one entry per bag.
|
||||
function BenillaOpenBackpack()
|
||||
for bagId = 0, (NUM_BAG_FRAMES or -1) do
|
||||
-- The backpack always exists; an equipped-bag window only when a bag is in the
|
||||
-- slot — `OpenBag` makes the same test itself, but the memory has to agree with it.
|
||||
-- OpenBag's own test, repeated so the memory agrees with it: the backpack always,
|
||||
-- another bag only when its slot holds one.
|
||||
if bagId == 0 or GetContainerNumSlots(bagId) > 0 then
|
||||
if IsBagOpen(bagId) then
|
||||
BENILLA_BAG_WAS_OPEN[bagId] = 1
|
||||
@@ -153,9 +86,8 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- The publish — after the reference's container file, so these are the definitions that
|
||||
-- stand (the stock `ShowUIPanel` calls `CloseAllBags` and the vendor's OnShow
|
||||
-- `OpenBackpack`, and they reach 0561's bodies).
|
||||
-- Published after the stock ContainerFrame.lua has loaded, so these are the definitions its
|
||||
-- callers reach (the stock `ShowUIPanel`'s `CloseAllBags`, a vendor's `OpenBackpack`).
|
||||
OpenBackpack = BenillaOpenBackpack
|
||||
CloseBackpack = BenillaCloseBackpack
|
||||
CloseAllBags = BenillaCloseAllBags
|
||||
|
||||
@@ -1,106 +1,19 @@
|
||||
<Ui>
|
||||
<!--
|
||||
The GAME MENU — the frame ESC opens (decision 0674). The last hole in the game shell's
|
||||
outermost layer: until now ESC's chain ran out of things to close and did nothing, and the
|
||||
Main Menu micro button was drawn but dead (our since-retired MicroMenu.xml's SCOPE note).
|
||||
|
||||
PROVENANCE (0985 — the three-source split, stated the same way in every era-shaped
|
||||
window; OptionsFrame.xml carries the sibling block):
|
||||
· STRUCTURE & BEHAVIOR — the 1.15 era client (wow-ui-source-era @ 1.15.9.68940): the
|
||||
ladder shape, its groups and gaps, which entries exist (the SCOPE section below).
|
||||
If 1.15 has the mechanism, it STAYS.
|
||||
· CHROME MECHANISM — this window predates 1.13: the 1.12 client's OWN GameMenuFrame
|
||||
is the chrome ground truth (the dialog backdrop, header plate, red buttons — the
|
||||
same dialog family the 1.13/1.14 options windows kept), quoted below.
|
||||
· ART BYTES — the 1.12 MPQ chain only; nothing later-client at runtime (0978's law).
|
||||
|
||||
Ground truth, all quoted rather than re-derived:
|
||||
· **The frame** — ref `GameMenuFrame.xml` (patch.MPQ, extracted): 195x246, `frameStrata`
|
||||
DIALOG, the UI-DialogBox backdrop with the 11/12/12/11 insets and 32px tile/edge, the
|
||||
256x64 UI-DialogBox-Header at TOP +12 with MAIN_MENU centered 14px down it, and the
|
||||
eight-button ladder — first button CENTER on the frame's TOP at (0,-37), each next one
|
||||
TOP-to-BOTTOM at (0,-1), and Return to Game hung (0,-16) below Exit Game (the one gap in
|
||||
the stack, which is what makes the last entry read as separate).
|
||||
· **The button** — ref `GameMenuButtonTemplate` (UIPanelTemplates.xml l.403-410): a 144x21
|
||||
`UIPanelButtonTemplate` (the UI-Panel-Button Up/Down/Disabled/Highlight four-state art,
|
||||
TexCoords 0..0.625 / 0..0.6875) whose label trio is overridden to
|
||||
GameFontHighlight/GameFontHighlight/GameFontDisable — i.e. the menu's labels are WHITE,
|
||||
not the gold a normal panel button wears, and grey when disabled.
|
||||
· **The labels** — GlobalStrings.lua (patch-2.MPQ), verbatim: VIDEOOPTIONS_MENU "Video
|
||||
Options", SOUNDOPTIONS_MENU "Sound Options", UIOPTIONS_MENU "Interface Options",
|
||||
MACROS "Macros", LOGOUT "Logout", EXIT_GAME "Exit Game",
|
||||
RETURN_TO_GAME "Return to Game", MAIN_MENU "Main Menu". Declared as the real globals
|
||||
(they are addon-visible API, decision 0068) and carried on each button's own
|
||||
`text=` attribute — which this engine resolves as a global-string lookup exactly as
|
||||
the reference does (decision 0995, `loader/widgets.rs`'s `resolve_text`).
|
||||
· **The sounds** — the ref's own OnClick lines: igMainMenuOption (the five option buttons),
|
||||
igMainMenuLogout, igMainMenuQuit, igMainMenuContinue. All four kits verified present in
|
||||
SoundEntries.dbc (patch-2.MPQ) alongside the igMainMenuOpen/Close pair already in use.
|
||||
· **The two logout dialogs** — ref `StaticPopup.lua` l.564-600 (CAMP + QUIT) is the
|
||||
SPECIFICATION for them: the two dialog keys, the field names, the 20 s timeout and the
|
||||
asymmetric button sets are contract, and the entries carrying them are written here in
|
||||
our own shape on the shared popup engine (the stock StaticPopup), whose countdown-text branch
|
||||
already lists both `which` names. Their driver events are the reference's own
|
||||
(`re/events/event-catalog.tsv`: 276 PLAYER_CAMPING, 277 PLAYER_QUITING,
|
||||
278 LOGOUT_CANCEL), fired by `crate::ui_logout` off `SMSG_LOGOUT_RESPONSE`.
|
||||
|
||||
NAMES are the reference's, unprefixed (`GameMenuFrame`, `GameMenuButton*`) — the same call
|
||||
our retired MicroMenu.xml made for its straight transcriptions, and required here besides: `ToggleGameMenu`
|
||||
and `GameMenuFrame` are Era API surface an addon addresses by name.
|
||||
|
||||
SCOPE / divergences (noted, not bugs):
|
||||
- **The ladder is the ERA menu's** (director's call on the 0951 review — supersedes 0951
|
||||
§3's five-button collapse): the Era client's own ESC menu shape, greyed where benilla
|
||||
has nothing behind the entry yet. Geometry quoted from the era dump @ 1.15.9 (Gethe
|
||||
wow-ui-source, classic_era, the 0950 pin):
|
||||
· Blizzard_SharedXML/Classic/Frame/MainMenuFrameTemplates.xml l.18-33 — the menu is a
|
||||
VerticalLayoutFrame, buttonTemplate MainMenuFrameButtonTemplate, padding
|
||||
top/bottom/left/right = 32/28/28/28, spacing 0; l.11-16 — the button is a 144x21
|
||||
UIPanelButtonTemplate with the white/white/grey label trio (the SAME art + trio the
|
||||
1.12 GameMenuButtonTemplate carries — no era-only art in this menu at all).
|
||||
· Shared/Frame/MainMenuFrameTemplates.lua l.61-71 — AddSection() puts a 20-unit gap
|
||||
(the next button's topPadding) between groups; AddCloseButton uses the same 20.
|
||||
· Shared/GameMenuFrame.lua l.62-162 — the Era ladder: Options · [gap] · AddOns, Edit
|
||||
Mode, Support, Macros · [gap] · Logout, Exit Game · [gap] · Return to Game. (Store /
|
||||
Rewards / What's New / Ratings are structurally off in Era — store disabled, no
|
||||
account store, IsStandard() false, koKR only.)
|
||||
· The layout math those constants produce, precomputed here (this engine has no layout
|
||||
frames) — for OUR seven rungs, the era's eight minus AddOns (next bullet): width
|
||||
28+144+28 = 200; button tops 32/73/94/115/156/177/218; height 218+21+28 = 267.
|
||||
Anchored as a TOPLEFT chain so the gaps read in the XML.
|
||||
· No Key Bindings rung — the era menu's own shape: 1.15 ceded the entry to the
|
||||
Settings panel, and since decision 1008 ours lives there too (the Options window's
|
||||
Keybindings category, KeyBindingsPage.xml). 0997 had carved the 1.13/1.14 seat back
|
||||
in for its standalone window; that seat left with the window.
|
||||
· No AddOns rung — the director's call: the char-select AddOns screen
|
||||
(`char_select::addons`, the reference's own glue-side home for this list) is the only
|
||||
addon UI. The in-game panel 1197 put behind this rung (AddonList.xml) is retired,
|
||||
and the rung left with it — 1.12's own ESC menu has no AddOns row either.
|
||||
· The header — Classic/Dialog/DialogTemplates.xml l.11-43 (ClassicDialogHeaderTemplate):
|
||||
the same 256x64 UI-DialogBox-Header art, text = MAINMENU_BUTTON, swapped to
|
||||
GameFontNormalMed1 (13px) by MainMenuFrameMixin:OnLoad. Both header idioms (1.12's
|
||||
TOP+12 texture / era's 39-tall frame with the art centered) seat the art within half
|
||||
a unit of each other; the 1.12 transcription is kept, the font and string are era's.
|
||||
- **Edit Mode / Support ship DISABLED** — nothing behind them yet
|
||||
(`GameMenuButton_Pending`): the template's own `-Disabled` art + grey label, exactly
|
||||
how the era menu greys a dead entry (SetEnabled(false) → DisabledFont). Each goes live
|
||||
by deleting one `GameMenuButton_Pending` call and writing its OnClick.
|
||||
- The Options label stays a literal "Options": 1.12 GlobalStrings has no era
|
||||
GAMEMENU_OPTIONS (checked; its OPTIONS_MENU is "Options Menu", a different string).
|
||||
- The ref frame is `toplevel="true" movable="true" parent="UIParent"`. `parent="UIParent"` is
|
||||
ours too, and it attaches for real: the loader does read a cross-file `parent=`, resolving
|
||||
the name by global lookup at load (decision 1734), and the frame below declares it. Only
|
||||
`movable` is dropped, and it is inert in the ref too (GameMenuFrame defines no drag
|
||||
scripts, so it never actually moves).
|
||||
`frameStrata="DIALOG"` is what puts it over the world UI, and that is kept.
|
||||
**The "no frame-level raise" half of that reason is gone**: the raise law is built
|
||||
(`script::object::toplevel`, wow-re `ui/scratch/toplevel-raise.md`), the loader honours
|
||||
`toplevel="true"`, and the frame below carries it — so the Esc menu does come to the
|
||||
front over any other DIALOG-strata frame it overlaps.
|
||||
The game menu, the frame ESC opens. Its chrome is the reference GameMenuFrame.xml's: DIALOG
|
||||
strata, the UI-DialogBox backdrop (insets 11/12/12/11, 32px tile and edge), the 256x64
|
||||
header at TOP +12 with the title 14px down it, the reference's click sounds, and the stock
|
||||
`GameMenuButtonTemplate`, which the era's MainMenuFrameButtonTemplate matches in art,
|
||||
size and label fonts.
|
||||
Deviation: the ladder is the 1.15 era client's ESC menu (GameMenuFrame.lua l.62-162) at
|
||||
its layout numbers, padding 32 top and 28 elsewhere (MainMenuFrameTemplates.xml l.18-33)
|
||||
and a 20-unit gap between groups (MainMenuFrameTemplates.lua l.61-71). It drops the era's
|
||||
AddOns rung, because the character-select AddOns screen is the only addon UI; Key
|
||||
Bindings is in the options window, as in the era.
|
||||
Names are the reference's, unprefixed: addons address `GameMenuFrame` by name.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- The reference GlobalStrings (patch-2.MPQ Interface\FrameXML\GlobalStrings.lua), quoted
|
||||
-- verbatim. `X = X or "..."` so a later real strings table wins, the retired MicroMenu.xml's idiom.
|
||||
-- Label strings as `X = X or "..."`, so a stock GlobalStrings value wins where one exists.
|
||||
MAIN_MENU = MAIN_MENU or "Main Menu"
|
||||
HUD_EDIT_MODE_MENU = HUD_EDIT_MODE_MENU or "Edit Mode"
|
||||
GAMEMENU_SUPPORT = GAMEMENU_SUPPORT or "Support"
|
||||
@@ -110,50 +23,29 @@
|
||||
RETURN_TO_GAME = RETURN_TO_GAME or "Return to Game"
|
||||
CANCEL = CANCEL or "Cancel"
|
||||
|
||||
-- One label setter for a button with nothing behind it yet (Edit Mode and Support): set
|
||||
-- the ref's text, then disable (grey label + the template's -Disabled art). Making it
|
||||
-- live is deleting the call and writing its ref OnClick.
|
||||
-- Labels and disables a rung with nothing behind it yet (Edit Mode, Support), greyed on the
|
||||
-- template's -Disabled art the way the era menu greys a dead entry.
|
||||
function GameMenuButton_Pending(button, text)
|
||||
button:SetText(text)
|
||||
button:Disable()
|
||||
end
|
||||
|
||||
-- ══ THE THREE STOCK CONSUMERS THAT ASK "IS AN OPTIONS WINDOW OPEN?" (decision 2177) ══
|
||||
-- All three are the reference's own, off the chain, and all three name the reference's
|
||||
-- three windows literally. Two of those names are now REAL hidden stock frames
|
||||
-- (`SoundOptionsFrame` since 2147, `UIOptionsFrame` since 2115) and the third,
|
||||
-- `OptionsFrame`, became one when the stock VIDEO window joined the manifest — so the
|
||||
-- whole chain now reads "no", always, while the window the player actually opens is up.
|
||||
-- Until then `OptionsFrame` WAS our window and every one of them answered by accident.
|
||||
--
|
||||
-- **Wrappers, not replacements.** Each stock function keeps running and keeps deciding;
|
||||
-- ours adds the one term the reference could not know about. A rewrite would be thirty
|
||||
-- lines of the reference's ladder copied into our tree to drift.
|
||||
--
|
||||
-- **HERE, not in `OptionsFrame.xml`, because of load order.** `UpdateMicroButtons` is
|
||||
-- defined by `MainMenuBarMicroButtons.xml`, manifest l.576 — AFTER our options window at
|
||||
-- l.522, where a wrapper would capture a nil. This file is the manifest's LAST row
|
||||
-- (l.1273), so all three exist. It is also the right home on the merits: this is the ESC
|
||||
-- menu, and these three are exactly "the ESC ladder and the micro button know about our
|
||||
-- options window".
|
||||
-- ══ Our options window in the stock "is an options window open?" checks ══
|
||||
-- The three stock functions name the reference's options windows, which here are hidden
|
||||
-- stock frames, so each is wrapped to add `BenillaOptionsFrame` and still runs the stock
|
||||
-- body. Wrapped here because this file loads last: `UpdateMicroButtons`
|
||||
-- (MainMenuBarMicroButtons.xml) loads after our options window.
|
||||
local Ref_IsOptionFrameOpen = IsOptionFrameOpen
|
||||
local Ref_ToggleGameMenu = ToggleGameMenu
|
||||
local Ref_UpdateMicroButtons = UpdateMicroButtons
|
||||
|
||||
-- The one term all three add. Existence-guarded because these three wrappers stand in a
|
||||
-- file that can be seated without the options window: in the client `BenillaOptionsFrame`
|
||||
-- is manifest row 38 and this file is the last row, so it is always real — but a test kit
|
||||
-- that seats the game menu alone must get "no options window is open", not a raise inside
|
||||
-- the ESC handler.
|
||||
-- Guarded for a test kit that loads the game menu without the options window.
|
||||
local function BenillaOptions_IsOpen()
|
||||
return BenillaOptionsFrame and BenillaOptionsFrame:IsVisible()
|
||||
end
|
||||
|
||||
-- `UIParent.lua:996`. One caller: `CloseAllWindows_WithExceptions`, which passes
|
||||
-- `ignoreCenter = 1` when an options window is up so the ESC sweep does not close the
|
||||
-- centre panel out from under it. Ours IS the centre panel (`UIPanelWindows` row,
|
||||
-- `area = "center"`), so answering "no" would have ESC close it through the sweep instead
|
||||
-- of through its own rung, discarding pending edits with no Cancel semantics.
|
||||
-- `UIParent.lua:996`, read by `CloseAllWindows_WithExceptions`: a yes keeps the ESC sweep
|
||||
-- off the centre panel, which our window is, so ESC closes it through its own rung.
|
||||
function IsOptionFrameOpen()
|
||||
if BenillaOptions_IsOpen() then
|
||||
return 1
|
||||
@@ -161,20 +53,13 @@
|
||||
return Ref_IsOptionFrameOpen()
|
||||
end
|
||||
|
||||
-- `UIParent.lua:1465-1497`. The ESC ladder, and the rung matters as much as the answer:
|
||||
-- the reference tries the static popup FIRST, then an open options window, then the game
|
||||
-- menu. So this runs that first rung itself and returns if it fired — `StaticPopup_Escape -- Pressed` is a pure scan when nothing is shown (`StaticPopup.lua:1879-1893`), so the
|
||||
-- delegate below re-running it costs nothing and changes nothing — then takes the options
|
||||
-- rung, then hands the rest of the ladder back untouched.
|
||||
--
|
||||
-- The close is our own Close button's click, which is the same discard-and-hide the
|
||||
-- reference reaches through `OptionsFrameCancel:Click()`: our `<OnHide>` drops the pending
|
||||
-- page. (That alias is retired — `OptionsFrameCancel` is the stock video window's own
|
||||
-- button now, and ours loading later would have clobbered it.)
|
||||
-- `UIParent.lua:1465-1497`, the ESC ladder: static popup first, then an open options
|
||||
-- window, then the rest of the stock ladder. The stock body re-runs the popup rung, a pure
|
||||
-- scan when nothing is shown (`StaticPopup.lua:1879-1893`). Our Close button's click is
|
||||
-- the discard-and-hide the reference reaches through `OptionsFrameCancel:Click()`.
|
||||
function ToggleGameMenu(clicked)
|
||||
if clicked then
|
||||
-- The reference's `clicked` arm closes an open options window and then goes on to
|
||||
-- toggle the game menu, rather than returning. Same here.
|
||||
-- As the reference's `clicked` arm: close an options window, then still toggle.
|
||||
if BenillaOptions_IsOpen() then
|
||||
BenillaOptionsFrameCloseButton:Click()
|
||||
end
|
||||
@@ -190,17 +75,9 @@
|
||||
return Ref_ToggleGameMenu(clicked)
|
||||
end
|
||||
|
||||
-- `MainMenuBarMicroButtons.lua:47-58`. A POST-hook that can only ever push the button, never
|
||||
-- release it: the reference has already decided from the six frames it knows, and this adds
|
||||
-- the seventh. Written this way round so it cannot fight that decision — a wrapper that also
|
||||
-- set NORMAL would be a second copy of the reference's whole `or` chain.
|
||||
--
|
||||
-- **Existence-guarded, and only here.** The shipped manifest always carries
|
||||
-- `MainMenuBarMicroButtons.xml`, so in the client `Ref_UpdateMicroButtons` and
|
||||
-- `MainMenuMicroButton` are always real. A partial test kit that seats this file and not
|
||||
-- the micro bar is the case the guards are for, and a guard is the honest answer there:
|
||||
-- there is no micro button to push, so there is nothing to do — as against a raise inside
|
||||
-- an unrelated window's OnShow, which is what an unguarded wrapper turns that into.
|
||||
-- `MainMenuBarMicroButtons.lua:47-58`: a post-hook that only ever pushes the Main Menu
|
||||
-- button, never releases it, so the stock decision over its six frames stands and ours adds
|
||||
-- the seventh. Guarded for a test kit that loads this file without the micro bar.
|
||||
function UpdateMicroButtons()
|
||||
if Ref_UpdateMicroButtons then
|
||||
Ref_UpdateMicroButtons()
|
||||
@@ -210,35 +87,20 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- The CAMP/QUIT dialogs and the three-event driver MOVED OUT, to our UIParent.lua counterpart (the stock UIParent.lua since 1988) — the
|
||||
-- reference's own homes for both (StaticPopup.lua l.564/586; UIParent.lua l.304-315).
|
||||
-- They were here only because this is where they were first needed, and 1751 moving them
|
||||
-- is one less thing to carry when this window follows them onto the chain.
|
||||
]]></Script>
|
||||
|
||||
<!-- The era header text size (Classic/Dialog/DialogTemplates.xml + the OnLoad font swap):
|
||||
GameFontNormalMed1 = SystemFont_Med2 = FRIZQT 13, gold, the MasterFont shadow. Era's
|
||||
name, declared here beside its one consumer (the 0951 residue convention: Fonts.xml
|
||||
stays the complete 1.12 set). -->
|
||||
<!-- The era's header font, GameFontNormalMed1 (FRIZQT 13, gold): an era name 1.12's Fonts.xml
|
||||
lacks, declared beside its one consumer. -->
|
||||
<Font name="GameFontNormalMed1" inherits="GameFontNormal" virtual="true">
|
||||
<FontHeight><AbsValue val="13"/></FontHeight>
|
||||
</Font>
|
||||
|
||||
<!-- `GameMenuButtonTemplate` MOVED OUT, to UIPanelTemplates.xml — the reference's own file for
|
||||
it (ref UIPanelTemplates.xml l.403-410), and now ColorPickerFrame.xml's Okay/Cancel need it
|
||||
too, from far above this file's LAST-in-the-toc seat. It lived here, with its four state
|
||||
textures inlined, only because `UIPanelButtonTemplate` had not been transcribed yet; it now
|
||||
has been, so the reference's actual body (`inherits="UIPanelButtonTemplate"`, 144x21, the
|
||||
white/white/grey label trio) replaces the private copy verbatim. Era's
|
||||
MainMenuFrameButtonTemplate (Classic/Frame/MainMenuFrameTemplates.xml l.11-16) is that same
|
||||
button — same art, same trio, same size — so the header's ART BYTES note is unchanged. -->
|
||||
|
||||
<!-- `toplevel` is the ref's own (GameMenuFrame.xml l.3) and was dropped here: without the bit
|
||||
a DIALOG-strata window opened over another one stays behind it (the B180/Bagnon class,
|
||||
decision 1318). Restored alone; the ref's `movable`/`parent` stay off deliberately. -->
|
||||
<!-- The reference frame's attributes (GameMenuFrame.xml l.3) except `movable`, which is inert
|
||||
there: the reference defines no drag scripts. -->
|
||||
<Frame name="GameMenuFrame" parent="UIParent" frameStrata="DIALOG" toplevel="true" enableMouse="true" hidden="true">
|
||||
<!-- The era layout engine's own numbers (header SCOPE note): 28+144+28 wide, 32-top /
|
||||
28-bottom padding around our seven 21-unit rungs with three 20-unit section gaps. -->
|
||||
<!-- The era layout: 28+144+28 wide; 32 top and 28 bottom padding around seven 21-unit
|
||||
rungs and three 20-unit section gaps. -->
|
||||
<Size><AbsDimension x="200" y="267"/></Size>
|
||||
<Anchors><Anchor point="CENTER"/></Anchors>
|
||||
<Backdrop bgFile="Interface\DialogFrame\UI-DialogBox-Background" edgeFile="Interface\DialogFrame\UI-DialogBox-Border" tile="true">
|
||||
@@ -252,9 +114,8 @@
|
||||
<Size><AbsDimension x="256" y="64"/></Size>
|
||||
<Anchors><Anchor point="TOP"><Offset><AbsDimension x="0" y="12"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
<!-- Era swaps the header text to 13px (MainMenuFrameMixin:OnLoad →
|
||||
SetHeaderFont(GameFontNormalMed1)); the seat is the 1.12 one (within half a
|
||||
unit of era's — header SCOPE note). -->
|
||||
<!-- The era's 13px header font (MainMenuFrameMixin:OnLoad) on the 1.12 seat, which
|
||||
is within half a unit of the era's. -->
|
||||
<FontString name="GameMenuFrameTitle" inherits="GameFontNormalMed1" text="MAIN_MENU">
|
||||
<Anchors>
|
||||
<Anchor point="TOP" relativeTo="GameMenuFrameHeader"><Offset><AbsDimension x="0" y="-14"/></Offset></Anchor>
|
||||
@@ -263,18 +124,10 @@
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<!-- The era ladder minus its AddOns rung (header SCOPE notes): Options · gap ·
|
||||
Edit Mode, Support, Macros · gap · Logout, Exit Game · gap · Return to Game.
|
||||
A TOPLEFT chain at the era layout engine's numbers — x 28, first top -32, 0
|
||||
between group members, -20 at each section break. Live entries wire the era
|
||||
OnClick intent; pending ones grey out through GameMenuButton_Pending until
|
||||
something exists behind them. -->
|
||||
|
||||
<!-- Options — live: the era GameMenuFrame.lua l.88 entry (ShowUIPanel(SettingsPanel)
|
||||
there, BenillaOptionsFrame here) plus an explicit HideUIPanel first. The ref leans on
|
||||
the center-replacement rule for the swap (ShowUIPanel l.697-702: native-center
|
||||
frames replace each other); hiding the menu explicitly says the same thing
|
||||
without relying on the eviction path. -->
|
||||
<!-- The era's Options entry (GameMenuFrame.lua l.88), opening our options window. A
|
||||
literal label: 1.12 has no GAMEMENU_OPTIONS, and its OPTIONS_MENU is a different
|
||||
string, "Options Menu". -->
|
||||
<Button name="GameMenuButtonOptions" inherits="GameMenuButtonTemplate" text="Options">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="28" y="-32"/></Offset></Anchor>
|
||||
@@ -304,8 +157,7 @@
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="GameMenuButtonSupport" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="0"/></Offset></Anchor>
|
||||
</Anchors>
|
||||
<!-- LIVE since decision 0983 (MacroFrame.xml). The ref's own body
|
||||
(GameMenuFrame.xml l.118-122): hide the menu, then ShowMacroFrame(). -->
|
||||
<!-- The reference's body (GameMenuFrame.xml l.118-122). -->
|
||||
<Scripts>
|
||||
<OnClick>
|
||||
PlaySound("igMainMenuOption")
|
||||
@@ -315,10 +167,8 @@
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<!-- The three that are live. The ref's OnShow on both Logout and Quit disables the
|
||||
button while either logout dialog is already up (you cannot re-arm a running
|
||||
countdown); it runs on the menu's own show, which is when a re-opened menu has to
|
||||
re-read that state. -->
|
||||
<!-- The reference's OnShow disables Logout and Exit Game while a CAMP or QUIT countdown
|
||||
is up, so a running countdown cannot be re-armed. -->
|
||||
<Button name="GameMenuButtonLogout" inherits="GameMenuButtonTemplate" text="LOGOUT">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="GameMenuButtonMacros" relativePoint="BOTTOMLEFT"><Offset><AbsDimension x="0" y="-20"/></Offset></Anchor>
|
||||
@@ -369,23 +219,14 @@
|
||||
</Scripts>
|
||||
</Button>
|
||||
</Frames>
|
||||
<!-- ref GameMenuFrame OnShow/OnHide, verbatim: the micro row's Main Menu button pushes in
|
||||
while the menu is up, and the bag bar greys out under it (you cannot open bags from
|
||||
behind the menu — ShowUIPanel's center gate would refuse the window anyway, so the
|
||||
greying is the visible half of a rule the panel manager already enforces). Guarded, in
|
||||
the `CloseMenus and CloseMenus()` posture our retired panel file used: a headless test
|
||||
loads this file without the action bar behind it. -->
|
||||
<!-- The reference's OnShow/OnHide: the Main Menu micro button pushes in and the bag
|
||||
buttons grey out while the menu is up. Guarded for a test that loads this file
|
||||
without the action bar. -->
|
||||
<Scripts>
|
||||
<OnShow>
|
||||
-- The shared era-window scale first (ERA_WINDOW_SCALE, OptionsFrame.xml — loaded
|
||||
-- before this file): the era menu has no scale of its own (GameMenuFrame.xml +
|
||||
-- MainMenuFrameTemplates, grepped clean), it draws at UIParent's px-per-unit —
|
||||
-- the SAME density as the era options window, whose checkFit caps at 1. The knob
|
||||
-- is our stand-in for that UIParent scale; without it the menu drew 28% larger
|
||||
-- relative to the options window than the era pair ("esc menu scale too big" —
|
||||
-- director, 2026-08-04). `or 1` guards a load-order regression only. NOTE: this
|
||||
-- lives INSIDE the existing OnShow — a second Scripts/OnShow block on the same
|
||||
-- frame would silently REPLACE this one (SetScript semantics: last wins).
|
||||
-- Deviation: ERA_WINDOW_SCALE (OptionsFrame.xml, loaded earlier) stands in for the
|
||||
-- UIParent scale the era draws this menu and its options window at.
|
||||
-- A second OnShow on this frame would replace this one: the last SetScript wins.
|
||||
this:SetScale(ERA_WINDOW_SCALE or 1)
|
||||
if UpdateMicroButtons then UpdateMicroButtons() end
|
||||
if Disable_BagButtons then Disable_BagButtons() end
|
||||
|
||||
@@ -1,77 +1,23 @@
|
||||
<Ui>
|
||||
<!--
|
||||
The KEY BINDINGS page (decision 1008, superseding 0997's standalone window) — the
|
||||
Options window's Keybindings category, over the engine's binding table
|
||||
(benilla_ui::script::keybind + crate::bindings — GetNumBindings/GetBinding/SetBinding/
|
||||
GetBindingKey/GetBindingAction/LoadBindings/SaveBindings/GetCurrentBindingSet, the 1.12
|
||||
Lua API verbatim). This file is the page MODULE: the templates and the whole script; the
|
||||
page's static furniture (the rows, the checkbox row, the scroll seam, the search rows)
|
||||
lives in OptionsFrame.xml as ordinary children of its Keybindings body, inheriting these
|
||||
templates (manifest order puts this file first). Rows are the HONEST TREE: only commands
|
||||
the engine really dispatches are registered, so only they (and only their non-empty
|
||||
categories) appear.
|
||||
|
||||
PROVENANCE (0985 — the three-source split; OptionsFrame.xml carries the sibling block):
|
||||
· STRUCTURE & BEHAVIOR — the era Settings panel's own Keybindings page (wow-ui-source-era
|
||||
@ 1.15.9.68940: Blizzard_SettingsDefinitions_Frame/Keybindings.lua + Classic/
|
||||
KeybindingsOverrides.lua + Blizzard_Settings_Shared/Blizzard_Keybindings.*): 1.15 put
|
||||
keybindings INSIDE the Options window — the director's call this move executes — as
|
||||
ONE list of EXPANDABLE per-category sections (SettingsKeybindingSectionMixin over
|
||||
SettingsExpandableSectionMixin; 25-tall rows, sections COLLAPSED by default, categories
|
||||
in first-appearance order, a bar's section existing only when the bar does — their
|
||||
PROXY_SHOW_ACTIONBAR_N gate is our honest tree), the Character Specific Keybindings
|
||||
checkbox as the page's first row (PROXY_CHARACTER_SPECIFIC_BINDINGS, confirm popup on
|
||||
UNCHECK only), and per-binding search tags whose category REDIRECTS to this page
|
||||
(Blizzard_SettingsPanel.lua l.578 `category.redirectCategory or category`) with the
|
||||
matched rows LIVE in the results.
|
||||
· CAPTURE & SET LAW — 1.12's own Blizzard_BindingUI (extracted from patch.MPQ, 0997),
|
||||
KEPT over the era's listener where they differ (the game's fidelity truth is 1.12;
|
||||
the window furniture is the era's): click a key capsule to arm; the next key or
|
||||
mouse-3/4/5 press binds IMMEDIATELY (the host capture seam hands the canonical
|
||||
ALT-CTRL-SHIFT chord to KeyBindings_OnHostKey); a stolen key names its victim in red
|
||||
(KEY_UNBOUND_ERROR) only when the victim goes bare; left/right clicks select/deselect,
|
||||
never bind; lone modifiers are ignored; the wheel binds like any other key, to ANY
|
||||
command (decision 1295 — SetBinding refuses nothing but a key string that is not a key,
|
||||
and the KEYBINDINGFRAME_MOUSEWHEEL_ERROR arm below is that refusal's message, not a
|
||||
wheel rule); ESC while armed BINDS Escape (1.12's OnKeyDown has no ESCAPE filter — the
|
||||
era's ESC-cancels law is the one era behavior NOT taken, recorded in 1008; the Unbind
|
||||
Key button is the clearing affordance). ESC with nothing armed never reaches any of
|
||||
this: the ladder is the TOGGLEGAMEMENU binding, and the armed seam swallows the press.
|
||||
· ART BYTES — the 1.12 MPQ chain only (0978/0985): the capsules wear 1.12's own
|
||||
UIPanelButtonTemplate2 panel-button art (the Classic client's silver stretch set —
|
||||
UI-Silver-Button-Up/-Down/-Highlight/-Select, a 9-slice — is VERIFIED ABSENT from
|
||||
the 1.12 chain: 0997, re-pinned and reverted-to-red in 1088 after 1085's gray-dress
|
||||
detour), selection is 1.12's LockHighlight glow; section headers wear the 1.12
|
||||
collapsible-list dress — the QuestLog's own UI-PlusButton/UI-MinusButton square
|
||||
and gold label.
|
||||
|
||||
COMMIT LAW (1008, the page's one big behavior change from 0997): binds are LIVE-COMMIT.
|
||||
The options window's whole grammar writes live (SetCVar on click, no Okay/Cancel pair),
|
||||
and the era Settings panel commits keybinds as they land too — so every successful
|
||||
bind/steal/unbind/reset saves the current set to disk at once
|
||||
(SaveBindings(GetCurrentBindingSet())); 0997's Okay/Cancel/revert and the
|
||||
CONFIRM_LOSE_BINDING_CHANGES popup die with the standalone window. The two destructive
|
||||
gestures keep their confirms: Reset To Default (the page Defaults button) and leaving
|
||||
the character-specific set (which permanently deletes it).
|
||||
|
||||
All strings are the 1.12 GlobalStrings globals (the VM executes the real file at boot)
|
||||
through the `getglobal(...) or raw` fallback (the era's own `_G[x] or x` posture), so a
|
||||
headless test without GlobalStrings still renders raw tokens.
|
||||
|
||||
SCOPE / divergences (noted, not bugs — the ledger is 1008):
|
||||
- No Quick Keybind Mode row (era furniture over an overlay mechanism benilla lacks).
|
||||
- 1.12's BLANK spacer-headers between multibar blocks are not modeled — both bottom
|
||||
bars sit under the one MultiActionBar section.
|
||||
- 1.12's "Key Bindings for <name>" title swap died with the standalone window: the page
|
||||
title stays the category's, the checkbox is the character-set state (era's own shape).
|
||||
- Search shows at most KEYBINDINGS_SEARCH_ROWS matched bindings (static row pool; the
|
||||
era's results list scrolls, ours doesn't yet) — the group head jumps to the full page.
|
||||
The key bindings page: the options window's Keybindings category, over the engine's
|
||||
binding table and the 1.12 binding API. This file holds the templates and the script; the
|
||||
rows are children of the Keybindings body in OptionsFrame.xml, which loads after it. Only
|
||||
commands the engine dispatches are registered, so only they and their categories appear.
|
||||
Deviation: the page is the era Settings panel's Keybindings page (Keybindings.lua,
|
||||
KeybindingsOverrides.lua): one list of collapsible category sections, the Character
|
||||
Specific Keybindings checkbox as its first row, and search results that open this page.
|
||||
Capture and set follow 1.12's Blizzard_BindingUI where the two differ: a capsule click
|
||||
arms, the next key or mouse 3/4/5 press binds at once, left and right clicks only select,
|
||||
the wheel binds like any key, and ESC while armed binds Escape (1.12's OnKeyDown has no
|
||||
ESCAPE filter; Unbind Key clears).
|
||||
Deviation: every bind, unbind and reset saves at once, as the era panel commits, where
|
||||
1.12 had Okay and Cancel; only Reset To Default and leaving the character set confirm.
|
||||
-->
|
||||
|
||||
<!-- One KEY CAPSULE (1.12 KeyBindingFrameBindingButtonTemplate: UIPanelButtonTemplate2 at
|
||||
180x22 with the small-highlight font trio; its RegisterForClicks takes all five
|
||||
buttons so middle/4/5 presses reach the capture law). The SELECTED state is 1.12's
|
||||
LockHighlight — the ADD highlight held on. -->
|
||||
<!-- A key capsule, 1.12's KeyBindingFrameBindingButtonTemplate: UIPanelButtonTemplate2 art at
|
||||
180x22 with the small font trio, registered for all five buttons so a middle, 4 or 5
|
||||
press reaches the capture. Selected is its LockHighlight, the ADD highlight held on. -->
|
||||
<Button name="KeyBindingButtonTemplate" virtual="true">
|
||||
<Size><AbsDimension x="180" y="22"/></Size>
|
||||
<ButtonText name="$parentText"/>
|
||||
@@ -87,14 +33,10 @@
|
||||
</Scripts>
|
||||
</Button>
|
||||
|
||||
<!-- One LIST ROW, dual-mode (the era list mixes section headers and binding rows in one
|
||||
25-tall rhythm — SettingsKeybindingSectionInitializer:GetExtent's single bindingHeight).
|
||||
BIND mode: command name 170 wide at LEFT, the two capsules at 175/355 (0997's seats,
|
||||
kept). HEADER mode: the whole row is one toggle button wearing 1.12's own collapsible
|
||||
dress — the QuestLogTitleButton's 16x16 UI-Plus/MinusButton square at LEFT and the gold
|
||||
label (the era's Options_ListExpand arrows have no 1.12 counterpart). The paint
|
||||
(KeyBindings_PaintBindRow / _PaintHeaderRow) shows one mode's furniture and hides the
|
||||
other's. -->
|
||||
<!-- A list row, painted as a binding (name, two capsules) or as a section header: one toggle
|
||||
in 1.12's QuestLogTitleButton dress, the 16x16 plus/minus square and gold label, since
|
||||
the era's expand arrows are not in 1.12. Both are 25 tall, the era's one row height
|
||||
(SettingsKeybindingSectionInitializer:GetExtent). -->
|
||||
<Frame name="KeyBindingRowTemplate" virtual="true">
|
||||
<Size><AbsDimension x="560" y="25"/></Size>
|
||||
<Layers>
|
||||
@@ -141,19 +83,17 @@
|
||||
</Frame>
|
||||
|
||||
<Script><![CDATA[
|
||||
-- ── Strings ── the 1.12 GlobalStrings globals, each with the raw fallback (a headless
|
||||
-- harness without GlobalStrings still works; the era's own `_G[x] or x` posture).
|
||||
-- ── Strings ── a 1.12 GlobalStrings global, or the fallback for a harness without them.
|
||||
function KeyBindings_String(name, fallback)
|
||||
local s = getglobal(name)
|
||||
if s then return s end
|
||||
return fallback
|
||||
end
|
||||
|
||||
-- ── Key display ── the 1.12 GetBindingText law by hand: modifier prefixes stay the
|
||||
-- canonical uppercase tokens, the base key reads its KEY_* global (Spacebar, Up Arrow,
|
||||
-- Mouse Button 4...) else itself. "CTRL--" renders CTRL + the minus key. (The action
|
||||
-- bar's ABBREVIATED labels ride the stock GetBindingText (UIParent.lua);
|
||||
-- this page and the tooltips show the full form, both references' own split.)
|
||||
-- ── Key display ── 1.12's GetBindingText in full form, as the reference shows it here and
|
||||
-- in tooltips (the action bar's abbreviated form is the stock GetBindingText): modifier
|
||||
-- prefixes stay uppercase tokens, the base key reads its KEY_* global, else itself.
|
||||
-- "CTRL--" is CTRL plus the minus key.
|
||||
function KeyBindings_KeyText(key)
|
||||
if not key then
|
||||
return NORMAL_FONT_COLOR_CODE .. KeyBindings_String("NOT_BOUND", "Not Bound") .. FONT_COLOR_CODE_CLOSE
|
||||
@@ -178,10 +118,9 @@
|
||||
return prefix .. KeyBindings_String("KEY_" .. rest, rest)
|
||||
end
|
||||
|
||||
-- ── Page state ── no root frame anymore: the page's state lives here. `sections` is the
|
||||
-- era section model; `selected`/`keyID` the 1.12 selection; `searchPainted` the live
|
||||
-- search-result rows (rowName → binding index), set by OptionsFrame.xml's search display
|
||||
-- and repainted after a bind lands on one.
|
||||
-- ── Page state ── `sections` is the era section model, `selected`/`keyID` the 1.12
|
||||
-- selection, and `searchPainted` maps OptionsFrame.xml's live search rows (row name to
|
||||
-- binding index).
|
||||
KeyBindingsPage = { sections = {}, items = {} }
|
||||
|
||||
KEYBINDINGS_ROW_BASE = "BenillaOptionsFrameContainerBodyKeybindingsRow"
|
||||
@@ -189,10 +128,9 @@
|
||||
KEYBINDINGS_ROW_HEIGHT = 25
|
||||
KEYBINDINGS_SEARCH_ROWS = 8
|
||||
|
||||
-- ── The section model (era CreateKeybindingInitializers, cut to the honest tree): walk
|
||||
-- GetBinding(i), group runs of one category token; order = first appearance, which IS
|
||||
-- 1.12's Bindings.xml order (the registry registers in it). Sections default COLLAPSED
|
||||
-- (the era Classic overrides pass no `expanded`); a rebuild keeps each token's state.
|
||||
-- ── Section model ── the era's CreateKeybindingInitializers: one section per category
|
||||
-- token in first-appearance order, which is 1.12's Bindings.xml order. Sections start
|
||||
-- collapsed (the era's Classic overrides pass no `expanded`); a rebuild keeps each state.
|
||||
function KeyBindings_BuildSections()
|
||||
local page = KeyBindingsPage
|
||||
local was = {}
|
||||
@@ -214,8 +152,8 @@
|
||||
KeyBindings_Flatten()
|
||||
end
|
||||
|
||||
-- The flattened visible list (era EvaluateVisibility): every header, then the expanded
|
||||
-- sections' rows. The faux scroll runs over this.
|
||||
-- The visible list the faux scroll runs over (the era's EvaluateVisibility): each header,
|
||||
-- then its rows when expanded.
|
||||
function KeyBindings_Flatten()
|
||||
local page = KeyBindingsPage
|
||||
page.items = {}
|
||||
@@ -229,8 +167,7 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- The expand/collapse click (era OnExpandedChanged; the sound is the checkbox kit's,
|
||||
-- the page's one click voice).
|
||||
-- A header click (the era's OnExpandedChanged), voiced with the checkbox sound.
|
||||
function KeyBindings_ToggleSection(n)
|
||||
local sec = KeyBindingsPage.sections[n]
|
||||
if not sec then return end
|
||||
@@ -240,8 +177,7 @@
|
||||
KeyBindingsPage_Update()
|
||||
end
|
||||
|
||||
-- Expand/collapse by section index without the click voice — the capture fixture uses
|
||||
-- it to open a section for the lens.
|
||||
-- Expands or collapses a section silently; the capture fixture opens sections with it.
|
||||
function KeyBindings_ExpandSection(n, on)
|
||||
local sec = KeyBindingsPage.sections[n]
|
||||
if not sec then return end
|
||||
@@ -249,9 +185,8 @@
|
||||
KeyBindings_Flatten()
|
||||
end
|
||||
|
||||
-- ── Painters ── one row, one mode. Shared by the list repaint and the search rows
|
||||
-- (which are the SAME template painted bind-mode — the era reflows live rows into its
|
||||
-- results, and these capsules run the full capture law in place).
|
||||
-- ── Painters ── one row in one mode, shared by the list and the search rows, whose
|
||||
-- capsules capture in place as the era's live results do.
|
||||
function KeyBindings_PaintBindRow(name, index)
|
||||
local command, _, key1, key2 = GetBinding(index)
|
||||
local b1 = getglobal(name .. "Key1Button")
|
||||
@@ -265,7 +200,6 @@
|
||||
local desc = getglobal(name .. "Description")
|
||||
desc:SetText(KeyBindings_String("BINDING_NAME_" .. command, command))
|
||||
desc:Show()
|
||||
-- The 1.12 selection highlight: the pressed capsule holds its glow.
|
||||
local page = KeyBindingsPage
|
||||
if page.selected == command and page.keyID == 1 then
|
||||
b1:LockHighlight()
|
||||
@@ -320,16 +254,14 @@
|
||||
end
|
||||
FauxScrollFrame_Update(scroll, table.getn(page.items),
|
||||
KEYBINDINGS_DISPLAYED, KEYBINDINGS_ROW_HEIGHT)
|
||||
-- The trough is OURS — the reference has no trough at all, so nothing in its kit shows
|
||||
-- one. The owner pairs it with the bar the reference's `_Update` just decided about
|
||||
-- (1860); `BenillaScrollBar_SetShown` used to do it and is off this path now.
|
||||
-- The trough is ours and the stock kit never shows one, so it follows the bar
|
||||
-- `FauxScrollFrame_Update` has just shown or hidden.
|
||||
BenillaScrollTrough_Follow(scroll:GetName())
|
||||
KeyBindings_UpdateUnbindKey()
|
||||
end
|
||||
|
||||
-- The page entry (BenillaOptionsFrame_SelectCategory's Keybindings arm): rebuild the tree —
|
||||
-- the table may have moved under us (a rebind, a set switch) — sync the checkbox to
|
||||
-- the live set, park the scroll at the top, repaint.
|
||||
-- The page entry (BenillaOptionsFrame_SelectCategory): rebuilds the sections, since a
|
||||
-- rebind or a set switch may have changed the table, then repaints from the top.
|
||||
function KeyBindingsPage_Refresh()
|
||||
KeyBindings_BuildSections()
|
||||
local box = getglobal("BenillaOptionsFrameContainerBodyKeybindingsCharacterRowCheck")
|
||||
@@ -346,9 +278,9 @@
|
||||
getglobal("BenillaOptionsFrameContainerBodyKeybindingsOutput"):SetText(text)
|
||||
end
|
||||
|
||||
-- ── Selection + the capture arm ── selecting a capsule arms the HOST's capture seam
|
||||
-- (BenillaBindCapture): raw input is swallowed there and comes back canonical through
|
||||
-- KeyBindings_OnHostKey. Deselecting (either way) disarms.
|
||||
-- ── Selection ── selecting a capsule arms the host's capture (BenillaBindCapture), which
|
||||
-- swallows raw input and hands the canonical chord to KeyBindings_OnHostKey; deselecting
|
||||
-- disarms.
|
||||
function KeyBindings_SetSelected(command, keyID)
|
||||
KeyBindingsPage.selected = command
|
||||
KeyBindingsPage.keyID = keyID
|
||||
@@ -364,9 +296,8 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- Repaint whichever view currently shows binding rows: the page list, or the live
|
||||
-- search-result rows (a bind landed from one must relabel it — and its victim, which
|
||||
-- may sit on another visible row).
|
||||
-- Repaints whichever view shows binding rows, the list or every search row, since a bind
|
||||
-- from one search row can unbind a victim shown on another.
|
||||
function KeyBindings_RepaintVisible()
|
||||
if KeyBindingsPage.searchPainted then
|
||||
for name, index in pairs(KeyBindingsPage.searchPainted) do
|
||||
@@ -379,10 +310,9 @@
|
||||
end
|
||||
|
||||
function KeyBindingButton_OnClick(button)
|
||||
-- The 1.12 law: while something is selected, only LEFT/RIGHT on a capsule
|
||||
-- re-select or deselect — a middle/4/5 press is a BIND, and the host's armed
|
||||
-- capture seam (which runs after this click) takes it, so this handler must not
|
||||
-- touch the selection for those buttons.
|
||||
-- As 1.12: while armed, only a left or right click reselects or deselects. A middle,
|
||||
-- 4 or 5 press is a bind that the armed capture takes after this click, so it must
|
||||
-- leave the selection alone.
|
||||
local page = KeyBindingsPage
|
||||
if page.selected then
|
||||
if button ~= "LeftButton" and button ~= "RightButton" then
|
||||
@@ -406,15 +336,14 @@
|
||||
KeyBindings_RepaintVisible()
|
||||
end
|
||||
|
||||
-- The live-commit half of every mutation (1008's commit law — the header block): the
|
||||
-- table already moved; persist the set it belongs to.
|
||||
-- The live commit: saves the set a bind, unbind or reset has just changed.
|
||||
function KeyBindings_Commit()
|
||||
SaveBindings(GetCurrentBindingSet())
|
||||
end
|
||||
|
||||
-- The host's capture callback: `chord` is canonical (ALT-CTRL-SHIFT-TOKEN). The body is
|
||||
-- 1.12's OnKeyDown selected-branch verbatim: steal message, clear the command's keys,
|
||||
-- rebind in slot order so the pressed slot takes the new chord — then the live commit.
|
||||
-- The host's capture callback, `chord` canonical (ALT-CTRL-SHIFT-TOKEN): 1.12's OnKeyDown
|
||||
-- armed branch, then the live commit. A stolen chord names its victim in red only when it
|
||||
-- was the victim's last key; the command's keys rebind in slot order.
|
||||
function KeyBindings_OnHostKey(chord)
|
||||
local page = KeyBindingsPage
|
||||
if not page.selected then
|
||||
@@ -449,11 +378,9 @@
|
||||
KeyBindings_RepaintVisible()
|
||||
end
|
||||
|
||||
-- 1.12 KeyBindingFrame_SetBinding, kept verbatim: a refused bind restores the slot's old
|
||||
-- key and says why. The reference wrote this arm with the wheel in mind and named its
|
||||
-- string for it, but the engine refuses only a key string that is not a key
|
||||
-- (decision 1295), so this fires for a chord the capture seam could not name — never for
|
||||
-- a wheel notch. The message stays 1.12's; ours is not the place to correct Blizzard.
|
||||
-- 1.12's KeyBindingFrame_SetBinding: a refused bind restores the slot's old key. SetBinding
|
||||
-- refuses only a string that is not a key, so this fires for a chord the capture could not
|
||||
-- name, never for the wheel; the message stays 1.12's wheel text.
|
||||
function KeyBindings_SetOrRestore(key, command, oldKey)
|
||||
if not SetBinding(key, command) then
|
||||
if oldKey then SetBinding(oldKey, command) end
|
||||
@@ -463,8 +390,8 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- The Unbind Key button (the Container header band, OptionsFrame.xml): 1.12's body —
|
||||
-- the selected slot empties, the other slides into slot order — plus the live commit.
|
||||
-- The Unbind Key button (OptionsFrame.xml): 1.12's body, which empties the selected slot
|
||||
-- and slides the other into slot order, then the live commit.
|
||||
function KeyBindings_Unbind()
|
||||
local page = KeyBindingsPage
|
||||
local key1, key2 = GetBindingKey(page.selected)
|
||||
@@ -481,14 +408,12 @@
|
||||
KeyBindings_RepaintVisible()
|
||||
end
|
||||
|
||||
-- ── The character-specific set (1.12's three-set model under the era checkbox row) ──
|
||||
-- CHECK: switch to the character set and save it at once — it starts as a copy of what
|
||||
-- you had (LoadBindings(2) falls back to the account snapshot; SaveBindings(2) makes it
|
||||
-- exist, which IS the checkbox's persisted truth). UNCHECK: destructive — the era
|
||||
-- confirm-on-uncheck-only law; the box springs back until the popup decides. The accept
|
||||
-- order is load-then-save: LoadBindings(1) puts the account set live FIRST, so
|
||||
-- SaveBindings(1) persists it unchanged while dropping the character set (the confirmed
|
||||
-- permanent delete — saving live-as-account here would smuggle the character binds in).
|
||||
-- ── Character-specific set ── 1.12's binding sets under the era's checkbox. Checking
|
||||
-- saves set 2 at once, a copy of the current binds (LoadBindings(2) falls back to the
|
||||
-- account set), and that saved set is what the checkbox reads. Unchecking deletes it, so
|
||||
-- it confirms (the era confirms only on uncheck) and the box stays checked meanwhile.
|
||||
-- The accept loads set 1 before saving it; saving first would write the character binds
|
||||
-- into the account set.
|
||||
function KeyBindings_CharacterToggle(box)
|
||||
PlaySound("igMainMenuOptionCheckBoxOn")
|
||||
if box:GetChecked() then
|
||||
@@ -516,9 +441,8 @@
|
||||
KeyBindingsPage_Refresh()
|
||||
end,
|
||||
}
|
||||
-- Reset To Default — the page's Defaults button (era: Defaults confirms; the string is
|
||||
-- ours, 0997's note: the era's lives in its client binary, 1.12 has none). The reset
|
||||
-- lands in the CURRENT set and commits live like every other mutation.
|
||||
-- Reset To Default, the page's Defaults button: it confirms, as the era's does, with our
|
||||
-- own string since 1.12 has none, and resets the current set with a live commit.
|
||||
StaticPopupDialogs["CONFIRM_RESET_KEYBINDINGS"] = {
|
||||
text = "Are you sure you want to reset all keybindings to their defaults?",
|
||||
button1 = KeyBindings_String("OKAY", "Okay"),
|
||||
@@ -533,12 +457,11 @@
|
||||
end,
|
||||
}
|
||||
|
||||
-- ── Search (the era redirect law) ── every binding's search tag is its display name
|
||||
-- uppercased (era AddSearchTags(GetBindingName(action))); matches score by the shared
|
||||
-- law (OptionsSearch_Score) and surface under the Keybindings group head, whose click
|
||||
-- jumps to this page (era Blizzard_SettingsPanel.lua l.578: the fake category's results
|
||||
-- carry `redirectCategory`). The matched rows are LIVE (era reflows real initializers);
|
||||
-- OptionsFrame.xml paints them onto its static KeybindSearch row pool.
|
||||
-- ── Search ── a binding's tag is its display name uppercased (the era's
|
||||
-- AddSearchTags(GetBindingName(action))); matches list under the Keybindings group head,
|
||||
-- which opens this page (the era's `redirectCategory`, Blizzard_SettingsPanel.lua l.578).
|
||||
-- OptionsFrame.xml paints them as live rows. Deviation: a static pool of
|
||||
-- KEYBINDINGS_SEARCH_ROWS rows, where the era's results scroll.
|
||||
function KeyBindings_SearchMatches(words)
|
||||
local out = {}
|
||||
for i = 1, GetNumBindings() do
|
||||
|
||||
@@ -1,68 +1,31 @@
|
||||
<Ui>
|
||||
<!--
|
||||
benilla's **script error log** (decision 1495) — the window B293 asked for, and it is
|
||||
entirely OURS: 1.12 has no counterpart, so every name here wears the `Benilla` prefix (1254's
|
||||
rule, `tests::reference_surface`'s gate).
|
||||
|
||||
## Why it exists beside `ScriptErrors`
|
||||
|
||||
`BasicControls.xml`'s red `ScriptErrors` dialog is the reference's whole answer to a Lua
|
||||
fault, transcribed and installed by 1305 — and it stays exactly as it is. It shows **one**
|
||||
message, because `_ERRORMESSAGE` guards on `ScriptErrors:IsVisible()`, and it remembers
|
||||
nothing. Two reporters said so within days of it shipping: Goudy, *minutes after
|
||||
screenshotting that dialog working*, *"some kind of lua error frame probably needs to be
|
||||
implemented, as there are a lot of addons that still doesn't work"*; nazriel_0, *"probably we
|
||||
should start using something like Bug Grabber to report those stack traces better"*.
|
||||
|
||||
So: the dialog is the interrupt, this is the log. Additive, no divergence — the reference
|
||||
simply never had this instrument.
|
||||
|
||||
## Shape
|
||||
|
||||
`CraftFrame.xml`'s shape, which is the house idiom for "windowed panel, faux-scrolled row
|
||||
list, detail pane that repaints on selection": 13 fixed 16px rows over a
|
||||
`FauxScrollFrameTemplate`, and a **real** `<ScrollFrame>` detail pane below (the real kit,
|
||||
not the faux one — a message is a `mlua` error string with its `stack traceback:` tail
|
||||
attached, so the detail is genuinely variable-height content, which is what the real kit is
|
||||
for; `GossipFrame.xml`'s greeting pane is the same wiring).
|
||||
|
||||
The window registers itself in `UIPanelWindows` (below; the table is the stock UIParent.lua's
|
||||
since 1988), which is what seats it in the
|
||||
left slot and gets it closed by `CloseAllWindows()` — i.e. by ESC — with no code here. Our
|
||||
own toplevels deliberately stay OUT of `UISpecialFrames`: that table is the addons' (0544's
|
||||
note, 1206).
|
||||
|
||||
## Reading the model
|
||||
|
||||
Three engine reads, all from the retained log on the model
|
||||
(`benilla_ui::script::diagnostics`): `BenillaGetNumScriptErrors()` →
|
||||
`retained, totalEverRecorded`; `BenillaGetScriptErrorInfo(i)` → `seq, kind, message, count`
|
||||
(1-based, oldest first, nil past the end); `BenillaClearScriptErrors()`. There is no per-frame
|
||||
feed and nothing to keep in step — the window polls the log it is a view of.
|
||||
Benilla's script error log. 1.12 has no such window, so every name here carries the
|
||||
`Benilla` prefix (`tests::reference_surface` checks it). It sits beside the stock
|
||||
`ScriptErrors` dialog, which shows one message and keeps none: the dialog interrupts,
|
||||
this window keeps the log.
|
||||
Shaped like the reference's CraftFrame: 13 fixed 16px rows over the stock faux-scroll kit
|
||||
and a detail pane below. It registers in `UIPanelWindows`, which seats it in the left slot
|
||||
and closes it on ESC, and stays out of `UISpecialFrames`, which belongs to addons.
|
||||
The engine's log API: `BenillaGetNumScriptErrors()` returns retained and total-ever
|
||||
counts; `BenillaGetScriptErrorInfo(i)` returns `seq, kind, message, count` (1-based,
|
||||
oldest first, nil past the end); `BenillaClearScriptErrors()`. The window polls it.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- The slot-manager row (decision 1495): a left-area window one tier above every stock
|
||||
-- panel's pushable, so opening a game window pushes the log aside rather than the reverse.
|
||||
-- A left-slot window with a pushable above every stock panel's, so a game window opening
|
||||
-- pushes the log aside.
|
||||
UIPanelWindows["BenillaScriptLogFrame"] = { area = "left", pushable = 9, whileDead = 1 }
|
||||
|
||||
BENILLA_SCRIPTLOG_ROWS = 13
|
||||
BENILLA_SCRIPTLOG_ROW_HEIGHT = 16
|
||||
-- How much of a message a ROW shows. The full text is the detail pane's job; a row is an
|
||||
-- index, and a row that wraps would break the fixed-height list the faux kit requires.
|
||||
-- How much of a message a row shows; a wrapped row would break the faux kit's fixed rows.
|
||||
BENILLA_SCRIPTLOG_ROW_CHARS = 52
|
||||
|
||||
-- Which row is selected (a `seq`, not an index — indices shift when the cap evicts, and a
|
||||
-- selection that silently slides onto a different error is worse than one that goes away).
|
||||
-- The selected row's `seq`, not its index: indices shift when the cap evicts.
|
||||
BenillaScriptLogSelected = nil
|
||||
|
||||
-- The first line of a message, trimmed to a row's width. A traceback's later lines are
|
||||
-- indented continuations; on one row they read as noise.
|
||||
-- `strsub` is BYTE-indexed (Lua 5.0 strings are byte strings, and benilla keeps them that
|
||||
-- way — decision 1193), so a cut that lands inside a multi-byte character leaves half of
|
||||
-- one. Every message here is UTF-8 and ours are full of em dashes, so this was not
|
||||
-- theoretical: the log window's own repaint raised on the first warning it was ever asked
|
||||
-- to show (2135/2138). Back the cut up over any continuation byte (`10xxxxxx`, i.e.
|
||||
-- 0x80..0xBF) so it lands on a character boundary.
|
||||
-- A message's first line, trimmed to a row. `strsub` counts bytes and messages are UTF-8,
|
||||
-- so the cut backs up over continuation bytes (0x80..0xBF) onto a character boundary.
|
||||
function BenillaScriptLog_RowText(message)
|
||||
local cut = strfind(message, "\n", 1, 1)
|
||||
local head = message
|
||||
@@ -79,13 +42,10 @@
|
||||
return head
|
||||
end
|
||||
|
||||
-- The three kinds, coloured by CONSEQUENCE — how much of the addon is broken:
|
||||
-- `load` red an addon is not running at all (the thing a player can act on)
|
||||
-- Coloured by how much of the addon is broken:
|
||||
-- `load` red the addon is not running at all
|
||||
-- `error` amber code ran and raised; the addon is otherwise up
|
||||
-- `warn` grey a call was accepted and quietly did not do what it said (2135)
|
||||
-- Grey rather than a fourth bright colour because a warning is the least urgent of the
|
||||
-- three and there are more of them: colour has to keep ranking the list, not just label
|
||||
-- it. Colours come from the shipped tables so this window matches the rest of the UI.
|
||||
-- `warn` grey a call was accepted and quietly did not do what it said
|
||||
function BenillaScriptLog_RowColor(kind)
|
||||
if kind == "load" then return 1.0, 0.3, 0.3 end
|
||||
if kind == "warn" then return 0.65, 0.65, 0.65 end
|
||||
@@ -95,12 +55,8 @@
|
||||
function BenillaScriptLog_Update()
|
||||
local shown, total = BenillaGetNumScriptErrors()
|
||||
|
||||
-- The summary line carries the one number a bug report needs and the window cannot
|
||||
-- otherwise show: how many were DROPPED. `total` counts distinct failures ever
|
||||
-- recorded; `shown` is what survived the cap.
|
||||
-- "problems", not "errors": the log carries three kinds now and two of them are not
|
||||
-- errors (2135). The window is still `/errors` and still opens on the same key —
|
||||
-- what changed is that a count of 7 stopped meaning "7 things raised".
|
||||
-- `total` counts distinct failures ever recorded, `shown` what the cap kept. The
|
||||
-- summary says "problems" because two of the three kinds are not errors.
|
||||
local summary
|
||||
if total == 0 then
|
||||
summary = "No problems this session."
|
||||
@@ -126,9 +82,7 @@
|
||||
if seq then
|
||||
button.seq = seq
|
||||
button.message = message
|
||||
-- The count rides the index, not the text: `#12 x1113` says "this is one
|
||||
-- failure that happened 1,113 times", which is the whole reason the log
|
||||
-- deduplicates instead of listing 1,113 rows.
|
||||
-- `#12 x1113`: one deduplicated failure that happened 1,113 times.
|
||||
local label = "#" .. seq
|
||||
if count > 1 then label = label .. " x" .. count end
|
||||
button.index:SetText(label)
|
||||
@@ -143,9 +97,8 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- The moving highlight, seated onto the selected row only when that row is on screen —
|
||||
-- scrolling the selection away hides the glow rather than stranding it (CraftFrame's
|
||||
-- own behaviour).
|
||||
-- The highlight follows the selected row and hides when it scrolls away, as the
|
||||
-- reference's CraftFrame does.
|
||||
local highlight = getglobal("BenillaScriptLogHighlightFrame")
|
||||
if selectedRow then
|
||||
highlight:SetPoint("TOPLEFT", selectedRow:GetName(), "TOPLEFT", 0, 0)
|
||||
@@ -157,8 +110,8 @@
|
||||
BenillaScriptLog_UpdateDetail()
|
||||
end
|
||||
|
||||
-- The detail pane: the FULL message of the selected row, traceback and all. Selection is by
|
||||
-- `seq`, so this walks the log rather than indexing it.
|
||||
-- The selected message in full, traceback and all; selection is a `seq`, so this walks
|
||||
-- the log rather than indexing it.
|
||||
function BenillaScriptLog_UpdateDetail()
|
||||
local text = ""
|
||||
if BenillaScriptLogSelected then
|
||||
@@ -177,18 +130,14 @@
|
||||
getglobal("BenillaScriptLogDetailText"):SetText(text)
|
||||
end
|
||||
|
||||
-- Frame-late by nature (the shared kit's own note): text rects resolve after Lua runs, so
|
||||
-- the child is re-measured from OnUpdate until it stops changing, after which this is a
|
||||
-- no-op compare per frame. OnUpdate only fires on visible frames, so a closed window costs
|
||||
-- nothing.
|
||||
-- Text rects resolve after Lua runs, so the detail child is re-measured every frame; once
|
||||
-- it settles that is one compare, and OnUpdate fires only while the window is shown.
|
||||
function BenillaScriptLogFrame_OnUpdate()
|
||||
BenillaScroll_ResizeChild("BenillaScriptLogDetailScroll", { "BenillaScriptLogDetailText" })
|
||||
end
|
||||
|
||||
function BenillaScriptLogRow_OnClick(button)
|
||||
if not button.seq then return end
|
||||
-- Clicking the selected row deselects: the detail pane is a drawer, and a list with no
|
||||
-- way back to "nothing selected" is a small trap.
|
||||
if BenillaScriptLogSelected == button.seq then
|
||||
BenillaScriptLogSelected = nil
|
||||
else
|
||||
@@ -198,20 +147,17 @@
|
||||
end
|
||||
|
||||
function BenillaScriptLogFrame_OnLoad(self)
|
||||
-- The ring's icon, through the portrait verb rather than left at its `file=` — see the
|
||||
-- texture's own note. MailFrame.xml's open-letter icon is the same fix.
|
||||
-- Masks the square icon to the portrait ring.
|
||||
SetPortraitToTexture("BenillaScriptLogFramePortrait",
|
||||
"Interface\\Icons\\INV_Misc_Note_01")
|
||||
end
|
||||
|
||||
-- The shared helper steps the faux frame's bar, whose OnValueChanged runs the kit's
|
||||
-- offset handler and thence `updateFunc` — so the repaint is wired once, at OnLoad, and
|
||||
-- not repeated here.
|
||||
-- Stepping the bar repaints through the list frame's OnVerticalScroll, so no repaint here.
|
||||
function BenillaScriptLogFrame_OnMouseWheel(delta)
|
||||
BenillaFauxScrollFrame_OnMouseWheel(getglobal("BenillaScriptLogListScrollFrame"), delta)
|
||||
end
|
||||
|
||||
-- ── The three entry points the client and the player reach this by ────────────────────
|
||||
-- ── Entry points ──────────────────────────────────────────────────────────────────────
|
||||
|
||||
function BenillaScriptLog_Toggle()
|
||||
local frame = getglobal("BenillaScriptLogFrame")
|
||||
@@ -222,10 +168,8 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- `/errors clear`. Forgets the rows, keeps the numbering (the engine's own rule — a number
|
||||
-- must never name two different failures across a screenshot). Prints a confirmation
|
||||
-- because a command whose whole effect is "a window you may not have open is now empty"
|
||||
-- is otherwise indistinguishable from a typo.
|
||||
-- `/errors clear`: forgets the rows but keeps the numbering, so a number never names two
|
||||
-- failures. It confirms in chat because the window may be closed.
|
||||
function BenillaScriptLog_Clear()
|
||||
BenillaClearScriptErrors()
|
||||
BenillaScriptLogSelected = nil
|
||||
@@ -236,9 +180,8 @@
|
||||
end
|
||||
]]></Script>
|
||||
|
||||
<!-- One list row: the `#N xCount` index column, then the message head. Fixed columns, 1.12's
|
||||
own list idiom — not a width limitation: a FontString with a zero-authored axis takes its
|
||||
measured box into the resolve (`benilla-ui` `script/layout.rs`, decision 1664). -->
|
||||
<!-- One list row: the `#N xCount` index column, then the message head, in fixed columns as
|
||||
1.12's lists are. -->
|
||||
<Button name="BenillaScriptLogRowTemplate" virtual="true" hidden="true">
|
||||
<Size><AbsDimension x="293" y="16"/></Size>
|
||||
<Layers>
|
||||
@@ -268,18 +211,15 @@
|
||||
<Size><AbsDimension x="384" y="512"/></Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<!-- The same square ITEM icon the mail window's ring holds, and it needs the same
|
||||
portrait verb for the same reason (`SetPortraitToTexture` in OnLoad below):
|
||||
`file=` alone draws it unmasked and its corners show through the ring's
|
||||
transparent ones. -->
|
||||
<!-- A square item icon: OnLoad masks it with `SetPortraitToTexture`, since `file=`
|
||||
alone shows its corners through the ring. -->
|
||||
<Texture name="BenillaScriptLogFramePortrait" file="Interface\Icons\INV_Misc_Note_01">
|
||||
<Size><AbsDimension x="60" y="60"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT"><Offset><AbsDimension x="7" y="-6"/></Offset></Anchor></Anchors>
|
||||
</Texture>
|
||||
</Layer>
|
||||
<!-- The four quadrant slabs. The ClassTrainer family, all four, because it is the one
|
||||
UNIFORM set in the 1.12 art (CraftFrame's own note) and this window belongs to no
|
||||
profession — borrowing a themed half would make it look like a window it isn't. -->
|
||||
<!-- The ClassTrainer quadrants, the one uniform set in the 1.12 art; this window
|
||||
belongs to no profession. -->
|
||||
<Layer level="BORDER">
|
||||
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopLeft"><Size><AbsDimension x="256" y="256"/></Size><Anchors><Anchor point="TOPLEFT"/></Anchors></Texture>
|
||||
<Texture file="Interface\ClassTrainerFrame\UI-ClassTrainer-TopRight"><Size><AbsDimension x="128" y="256"/></Size><Anchors><Anchor point="TOPRIGHT"/></Anchors></Texture>
|
||||
@@ -288,22 +228,18 @@
|
||||
<FontString name="BenillaScriptLogTitle" inherits="GameFontNormal" justifyH="CENTER" text="Script Errors">
|
||||
<Anchors><Anchor point="TOP" relativePoint="TOP"><Offset><AbsDimension x="0" y="-17"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
<!-- The summary line, and the ONLY place the dropped-row count can be told. -->
|
||||
<!-- The summary line, the only place the dropped-row count shows. -->
|
||||
<FontString name="BenillaScriptLogSummary" inherits="GameFontHighlightSmall" justifyH="LEFT" text="">
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="26" y="-76"/></Offset></Anchor></Anchors>
|
||||
</FontString>
|
||||
</Layer>
|
||||
</Layers>
|
||||
<Frames>
|
||||
<!-- The top-right X: the chain's own `UIPanelCloseButton` (seated at benilla.toc's
|
||||
`Interface\FrameXML\UIPanelTemplates.xml`, 757 lines above this file). Its
|
||||
32x32, its three minimize-button state textures and its
|
||||
`HideUIPanel(this:GetParent())` are byte-for-byte what stood here authored. -->
|
||||
<Button name="BenillaScriptLogCloseButton" inherits="UIPanelCloseButton">
|
||||
<Anchors><Anchor point="TOPRIGHT"><Offset><AbsDimension x="-29" y="-8"/></Offset></Anchor></Anchors>
|
||||
</Button>
|
||||
|
||||
<!-- Declared BEFORE the rows so row text paints over the glow (the house z-tiebreak). -->
|
||||
<!-- Declared before the rows so their text draws over the glow. -->
|
||||
<Frame name="BenillaScriptLogHighlightFrame" hidden="true">
|
||||
<Size><AbsDimension x="293" y="16"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
|
||||
@@ -335,16 +271,14 @@
|
||||
<Size><AbsDimension x="293" y="208"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-94"/></Offset></Anchor></Anchors>
|
||||
<Scripts>
|
||||
<!-- The reference's kit writes `frame.offset` ONLY from here; its
|
||||
`updateFunc` field does not exist, so without this the bar moves and
|
||||
the list does not follow (1868). -->
|
||||
<!-- The stock kit sets the offset only from here and has no `updateFunc`, so
|
||||
the repaint is wired here too. -->
|
||||
<OnVerticalScroll>FauxScrollFrame_OnVerticalScroll(BENILLA_SCRIPTLOG_ROW_HEIGHT, BenillaScriptLog_Update)</OnVerticalScroll>
|
||||
</Scripts>
|
||||
</ScrollFrame>
|
||||
|
||||
<!-- The detail pane: the REAL scroll kit, because a message carries its `stack
|
||||
traceback:` tail and is genuinely variable-height. GossipFrame's greeting pane is
|
||||
the same wiring. -->
|
||||
<!-- The detail pane, on the real-scroll kit: a message with its `stack traceback:` tail
|
||||
has variable height. -->
|
||||
<ScrollFrame name="BenillaScriptLogDetailScroll">
|
||||
<Size><AbsDimension x="293" y="128"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="22" y="-314"/></Offset></Anchor></Anchors>
|
||||
@@ -367,8 +301,7 @@
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"/></Anchors>
|
||||
<Layers>
|
||||
<Layer level="ARTWORK">
|
||||
<!-- 270 wide so the engine wraps the traceback rather than clipping
|
||||
it; the pane pans over whatever height that comes to. -->
|
||||
<!-- 270 wide so the traceback wraps rather than clips. -->
|
||||
<FontString name="BenillaScriptLogDetailText" inherits="GameFontHighlightSmall" justifyH="LEFT" justifyV="TOP" text="">
|
||||
<Size><AbsDimension x="270" y="0"/></Size>
|
||||
<Anchors><Anchor point="TOPLEFT" relativePoint="TOPLEFT"><Offset><AbsDimension x="6" y="-6"/></Offset></Anchor></Anchors>
|
||||
@@ -392,18 +325,16 @@
|
||||
</Frames>
|
||||
<Scripts>
|
||||
<OnLoad>BenillaScriptLogFrame_OnLoad(self)</OnLoad>
|
||||
<!-- The list wheel lives on the WINDOW: the engine bubbles a spin up the parent chain
|
||||
(CraftFrame's own note). The detail pane has its own, which wins over its own rect. -->
|
||||
<!-- The list's wheel handler is on the window, since a spin bubbles up the parent
|
||||
chain; the detail pane's own handler wins over its rect. -->
|
||||
<OnMouseWheel>BenillaScriptLogFrame_OnMouseWheel(arg1)</OnMouseWheel>
|
||||
<OnShow>BenillaScriptLog_Update()</OnShow>
|
||||
<OnUpdate>BenillaScriptLogFrame_OnUpdate()</OnUpdate>
|
||||
</Scripts>
|
||||
</Frame>
|
||||
<!-- benilla's own commands over the reference's SlashCmdList (decision 1948). The stock
|
||||
ChatEdit_ParseText walks SlashCmdList after its own built-ins, so each of benilla's commands
|
||||
(ui_chat::commands — the dev instruments and the three additions) hands its line to
|
||||
SubmitChatInput, where ui_chat::input's parser still owns it. This file loads after the
|
||||
reference's ChatFrame.xml, which is what declares the table. -->
|
||||
<!-- Benilla's slash commands in the stock SlashCmdList, which ChatEdit_ParseText walks after
|
||||
its built-ins; each hands its line to SubmitChatInput, where ui_chat::input parses it.
|
||||
This file loads after the stock ChatFrame.xml, which declares the table. -->
|
||||
<Script><![CDATA[
|
||||
local BENILLA_COMMANDS = {
|
||||
{ "CHATTEST", "/chattest" },
|
||||
|
||||
@@ -1,54 +1,30 @@
|
||||
<Ui>
|
||||
<!--
|
||||
benilla's reusable scroll kits — TWO of them, sharing one scrollbar, one pair of arrow
|
||||
helpers and one optional TROUGH (`BenillaScrollTroughTemplate`, at the foot: the recessed
|
||||
channel a bar rides in — 1.12 authors that art per window, so an owner declares one beside
|
||||
its bar and the kit shows and hides the two together).
|
||||
The **faux-scroll** kit (this header) re-binds a fixed set of rows to a moving data
|
||||
offset; the **real-scroll** kit (second half of the script block) pans and CLIPS a
|
||||
variable-height child through the engine's `<ScrollFrame>` (decision 0112) — the one a gossip
|
||||
menu of wrapped options needs, since its content height isn't known until it is laid out.
|
||||
Only their OnValueChanged differs, and an instance overrides the template's (the loader
|
||||
SetScripts every `<Scripts>` block in order, so the instance's handler wins).
|
||||
|
||||
The faux kit (decisions 0247/0250/0251): a draggable scroll bar over
|
||||
the `Slider` primitive (0250) plus the fixed-row "faux scroll" Lua layer — the reference's
|
||||
`FauxScrollFrame`, which never really scrolls a child: it re-binds a fixed set of row buttons to
|
||||
a moving data offset. First customer: the class trainer's skill-line list (TrainerFrame.xml).
|
||||
The quest log's chunk-local faux-scroll upvalue is a later migration onto this.
|
||||
|
||||
DESIGN — collapsed from the real client's three-part decomposition (0250: FauxScrollFrame =
|
||||
ScrollFrame + Slider + Lua). The real `FauxScrollFrameTemplate` routes the scrollbar's value
|
||||
through its parent `ScrollFrame` (`OnValueChanged → parent:SetVerticalScroll(v)`; the
|
||||
ScrollFrame's `OnVerticalScroll` then derives `offset = floor(v/itemHeight + 0.5)`). That
|
||||
ScrollFrame adds nothing to the value on its way through: 0251 believed it clamped to the
|
||||
scroll range and called the clamp a liability, and decision 2017 read the bytes — the
|
||||
reference's `SetVerticalScroll` stores what the bar hands it. So the relay is a pure
|
||||
pass-through, and benilla's faux frame is a plain `<Frame>` + a `Slider`: fewer moving parts
|
||||
and the identical on-screen result (a smooth, pixel-granularity thumb whose position maps
|
||||
1:1 to the scroll offset). See 0251 for the full rationale + the divergence from 0250's
|
||||
described (real-client) shape.
|
||||
|
||||
VALUE MODEL. The scrollbar's value is in PIXELS: `[0, (numItems − numToDisplay) * itemHeight]`,
|
||||
`valueStep = itemHeight`. The row offset is `floor(value / itemHeight + 0.5)` — sub-pixel
|
||||
rounding, so the thumb (drawn at the raw value fraction) and the list (drawn at the rounded
|
||||
offset) never visibly disagree. The frame carries `frame.offset` (the top visible row, 0-based)
|
||||
and `frame.updateFunc` (the owner's list-repaint, set by the owner's OnLoad).
|
||||
Benilla's scroll kits, sharing one `Slider` bar, its arrow helpers and an optional trough:
|
||||
a faux-scroll kit that re-binds a fixed set of rows to a moving data offset, and a
|
||||
real-scroll kit that pans and clips a `<ScrollFrame>` child whose height is known only after
|
||||
layout. A real-scroll bar overrides the template's OnValueChanged on its instance, which
|
||||
wins because the loader runs every `<Scripts>` block in order.
|
||||
Faux model: the value is pixels, `[0, (numItems - numToDisplay) * itemHeight]` in steps of
|
||||
`itemHeight`; the parent holds the 0-based top row in `frame.offset` and its repaint in
|
||||
`frame.updateFunc`, set by the owner's OnLoad.
|
||||
Deviation: the faux parent is a plain `<Frame>`, not the reference's ScrollFrame relay,
|
||||
which adds nothing because the reference's `SetVerticalScroll` stores the bar's value as is.
|
||||
`FauxScrollFrameTemplate` and the `FauxScrollFrame_*`, `ScrollFrame_*` and
|
||||
`ScrollingEdit_*` API are the stock `UIPanelTemplates` on the player's chain, which loads
|
||||
after this file, so its declarations win.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
-- Step the bar one row (the slider's valueStep) — the sensible step for an item list (the
|
||||
-- generic ref scrollbar steps half its height; a discrete row list wants one). Silent by
|
||||
-- design: the ref plays UChatScrollButton only on the arrow *buttons'* OnClick, never on the
|
||||
-- wheel (ScrollFrameTemplate_OnMouseWheel is soundless). Both the wheel and the arrows call
|
||||
-- this, so the click lives on the arrow OnClick, keeping wheel-scroll silent.
|
||||
-- Steps the bar by its valueStep. Deviation: one row on a faux list, where the reference
|
||||
-- bar's arrows step half its height. Soundless: the reference plays UChatScrollButton on
|
||||
-- an arrow click, never on the wheel, so the arrows' OnClick plays it.
|
||||
function BenillaScrollBar_Step(scrollBar, dir)
|
||||
local step = scrollBar:GetValueStep()
|
||||
if step <= 0 then step = 1 end
|
||||
scrollBar:SetValue(scrollBar:GetValue() + dir * step)
|
||||
end
|
||||
|
||||
-- Grey the up arrow at the top and the down arrow at the bottom (ref
|
||||
-- ScrollFrame_OnScrollRangeChanged's arrow handling), from the slider's own value/range.
|
||||
-- Greys each arrow at its end of the range (reference: ScrollFrame_OnScrollRangeChanged).
|
||||
function BenillaScrollBar_UpdateArrows(scrollBar)
|
||||
local minV, maxV = scrollBar:GetMinMaxValues()
|
||||
local val = scrollBar:GetValue()
|
||||
@@ -58,29 +34,11 @@
|
||||
if val >= maxV then down:Disable() else down:Enable() end
|
||||
end
|
||||
|
||||
-- The scrollbar's OnValueChanged: derive the row offset from the (pixel) value and, only when
|
||||
-- it actually moved, repaint the owner's list. The change-gate is load-bearing: the owner's
|
||||
-- repaint calls _Update below, which SetValue()s the thumb back to offset*itemHeight — a value
|
||||
-- that maps to the SAME offset, so this handler no-ops and the cycle stops after one hop (the
|
||||
-- Slider's own SetValue change-gate stops the value side, decision 0250).
|
||||
--
|
||||
-- THE SECOND HALF IS FOR ADDONS, and it is the reference's own wiring. The ref's
|
||||
-- `UIPanelScrollBarTemplate` routes its value through the ScrollFrame it rides
|
||||
-- (`<OnValueChanged>this:GetParent():SetVerticalScroll(arg1)</OnValueChanged>`,
|
||||
-- ref-UIPanelTemplates.xml l.156-158), and that frame's `<OnVerticalScroll>` is where an
|
||||
-- addon puts `FauxScrollFrame_OnVerticalScroll(itemHeight, MyUpdate)`. Our OWN faux frames
|
||||
-- are plain `<Frame>`s (the 0251 divergence in the header above), so they have no
|
||||
-- SetVerticalScroll and this line never fires for them — but an addon's is declared
|
||||
-- `<ScrollFrame ... inherits="FauxScrollFrameTemplate">`, and the instance's own tag wins
|
||||
-- over the template's on inherit (framexml.rs `merge`), so it really is one. Duck-typed for
|
||||
-- exactly that reason.
|
||||
--
|
||||
-- The engine's SetVerticalScroll takes the bar's value as given (decision 2017 — the
|
||||
-- reference's never reads the scroll range), so an addon frame's `<OnVerticalScroll>`
|
||||
-- fires with this very value and its `FauxScrollFrame_OnVerticalScroll` derives the offset
|
||||
-- from it, the reference's path end to end. The offset our own kit computes above is the
|
||||
-- same number by construction — it comes straight off the Slider, which is the whole point
|
||||
-- of 0251.
|
||||
-- The faux bar's OnValueChanged: the top row is the value rounded to the nearest row, so
|
||||
-- thumb and rows agree, and `updateFunc` runs only when that row changes, which also ends
|
||||
-- the loop when a repaint re-seats the thumb.
|
||||
-- A ScrollFrame parent also gets the value through SetVerticalScroll, as the reference's
|
||||
-- `UIPanelScrollBarTemplate` routes it (UIPanelTemplates.xml l.156-158).
|
||||
function BenillaFauxScrollBar_OnValueChanged(scrollBar, value)
|
||||
local frame = scrollBar:GetParent()
|
||||
local step = scrollBar:GetValueStep()
|
||||
@@ -94,27 +52,10 @@
|
||||
if frame.SetVerticalScroll then frame:SetVerticalScroll(value) end
|
||||
end
|
||||
|
||||
-- Seat a trough ON ITS BAR. THE SOCKETS, and why this is not a free choice: the channel art
|
||||
-- is not a plain tube — each end carries a 16-tall SOCKET that the arrow button drops into,
|
||||
-- bounded by the frame's outer cap and an inner crossbar. Measured texel-exact off
|
||||
-- UI-Character-ScrollBar (64x256):
|
||||
-- top piece (left column) : rows 0-3 CAP · rows 4-19 SOCKET · rows 20-21 crossbar
|
||||
-- bottom piece (right column): rows 85-86 crossbar · rows 87-102 SOCKET · rows 104-106 CAP
|
||||
-- Both pieces draw 1:1 anchored to their own end of the trough, and the arrow buttons sit 16
|
||||
-- outside the slider's ends — so a socket lands on its arrow only when the trough OVERHANGS
|
||||
-- the bar by 21 above and 20 below (4 rows of cap at the top against 3+1 at the bottom; that
|
||||
-- 1 is the whole asymmetry). Those are the ref's own numbers, reached from the other side:
|
||||
-- ReputationFrame hangs its trough at the scroll frame's TOPRIGHT (-2,+5) / BOTTOMRIGHT
|
||||
-- (-2,-4) against a bar seated (6,-16)/(6,+16) — 21, 20, and the 8-left hang
|
||||
-- (ref-ReputationFrame.xml l.573-599 against ref-UIPanelTemplates.xml l.166-181). Miss it by
|
||||
-- even 4 and the arrows ride OUT of their sockets onto the caps with a sliver of bare socket
|
||||
-- showing beside them, which is exactly what B224 reported.
|
||||
--
|
||||
-- Seated HERE, off the bar, rather than restated in each owner's XML: it is one law, and an
|
||||
-- owner made to re-derive it in offsets is an owner that can get it wrong — which is how
|
||||
-- B224 happened. So a trough carries NO anchors of its own; declare it before the bar (so it
|
||||
-- draws beneath) and name it `<barname>Trough`, and that is the whole contract. Lazy,
|
||||
-- because the bar is declared after the trough and does not exist at the trough's OnLoad.
|
||||
-- Seats a trough on its bar with the reference's ReputationFrame hang, 8 left, 21 above and
|
||||
-- 20 below (ReputationFrame.xml l.573-599 against UIPanelTemplates.xml l.166-181), which
|
||||
-- puts each art socket on its arrow button. Lazy, because the bar is declared after its
|
||||
-- trough and does not exist at the trough's OnLoad.
|
||||
function BenillaScrollTrough_Seat(trough, scrollBar)
|
||||
if trough.seated then return end
|
||||
trough.seated = true
|
||||
@@ -123,19 +64,9 @@
|
||||
trough:SetPoint("BOTTOMRIGHT", scrollBar, "BOTTOMRIGHT", 7, -20)
|
||||
end
|
||||
|
||||
-- Show/hide a bar TOGETHER WITH ITS TROUGH — the recessed channel it rides in, if the
|
||||
-- owner authored one beside it (a `<barname>Trough` frame on BenillaScrollTroughTemplate
|
||||
-- below). Every path that shows or hides a bar goes through here, so a window that fits
|
||||
-- shows neither and one that overflows shows both — and so a trough is seated on its bar
|
||||
-- before it can ever be visible, whichever way the first call goes.
|
||||
--
|
||||
-- Why the trough is OPT-IN rather than part of the bar template: 1.12 authors trough art
|
||||
-- PER WINDOW, never in the shared scroll kit — `UIPanelScrollBarTemplate` carries only the
|
||||
-- two arrow buttons and the knob, and the ref's FrameXML then hangs loose trough textures
|
||||
-- beside 29 individual bars, in two different skins (UI-Character-ScrollBar for the
|
||||
-- dialog/paperdoll family, UI-ClassTrainer-ScrollBar for the trainer family). The era's own
|
||||
-- MinimalScrollBar went the other way and made its Track part of the bar; this pair keeps
|
||||
-- the era's COUPLING (track lives and dies with the bar) on 1.12's per-window SKIN.
|
||||
-- Shows or hides a bar and its `<barname>Trough` together, seating the trough first; show
|
||||
-- and hide a bar only through here. The trough is opt-in because 1.12 draws trough art per
|
||||
-- window: `UIPanelScrollBarTemplate` carries only the arrows and the knob.
|
||||
function BenillaScrollBar_SetShown(scrollBar, shown)
|
||||
local trough = getglobal(scrollBar:GetName() .. "Trough")
|
||||
if trough then BenillaScrollTrough_Seat(trough, scrollBar) end
|
||||
@@ -148,17 +79,9 @@
|
||||
end
|
||||
end
|
||||
|
||||
-- **Our trough, following the REFERENCE's bar.** `BenillaScrollBar_SetShown` above is the
|
||||
-- Benilla kit's own pairing and nothing in the reference's faux kit calls it — the
|
||||
-- reference has no trough at all, which is exactly why this template stays ours (1837's
|
||||
-- manifest note). Since the faux frames took the chain's `FauxScrollFrameTemplate` their
|
||||
-- bar is `UIPanelScrollBarTemplate`, so an owner that wants its trough asks for it here,
|
||||
-- right after its `FauxScrollFrame_Update`. Seating is idempotent and the visibility
|
||||
-- simply mirrors the bar's, which the reference's `_Update` has already decided by
|
||||
-- showing or hiding the scroll FRAME.
|
||||
--
|
||||
-- Decision 1860. Named after the bar rather than the frame because the trough is declared
|
||||
-- `<barname>Trough` — one law, stated once, exactly as BenillaScrollTrough_Seat wants it.
|
||||
-- The same for a stock `FauxScrollFrameTemplate` frame, whose bar is the reference's: seats
|
||||
-- `<frame>ScrollBarTrough` and mirrors the frame's shown state. Call it after
|
||||
-- `FauxScrollFrame_Update`, which shows or hides the frame.
|
||||
function BenillaScrollTrough_Follow(scrollFrameName)
|
||||
local sf = getglobal(scrollFrameName)
|
||||
if not sf then return end
|
||||
@@ -170,45 +93,30 @@
|
||||
if sf:IsShown() then trough:Show() else trough:Hide() end
|
||||
end
|
||||
|
||||
-- A wheel spin over the list → one row (WoW convention: +1 up, −1 down). Called from the
|
||||
-- owner window's OnMouseWheel (the engine bubbles the spin up the parent chain, so the handler
|
||||
-- lives on the window, not a child — TrainerFrame.xml's own note).
|
||||
--
|
||||
-- KEEPS the `Benilla` prefix on purpose: the reference has no faux-scroll wheel function to
|
||||
-- take the name of. Its `ScrollFrameTemplate_OnMouseWheel` is the REAL-scroll one and moves
|
||||
-- by half the bar's height; its faux owners each write a wheel body of their own inline.
|
||||
-- One row per wheel notch (+1 up) on a faux list, called from the owner window's
|
||||
-- OnMouseWheel since the spin bubbles up the parent chain. Benilla-named: the reference has
|
||||
-- no faux wheel function (its `ScrollFrameTemplate_OnMouseWheel` moves half the bar).
|
||||
function BenillaFauxScrollFrame_OnMouseWheel(frame, delta)
|
||||
local scrollBar = getglobal(frame:GetName() .. "ScrollBar")
|
||||
BenillaScrollBar_Step(scrollBar, -delta)
|
||||
end
|
||||
|
||||
-- ══════════════════════════ The REAL-scroll kit ══════════════════════════════════════════
|
||||
--
|
||||
-- The other half of the file: a true `<ScrollFrame>` (engine mechanism, decision 0112) that
|
||||
-- pans and CLIPS a variable-height child, as against the faux kit above (fixed rows re-bound
|
||||
-- to a data offset). Windows whose content height is unknown until it is laid out — a gossip
|
||||
-- menu of wrapped options, a quest's body text — need this one; a uniform list wants the faux.
|
||||
--
|
||||
-- SHAPE, from the reference `UIPanelScrollFrameTemplate` (Interface\FrameXML\
|
||||
-- UIPanelTemplates.xml l.166-211, extracted via mpqx): the scrollbar is a SLIDER that rides
|
||||
-- its ScrollFrame as a child, so `bar:GetParent()` IS the frame it scrolls — no name
|
||||
-- derivation, and `$parentBar` gives SyncBar its handle. The ref's OnVerticalScroll drives
|
||||
-- the bar's value and greys the two arrows at the ends; ours does the same through the
|
||||
-- shared arrow helper above.
|
||||
BENILLA_SCROLL_STEP = 20 -- px per arrow click / wheel notch
|
||||
BENILLA_SCROLL_PAD = 20 -- breathing room under the last element in the child
|
||||
-- ══════════════════════════ The real-scroll kit ══════════════════════════════════════════
|
||||
-- Shaped on the reference `UIPanelScrollFrameTemplate` (UIPanelTemplates.xml l.166-211):
|
||||
-- the bar is a `Slider` child, `$parentBar`, so `bar:GetParent()` is the frame it pans.
|
||||
BENILLA_SCROLL_STEP = 20 -- px per arrow click or wheel notch
|
||||
BENILLA_SCROLL_PAD = 20 -- px of room below the child's last element
|
||||
|
||||
-- The bar moved (drag, arrow, or SyncBar): pan the frame it rides. The loop this could form
|
||||
-- (bar → scroll → OnVerticalScroll → SyncBar → bar) dead-ends in one hop — SyncBar writes the
|
||||
-- value the bar already holds, and the Slider's SetValue change-gate (decision 0250) drops it.
|
||||
-- Pans the frame the bar rides. The loop back through OnVerticalScroll and SyncBar ends in
|
||||
-- one hop: SyncBar writes the value the bar already holds, which Slider:SetValue ignores.
|
||||
function BenillaScrollBar_OnValueChanged(bar, value)
|
||||
bar:GetParent():SetVerticalScroll(value)
|
||||
BenillaScrollBar_UpdateArrows(bar)
|
||||
end
|
||||
|
||||
-- Reconcile a ScrollFrame's bar to its live range (ref ScrollFrame_OnScrollRangeChanged):
|
||||
-- the bar spans [0, range] in pixels, seats at the current scroll, and hides entirely when
|
||||
-- the content fits. Wired to both OnScrollRangeChanged and OnVerticalScroll by the owner.
|
||||
-- Fits the bar to the frame's live range, [0, range] px, and hides it when the content fits
|
||||
-- (reference: ScrollFrame_OnScrollRangeChanged). Owners call it from OnScrollRangeChanged
|
||||
-- and OnVerticalScroll.
|
||||
function BenillaScroll_SyncBar(sf)
|
||||
local bar = getglobal(sf:GetName() .. "Bar")
|
||||
if not bar then return end
|
||||
@@ -220,10 +128,8 @@
|
||||
BenillaScrollBar_SetShown(bar, range > 0)
|
||||
end
|
||||
|
||||
-- Pan by `delta` px, clamped to [0, range] HERE: the engine's SetVerticalScroll stores
|
||||
-- what it is given (decision 2017 — the reference's never reads the range), and the
|
||||
-- reference's own wheel handler bounds itself the same way, through its bar's [min, max]
|
||||
-- (ScrollFrameTemplate_OnMouseWheel → scrollBar:SetValue).
|
||||
-- Pans by `delta` px, clamped here: SetVerticalScroll stores any value, as the reference's
|
||||
-- does; the reference's ScrollFrameTemplate_OnMouseWheel clamps through its bar's range.
|
||||
function BenillaScroll_Step(sf, delta)
|
||||
local target = sf:GetVerticalScroll() + delta
|
||||
local range = sf:GetVerticalScrollRange()
|
||||
@@ -233,13 +139,12 @@
|
||||
BenillaScroll_SyncBar(sf)
|
||||
end
|
||||
|
||||
-- WoW wheel convention: +1 per notch up, -1 down — up lifts the content.
|
||||
-- The wheel's delta is +1 per notch up, and up scrolls toward the top.
|
||||
function BenillaScroll_OnMouseWheel(sf, delta)
|
||||
BenillaScroll_Step(sf, -delta * BENILLA_SCROLL_STEP)
|
||||
end
|
||||
|
||||
-- A widget's resolved bottom edge for the content sum — nil when it isn't contributing
|
||||
-- (hidden frame / blank FontString / not yet resolved this frame).
|
||||
-- A widget's resolved bottom, or nil if hidden, a blank FontString, or not laid out yet.
|
||||
function BenillaScroll_WidgetBottom(w)
|
||||
if not w then return nil end
|
||||
if w.IsShown then
|
||||
@@ -251,19 +156,13 @@
|
||||
return w:GetBottom()
|
||||
end
|
||||
|
||||
-- Size a scroll child to its MEASURED content — the resolved bottom of the lowest visible
|
||||
-- widget, plus padding — instead of a fixed budget, and tell the frame (which fires
|
||||
-- OnScrollRangeChanged → the owner's SyncBar). Frame-late by nature: rects resolve after Lua
|
||||
-- runs, so owners re-run this from an OnUpdate until it stops changing. Returns true when it
|
||||
-- actually resized (only a real change writes: SetHeight invalidates layout, and an
|
||||
-- every-frame write would fight the resolver).
|
||||
-- Sizes the scroll child to its lowest visible widget plus BENILLA_SCROLL_PAD and updates
|
||||
-- the frame's range. Rects resolve after Lua runs, so owners re-run it from OnUpdate until
|
||||
-- it returns false; it writes only on a real change, since SetHeight invalidates layout.
|
||||
function BenillaScroll_ResizeChild(scrollName, widgets)
|
||||
local sf = getglobal(scrollName)
|
||||
-- ASK THE FRAME, don't guess a name. This used to be
|
||||
-- `getglobal(scrollName .. "Child")`, which only ever worked because every caller wore
|
||||
-- a private name in that exact shape — the reference's own convention is
|
||||
-- `<X>ScrollFrame` / `<X>ScrollChildFrame`, not a suffix, so the derivation broke the
|
||||
-- moment QuestFrame took the reference's names. A ScrollFrame knows its own child.
|
||||
-- The frame's own child, not a derived name: the reference pairs `<X>ScrollFrame` with
|
||||
-- `<X>ScrollChildFrame`.
|
||||
local child = sf:GetScrollChild()
|
||||
if not child then return false end
|
||||
local top = child:GetTop()
|
||||
@@ -284,11 +183,9 @@
|
||||
]]></Script>
|
||||
|
||||
<!--
|
||||
The scroll bar: a `Slider` (0250) 16px wide, its up/down step buttons pinned above/below the
|
||||
track, and the knob thumb. The value is in pixels (see the header); the thumb draws at the
|
||||
value fraction along the track (engine-side, decision 0250 §4), draggable (§5). The step-button
|
||||
+ knob art is the stock 1.12 scrollbar kit, each texture cropped to its centred 16px (the
|
||||
`0.25..0.75` TexCoords the ref's `UIPanelScrollBarButton` applies).
|
||||
The bar: a 16px `Slider` with step buttons above and below the track, on the stock 1.12
|
||||
scrollbar art cropped to its centred 16px (the `0.25..0.75` TexCoords of the reference's
|
||||
`UIPanelScrollBarButton`).
|
||||
-->
|
||||
<Slider name="BenillaScrollBarTemplate" virtual="true">
|
||||
<Size><AbsDimension x="16" y="0"/></Size>
|
||||
@@ -322,39 +219,24 @@
|
||||
</Slider>
|
||||
|
||||
<!--
|
||||
The scroll bar's TROUGH — the recessed channel the bar rides in: the "background and
|
||||
border" a bare arrows+knob bar is missing. An owner declares one of these BESIDE its bar,
|
||||
named `<barname>Trough` and WITH NO ANCHORS — it seats itself on the bar the first time
|
||||
BenillaScrollBar_SetShown touches it (BenillaScrollTrough_Seat above, where the socket law
|
||||
and the ref citations live). Declare it BEFORE the bar so it draws beneath.
|
||||
|
||||
THE ART, and why it is sliced this way. `Interface\PaperDollInfoFrame\UI-Character-ScrollBar`
|
||||
is a 64x256 sheet holding TWO 31-wide channel pieces, measured texel-exact from the file —
|
||||
rails at x 2-5 and 25-30 with a 19-wide interior between them, and each piece bearing a
|
||||
16-tall SOCKET for its arrow button:
|
||||
· LEFT column (u 0 .. 31/64): rows 0-3 the solid TOP cap, 4-19 the up-arrow socket,
|
||||
20-21 the crossbar that closes it, then plain channel from row 22 to the bottom edge.
|
||||
· RIGHT column (u 33/64 .. 1): plain channel, then rows 85-86 the crossbar, 87-102 the
|
||||
down-arrow socket, 104-106 the solid BOTTOM cap (nothing below 107).
|
||||
The ref composes a trough from exactly two quads — the whole left column plus the right
|
||||
column's bottom 108 rows — which reaches 362 units and no further, and only works because
|
||||
its scroll frames are short enough for the two to OVERLAP. Ours must span a 563-tall page,
|
||||
so the same art is cut as a THREE-SLICE instead: the two socket ends at their natural
|
||||
heights and a plain-channel slice stretched between them. The middle is constant down its
|
||||
whole length, so stretching it is exact — there is no seam to find.
|
||||
|
||||
The black `Fill` is the ref's own trough backing, not an invention: ItemTextFrame draws a
|
||||
0.8-alpha black quad behind its channel art for exactly this (ref-ItemTextFrame.xml's
|
||||
`Middle`). It sits in BACKGROUND and the channel art in BORDER — two layers, because within
|
||||
ONE layer the client re-sorts overlapping quads by texture handle and their order is not
|
||||
ours to depend on (order.rs). The bar's own furniture stays clear above both: button state
|
||||
textures are ARTWORK, the knob is OVERLAY.
|
||||
A bar's trough, the recessed channel it rides in; the reference's scroll kit has none.
|
||||
Declare it before its bar, so it draws beneath, named `<barname>Trough` and with no
|
||||
anchors: BenillaScrollTrough_Seat seats it on the bar.
|
||||
`UI-Character-ScrollBar` (64x256) holds two 31-wide channel pieces, the left column
|
||||
(u 0 .. 31/64) capped at the top and the right (u 33/64 .. 1) at the bottom, each with a
|
||||
16-tall socket for its arrow button: rows 4-19 at the top, rows 87-102 at the bottom.
|
||||
Deviation: cut as a three-slice (both socket ends at natural height, the plain channel
|
||||
stretched between) where the reference overlaps two quads, since two quads reach only 362
|
||||
units and a page here is 563 tall.
|
||||
The black fill is the reference's trough backing (ItemTextFrame's 0.8-alpha `Middle`). It
|
||||
is in BACKGROUND and the art in BORDER because the client orders overlapping quads within
|
||||
one layer by texture handle; the bar's buttons (ARTWORK) and knob (OVERLAY) draw above both.
|
||||
-->
|
||||
<Frame name="BenillaScrollTroughTemplate" virtual="true" hidden="true">
|
||||
<Size><AbsDimension x="31"/></Size>
|
||||
<Layers>
|
||||
<Layer level="BACKGROUND">
|
||||
<!-- The channel interior: x 6..24 of the 31-wide piece, i.e. inset 6 from each edge. -->
|
||||
<!-- The channel interior: x 6..24 of the 31-wide piece, inset 6 from each edge. -->
|
||||
<Texture name="$parentFill">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT"><Offset><AbsDimension x="6" y="0"/></Offset></Anchor>
|
||||
@@ -374,9 +256,7 @@
|
||||
<TexCoords left="0" right="0.484375" top="0" bottom="0.08984375"/>
|
||||
</Texture>
|
||||
<!-- Crossbar + down-arrow socket + cap: right column, rows 82..107
|
||||
(v 82/256 .. 108/256) — the mirror of the top's 23, cut to put row 107 on the
|
||||
trough's bottom edge exactly as ReputationFrame's own 108/0.421875 does, which
|
||||
is what lands the socket on the arrow (BenillaScrollTrough_Seat's law). -->
|
||||
(v 82/256 .. 108/256), ending on v 0.421875 as ReputationFrame's does. -->
|
||||
<Texture name="$parentBottom" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Size><AbsDimension y="26"/></Size>
|
||||
<Anchors>
|
||||
@@ -386,8 +266,7 @@
|
||||
<TexCoords left="0.515625" right="1.0" top="0.3203125" bottom="0.421875"/>
|
||||
</Texture>
|
||||
<!-- The stretched run between them: left column rows 30..70, plain rails only.
|
||||
Declared LAST because it anchors to the two above by name, and an anchor whose
|
||||
target is declared later falls back to the parent (ItemTextFrame's own note). -->
|
||||
Declared last: an anchor to a later sibling falls back to the parent. -->
|
||||
<Texture name="$parentMiddle" file="Interface\PaperDollInfoFrame\UI-Character-ScrollBar">
|
||||
<Anchors>
|
||||
<Anchor point="TOPLEFT" relativeTo="$parentTop" relativePoint="BOTTOMLEFT"/>
|
||||
@@ -399,22 +278,10 @@
|
||||
</Layers>
|
||||
</Frame>
|
||||
|
||||
<!-- ══ Carried here from UiPanels.xml when that file went stock (decision 1988) ══
|
||||
The placeholder icon our UIParent.lua counterpart held and the reference's does not,
|
||||
seated in the earliest file of ours every consumer loads after. The window tab template
|
||||
and its settle stood here too until 1993 put them on the chain. -->
|
||||
<Script><![CDATA[
|
||||
-- The placeholder icon for an item/spell whose texture has not arrived yet (Craft, Quest,
|
||||
-- QuestLog, Trainer and TradeSkill read it). Was ActionBar.xml's (stock since 1938), then
|
||||
-- UiPanels.xml's (stock since 1988); this file is the earliest of ours every reader loads
|
||||
-- after. A benilla name, so nothing stock reads it.
|
||||
-- Placeholder icon for an item or spell whose texture has not arrived; a benilla global
|
||||
-- that the reference does not declare, so no stock file reads it.
|
||||
BENILLA_FALLBACK_ICON = "Interface\\Icons\\INV_Misc_QuestionMark"
|
||||
]]></Script>
|
||||
|
||||
<!-- `FauxScrollFrameTemplate` and the `FauxScrollFrame_*`/`ScrollFrame_*`/`ScrollingEdit_*`
|
||||
API are the REFERENCE's, off the player's chain (`Interface\FrameXML\UIPanelTemplates`,
|
||||
seated below this file — so its declarations win). Our transcriptions stood here dead
|
||||
from the day that entry landed and are gone (1860). What stays is
|
||||
`BenillaScrollBarTemplate` + `BenillaScrollTroughTemplate` and their helpers, which are
|
||||
ours: six windows inherit the bar directly and the reference has no trough at all. -->
|
||||
</Ui>
|
||||
|
||||
@@ -1,27 +1,13 @@
|
||||
<Ui>
|
||||
<!--
|
||||
BENILLA_ALLOW_OWN_UI=1 — the director's two LOOK deviations on the pet page's autocast
|
||||
button, over the reference's own SpellBookFrame.xml (decisions 1392/1393; re-seated by 2014
|
||||
onto the reference's own Model now that this engine renders it — 2013).
|
||||
|
||||
Since 2013 the `$parentAutoCast` <Model> on every SpellButton draws its own file
|
||||
(Interface\Buttons\UI-AutoCastButton.mdx) as a tile: four bones chasing the corners of a
|
||||
0.02-unit square, four spline emitters trailing GlowStar sparks off them, on the pane's own
|
||||
clock. The reference declares it 36x36 at CENTER (1,1) at scale 1.22 — a 0.87x rim that
|
||||
floats clear of the viewport edge, never clipped, washing the icon; the real 1.12 client
|
||||
looks the same way (director-checked, 1392), so this is taste, not fidelity, and the
|
||||
director chose the pet button's proportions instead:
|
||||
|
||||
· the shine pane squared on the button — 37x37 at CENTER (0,0), scale 1.48 — so the rim
|
||||
runs ON the viewport edge (1.024x, the pet button's own ratio) and the tile's cell, the
|
||||
widget's own scissor, halves every star; and so the glow and the brackets share a centre
|
||||
(1393's "top and right are off" was the reference's own +1,+1);
|
||||
· the corner brackets at 71.53 rather than the reference's 60 — the art fills 33/64 of
|
||||
UI-AutoCastableOverlay.blp, and 71.53 = 37 x 58/30 puts the bracket square on this
|
||||
button's corners at the pet button's 0.997x.
|
||||
|
||||
Reverting the widget to 1.12 exactly is: overlay 60, pane 36x36 CENTER (1,1), scale 1.22 —
|
||||
i.e. deleting this file. Nothing else here: the reference's file is sourced unedited.
|
||||
BENILLA_ALLOW_OWN_UI=1. Deviation: over the unedited stock SpellBookFrame.xml, the pet
|
||||
page's autocast widgets take the pet bar button's proportions.
|
||||
The `$parentAutoCast` shine (UI-AutoCastButton.mdx) is squared on the button, 37x37 at
|
||||
CENTER (0,0) and scale 1.48, so its rim runs on the viewport edge at the pet button's
|
||||
1.024x and shares the brackets' centre; the reference's is 36x36 at CENTER (1,1), 1.22.
|
||||
The corner brackets are 71.53 rather than 60: the art fills 33/64 of
|
||||
UI-AutoCastableOverlay.blp, and 71.53 = 37 x 58/30 puts them on the button's corners at
|
||||
the pet button's 0.997x. Deleting this file restores the reference's look.
|
||||
-->
|
||||
<Script><![CDATA[
|
||||
for i = 1, 12 do
|
||||
|
||||
Reference in New Issue
Block a user