NTFS load order, a title-sorted AddOns list, and tooltips at the cursor
- addons load in the order NTFS lists a folder, case-insensitive with an underscore before the letters, the order the vanilla corpus was written against and the one that decides which addon provides a shared library (2178) - GetNumAddOns and the index form of every AddOn verb walk a title-sorted list that hides the Blizzard addons the server says to hide, and the character-select AddOns list is in title order (2175) - SetPoint and SetAllPoints raise the reference's own errors for a frame name that does not exist or an omitted relative frame instead of quietly anchoring to the parent, and an explicit nil means the screen root (2176) - a tooltip owned with ANCHOR_CURSOR follows the mouse, which pfUI's tooltip, xpbar and chat modules, pfQuest's browser and TipBuddy ask for; an omitted or unknown anchor is ANCHOR_LEFT (2176) - a $parent name handed to CreateFrame, CreateTexture or CreateFontString expands as it does in XML, so FonzAppraiser's thirty dropdowns and pfQuest's search icon get the names they asked for (2176) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -284,21 +284,15 @@ impl Loader<'_> {
|
||||
x.unwrap_or(0.0),
|
||||
y.unwrap_or(0.0),
|
||||
);
|
||||
// A target that is not built yet waits for the frame's subtree (`Loader::deferred_anchors`).
|
||||
if args
|
||||
.1
|
||||
.as_deref()
|
||||
.is_some_and(|n| !self.anchor_target_exists(n))
|
||||
{
|
||||
self.deferred_anchors.push(super::DeferredAnchor {
|
||||
wrapper: wrapper.clone(),
|
||||
region: false,
|
||||
args,
|
||||
dbg: dbg.to_string(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
self.call(wrapper, "SetPoint", args, dbg);
|
||||
let d = super::DeferredAnchor {
|
||||
wrapper: wrapper.clone(),
|
||||
region: false,
|
||||
args,
|
||||
dbg: dbg.to_string(),
|
||||
};
|
||||
// The XML path's own law, which resolves the name itself and defers a target
|
||||
// the enclosing frame's subtree has not built yet (`Loader::apply_anchor`).
|
||||
self.apply_anchor(d, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -975,10 +975,52 @@ impl Loader<'_> {
|
||||
/// Returns the frame's own name (`None` if it is anonymous) and the name of its nearest
|
||||
/// **named** ancestor — rf27 rule 3's walk, with [`framexml::DEFAULT_PARENT_NAME`] when there
|
||||
/// is none.
|
||||
/// Can a `relativeTo` name resolve right now — a frame in the arena or a named region?
|
||||
pub(super) fn anchor_target_exists(&self, name: &str) -> bool {
|
||||
let model = self.model();
|
||||
model.arena.lookup(name).is_some() || model.region_names.contains_key(name)
|
||||
pub(super) fn apply_anchor(&mut self, d: DeferredAnchor, may_defer: bool) {
|
||||
let rel: Value = match d.args.1.as_deref() {
|
||||
Some(name) => {
|
||||
match crate::script::object::anchor_args::resolve_xml_relative_to(self.lua(), name)
|
||||
{
|
||||
Some(t) => Value::Table(t),
|
||||
// Nothing by that name YET — a target declared later in the enclosing frame's
|
||||
// subtree. Hold it until the subtree is complete (`deferred_anchors`) and try
|
||||
// once more; a miss THEN is a real miss and takes the reference's leg.
|
||||
None if may_defer => {
|
||||
self.deferred_anchors.push(d);
|
||||
return;
|
||||
}
|
||||
None => {
|
||||
self.report
|
||||
.warnings
|
||||
.push(format!("{}: Couldn't find relative frame: {name}", d.dbg));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// No `relativeTo=`: `ebx` still holds `[ebp-0x14]`, the layout parent default that
|
||||
// `0x76785b call [edx+0xc]` (= `0x76c6e0`) seeded before the `<Anchor>` loop. A frame
|
||||
// with no parent gets the screen root there, which is what a Lua `nil` means here.
|
||||
None => match d
|
||||
.wrapper
|
||||
.call_method::<Option<Table>>("GetParent", ())
|
||||
.ok()
|
||||
.flatten()
|
||||
{
|
||||
Some(t) => Value::Table(t),
|
||||
None => Value::Nil,
|
||||
},
|
||||
};
|
||||
let DeferredAnchor {
|
||||
wrapper,
|
||||
region,
|
||||
args: (point, _, rel_point, x, y),
|
||||
dbg,
|
||||
} = d;
|
||||
let args = (point, rel, rel_point, x, y);
|
||||
if region {
|
||||
self.call_region(&wrapper, "SetPoint", args, &dbg);
|
||||
} else {
|
||||
self.call(&wrapper, "SetPoint", args, &dbg);
|
||||
}
|
||||
}
|
||||
|
||||
/// Apply the anchors deferred since `mark` (the enclosing `decorate`'s entry), in order. A
|
||||
@@ -986,11 +1028,7 @@ impl Loader<'_> {
|
||||
pub(super) fn drain_deferred_anchors(&mut self, mark: usize) {
|
||||
let pending: Vec<DeferredAnchor> = self.deferred_anchors.drain(mark..).collect();
|
||||
for d in pending {
|
||||
if d.region {
|
||||
self.call_region(&d.wrapper, "SetPoint", d.args, &d.dbg);
|
||||
} else {
|
||||
self.call(&d.wrapper, "SetPoint", d.args, &d.dbg);
|
||||
}
|
||||
self.apply_anchor(d, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -283,21 +283,15 @@ impl Loader<'_> {
|
||||
x.unwrap_or(0.0),
|
||||
y.unwrap_or(0.0),
|
||||
);
|
||||
// A target that is not built yet waits for the frame's subtree (`Loader::deferred_anchors`).
|
||||
if args
|
||||
.1
|
||||
.as_deref()
|
||||
.is_some_and(|n| !self.anchor_target_exists(n))
|
||||
{
|
||||
self.deferred_anchors.push(super::DeferredAnchor {
|
||||
wrapper: wrapper.clone(),
|
||||
region: true,
|
||||
args,
|
||||
dbg: dbg.to_string(),
|
||||
});
|
||||
continue;
|
||||
}
|
||||
self.call_region(wrapper, "SetPoint", args, dbg);
|
||||
let d = super::DeferredAnchor {
|
||||
wrapper: wrapper.clone(),
|
||||
region: true,
|
||||
args,
|
||||
dbg: dbg.to_string(),
|
||||
};
|
||||
// The XML path's own law, which resolves the name itself and defers a target
|
||||
// the enclosing frame's subtree has not built yet (`Loader::apply_anchor`).
|
||||
self.apply_anchor(d, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user