the mailbox takes typing again, and events carry their real arguments
- the mail window's send tab takes typing again: the subject, body and money boxes were taking no input while the recipient box swallowed every key, and a sent letter clears its subject and item icon instead of putting them straight back (2145) - seven events fire the way the reference does: UNIT_DYNAMIC_FLAGS fires at all, swapping two identical stacks between bank slots fires PLAYERBANKSLOTS_CHANGED twice, the pet sheet's training points repaint, and ITEM_LOCK_CHANGED loses its invented arguments (2140) - nine widget methods 1.12 addons probe for are in: ShaguTweaks stops recolouring textures the reference leaves alone, pfUI and _Nameplates stop raising on a tooltip's GetAnchorType, and 63 AceEvent addons get RegisterAllEvents (2142) - six post-1.12 methods leave the widget surface (SetSize, SetShown, HasFocus, SetFormattedText and two tooltip verbs), so an addon reaching for a later client's API finds what a real 1.12 client gives it (2142, 2146) Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -47,6 +47,37 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
// `RegisterAllEvents()` — this frame's `OnEvent` receives EVERY event that is dispatched
|
||||
// (`0x774c20`, table `0x878ec0`, argc 1, arity 0). Cleared by `UnregisterAllEvents` below, and
|
||||
// by nothing else: there is no `UnregisterAllEvents`-less way back out, which is exactly how
|
||||
// the reference's pair reads.
|
||||
//
|
||||
// **Latent, not live, and the widest-carried gap the corpus census found.** Nothing in either
|
||||
// addon corpus calls it *today*, but 63 vanilla addons and 6 of the top 20 ship
|
||||
// **AceEvent-2.0**, whose `AceEvent:RegisterAllEvents(handler)` runs
|
||||
// `AceEvent.frame:RegisterAllEvents()` the first time any addon asks for all events — so the
|
||||
// first one that does raised here. AceEvent is also why the *clearing* half is load-bearing
|
||||
// rather than tidy: while all-events is on it deliberately stops calling
|
||||
// `frame:UnregisterEvent(event)` at all, and `AceEvent:UnregisterAllEvents()` gets back to a
|
||||
// per-event registration by calling `frame:UnregisterAllEvents()` and then re-registering each
|
||||
// event it still wants. A `RegisterAllEvents` that outlived that call would leave every Ace2
|
||||
// addon on the whole event stream forever.
|
||||
//
|
||||
// Registered as a FLAG (the frame joins `Model::all_event_frames`), never as an expansion into
|
||||
// a name list — see that field for why, and `tick::fire_event_into` for the dispatch.
|
||||
m.set(
|
||||
"RegisterAllEvents",
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model");
|
||||
// Registration order, and re-registering keeps the original position — the same law
|
||||
// `RegisterEvent` above holds, for the same reason.
|
||||
if !model.all_event_frames.contains(&h) {
|
||||
model.all_event_frames.push(h);
|
||||
}
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
// `UnregisterAllEvents()` — drop every registration this frame holds, in one call.
|
||||
//
|
||||
// 10 corpus addons stop on it (decision 1195), and the idiom is why: an addon's "disable me"
|
||||
@@ -67,6 +98,9 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
list.retain(|x| x != &h);
|
||||
}
|
||||
}
|
||||
// …and the all-events registration, which is a registration like any other. See
|
||||
// `RegisterAllEvents` above: AceEvent-2.0's own re-registration path depends on this.
|
||||
model.all_event_frames.retain(|x| x != &h);
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
@@ -320,12 +354,15 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
/// * **`OnAttributeChanged`** (1 site, `Roid-Macros`) — **raising, permanently.** It is 2.0's secure
|
||||
/// frame/attribute system; there is no such slot in any 1.12 resolver. That addon is asking for a
|
||||
/// later client and should hear so.
|
||||
/// * **`OnHorizontalScroll` · `OnHyperlinkEnter` · `OnHyperlinkLeave` · `OnMessageScrollChanged` ·
|
||||
/// * **`OnHyperlinkEnter` · `OnHyperlinkLeave` · `OnMessageScrollChanged` ·
|
||||
/// `OnMovieFinished`/`ShowSubtitle`/`HideSubtitle` · `OnInputLanguageChanged`** — **raising.**
|
||||
/// Real 1.12 slots that we do not fire, and measured at **zero** call sites across the
|
||||
/// 218-addon corpus, so there is nothing to weigh against the trap: they land when their
|
||||
/// mechanism does (horizontal scroll isn't modeled at all — see
|
||||
/// [`crate::script::scrollframe`]'s module doc).
|
||||
/// mechanism does.
|
||||
/// * **`OnHorizontalScroll`** — **accepted, because `SetHorizontalScroll` fires it.** It was on
|
||||
/// the line above while "horizontal scroll isn't modeled at all", which stopped being true when
|
||||
/// the ScrollFrame's horizontal offset pair landed beside the vertical one
|
||||
/// ([`crate::script::scrollframe`]); `aux-addon` reads and writes both axes.
|
||||
/// * **`OnUpdateModel` · `OnAnimFinished`** — **accepted since decision 2007**, because the tick
|
||||
/// fires them: the model pane's scene clock runs in this engine now (`tick.rs`), `OnUpdateModel`
|
||||
/// at the top of every paint of a visible pane with a file (`0x76d1a0`) and `OnAnimFinished`
|
||||
|
||||
@@ -14,7 +14,10 @@ use super::{decode_id, draw_layer_from_str, frame_handle_of, frame_wrapper, stra
|
||||
|
||||
/// Populate `m`'s visibility/hierarchy/strata/backdrop/mouse methods (see the module doc).
|
||||
pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
// Show / Hide / visibility
|
||||
// Show / Hide / visibility. **`SetShown` is not here and must not come back**: the branchless
|
||||
// setter belongs to a later expansion — no 1.12 method table registers it, and neither the
|
||||
// stock chain nor either addon corpus calls it (decision 2142's census). A frame is shown or
|
||||
// hidden by the two verbs the era has.
|
||||
m.set(
|
||||
"Show",
|
||||
lua.create_function(|lua, this: Table| set_shown(lua, &this, true))?,
|
||||
@@ -23,21 +26,12 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
"Hide",
|
||||
lua.create_function(|lua, this: Table| set_shown(lua, &this, false))?,
|
||||
)?;
|
||||
// SetShown(bool) — the live API's branchless Show/Hide (a consensus call across the 0068
|
||||
// target addons; Lua truthiness, so SetShown(nil) hides).
|
||||
m.set(
|
||||
"SetShown",
|
||||
lua.create_function(|lua, (this, shown): (Table, Value)| {
|
||||
let show = !matches!(shown, Value::Nil | Value::Boolean(false));
|
||||
set_shown(lua, &this, show)
|
||||
})?,
|
||||
)?;
|
||||
m.set(
|
||||
"IsShown",
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.frame(h).map(|f| f.shown).unwrap_or(false),
|
||||
))
|
||||
})?,
|
||||
@@ -47,7 +41,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model
|
||||
.arena
|
||||
.frame(h)
|
||||
@@ -759,7 +753,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.is_mouse_enabled(h),
|
||||
))
|
||||
})?,
|
||||
@@ -795,7 +789,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.is_keyboard_enabled(h),
|
||||
))
|
||||
})?,
|
||||
@@ -863,7 +857,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.is_mouse_wheel_enabled(h),
|
||||
))
|
||||
})?,
|
||||
@@ -889,7 +883,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.is_clamped_to_screen(h),
|
||||
))
|
||||
})?,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
//! Frame method-table cluster: anchoring and size — `SetPoint`/`ClearAllPoints`/`GetPoint`/
|
||||
//! `SetAllPoints`/`SetWidth`/`SetHeight`/`SetSize`/`GetWidth`/`GetHeight` and the resolved-edge
|
||||
//! readers (`GetLeft`/`GetRight`/`GetTop`/`GetBottom`). Split out of [`super`] purely for size — see
|
||||
//! `SetAllPoints`/`SetWidth`/`SetHeight`/`GetWidth`/`GetHeight` and the resolved-edge readers
|
||||
//! (`GetLeft`/`GetRight`/`GetTop`/`GetBottom`). Split out of [`super`] purely for size — see
|
||||
//! its module doc for the shared id/handle plumbing and method-table wiring.
|
||||
|
||||
use mlua::{Lua, Table, Value};
|
||||
@@ -41,7 +41,7 @@ fn prefetch_relative_to(
|
||||
|
||||
/// Populate `m`'s layout (anchor/size) methods (see the module doc).
|
||||
pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
// Layout: SetPoint / ClearAllPoints / SetSize / SetWidth / SetHeight / GetWidth / GetHeight
|
||||
// Layout: SetPoint / ClearAllPoints / SetWidth / SetHeight / GetWidth / GetHeight
|
||||
m.set(
|
||||
"SetPoint",
|
||||
lua.create_function(
|
||||
@@ -209,23 +209,9 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
m.set(
|
||||
"SetSize",
|
||||
lua.create_function(|lua, (this, w, ht): (Table, f32, f32)| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model");
|
||||
let input = model.layout_inputs.entry(h).or_default();
|
||||
let changed =
|
||||
input.width.to_bits() != w.to_bits() || input.height.to_bits() != ht.to_bits();
|
||||
input.width = w;
|
||||
input.height = ht;
|
||||
if changed {
|
||||
model.touch_layout_frame(h);
|
||||
}
|
||||
model.note_authored_size(h);
|
||||
Ok(())
|
||||
})?,
|
||||
)?;
|
||||
// **No `SetSize`.** It is an Era geometry verb, in neither the Frame nor the Region method
|
||||
// table of 1.12 — and neither the stock chain nor either addon corpus writes it (decision
|
||||
// 2142's census). The two setters above are the era's whole size surface.
|
||||
m.set(
|
||||
"GetWidth",
|
||||
lua.create_function(|lua, this: Table| {
|
||||
@@ -370,7 +356,7 @@ pub(crate) fn eff_scale(model: &Model, h: FrameHandle) -> f32 {
|
||||
/// `GetWidth`/`GetHeight`: the resolved rect's span in LOCAL units (screen ÷ effective scale — the
|
||||
/// client returns the value as authored, and `SetWidth(w)` on a scaled frame resolves to `w·scale`
|
||||
/// screen px) if `resolve` has produced one, else the explicit size the frame was given
|
||||
/// (`SetWidth`/`SetHeight`/`SetSize`, already local) — matching the client's "0 = derive".
|
||||
/// (`SetWidth`/`SetHeight`, already local) — matching the client's "0 = derive".
|
||||
fn size_read(model: &Model, h: FrameHandle, width: bool) -> f32 {
|
||||
if let Some(r) = model.resolved.get(&h) {
|
||||
let span = if width { r.width() } else { r.height() };
|
||||
|
||||
@@ -405,11 +405,11 @@ fn set_flag(lua: &Lua, this: &Table, which: Flag, value: bool) -> mlua::Result<(
|
||||
}
|
||||
|
||||
/// `IsMovable`/`IsResizable`/`IsUserPlaced` — the NUMBER 1 or nil, never a boolean (1830,
|
||||
/// [`crate::script::binding_abi::predicate`]).
|
||||
/// [`crate::script::binding_abi::flag`]).
|
||||
fn get_flag(lua: &Lua, this: &Table, which: Flag) -> mlua::Result<Value> {
|
||||
let h = frame_handle_of(lua, this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
Ok(crate::script::binding_abi::flag(
|
||||
model.arena.frame(h).is_some_and(|f| match which {
|
||||
Flag::Movable => f.movable,
|
||||
Flag::Resizable => f.resizable,
|
||||
|
||||
@@ -132,9 +132,7 @@ pub(super) fn install(lua: &Lua, m: &Table) -> mlua::Result<()> {
|
||||
lua.create_function(|lua, this: Table| {
|
||||
let h = frame_handle_of(lua, &this)?;
|
||||
let model = lua.app_data_ref::<Model>().expect("model");
|
||||
Ok(crate::script::binding_abi::predicate(
|
||||
model.arena.is_toplevel(h),
|
||||
))
|
||||
Ok(crate::script::binding_abi::flag(model.arena.is_toplevel(h)))
|
||||
})?,
|
||||
)?;
|
||||
// Raise() — `0x775a50` → `0x76a5b0` → `0x7650f0(this, force = 1)`. Note it is legal on any
|
||||
|
||||
Reference in New Issue
Block a user