messageframe: a scroll that moves the view fires OnMessageScrollChanged
WIM sets this script on every window it builds — the history frame at load, then one per correspondent (six whisper windows in one session of his log) — and we refused the name by raise, which killed the rest of the builder function each time, not just the handler. It was on the deliberate "real 1.12 kinds nothing fires raise rather than silently accept" list. That rule is right and this was the wrong side of it: the slot now fires, from the ScrollingMessageFrame's own scroll verbs when a scroll actually moves the view, which is what a chat addon hangs its scroll-down arrow on. A scroll refused at either end moves nothing and says nothing; whether the reference fires one there is untraced. The raise list keeps its rule and its test — `OnInputLanguageChanged` and the movie frame's are still raises, and a name leaves that list only when something fires it. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SgstFWtn1RxEJ5kVrpgYwn
This commit is contained in:
@@ -59,19 +59,32 @@ fn scroll(
|
||||
op: impl FnOnce(&mut ScrollingMessageState, usize),
|
||||
) -> mlua::Result<()> {
|
||||
let h = frame_handle_of(lua, this)?;
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
let viewport_rows = UiScript::message_viewport_rows(&model, h);
|
||||
let frame = model
|
||||
.arena
|
||||
.frame_mut(h)
|
||||
.ok_or_else(|| mlua::Error::runtime("stale frame handle"))?;
|
||||
match &mut frame.kind_state {
|
||||
KindState::ScrollingMessage(smf) => {
|
||||
op(smf, viewport_rows);
|
||||
Ok(())
|
||||
let id = crate::script::object::decode_id(this)?;
|
||||
let moved = {
|
||||
let mut model = lua.app_data_mut::<Model>().expect("model app_data");
|
||||
let viewport_rows = UiScript::message_viewport_rows(&model, h);
|
||||
let frame = model
|
||||
.arena
|
||||
.frame_mut(h)
|
||||
.ok_or_else(|| mlua::Error::runtime("stale frame handle"))?;
|
||||
match &mut frame.kind_state {
|
||||
KindState::ScrollingMessage(smf) => {
|
||||
let before = smf.scroll_offset;
|
||||
op(smf, viewport_rows);
|
||||
smf.scroll_offset != before
|
||||
}
|
||||
_ => return Err(mlua::Error::runtime("not a ScrollingMessageFrame")),
|
||||
}
|
||||
_ => Err(mlua::Error::runtime("not a ScrollingMessageFrame")),
|
||||
};
|
||||
// `OnMessageScrollChanged`, with no model borrow alive: the handler re-enters Lua and may call
|
||||
// straight back in. The scroll frame's own slot, the one a chat addon hangs its scroll-down
|
||||
// arrow on (`WIM_HistoryFrameMessageListScrollingMessageFrame` and every whisper window it
|
||||
// builds). Only a scroll that *moved* fires; whether the reference also fires one refused at
|
||||
// an end is untraced.
|
||||
if moved {
|
||||
crate::script::event::fire_widget_handler(lua, id, "OnMessageScrollChanged", Vec::new())?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn install(lua: &Lua) -> mlua::Result<()> {
|
||||
@@ -890,6 +903,45 @@ mod tests {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
/// WIM's mechanism: it hangs its scroll-down arrow on the frame's own
|
||||
/// `OnMessageScrollChanged` and sets it on every window it builds, so the name has to be
|
||||
/// accepted *and* fired. A scroll refused at an end moved nothing and says nothing.
|
||||
#[test]
|
||||
fn a_scroll_that_moves_the_view_fires_the_frames_scroll_script() {
|
||||
let mut s = UiScript::new().unwrap();
|
||||
s.set_screen_size(800.0, 600.0);
|
||||
s.run(
|
||||
"local f = CreateFrame('ScrollingMessageFrame', 'CF')\n\
|
||||
f:SetPoint('BOTTOMLEFT', 32, 90)\n\
|
||||
f:SetWidth(430)\n\
|
||||
f:SetHeight(34)\n\
|
||||
scrolls = 0\n\
|
||||
f:SetScript('OnMessageScrollChanged', function() scrolls = scrolls + 1 end)",
|
||||
)
|
||||
.unwrap();
|
||||
for i in 1..=8 {
|
||||
s.run(&format!("CF:AddMessage('line {i}')")).unwrap();
|
||||
}
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return scrolls").unwrap(),
|
||||
0,
|
||||
"nothing scrolled yet"
|
||||
);
|
||||
|
||||
s.run("CF:ScrollUp()").unwrap();
|
||||
assert_eq!(s.eval::<i64>("return scrolls").unwrap(), 1);
|
||||
s.run("CF:ScrollToBottom()").unwrap();
|
||||
assert_eq!(s.eval::<i64>("return scrolls").unwrap(), 2);
|
||||
// Already at the bottom: refused, so no edge.
|
||||
s.run("CF:ScrollDown()").unwrap();
|
||||
assert_eq!(
|
||||
s.eval::<i64>("return scrolls").unwrap(),
|
||||
2,
|
||||
"a scroll that cannot move fires nothing"
|
||||
);
|
||||
assert!(s.take_errors().is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn lua_addmessage_and_scroll_surface() {
|
||||
let s = UiScript::new().unwrap();
|
||||
|
||||
Reference in New Issue
Block a user