diff --git a/crates/benilla-app/assets/ui/ContainerFrameAdapters.xml b/crates/benilla-app/assets/ui/ContainerFrameAdapters.xml index bc879b6b..175dbf89 100644 --- a/crates/benilla-app/assets/ui/ContainerFrameAdapters.xml +++ b/crates/benilla-app/assets/ui/ContainerFrameAdapters.xml @@ -96,25 +96,72 @@ refItemButtonOnEnter(button) end - -- ══ Re-publishing the three bag verbs 0561 shadows, and why it is HERE ═════════════ + -- ══ The three bag verbs benilla answers differently (decisions 0561 + 1751) ═════════ -- - -- `OpenBackpack`, `CloseBackpack` and `CloseAllBags` are all defined by the sourced - -- `ContainerFrame.lua` (l.187-217, l.702-707) — and benilla answers all three differently, - -- for one reason: **0561, the director's call that a window opening your bags opens EVERY - -- equipped bag**, not just the backpack. The vendor, bank, mail and trade windows call - -- `OpenBackpack` from their OnShow so your bags come up with them and you can sell or - -- deposit from one. + -- `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 bodies live in `UiPanels.xml`, because every window that calls them is loaded by - -- harnesses that have no bag stack at all, and a nil `OpenBackpack` in a merchant test is - -- a nil for no reason. But UiPanels is manifest entry 7 and the reference's container file - -- is entry 46, so **its publications lose the collision** — the reference's bodies land on - -- top and the vendor silently stops opening your bags. That is not hypothetical: it is - -- what shipped for the length of one session before a test caught it. + -- **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. -- - -- So the bodies stay where their callers can reach them, under `Benilla*` names that - -- nothing overwrites, and this file — entry 49, after the reference's — puts them back on - -- the reference's names. Three lines, and they are the whole of the fix. + -- 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. + 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. + 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. + 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. + if bagId == 0 or GetContainerNumSlots(bagId) > 0 then + if IsBagOpen(bagId) then + BENILLA_BAG_WAS_OPEN[bagId] = 1 + else + BENILLA_BAG_WAS_OPEN[bagId] = nil + OpenBag(bagId) + end + end + end + end + + function BenillaCloseBackpack() + for bagId = 0, (NUM_BAG_FRAMES or -1) do + if not BENILLA_BAG_WAS_OPEN[bagId] then + CloseBag(bagId) + end + 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). OpenBackpack = BenillaOpenBackpack CloseBackpack = BenillaCloseBackpack CloseAllBags = BenillaCloseAllBags diff --git a/crates/benilla-app/assets/ui/GameMenuFrame.xml b/crates/benilla-app/assets/ui/GameMenuFrame.xml index cd4aebe1..7911677b 100644 --- a/crates/benilla-app/assets/ui/GameMenuFrame.xml +++ b/crates/benilla-app/assets/ui/GameMenuFrame.xml @@ -39,7 +39,7 @@ · **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 (UiPanels.xml), whose countdown-text branch + 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`. @@ -119,7 +119,7 @@ button:Disable() end - -- The CAMP/QUIT dialogs and the three-event driver MOVED OUT, to UiPanels.xml — the + -- 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. @@ -287,7 +287,7 @@ 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, the - UiPanels.xml `CloseMenus and CloseMenus()` posture: a headless test loads this file + the old UiPanels.xml `CloseMenus and CloseMenus()` posture: a headless test loads this file without the action bar behind it. --> GameMenuFrameTitle:SetText(MAIN_MENU) diff --git a/crates/benilla-app/assets/ui/KeyBindingsPage.xml b/crates/benilla-app/assets/ui/KeyBindingsPage.xml index bb5fe601..e2f08cde 100644 --- a/crates/benilla-app/assets/ui/KeyBindingsPage.xml +++ b/crates/benilla-app/assets/ui/KeyBindingsPage.xml @@ -152,7 +152,7 @@ -- ── 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 our own GetBindingText in UIParent.xml; + -- bar's ABBREVIATED labels ride the stock GetBindingText (UIParent.lua); -- this page and the tooltips show the full form, both references' own split.) function KeyBindings_KeyText(key) if not key then diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml index 1f45cd98..13f31ab8 100644 --- a/crates/benilla-app/assets/ui/OptionsFrame.xml +++ b/crates/benilla-app/assets/ui/OptionsFrame.xml @@ -4063,9 +4063,14 @@ is down (MainMenuBarMicroButtons.lua l.47-52; decision 1987). These two aliases are the adapter: a stock read of either sibling is answered by this window, which is the honest answer — while ours is up, the reference's question "is an options window open" is yes. - BENILLA_ALLOW_OWN_UI=1 --> + The third alias is the ESC ladder's: the stock `ToggleGameMenu` (UIParent.lua l.1465-1497, + off the chain since 1988) closes an open options window with `OptionsFrameCancel:Click()`, + the reference's Cancel button; ours has a corner X whose click is the same discard-and-hide + (`HideUIPanel(OptionsFrame)`, and OnHide discards the pending page), so that button answers + to the reference's name. BENILLA_ALLOW_OWN_UI=1 --> diff --git a/crates/benilla-app/assets/ui/ScriptLogFrame.xml b/crates/benilla-app/assets/ui/ScriptLogFrame.xml index 89a0cab0..ce16f2da 100644 --- a/crates/benilla-app/assets/ui/ScriptLogFrame.xml +++ b/crates/benilla-app/assets/ui/ScriptLogFrame.xml @@ -26,7 +26,8 @@ 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 is registered in `UIPanelWindows` (`UiPanels.xml`), which is what seats it in the + 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). @@ -40,6 +41,10 @@ feed and nothing to keep in step — the window polls the log it is a view of. --> + + + + - - - - this:RegisterEvent("VARIABLES_LOADED") - this:RegisterEvent("TRAINER_SHOW") - this:RegisterEvent("TRAINER_CLOSED") - this:RegisterEvent("AUCTION_HOUSE_SHOW") - this:RegisterEvent("AUCTION_HOUSE_CLOSED") - this:RegisterEvent("TRADE_SKILL_SHOW") - this:RegisterEvent("TRADE_SKILL_CLOSE") - this:RegisterEvent("CRAFT_SHOW") - this:RegisterEvent("CRAFT_CLOSE") - this:RegisterEvent("GUILD_INVITE_REQUEST") - this:RegisterEvent("GUILD_INVITE_CANCEL") - this:RegisterEvent("MIRROR_TIMER_START") - this:RegisterEvent("PLAYER_ENTERING_WORLD") - this:RegisterEvent("READY_CHECK") - this:RegisterEvent("START_LOOT_ROLL") - this:RegisterEvent("CONFIRM_LOOT_ROLL") - -- The dialog-raising events below (ref UIParent.lua l.76-127), registered here - -- since the dialog engine became the reference's file (1960); what the engine does - -- not produce yet is left unregistered and named in that record, never armed blind. - this:RegisterEvent("PLAYER_DEAD") - this:RegisterEvent("PLAYER_ALIVE") - this:RegisterEvent("PLAYER_UNGHOST") - this:RegisterEvent("RESURRECT_REQUEST") - this:RegisterEvent("PARTY_INVITE_REQUEST") - this:RegisterEvent("PARTY_INVITE_CANCEL") - this:RegisterEvent("PLAYER_CAMPING") - this:RegisterEvent("PLAYER_QUITING") - this:RegisterEvent("LOGOUT_CANCEL") - this:RegisterEvent("LOOT_BIND_CONFIRM") - this:RegisterEvent("EQUIP_BIND_CONFIRM") - this:RegisterEvent("AUTOEQUIP_BIND_CONFIRM") - this:RegisterEvent("USE_BIND_CONFIRM") - this:RegisterEvent("DELETE_ITEM_CONFIRM") - this:RegisterEvent("QUEST_ACCEPT_CONFIRM") - this:RegisterEvent("CURSOR_UPDATE") - this:RegisterEvent("DUEL_REQUESTED") - this:RegisterEvent("DUEL_OUTOFBOUNDS") - this:RegisterEvent("DUEL_INBOUNDS") - this:RegisterEvent("DUEL_FINISHED") - this:RegisterEvent("CONFIRM_XP_LOSS") - this:RegisterEvent("CORPSE_IN_RANGE") - this:RegisterEvent("CORPSE_IN_INSTANCE") - this:RegisterEvent("CORPSE_OUT_OF_RANGE") - this:RegisterEvent("BIND_ENCHANT") - this:RegisterEvent("REPLACE_ENCHANT") - this:RegisterEvent("CURRENT_SPELL_CAST_CHANGED") - this:RegisterEvent("CONFIRM_TALENT_WIPE") - this:RegisterEvent("CONFIRM_BINDER") - this:RegisterEvent("CONFIRM_SUMMON") - -- The dialog engine's verbs (1963): the feeds behind these exist now. - this:RegisterEvent("INSTANCE_BOOT_START") - this:RegisterEvent("INSTANCE_BOOT_STOP") - this:RegisterEvent("CONFIRM_PET_UNLEARN") - this:RegisterEvent("AREA_SPIRIT_HEALER_IN_RANGE") - this:RegisterEvent("AREA_SPIRIT_HEALER_OUT_OF_RANGE") - - UIParent_OnEvent(event) - - if FCF_OnUpdate then FCF_OnUpdate(arg1) end if ButtonPulse_OnUpdate then ButtonPulse_OnUpdate(arg1) end if UnitPopup_OnUpdate then UnitPopup_OnUpdate(arg1) end - - - - - - - - diff --git a/crates/benilla-app/assets/ui/UiPanels.xml b/crates/benilla-app/assets/ui/UiPanels.xml deleted file mode 100644 index 0279eece..00000000 --- a/crates/benilla-app/assets/ui/UiPanels.xml +++ /dev/null @@ -1,1540 +0,0 @@ - - - - - - - - - - if not FLASHFRAMES[1] then this:Hide() return end UIFrameFlashUpdate(arg1) - - - - - - if not FADEFRAMES[1] then this:Hide() return end UIFrameFadeUpdate(arg1) - - - - - - - if not PULSEBUTTONS[1] then this:Hide() return end ButtonPulse_OnUpdate(arg1) - - - - - - - - diff --git a/crates/benilla-app/assets/ui/benilla.toc b/crates/benilla-app/assets/ui/benilla.toc index 76a012bd..7239435f 100644 --- a/crates/benilla-app/assets/ui/benilla.toc +++ b/crates/benilla-app/assets/ui/benilla.toc @@ -124,7 +124,21 @@ Interface\FrameXML\BasicControls.xml # the reference's toc has it (l.7-8). Needs Fonts.xml's SystemFont above; everything else it # names is call-time. Interface\FrameXML\WorldFrame.xml -UIParent.xml +# UIParent itself and its Lua — the reference's own file (1751; decision 1988), at the slot its +# toc gives it (l.8, right after BasicControls). UIParent.lua is the whole of the glue this +# engine used to carry as UIParent.xml + UiPanels.xml: the UIPanelWindows table and the slot +# manager (Show/HideUIPanel, CloseAllWindows, ToggleGameMenu's ESC ladder), the fade/flash/pulse +# kits (ticked from UIParent's own OnUpdate, so the three driver frames of ours retired with +# it), the managed bottom-stack positions, the model turntable, the LoadOnDemand shims, and the +# sixty-six globally-routed events and their arms. It sources LocaleProperties.lua itself. Its +# OnUpdate also ticks FCF_OnUpdate, UnitPopup_OnUpdate, BattlefieldFrame_OnUpdate, the +# battlefield-position ask (1980) and the ready-check timeout (1989) — call-time names, all of +# them below. LowHealthFrame (parent WorldFrame, above) and OutOfControlFrame ride along. +# What stayed ours moved out: the window tab template and its settle + the placeholder icon +# (ScrollTemplates.xml), 0561's bag verbs (ContainerFrameAdapters.xml), the script log's row +# (ScriptLogFrame.xml), and three aliases in OptionsFrame.xml that answer the reference's three +# options windows and its Cancel button. +Interface\FrameXML\UIParent.xml # ══ THE ITEM-SLOT BASE — the reference's own, off the player's chain (1751, window 24) ══ # `ItemButtonTemplate` and the eight `SetItemButton*` verbs out of `ItemButtonTemplate.lua`. It # sits as early as it can be read: no frames, no dependencies of its own, and every window that @@ -169,12 +183,6 @@ Interface\FrameXML\MoneyFrame.xml # `every_chain_xml_brings_its_own_lua` is now a gate so neither can recur. Interface\FrameXML\MoneyInputFrame.lua Interface\FrameXML\MoneyInputFrame.xml -# The UIPanel slot manager (decision 0084 §2) and the fade/flash/pulse kits — the UIParent.lua -# halves this engine still carries itself: script-only but for the kits' tick drivers, loaded -# before any panel frame so ShowUIPanel/HideUIPanel already exist when those frames' OnLoad/OnEvent -# reference them. The StaticPopup engine and every dialog it used to carry are the reference's -# own file now, far below at the reference's own slot after UIPanelTemplates (decision 1960). -UiPanels.xml # ══ THE TOOLTIP — the reference's own, off the player's chain (1751; decision 1968) ══ # GameTooltip.xml sources HealthBar.lua and GameTooltip.lua and s GameTooltipTemplate.xml: # the two virtual templates (GameTooltipTemplate, ShoppingTooltipTemplate — thirty declared line @@ -592,14 +600,6 @@ Interface\FrameXML\CastingBarFrame.xml # 1.12 never reaches it — vmangos refuses to send that packet and substitutes a full START, # saying so in `Player::SendMirrorTimers` — and `ui_mirror.rs`'s Pause edge carries the note. Interface\FrameXML\MirrorTimer.xml -# ══ THE WORLD-STATE FRAMES — the reference's own, off the player's chain (1751; decision 1972) ══ -# WorldStateFrame.xml + WorldStateFrame.lua: the always-up readout 1586 built (the tower/flag/base -# counters at top-centre, `UPDATE_WORLD_STATES` from `crate::world_state_ui`), the capture bars, -# and the battleground SCOREBOARD ours never had — `WorldStateScoreFrame`, over the score family -# 1972 built (`MSG_PVP_LOG_DATA`, the eleven verbs, `UPDATE_BATTLEFIELD_SCORE`) and UIParent.xml's -# `BattlefieldMinimap_LoadUI`. Needs Fonts, GameTooltip, the panel kit (its close button, tabs, -# faux scroll frame) — all above; sits beside the mirror timers, which share its top-centre lane. -Interface\FrameXML\WorldStateFrame.xml # ══ THE DURABILITY ALERT — the reference's own, off the player's chain (1751, window 4) ══ # The "armor guy" under the minimap: `DurabilityFrame`, its eleven glyph textures, and # `DurabilityFrame_SetAlerts` / `_OnUpdate` with `INVENTORY_ALERT_STATUS_SLOTS` and @@ -1053,6 +1053,20 @@ Interface\FrameXML\QuestTimerFrame.xml # CHAT_MSG_* events the reference's ChatFrame_OnEvent prints from — it no longer prints them itself. Interface\FrameXML\ChatFrame.xml Interface\FrameXML\FloatingChatFrame.xml + +# ══ THE WORLD-STATE FRAMES — the reference's own, off the player's chain (1751; decision 1972) ══ +# WorldStateFrame.xml + WorldStateFrame.lua: the always-up readout 1586 built (the tower/flag/base +# counters at top-centre, `UPDATE_WORLD_STATES` from `crate::world_state_ui`), the capture bars, +# and the battleground SCOREBOARD ours never had — `WorldStateScoreFrame`, over the score family +# 1972 built (`MSG_PVP_LOG_DATA`, the eleven verbs, `UPDATE_BATTLEFIELD_SCORE`) and UIParent.xml's +# `BattlefieldMinimap_LoadUI`. Needs Fonts, GameTooltip, the panel kit (its close button, tabs, +# faux scroll frame) — all above. It sat beside the mirror timers until 1988: the always-up +# frame's OnLoad runs the stock UIParent_ManageFramePositions, and that pass indexes half the +# HUD unguarded — ReputationWatchBar, QuestTimerFrame, QuestWatchFrame (QuestLogFrame.xml), +# MinimapCluster, DurabilityFrame, the bars and their sliding textures — and ends by calling +# FCF_DockUpdate and re-anchoring the container frames. So this file sits after all of them, +# which is why the reference's own toc has it at l.89, near the end (1988). +Interface\FrameXML\WorldStateFrame.xml # ══ THE MACRO WINDOW — the reference's own, off the player's chain (1751, window 32) ══ # A LoadOnDemand addon with no row here since 1967 (the addons header far above); what stays in # this slot is the FrameXML file it needs loaded first. diff --git a/crates/benilla-app/src/ui_chat/tests.rs b/crates/benilla-app/src/ui_chat/tests.rs index 371a59e9..e920bc4b 100644 --- a/crates/benilla-app/src/ui_chat/tests.rs +++ b/crates/benilla-app/src/ui_chat/tests.rs @@ -451,7 +451,7 @@ fn chat_vm() -> benilla_ui::script::UiScript { "Interface\\FrameXML\\ChatFrame.xml", "Interface\\FrameXML\\UIPanelTemplates.lua", "Interface\\FrameXML\\UIPanelTemplates.xml", - "UiPanels.xml", + r"Interface\FrameXML\UIParent.xml", "Interface\\FrameXML\\LocaleProperties.lua", "Interface\\FrameXML\\StaticPopup.xml", "Interface\\FrameXML\\FloatingChatFrame.xml", @@ -2039,37 +2039,47 @@ fn both_dock_windows_carry_the_same_chrome() { } } -/// **No docked chat window may appear in `UIParent.xml`'s managed-position table.** +/// **A docked chat window survives the managed-position pass.** /// -/// The structural guard on the root cause. That pass owns a frame's whole seat — it -/// `ClearAllPoints()` first, by design (decision 1499) — so a row naming a *docked* window wipes -/// the anchors that tie it to the dock and re-seats it with a single point and no size: an -/// unresolvable rect. The reference's own ChatFrame2 row is for an UNDOCKED window, which is chat -/// settings and not built (0288 §2); carrying it here cost B297 a visible fix. -/// -/// A string check because that is the level the bug lives at — the row's mere presence is the -/// defect, whatever it contains. +/// The pass owns a frame's whole seat — it `ClearAllPoints()` first, by design (decision 1499) — +/// and the reference's own table carries a `ChatFrame2` row. That row cost B297 a visible fix +/// while the pass was ours, and our answer then was to drop the row from our copy. The stock pass +/// is the one that runs now (1988), row and all, and it ends by calling `FCF_DockUpdate()` — +/// which re-anchors every docked window onto `DEFAULT_CHAT_FRAME` in the same breath. That is the +/// reference's answer to its own row, and this is the behavioural check that it holds: after the +/// pass, the docked window is still exactly on the dock. #[test] -fn no_docked_chat_window_is_managed_by_uiparent() { +fn a_docked_chat_window_survives_the_managed_position_pass() { let _data = benilla_formats::wow_data_or_skip!(); - let xml = std::fs::read_to_string( - std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("assets/ui/UIParent.xml"), - ) - .expect("UIParent.xml"); - for line in xml.lines() { - let code = line.trim_start(); - if code.starts_with("--") { - continue; // the comment explaining why the row is gone names it, deliberately - } - assert!( - !code.starts_with("ChatFrame2 ="), - "ChatFrame2 is DOCKED — a managed row clears its dock anchors: {code}" - ); - } - assert!( - xml.contains("ChatFrame1 = {baseY = 85"), - "ChatFrame1 IS managed (it is the dock's own seat) — this test must not pass vacuously" + let mut s = benilla_ui::script::UiScript::new().expect("VM"); + s.set_screen_size(1024.0, 768.0); + s.set_unit( + "player", + Some(benilla_ui::script::UnitState { + exists: true, + name: Some("Probefour".into()), + level: 60, + ..Default::default() + }), ); + assert!(crate::ui_script::load_default_ui(&s).is_empty()); + s.resolve(); + // Dock the combat log the way the reference's own default layout does, then run the pass. + s.run("FCF_DockFrame(ChatFrame2, 2, nil) UIParent_ManageFramePositions()") + .unwrap(); + s.resolve(); + let rect = |name: &str| { + s.eval::<(f64, f64, f64, f64)>(&format!( + "return {name}:GetLeft(), {name}:GetBottom(), {name}:GetRight(), {name}:GetTop()" + )) + .unwrap_or_else(|e| panic!("{name}: {e}")) + }; + assert_eq!( + rect("ChatFrame2"), + rect("ChatFrame1"), + "the docked window still sits exactly on the dock" + ); + assert!(s.errors().is_empty(), "script errors: {:?}", s.errors()); } /// **What does NOT gate a `/sit` underwater** — the negative that sends B155's refusal to the diff --git a/crates/benilla-app/src/ui_items/drain.rs b/crates/benilla-app/src/ui_items/drain.rs index 233648bf..56da9012 100644 --- a/crates/benilla-app/src/ui_items/drain.rs +++ b/crates/benilla-app/src/ui_items/drain.rs @@ -1176,12 +1176,11 @@ mod bind_confirm_tests { "Interface\\FrameXML\\Fonts.xml", r"Interface\FrameXML\MoneyFrame.lua", r"Interface\FrameXML\MoneyFrame.xml", - "UiPanels.xml", + r"Interface\FrameXML\UIParent.xml", "Interface\\FrameXML\\GlobalStrings.lua", "Interface\\FrameXML\\BasicControls.xml", "Interface\\FrameXML\\LocaleProperties.lua", "Interface\\FrameXML\\StaticPopup.xml", - "UIParent.xml", ] { crate::ui_script::load_ui_for_test(s, file); } diff --git a/crates/benilla-app/src/ui_script/action_bar_tests.rs b/crates/benilla-app/src/ui_script/action_bar_tests.rs index fa2dba8c..a6ead5d0 100644 --- a/crates/benilla-app/src/ui_script/action_bar_tests.rs +++ b/crates/benilla-app/src/ui_script/action_bar_tests.rs @@ -21,7 +21,7 @@ fn shipped_action_bar_drives_end_to_end() { super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml"); - super::test_ui::load_ui(&s, "UIParent.xml"); + super::test_ui::load_ui(&s, r"Interface\FrameXML\UIParent.xml"); super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua"); super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\GameTooltip.xml"); @@ -185,7 +185,7 @@ fn load_action_bar(s: &UiScript) { super::test_ui::load_ui(s, "Interface\\FrameXML\\TextStatusBar.lua"); super::test_ui::load_ui(s, "Interface\\FrameXML\\TextStatusBar.xml"); super::test_ui::load_ui(s, "Interface\\FrameXML\\Fonts.xml"); - super::test_ui::load_ui(s, "UIParent.xml"); + super::test_ui::load_ui(s, r"Interface\FrameXML\UIParent.xml"); super::test_ui::load_ui(s, "Interface\\FrameXML\\GlobalStrings.lua"); super::test_ui::load_ui(s, "Interface\\FrameXML\\MainMenuBar.xml"); super::test_ui::load_ui(s, r"Interface\FrameXML\MoneyFrame.lua"); @@ -208,7 +208,6 @@ fn load_action_bar(s: &UiScript) { // The options window: `LOCK_ACTIONBAR` and `ALWAYS_SHOW_MULTIBARS` are declared there, as the // reference declares them in UIOptionsFrame_Init (1938) — and the manifest loads it before // the bars. - super::test_ui::load_ui(s, "UiPanels.xml"); // The dialog engine — the keybindings page registers its two confirms into its table (1960). super::test_ui::load_ui(s, r"Interface\FrameXML\BasicControls.xml"); // `TEXT` super::test_ui::load_ui(s, r"Interface\FrameXML\LocaleProperties.lua"); // `GetText` @@ -835,7 +834,8 @@ fn shipped_bag_frame_drives_end_to_end() { load_ui(&s, "Interface\\FrameXML\\TextStatusBar.lua"); load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml"); load_ui(&s, "Interface\\FrameXML\\Fonts.xml"); - load_ui(&s, "UIParent.xml"); + load_ui(&s, r"Interface\FrameXML\UIParent.xml"); + load_ui(&s, "ScrollTemplates.xml"); // the window tab template, ours (1004/1988) load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua"); load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml"); load_ui(&s, "Interface\\FrameXML\\ActionBarFrame.xml"); @@ -1350,7 +1350,7 @@ fn the_main_bar_pages_and_a_bonus_page_still_outranks_it() { for file in [ // Fonts first: the pane's check-box labels colour from `RED_FONT_COLOR` in their own OnLoad. "Interface\\FrameXML\\Fonts.xml", - "UIParent.xml", + r"Interface\FrameXML\UIParent.xml", "Cooldown.xml", "Interface\\FrameXML\\ActionButtonTemplate.xml", "Interface\\FrameXML\\TextStatusBar.lua", @@ -1371,7 +1371,6 @@ fn the_main_bar_pages_and_a_bonus_page_still_outranks_it() { r"Interface\FrameXML\OptionsFrameTemplates.xml", r"Interface\FrameXML\ReputationFrame.xml", "Interface\\FrameXML\\ActionBarFrame.xml", - "UiPanels.xml", "Interface\\FrameXML\\UIDropDownMenu.xml", "ScrollTemplates.xml", r"Interface\FrameXML\UIPanelTemplates.lua", @@ -1523,7 +1522,7 @@ fn bonus_bar_slides_up_with_sound_and_down_without() { super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml"); - super::test_ui::load_ui(&s, "UIParent.xml"); + super::test_ui::load_ui(&s, r"Interface\FrameXML\UIParent.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml"); super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua"); @@ -1745,7 +1744,7 @@ fn bonus_bar_turnaround_continues_from_position() { super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml"); - super::test_ui::load_ui(&s, "UIParent.xml"); + super::test_ui::load_ui(&s, r"Interface\FrameXML\UIParent.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml"); super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua"); @@ -1827,7 +1826,7 @@ fn the_page_arrows_do_not_steal_each_other_s_clicks() { super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\TextStatusBar.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\Fonts.xml"); - super::test_ui::load_ui(&s, "UIParent.xml"); + super::test_ui::load_ui(&s, r"Interface\FrameXML\UIParent.xml"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\GlobalStrings.lua"); super::test_ui::load_ui(&s, "Interface\\FrameXML\\MainMenuBar.xml"); super::test_ui::load_ui(&s, r"Interface\FrameXML\MoneyFrame.lua"); diff --git a/crates/benilla-app/src/ui_script/bag_tests.rs b/crates/benilla-app/src/ui_script/bag_tests.rs index e1a4198a..bed12058 100644 --- a/crates/benilla-app/src/ui_script/bag_tests.rs +++ b/crates/benilla-app/src/ui_script/bag_tests.rs @@ -133,6 +133,7 @@ fn b_opens_the_backpack_alone_and_closes_every_bag() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -214,6 +215,7 @@ fn shift_b_toggles_every_bag_at_once() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); s.set_container( @@ -295,6 +297,7 @@ fn bag_bar_buttons_light_while_their_bag_is_open() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); s.set_container( @@ -412,6 +415,7 @@ fn bag_tooltip_hangs_left_when_the_slot_sits_in_the_right_half() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -489,6 +493,7 @@ fn hovered_bag_tooltip_fills_itself_when_the_stats_land() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -593,6 +598,7 @@ fn vendor_bag_hover_shows_sell_price_and_arms_the_pouch_cursor() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -729,6 +735,7 @@ fn readable_letter_hover_shows_the_inspect_magnifier() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -799,6 +806,7 @@ fn drag_across_two_slots_queues_the_same_move_a_click_pickup_would() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -883,6 +891,7 @@ fn a_second_bag_window_feeds_and_paints_via_the_bag_bar() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -956,6 +965,7 @@ fn equipped_bag_window_snug_fits_its_row_count() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -1015,6 +1025,7 @@ fn open_backpack_with_a_five_stack(s: &mut UiScript) -> (f32, f32) { for file in BAG_UI { load_xml(s, file); } + load_xml(s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(s, "Interface\\FrameXML\\MerchantFrame.xml"); load_xml(s, "Interface\\FrameXML\\StackSplitFrame.xml"); s.set_money(0); @@ -1356,6 +1367,7 @@ fn bag_slot_cooldown_sweeps_through_the_xml() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); s.tick(100.0); // a nonzero clock epoch, like the engine cooldown tests @@ -1429,6 +1441,7 @@ fn the_bar_bag_buttons_name_themselves_on_hover() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.resolve(); @@ -1563,6 +1576,7 @@ fn keyring_surface(s: &UiScript) { for file in BAG_UI { load_xml(s, file); } + load_xml(s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(s, "Interface\\FrameXML\\MerchantFrame.xml"); load_xml(s, "Interface\\FrameXML\\UIErrorsFrame.xml"); } @@ -1837,6 +1851,7 @@ fn an_item_push_drops_its_icon_into_the_bag_that_took_it() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.resolve(); @@ -2093,6 +2108,7 @@ fn an_addon_that_hooks_toggle_backpack_receives_the_click() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); @@ -2149,6 +2165,7 @@ fn the_bag_slots_carry_the_references_names_and_icon_names() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); for i in 0..4 { @@ -2209,6 +2226,7 @@ fn the_backpack_buttons_ring_follows_its_own_window_through_real_clicks() { for file in BAG_UI { load_xml(&s, file); } + load_xml(&s, "ScrollTemplates.xml"); // our window tab template, before the window that inherits it (1988) load_xml(&s, "Interface\\FrameXML\\MerchantFrame.xml"); s.set_money(0); // The reference's `ToggleBag` opens nothing for a container of size 0, so the backpack has to diff --git a/crates/benilla-app/src/ui_script/battlefield_tests.rs b/crates/benilla-app/src/ui_script/battlefield_tests.rs index bb29292a..e9d9cf2f 100644 --- a/crates/benilla-app/src/ui_script/battlefield_tests.rs +++ b/crates/benilla-app/src/ui_script/battlefield_tests.rs @@ -34,10 +34,9 @@ fn session() -> UiScript { "Interface\\FrameXML\\Localization.xml", "Interface\\FrameXML\\LocaleProperties.lua", "Interface\\FrameXML\\BasicControls.xml", - "UIParent.xml", + r"Interface\FrameXML\UIParent.xml", r"Interface\FrameXML\MoneyFrame.lua", r"Interface\FrameXML\MoneyFrame.xml", - "UiPanels.xml", "Interface\\FrameXML\\GameTooltip.xml", "Interface\\FrameXML\\UIDropDownMenu.xml", r"Interface\FrameXML\UIPanelTemplates.lua", diff --git a/crates/benilla-app/src/ui_script/binder_tests.rs b/crates/benilla-app/src/ui_script/binder_tests.rs index 66a603ec..26cbaa4a 100644 --- a/crates/benilla-app/src/ui_script/binder_tests.rs +++ b/crates/benilla-app/src/ui_script/binder_tests.rs @@ -14,14 +14,13 @@ fn setup() -> UiScript { load_xml(&s, "Interface\\FrameXML\\Fonts.xml"); load_xml(&s, r"Interface\FrameXML\MoneyFrame.lua"); load_xml(&s, r"Interface\FrameXML\MoneyFrame.xml"); - load_xml(&s, "UiPanels.xml"); + load_xml(&s, r"Interface\FrameXML\UIParent.xml"); load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.lua"); load_xml(&s, r"Interface\FrameXML\UIPanelTemplates.xml"); load_xml(&s, r"Interface\FrameXML\GlobalStrings.lua"); load_xml(&s, r"Interface\FrameXML\BasicControls.xml"); load_xml(&s, r"Interface\FrameXML\LocaleProperties.lua"); load_xml(&s, r"Interface\FrameXML\StaticPopup.xml"); - load_xml(&s, "UIParent.xml"); s.set_binder_pending(true); s } diff --git a/crates/benilla-app/src/ui_script/bottom_hud_tests.rs b/crates/benilla-app/src/ui_script/bottom_hud_tests.rs index 8e6c790f..9fec8868 100644 --- a/crates/benilla-app/src/ui_script/bottom_hud_tests.rs +++ b/crates/benilla-app/src/ui_script/bottom_hud_tests.rs @@ -100,7 +100,7 @@ fn shipped_xml() -> Vec<(String, String)> { fn no_shipped_file_declares_its_own_copy_of_a_managed_offset() { let mut offences = Vec::new(); for (name, text) in shipped_xml() { - if name == "UIParent.xml" { + if name == r"Interface\FrameXML\UIParent.xml" { continue; // the owner: its var rows are where these numbers are defined } for (n, line) in without_xml_comments(&text).lines().enumerate() { @@ -208,11 +208,17 @@ const BOTTOM_EXEMPT: &[(&str, &str)] = &[ /// not need to. Failing here is not a bug report; it is a prompt to pick one. #[test] fn every_bottom_anchored_top_level_frame_is_accounted_for() { - let pass = std::fs::read_to_string(ui_dir().join("UIParent.xml")).expect("UIParent.xml"); + // The pass is the stock `UIParent.lua`'s since 1988 — read off the player's chain. + let _data = benilla_formats::wow_data_or_skip!(); + let pass = String::from_utf8_lossy( + &super::reference_ui::read(r"Interface\FrameXML\UIParent.lua") + .expect("the stock UIParent.lua off the chain"), + ) + .into_owned(); for f in MANAGED_FRAMES { assert!( pass.contains(f), - "{f} is listed here as managed but does not appear in UIParent.xml's pass — this \ + "{f} is listed here as managed but does not appear in UIParent.lua's pass — this \ list has drifted from the file it mirrors" ); } @@ -222,8 +228,7 @@ fn every_bottom_anchored_top_level_frame_is_accounted_for() { let doc = benilla_ui::framexml::parse(&text).unwrap_or_else(|e| panic!("{file}: {e}")); for (name, anchors) in bottom_anchored_top_level(&doc) { let known = MANAGED_FRAMES.contains(&name.as_str()) - || BOTTOM_EXEMPT.iter().any(|(n, _)| *n == name) - || registers_a_listener(&text, &name); + || BOTTOM_EXEMPT.iter().any(|(n, _)| *n == name); if !known { unaccounted.push(format!("{file}: {name} ({anchors})")); } @@ -233,21 +238,12 @@ fn every_bottom_anchored_top_level_frame_is_accounted_for() { unaccounted.is_empty(), "these top-level frames anchor to the screen's bottom edge but nothing decides their \ clearance over the action bars. Give each one a row in \ - UIPARENT_MANAGED_FRAME_POSITIONS (UIParent.xml), or seat it from a managed global and \ - register UIParent_RegisterManagedPositionListener, or add it to BOTTOM_EXEMPT here with \ - the reason it needs neither (decision 1499):\n{}", + UIPARENT_MANAGED_FRAME_POSITIONS (the stock UIParent.lua's table, since 1988), or add \ + it to BOTTOM_EXEMPT here with the reason it needs no row (decision 1499):\n{}", unaccounted.join("\n") ); } -/// A file that registers a managed-position listener is seating something itself; the frames it -/// declares are covered by that registration. Coarse on purpose — the listener seats a whole -/// family (the bag stack is five windows), and naming each one here would be the same drift trap -/// `MANAGED_FRAMES` guards against. -fn registers_a_listener(text: &str, _frame: &str) -> bool { - text.contains("UIParent_RegisterManagedPositionListener") -} - /// Every top-level INSTANCE (a ``/`