A drag release fires OnDragStop and the drop, not OnMouseUp (#928)
A release that ended a started drag fired the frame's `OnMouseUp` before `OnDragStop`, so a frame that both drags and acts on mouse-up ran its mouse-up action at the end of every drag. In the reference, the mouse-up first checks the frame's drag registration for the released button and its started drag: if both hold it fires `OnDragStop` and the hover frame's `OnReceiveDrag`, and returns before `OnMouseUp`, which only a release without a started drag reaches. The release now follows that: a started drag fires `OnDragStop` and the drop in `OnMouseUp`'s place, and any other release fires `OnMouseUp` as before. Tests cover a drag, a plain click, a button the frame is not registered to drag, and a resize grip. Fixes #920 Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5.5
parent
eec1804d37
commit
b0ad656aaa
@@ -183,6 +183,101 @@ mod tests {
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
}
|
||||
|
||||
/// A frame whose every mouse script appends to `log`, registered for a LeftButton drag.
|
||||
fn logged_drag_frame() -> UiScript {
|
||||
let mut s = drag_script();
|
||||
s.run(
|
||||
r#"
|
||||
log = {}
|
||||
local function note(name) return function() table.insert(log, name) end end
|
||||
local a = CreateFrame("Frame", "A")
|
||||
a:SetPoint("BOTTOMLEFT", 0, 0); a:SetWidth(400); a:SetHeight(600); a:EnableMouse(true)
|
||||
a:RegisterForDrag("LeftButton")
|
||||
a:SetScript("OnMouseDown", note("down"))
|
||||
a:SetScript("OnMouseUp", note("up"))
|
||||
a:SetScript("OnDragStart", note("dragstart"))
|
||||
a:SetScript("OnDragStop", note("dragstop"))
|
||||
a:SetScript("OnReceiveDrag", note("receive"))
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
s.resolve();
|
||||
s
|
||||
}
|
||||
|
||||
fn log(s: &UiScript) -> String {
|
||||
s.eval::<String>("return table.concat(log, ' ')").unwrap()
|
||||
}
|
||||
|
||||
/// `0x76c040` on a release for a drag-registered button whose drag has started clears the arm,
|
||||
/// fires `OnDragStop` (`0x76c06e`) and the hover frame's drop (`0x76c081`) and returns 1
|
||||
/// (`0x76c08f`), never reaching its `OnMouseUp` (`0x76c099`).
|
||||
#[test]
|
||||
fn a_drag_release_fires_dragstop_and_no_mouseup() {
|
||||
let mut s = logged_drag_frame();
|
||||
s.mouse_button(100.0, 300.0, "LeftButton", true);
|
||||
s.mouse_move(130.0, 300.0); // 30 units: past the threshold
|
||||
s.mouse_button(130.0, 300.0, "LeftButton", false);
|
||||
assert_eq!(log(&s), "down dragstart dragstop receive");
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
}
|
||||
|
||||
/// A press that never crossed the threshold falls through to `OnMouseUp` (`0x76c067` je).
|
||||
#[test]
|
||||
fn a_plain_click_on_a_drag_frame_fires_mouseup() {
|
||||
let mut s = logged_drag_frame();
|
||||
s.mouse_button(100.0, 300.0, "LeftButton", true);
|
||||
s.mouse_button(100.0, 300.0, "LeftButton", false);
|
||||
assert_eq!(log(&s), "down up");
|
||||
}
|
||||
|
||||
/// The gate `0x76c050` tests the released button against the frame's registered buttons: a
|
||||
/// frame registered for LeftButton drag still gets `OnMouseUp` for a RightButton release.
|
||||
#[test]
|
||||
fn a_button_not_registered_for_drag_still_fires_mouseup() {
|
||||
let mut s = logged_drag_frame();
|
||||
s.mouse_button(100.0, 300.0, "RightButton", true);
|
||||
s.mouse_move(130.0, 300.0);
|
||||
s.mouse_button(130.0, 300.0, "RightButton", false);
|
||||
assert_eq!(log(&s), "down up");
|
||||
}
|
||||
|
||||
/// A stock resize grip starts sizing in `OnMouseDown` and is not drag-registered, so its
|
||||
/// `OnMouseUp` (which stops the sizing) fires even after the cursor left it.
|
||||
#[test]
|
||||
fn a_resize_grip_still_gets_mouseup() {
|
||||
let mut s = drag_script();
|
||||
s.run(
|
||||
r#"
|
||||
ups = 0
|
||||
local f = CreateFrame("Frame", "Sized", UIParent)
|
||||
f:SetResizable(true)
|
||||
f:SetPoint("BOTTOMLEFT", 100, 100); f:SetWidth(200); f:SetHeight(100)
|
||||
local g = CreateFrame("Button", "Grip", f)
|
||||
g:SetPoint("BOTTOMRIGHT", 0, 0); g:SetWidth(16); g:SetHeight(16)
|
||||
g:EnableMouse(true)
|
||||
g:SetScript("OnMouseDown", function() Sized:StartSizing("BOTTOMRIGHT") end)
|
||||
g:SetScript("OnMouseUp", function() ups = ups + 1; Sized:StopMovingOrSizing() end)
|
||||
"#,
|
||||
)
|
||||
.unwrap();
|
||||
s.resolve();
|
||||
s.mouse_button(295.0, 105.0, "LeftButton", true);
|
||||
s.mouse_move(395.0, 55.0); // dragged 100 right, 50 down, off the grip
|
||||
s.mouse_button(395.0, 55.0, "LeftButton", false);
|
||||
s.resolve();
|
||||
assert_eq!(s.eval::<i64>("return ups").unwrap(), 1);
|
||||
assert_eq!(s.eval::<f32>("return Sized:GetWidth()").unwrap(), 300.0);
|
||||
s.mouse_move(500.0, 300.0);
|
||||
s.resolve();
|
||||
assert_eq!(
|
||||
s.eval::<f32>("return Sized:GetWidth()").unwrap(),
|
||||
300.0,
|
||||
"the sizing ended on the release"
|
||||
);
|
||||
assert!(s.errors().is_empty(), "{:?}", s.errors());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drag_not_started_leaves_the_ordinary_click_path_untouched() {
|
||||
let mut s = drag_script();
|
||||
|
||||
@@ -464,9 +464,34 @@ impl UiScript {
|
||||
// the hover frame, so a press on A released over B fires `OnMouseUp` on A, and a press
|
||||
// that captured nothing fires nothing (`0x766498`). A resize grip dragged out from under
|
||||
// the cursor still gets its `OnMouseUp`, so its resize ends.
|
||||
let script = if down { "OnMouseDown" } else { "OnMouseUp" };
|
||||
if let Some(id) = if down { press_id } else { captured_id } {
|
||||
if let Err(e) = event::fire_widget_handler(&self.lua, id, script, vec![btn.clone()]) {
|
||||
//
|
||||
// The frame's mouse-up `0x76c040` runs the drag leg first: a release of a button the frame
|
||||
// is registered to drag (`0x76c050`), with the drag started (`0x76c055`), fires
|
||||
// `OnDragStop` (`0x76c06e`) and the hover frame's `OnReceiveDrag` (`0x76c081`) and returns
|
||||
// 1 (`0x76c08f`) before the `OnMouseUp` leg (`0x76c099`). So a drag release fires no
|
||||
// `OnMouseUp`; a release of a button the frame does not drag, or of a drag that never
|
||||
// started, still does.
|
||||
if down {
|
||||
if let Some(id) = press_id {
|
||||
if let Err(e) =
|
||||
event::fire_widget_handler(&self.lua, id, "OnMouseDown", vec![btn.clone()])
|
||||
{
|
||||
self.push_error(e);
|
||||
}
|
||||
}
|
||||
} else if let Some(release) = drag_release.as_ref().filter(|r| r.started) {
|
||||
self.fire_drag_stop(release.source);
|
||||
if let Some(id) = hit_id {
|
||||
if let Err(e) =
|
||||
event::fire_widget_handler(&self.lua, id, "OnReceiveDrag", Vec::new())
|
||||
{
|
||||
self.push_error(e);
|
||||
}
|
||||
}
|
||||
} else if let Some(id) = captured_id {
|
||||
if let Err(e) =
|
||||
event::fire_widget_handler(&self.lua, id, "OnMouseUp", vec![btn.clone()])
|
||||
{
|
||||
self.push_error(e);
|
||||
}
|
||||
}
|
||||
@@ -523,16 +548,6 @@ impl UiScript {
|
||||
if let Some(source) = abandoned {
|
||||
self.fire_drag_stop(source);
|
||||
}
|
||||
if let Some(release) = drag_release.filter(|r| r.started) {
|
||||
self.fire_drag_stop(release.source);
|
||||
if let Some(id) = hit_id {
|
||||
if let Err(e) =
|
||||
event::fire_widget_handler(&self.lua, id, "OnReceiveDrag", Vec::new())
|
||||
{
|
||||
self.push_error(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(id) = click_id {
|
||||
// Shared with `Click()`: gated on the Button being enabled; a CheckButton toggles
|
||||
// before `OnClick` fires.
|
||||
@@ -554,7 +569,7 @@ impl UiScript {
|
||||
}
|
||||
|
||||
/// The Button release slot `0x7792d0` past its base (`0x76c040`: `OnMouseUp`, which the caller
|
||||
/// has fired), on the frame that took the press: a PUSHED button (`0x7792fc`) with the cursor
|
||||
/// has fired; a started drag returns 1 there and never reaches this), on the frame that took the press: a PUSHED button (`0x7792fc`) with the cursor
|
||||
/// inside its own rect (`0x779335`), not "the topmost frame under the cursor", and registered
|
||||
/// for this button's Up (`0x77934e`) fires `OnDoubleClick` (`0x77938d`) or `OnClick`
|
||||
/// (`0x7793a9`); only then does the tail set NORMAL (`0x7793de`) if the button is unlocked and
|
||||
|
||||
Reference in New Issue
Block a user