avm2: Suppress events for selectable was_static TextFields

TextFields have a very unusual behavior - if they are selectable
and have `was_static`, they *block* the dispatch of mouse events when
they're targeted (not even the Stage will receive the event).
This only occurs when the TextField is actually targeted
(which requires mouseEnabled=true). With mouseEnabled=false,
the event will be dispatched with an ancestor as the target,
following the usual logic.

Also, TextFields now properly propagate mouse picks to
their parent if mouseEnabled=false. Previously, setting
mouseEnabled=false for a TextField made all mouse picks
cause a miss on it. This was the cause of the Turbo Kids
regression.

Fixes #10245
This commit is contained in:
Aaron Hill 2023-05-22 15:21:36 -05:00
parent cc567c4e40
commit c53a903d35
12 changed files with 199 additions and 11 deletions

View File

@ -1932,14 +1932,15 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> {
_require_button_mode: bool,
) -> Avm2MousePick<'gc> {
// The text is hovered if the mouse is over any child nodes.
if self.visible()
&& self.mouse_enabled()
&& self.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK)
{
if self.was_static() {
Avm2MousePick::PropagateToParent
} else {
if self.visible() && self.hit_test_shape(context, point, HitTestOptions::MOUSE_PICK) {
// Note - for mouse-enabled selectable text, we consider this to be a hit (which
// will cause us to show the proper cursor on mouse over).
// However, in `Interactive::event_dispatch_to_avm2`, we will prevent mouse events
// from being fired at all if the text is selectable and 'was_static()'.
if self.mouse_enabled() && (self.is_selectable() || !self.was_static()) {
Avm2MousePick::Hit((*self).into())
} else {
Avm2MousePick::PropagateToParent
}
} else {
Avm2MousePick::Miss

View File

@ -222,6 +222,16 @@ pub trait TInteractiveObject<'gc>:
context: &mut UpdateContext<'_, 'gc>,
event: ClipEvent<'gc>,
) -> ClipEventResult {
// Flash appears to not fire events *at all* for a targeted EditText
// that was originally created by the timeline. Normally, one of the ancestors
// of the TextField would get targeted, but instead, the event isn't fired
// (not even the Stage receives the event)
if let Some(text) = self.as_displayobject().as_edit_text() {
if text.is_selectable() && text.was_static() {
return ClipEventResult::NotHandled;
}
}
let target = if let Avm2Value::Object(target) = self.as_displayobject().object2() {
target
} else {

View File

@ -1,4 +1,5 @@
Clicked at 522, 27
Clicked at 26, 373
Clicked at 477, 343
Clicked at 276, 197
Clicked at 21, 24 target: [object MovieClip] instance2
Clicked at 522, 27 target: [object MovieClip] click_mc
Clicked at 26, 373 target: [object MovieClip] click_mc
Clicked at 477, 343 target: [object MovieClip] click_mc
Clicked at 276, 197 target: [object MovieClip] click_mc

View File

@ -0,0 +1,50 @@
package {
import flash.display.MovieClip;
import flash.display.Sprite;
import flash.events.MouseEvent;
import flash.text.TextField;
public class Main extends MovieClip {
public function Main() {
super();
this.stage.addEventListener("mouseDown", function(e) {
trace("mouseDown: " + e.target + " at: " + e.stageX + " " + e.stageY);
});
var childClip = Sprite(this.getChildAt(0));
var firstTimelineText = childClip.getChildAt(0);
// Uncomment this line to verify that the coordinates in our input.json
// are correct (a new event should get generated)
//firstTimelineText.mouseEnabled = false;
var secondTimelineText = childClip.getChildAt(1);
trace("Setting mouseEnabled=false for: " + secondTimelineText.text);
trace("Before: secondTimelineText.selectable = " + secondTimelineText.selectable);
secondTimelineText.mouseEnabled = false;
trace("After: secondTimelineText.selectable = " + secondTimelineText.selectable);
// The timeline-add field remembers that it originally came from the timeline
//childClip.removeChild(firstTimelineText)
//childClip.addChildAt(firstTimelineText, 0)
var avmText = new TextField();
avmText.y = 100;
avmText.text = "Hello from AS3";
var avmTextNonSelectable = new TextField();
avmTextNonSelectable.selectable = false;
avmTextNonSelectable.x = 140;
avmTextNonSelectable.y = 100;
avmTextNonSelectable.text = "AS3 non-selectable";
childClip.addChild(avmText);
childClip.addChild(avmTextNonSelectable);
}
}
}

View File

@ -0,0 +1,117 @@
[
{
"type": "MouseMove",
"pos": [
208.45,
139.05
]
},
{
"type": "MouseDown",
"pos": [
208.45,
139.05
],
"btn": "Left"
},
{
"type": "MouseUp",
"pos": [
208.45,
139.05
],
"btn": "Left"
},
{
"type": "MouseMove",
"pos": [
202.45,
171.05
]
},
{
"type": "MouseDown",
"pos": [
202.45,
171.05
],
"btn": "Left"
},
{
"type": "MouseUp",
"pos": [
202.45,
171.05
],
"btn": "Left"
},
{
"type": "MouseMove",
"pos": [
208.45,
266.05
]
},
{
"type": "MouseDown",
"pos": [
208.45,
266.05
],
"btn": "Left"
},
{
"type": "MouseUp",
"pos": [
208.45,
266.05
],
"btn": "Left"
},
{
"type": "MouseMove",
"pos": [
211,
373
]
},
{
"type": "MouseDown",
"pos": [
211,
373
],
"btn": "Left"
},
{
"type": "MouseUp",
"pos": [
211,
373
],
"btn": "Left"
},
{
"type": "MouseMove",
"pos": [
375.45,
267.05
]
},
{
"type": "MouseDown",
"pos": [
375.45,
267.05
],
"btn": "Left"
},
{
"type": "MouseUp",
"pos": [
375.45,
267.05
],
"btn": "Left"
}
]

View File

@ -0,0 +1,8 @@
Setting mouseEnabled=false for: Second timeline text
Before: secondTimelineText.selectable = true
After: secondTimelineText.selectable = true
mouseDown: [object MovieClip] at: 202.45 171.05
mouseDown: [object TextField] at: 208.45 266.05
mouseDown: [object Stage] at: 211 373
mouseDown: [object TextField] at: 375.45 267.05

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1 @@
num_frames = 1