A MessageFrame's link is text; only chat lines take link buttons (#1149)

- A MessageFrame's link is text; only chat lines take link buttons
- set_spans' doc says only a ScrollingMessageFrame's spans take buttons

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Sam <samuv.eth@gmail.com>
This commit is contained in:
Mathih13
2026-10-14 09:57:15 +02:00
committed by GitHub
parent 5338675fc8
commit b15fb9abc6
3 changed files with 93 additions and 8 deletions
@@ -480,6 +480,11 @@ mod caret_tests {
fn app_with(lua: &str) -> App {
let script = UiScript::new().unwrap();
script.run(lua).unwrap();
app_on(script)
}
/// The whole UI pass on a 1024×768 window (`s = 1`) over `script`.
pub(super) fn app_on(script: UiScript) -> App {
let mut app = App::new();
app.insert_non_send_resource(script);
app.insert_resource(UiFontAtlas::for_test(1.0).expect("the client faces open"));
@@ -625,3 +630,56 @@ mod caret_tests {
}
}
}
/// A message line's links through the whole UI pass, on the stock `UIErrorsFrame`.
#[cfg(test)]
mod link_span_tests {
use benilla_ui::script::UiScript;
use super::caret_tests::app_on;
use crate::ui_script::test_ui::load_ui;
const LINE: &str = "|cff1eff00|Hitem:7073:0:0:0|h[Broken Fang]|h|r";
/// Stock `UIErrorsFrame` is a `<MessageFrame>` (`UIErrorsFrame.xml:4`), whose update
/// (`0x786200`) never takes a link button (`0x7a3880`), so its link line takes no mouse; a
/// ScrollingMessageFrame's same line does (`0x788ce7`).
#[test]
fn a_link_in_the_stock_errors_frame_takes_no_mouse() {
let _data = benilla_formats::wow_data_or_skip!();
let script = UiScript::new().unwrap();
for file in [
"Interface\\FrameXML\\GlobalStrings.lua",
"Interface\\FrameXML\\Fonts.xml",
"Interface\\FrameXML\\UIParent.xml",
"Interface\\FrameXML\\UIErrorsFrame.xml",
] {
load_ui(&script, file);
}
script
.run(&format!(
r#"
UIErrorsFrame:AddMessage("{LINE}", 1.0, 0.1, 0.1, 1.0)
CF = CreateFrame("ScrollingMessageFrame", "CF", UIParent)
CF:SetPoint("BOTTOMLEFT", 32, 100); CF:SetWidth(430); CF:SetHeight(120)
CF:SetFontObject(ChatFontNormal)
CF:AddMessage("{LINE}")
"#
))
.unwrap();
let app = app_on(script);
let s = app.world().non_send_resource::<UiScript>();
assert_eq!(
s.eval::<i64>("return CF:GetNumChildren()").unwrap(),
1,
"the chat line's link has its button"
);
assert_eq!(
s.eval::<i64>("return UIErrorsFrame:GetNumChildren()")
.unwrap(),
0,
"the error line's link is text"
);
assert!(s.errors().is_empty(), "{:?}", s.errors());
}
}
+9 -4
View File
@@ -78,9 +78,9 @@ fn fire(lua: &Lua, owner: FrameHandle, script: &str, args: &[&str]) {
}
}
/// Replace the spans on screen, `(text frame, y-up rect, link, text)`: each text frame's spans take
/// its pooled buttons in order, a new one built when the pool runs out, and the buttons left over,
/// or of a text frame with no span now, hide.
/// Replace the spans on screen, `(text frame, y-up rect, link, text)`: each ScrollingMessageFrame's
/// spans take its pooled buttons in order, a new one built when the pool runs out, and the buttons
/// left over, or of a text frame with no span now, hide. A MessageFrame's spans stay text.
pub(super) fn set_spans(lua: &Lua, spans: Vec<(FrameHandle, Rect, String, String)>) {
// One span: its rect, link and text.
type Span = (Rect, String, String);
@@ -100,7 +100,12 @@ pub(super) fn set_spans(lua: &Lua, spans: Vec<(FrameHandle, Rect, String, String
.link_buttons
.retain(|owner, _| model.arena.frame(*owner).is_some());
for (owner, list) in &by_owner {
if model.arena.frame(*owner).is_none() {
// Only the SMF (`0x788ce7`) and SimpleHTML (`0x78b052`) take one; ours draws no links.
if !model
.arena
.frame(*owner)
.is_some_and(|f| matches!(f.kind_state, KindState::ScrollingMessage(_)))
{
continue;
}
for (i, (rect, link, text)) in list.iter().enumerate() {
@@ -29,14 +29,14 @@ fn chat() -> UiScript {
)
.unwrap();
s.resolve();
set_link(&mut s);
set_link(&mut s, "CF");
s
}
fn set_link(s: &mut UiScript) {
let cf = s.model_ref().arena.lookup("CF").expect("CF");
fn set_link(s: &mut UiScript, owner: &str) {
let owner = s.model_ref().arena.lookup(owner).expect("owner");
s.set_link_spans(vec![(
cf,
owner,
Rect::new(120.0, 110.0, 134.0, 160.0),
LINK.to_string(),
MARKUP.to_string(),
@@ -140,3 +140,25 @@ fn a_hittable_body_title_drags_but_its_link_still_clicks() {
assert!(s.eval::<bool>("return CF:IsUserPlaced()").unwrap());
assert!(s.errors().is_empty(), "{:?}", s.errors());
}
/// The take `0x7a3880` is called from the ScrollingMessageFrame and SimpleHTML updates alone; the
/// MessageFrame's update (`0x786200`) reaches neither, so a span on one builds no button and the
/// pointer over it passes through.
#[test]
fn a_message_frame_line_gets_no_link_button() {
let mut s = script();
s.set_screen_size(800.0, 600.0);
s.run(
r#"
EF = CreateFrame("MessageFrame", "EF")
EF:SetPoint("BOTTOMLEFT", 100, 100); EF:SetWidth(430); EF:SetHeight(120)
"#,
)
.unwrap();
s.resolve();
set_link(&mut s, "EF");
assert_eq!(s.eval::<i64>("return EF:GetNumChildren()").unwrap(), 0);
s.mouse_move(130.0, 127.0);
assert!(s.eval::<bool>("return GetMouseFocus() == nil").unwrap());
assert!(s.errors().is_empty(), "{:?}", s.errors());
}