A double load, a stand-in that won a race, and the unified ActionButtonUp
Three of these are the harness telling the truth about load order, which is worth more than the tests they unblocked: - **The load ledger now guards a file a kit names twice**, not just a prerequisite. A second load builds a second frame under the same name, the global keeps one of them, and the reference's own `parent == MultiBarRight` (`ActionButton.lua:363`) then reads false for a button whose parent really is the bar. The toc names each file once; a kit should too. - **`MultiActionBars.xml` belongs before `BonusActionBarFrame.xml`** (toc rows 48 and 84), and the order is load-bearing: `ShapeshiftBar_OnLoad` runs `UIParent_ManageFramePositions` at load, whose stand-in pass seats a hidden parentless frame for every managed name still absent — `MultiBarRight` among them — and a frame's publish to `_G` never overwrites (`0x701bd0`). The stand-in kept the global for good and the anchor fork read the wrong side. - **`paperdollframe.xml` must not name `UIDropDownMenu.lua`** beside its `.xml`. The XML brings its own `<Script file=>`; naming the chunk again **re-ran** it, and the file declares `UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT = nil` at scope while `DropDownList1`'s OnLoad is what fills it from Button1's font. The second run put the nil back, and then every dropdown row went to `SetFont(STANDARD_TEXT_FONT, nil)` — a usage refusal that took out a whole file's OnLoads, seven at a time. The ledger could not catch it: a `<Script file=>` chunk never passes through `load_entry`. `uidropdownmenu.xml` also gained `Fonts.xml` as a prerequisite for the same height. - **`ActionButtonUp` is one body now** (`ActionButton.lua:27-46`): it picks the bonus or the normal button and passes the caller's own `onSelf` through. Stock's separate BONUS branch handed `UseAction` a literal 0, so ALT-1 in a stance drove the bonus page without self-casting. Gone. - The bonus bar is driven by an offset plus `UPDATE_BONUS_ACTIONBAR`, not a bare `Show()`: the event is the only thing that runs `ActionButton_Update` on that row, and a button registers `ACTIONBAR_UPDATE_COOLDOWN` only inside its `HasAction` arm — while the event's own handler hides the bar again when the offset is 0, so both halves are needed. - And `tooltip_compare`'s player needed the name `CharacterFrame_OnShow` gsubs unguarded. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -573,8 +573,15 @@ fn a_cooldown_count_draws_over_the_bonus_bars_sweep() {
|
||||
consumable: false,
|
||||
}),
|
||||
);
|
||||
// Driven the way the server drives it, not by a bare `Show()`: an offset, then
|
||||
// `UPDATE_BONUS_ACTIONBAR`. Both halves are needed. The event is what runs
|
||||
// `ActionButton_Update` on the bonus row, and only there does a button register the cooldown
|
||||
// event at all — inside the `HasAction` arm (`ActionButton.lua:137-155`) — so a bar merely
|
||||
// shown carries no action and never hears `ACTIONBAR_UPDATE_COOLDOWN`. And the offset is what
|
||||
// keeps the bar up: the event's own handler hides it again when `GetBonusBarOffset()` is 0.
|
||||
s.set_bonus_bar_offset(1);
|
||||
s.fire_event("PLAYER_ENTERING_WORLD", vec![]);
|
||||
s.run("BonusActionBarFrame:Show()").unwrap();
|
||||
s.fire_event("UPDATE_BONUS_ACTIONBAR", vec![]);
|
||||
s.tick(10.0);
|
||||
s.set_action_state(
|
||||
73,
|
||||
@@ -1700,10 +1707,12 @@ fn bonus_bar_slides_up_with_sound_and_down_without() {
|
||||
.into_iter()
|
||||
.map(|u| (u.action, u.on_self))
|
||||
.collect::<Vec<_>>(),
|
||||
vec![(73, false)],
|
||||
"the BONUS branch of stock ActionButtonUp passes a literal 0 for onSelf (ActionButton.lua:38) \
|
||||
— in a stance, ALT-1 drives the bonus page without self-cast; the reference's own rule, \
|
||||
not the (73, true) a self-cast would give"
|
||||
vec![(73, true)],
|
||||
"the realm unified `ActionButtonUp`: one body picks the bonus or the normal button and \
|
||||
then passes the caller's own `onSelf` through to `UseAction` \
|
||||
(`ActionButton.lua:27-46`). Stock 1.12 had a separate BONUS branch that handed `UseAction` \
|
||||
a literal 0, so ALT-1 in a stance drove the bonus page *without* self-casting — that \
|
||||
quirk is gone, and a self-cast binding self-casts on the bonus bar too."
|
||||
);
|
||||
s.run("BonusActionBarFrame:Hide() ActionButtonDown(1) ActionButtonUp(1, 1)")
|
||||
.unwrap();
|
||||
|
||||
@@ -107,11 +107,17 @@ const PREREQS: &[(&str, &[&str])] = &[
|
||||
],
|
||||
),
|
||||
(
|
||||
// The `.xml` only. It brings its own `<Script file="UIDropDownMenu.lua">`, and listing the
|
||||
// chunk here as well **re-runs** it: the file declares
|
||||
// `UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT = nil` at scope (`UIDropDownMenu.lua:16`) and
|
||||
// `DropDownList1`'s OnLoad is what fills it in from Button1's own font
|
||||
// (`UIDropDownMenu.xml:11`). A second chunk run after that OnLoad put the nil back, and
|
||||
// then every dropdown row `UIDropDownMenu_AddButton` painted hit
|
||||
// `SetFont(STANDARD_TEXT_FONT, nil)` — which is a usage refusal, so a whole file's OnLoads
|
||||
// failed one after another. The load ledger cannot catch it: a `<Script file=>` chunk goes
|
||||
// through the loader, not through `load_entry`, so it is not in the ledger to begin with.
|
||||
"paperdollframe.xml",
|
||||
&[
|
||||
r"Interface\FrameXML\UIDropDownMenu.lua",
|
||||
r"Interface\FrameXML\UIDropDownMenu.xml",
|
||||
],
|
||||
&[r"Interface\FrameXML\UIDropDownMenu.xml"],
|
||||
),
|
||||
(
|
||||
"containerframe.xml",
|
||||
@@ -121,6 +127,12 @@ const PREREQS: &[(&str, &[&str])] = &[
|
||||
"merchantframe.xml",
|
||||
&[r"Interface\FrameXML\ItemButtonTemplate.xml"],
|
||||
),
|
||||
// `DropDownList1`'s OnLoad reads `DropDownList1Button1NormalText:GetFont()` for
|
||||
// `UIDROPDOWNMENU_DEFAULT_TEXT_HEIGHT` (`UIDropDownMenu.xml:11`), and every row
|
||||
// `UIDropDownMenu_AddButton` paints goes through `SetFont(STANDARD_TEXT_FONT, that)`
|
||||
// (`UIDropDownMenu.lua:199`), which refuses a nil height. The realm's toc has `Fonts.xml` at
|
||||
// row 14 and this file at 26, so the live client always has the fonts first.
|
||||
("uidropdownmenu.xml", &[r"Interface\FrameXML\Fonts.xml"]),
|
||||
// `CONTINENTS_LENGTH = 2`, the realm's two-continent map (`Overrides.lua:1`, toc row 13 — 72
|
||||
// rows above this file). `WorldMap_UpdateContinentDropDownText` and its zone twin compare the
|
||||
// current continent against it unguarded (`WorldMapFrame.lua:440`, `:457`), so a harness that
|
||||
@@ -189,6 +201,13 @@ const PREREQS: &[(&str, &[&str])] = &[
|
||||
/// loaded again for the next — and these are not virtual-only files: `MoneyInputFrame.xml`,
|
||||
/// `UIDropDownMenu.xml` and `ItemButtonTemplate.xml` each build concrete frames too, so a second
|
||||
/// load would duplicate them.
|
||||
///
|
||||
/// It guards a file a kit names **twice** as well, which is not hypothetical tidiness: a second
|
||||
/// load builds a second frame under the same name, the global keeps one of them, and a parent
|
||||
/// comparison in the reference's own Lua (`parent == MultiBarRight`,
|
||||
/// `ActionButton.lua:363`) then reads false for a button whose parent *is* named that. That is
|
||||
/// what a double XML load looks like from inside a test, and the reference never does one — the
|
||||
/// toc names each file once.
|
||||
const LOAD_LEDGER: &str = "__benilla_test_loaded";
|
||||
|
||||
fn already_seated(s: &UiScript, path: &str) -> bool {
|
||||
@@ -223,6 +242,9 @@ fn load_entry(s: &UiScript, entry: &str, strict_templates: bool, no_warnings: bo
|
||||
}
|
||||
}
|
||||
}
|
||||
if already_seated(s, &path) {
|
||||
return 0;
|
||||
}
|
||||
mark_seated(s, &path);
|
||||
let bytes = read(&path).unwrap_or_else(|| panic!("{entry}: not found"));
|
||||
if path.to_ascii_lowercase().ends_with(".lua") {
|
||||
|
||||
@@ -259,6 +259,8 @@ fn action_button_hover_takes_the_default_corner() {
|
||||
"Interface\\FrameXML\\Fonts.xml",
|
||||
r"Interface\FrameXML\UIParent.xml",
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\TextStatusBar.lua",
|
||||
"Interface\\FrameXML\\TextStatusBar.xml",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
@@ -297,8 +299,15 @@ fn ubertooltips_off_seats_action_bar_plates_beside_the_button() {
|
||||
"Interface\\FrameXML\\GlobalStrings.lua",
|
||||
"Interface\\FrameXML\\MainMenuBar.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
// Before `BonusActionBarFrame.xml`, as the realm's toc has it (rows 48 and 84), and the
|
||||
// order is load-bearing: `ShapeshiftBar_OnLoad` runs `UIParent_ManageFramePositions` at
|
||||
// load, whose stand-in pass seats a hidden, parentless frame for any managed name that is
|
||||
// still absent — `MultiBarRight` among them. A frame's publish to `_G` never overwrites
|
||||
// (`0x701bd0`), so a stand-in seated first keeps the global for good, and
|
||||
// `ActionButton_SetTooltip`'s `parent == MultiBarRight` (`ActionButton.lua:363`) then
|
||||
// reads false for a button whose parent really is the bar.
|
||||
"Interface\\FrameXML\\MultiActionBars.xml",
|
||||
"Interface\\FrameXML\\BonusActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\ActionBarFrame.xml",
|
||||
"Interface\\FrameXML\\UIDropDownMenu.xml",
|
||||
"ScrollTemplates.xml",
|
||||
r"Interface\FrameXML\UIPanelTemplates.lua",
|
||||
@@ -308,7 +317,6 @@ fn ubertooltips_off_seats_action_bar_plates_beside_the_button() {
|
||||
"Interface\\FrameXML\\StaticPopup.xml",
|
||||
"KeyBindingsPage.xml",
|
||||
"OptionsFrame.xml",
|
||||
"Interface\\FrameXML\\MultiActionBars.xml",
|
||||
]);
|
||||
s.register_cvars(crate::cvars::registered_pairs());
|
||||
s.set_cvar_engine("UberTooltips", "0");
|
||||
|
||||
@@ -47,6 +47,9 @@ const ROUTER_UI: [&str; 4] = [
|
||||
fn player() -> UnitState {
|
||||
UnitState {
|
||||
exists: true,
|
||||
// `CharacterFrame_OnShow` gsubs `UnitPVPName("player")` unguarded
|
||||
// (`CharacterFrame.lua:85`), and `gsub` raises on a nil first argument.
|
||||
name: Some("Gromgosh".into()),
|
||||
level: 60,
|
||||
race: Some("Human".into()),
|
||||
race_file: Some("Human".into()),
|
||||
|
||||
Reference in New Issue
Block a user