Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions core/src/focus_tracker.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ impl<'gc> FocusTracker<'gc> {
&self,
new: Option<InteractiveObject<'gc>>,
context: &mut UpdateContext<'gc>,
should_reset_focus: bool,
) {
let old = self.0.focus.get();

Expand All @@ -117,6 +118,10 @@ impl<'gc> FocusTracker<'gc> {
return;
}

if !should_reset_focus {
return;
}

// When clicking an object that is not focusable by mouse,
// the real object will be used to dispatch focus change events,
// but `None` will be used when setting the focus.
Expand Down
12 changes: 9 additions & 3 deletions core/src/player.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1909,13 +1909,19 @@ impl Player {
|| int.is_focusable_by_mouse(context)
});
if should_focus {
tracker.set_by_mouse(pressed_object, context);
tracker.set_by_mouse(pressed_object, context, true);
} else if tracker
.get()
.is_some_and(|int| int.is_focusable_by_mouse(context))
{
// Need to clear the focus if an object focusable by mouse was un-focused.
tracker.set_by_mouse(None, context);
// In SWF version 9 and above, when the pressed object is not
// focusable, the focus is not cleared, but only in AVM2 does
// clicking the stage (pressed_object.is_none()) clear the focus.
// (See the avm1/focus_swf8, avm1/focus_swf9, and avm2/focus_mixed_avm
// tests for examples of this behavior)
let should_reset_focus = context.root_swf.version() < 9
|| (pressed_object.is_none() && context.root_swf.is_action_script_3());
tracker.set_by_mouse(None, context, should_reset_focus);
}
}

Expand Down
17 changes: 17 additions & 0 deletions tests/tests/swfs/avm1/focus_swf8/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{ "type": "Wait" },
{ "type": "Wait" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" }
]
15 changes: 15 additions & 0 deletions tests/tests/swfs/avm1/focus_swf8/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
null _level0.txt1
_level0.txt1 null
null _level0.txt1
_level0.txt1 null
btn clicked
null _level0.txt1
_level0.txt1 null
null _level0.txt1
_level0.txt1 _level0.txt2
_level0.txt2 null
null _level0.txt2
_level0.txt2 null
btn clicked
null _level0.txt2
_level0.txt2 null
34 changes: 34 additions & 0 deletions tests/tests/swfs/avm1/focus_swf8/test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
function drawRect(m, color) {
with(m) {
beginFill(color);
lineTo(200, 0);
lineTo(200, 50);
lineTo(0, 50);
lineTo(0, 0);
endFill();
}
}

var txt1 = this.createTextField("txt1", 1, 0, 0, 200, 50);
txt1.type = "input";

var txt2 = this.createTextField("txt2", 2, 0, 50, 200, 50);

this.createEmptyMovieClip("mc", 3);
mc._y = 100;
drawRect(mc, 0xFF0000);
mc.focusEnabled = true;
mc.tabEnabled = true;

this.createEmptyMovieClip("btn", 4);
btn._y = 150;
drawRect(btn, 0x00FF00);
btn.onRelease = function() {
trace("btn clicked");
}

function onSetFocus(oldfocus, newfocus) {
trace(oldfocus + " " + newfocus);
}

Selection.addListener(this);
Binary file added tests/tests/swfs/avm1/focus_swf8/test.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/avm1/focus_swf8/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_ticks = 3
17 changes: 17 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
[
{ "type": "Wait" },
{ "type": "Wait" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" }
]
4 changes: 4 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
null _level0.txt1
btn clicked
_level0.txt1 _level0.txt2
btn clicked
34 changes: 34 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9/test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
function drawRect(m, color) {
with(m) {
beginFill(color);
lineTo(200, 0);
lineTo(200, 50);
lineTo(0, 50);
lineTo(0, 0);
endFill();
}
}

var txt1 = this.createTextField("txt1", 1, 0, 0, 200, 50);
txt1.type = "input";

var txt2 = this.createTextField("txt2", 2, 0, 50, 200, 50);

this.createEmptyMovieClip("mc", 3);
mc._y = 100;
drawRect(mc, 0xFF0000);
mc.focusEnabled = true;
mc.tabEnabled = true;

this.createEmptyMovieClip("btn", 4);
btn._y = 150;
drawRect(btn, 0x00FF00);
btn.onRelease = function() {
trace("btn clicked");
}

function onSetFocus(oldfocus, newfocus) {
trace(oldfocus + " " + newfocus);
}

Selection.addListener(this);
Binary file added tests/tests/swfs/avm1/focus_swf9/test.swf
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/avm1/focus_swf9/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_ticks = 3
2 changes: 2 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9_loading_swf8/child.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
var txt = this.createTextField("txt", 1, 0, 0, 200, 50);
txt.type = "input";
Binary file not shown.
7 changes: 7 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9_loading_swf8/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[
{ "type": "Wait" },
{ "type": "Wait" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" }
]
2 changes: 2 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9_loading_swf8/output.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
null _level0.txt
_level0.txt _level0.child.txt
13 changes: 13 additions & 0 deletions tests/tests/swfs/avm1/focus_swf9_loading_swf8/test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
var txt = this.createTextField("txt", 1, 0, 0, 200, 50);
txt.type = "input";

this.createEmptyMovieClip("child", 3);
child._y = 50;

new MovieClipLoader().loadClip("child.swf", child);

function onSetFocus(oldfocus, newfocus) {
trace(oldfocus + " " + newfocus);
}

Selection.addListener(this);
Binary file not shown.
1 change: 1 addition & 0 deletions tests/tests/swfs/avm1/focus_swf9_loading_swf8/test.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
num_ticks = 3
96 changes: 96 additions & 0 deletions tests/tests/swfs/avm2/focus_mixed_avm/Test.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package {

import flash.display.MovieClip;
import flash.display.Loader;
import flash.events.FocusEvent;
import flash.net.URLRequest;
import flash.text.TextField;
import flash.text.TextFormat;
import flash.display.Sprite;
import flash.display.Shape;
import flash.display.SimpleButton;


public class Test extends MovieClip {


var loader:Loader = new Loader();

public function Test() {
super();
stage.addEventListener("mouseFocusChange", this.onMouseFocusChange);

loader.load(new URLRequest("avm1_child.swf"));
loader.y = 250
addChild(loader);

var txt1 = new TextField();
txt1.name = "txt1";
txt1.type = "input";
txt1.width = 200;
txt1.height = 50;
addChild(txt1);
txt1.addEventListener("focusIn", this.onFocusIn);
txt1.addEventListener("focusOut", this.onFocusOut);

var rectTab = new Sprite();
rectTab.name = "rectTabEnabled";
rectTab.graphics.beginFill(0xFF0000);
rectTab.graphics.drawRect(0, 0, 200, 50);
rectTab.graphics.endFill();
rectTab.y = 50;
rectTab.tabEnabled = true;
addChild(rectTab);
rectTab.addEventListener("focusIn", this.onFocusIn);
rectTab.addEventListener("focusOut", this.onFocusOut);

var rectBtn = new Sprite();
rectBtn.name = "rectButtonMode";
rectBtn.graphics.beginFill(0x0000FF);
rectBtn.graphics.drawRect(0, 0, 200, 50);
rectBtn.graphics.endFill();
rectBtn.y = 100;
rectBtn.buttonMode = true;
addChild(rectBtn);
rectBtn.addEventListener("focusIn", this.onFocusIn);
rectBtn.addEventListener("focusOut", this.onFocusOut);

var rectNorm = new Sprite();
rectNorm.name = "rectNormal";
rectNorm.graphics.beginFill(0x00FF00);
rectNorm.graphics.drawRect(0, 0, 200, 50);
rectNorm.graphics.endFill();
rectNorm.y = 150;
addChild(rectNorm);

var btnShape = new Shape();
btnShape.graphics.beginFill(0xFFFF00);
btnShape.graphics.drawRect(0, 0, 200, 50);
btnShape.graphics.endFill();

var btn = new SimpleButton(btnShape, btnShape, btnShape, btnShape);
btn.name = "simpleBtn";
btn.y = 200;
addChild(btn);
btn.addEventListener("focusIn", this.onFocusIn);
btn.addEventListener("focusOut", this.onFocusOut);
}

public function onFocusIn(e:FocusEvent) {
trace("focusIn", e.target.name, e.relatedObject);
}

public function onFocusOut(e:FocusEvent) {
trace("focusOut", e.target.name, e.relatedObject);
}

public function onMouseFocusChange(e:FocusEvent) {
if (e.relatedObject != null) {
trace("mouse " + e.relatedObject + " (" + e.relatedObject.name + ")");
} else {
trace("mouse " + e.relatedObject);
}
}
}

}
43 changes: 43 additions & 0 deletions tests/tests/swfs/avm2/focus_mixed_avm/avm1_child.as
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
function drawRect(m, color) {
with(m) {
beginFill(color);
lineTo(200, 0);
lineTo(200, 50);
lineTo(0, 50);
lineTo(0, 0);
endFill();
}
}

var txt = this.createTextField("txt", 1, 0, 0, 200, 50);
txt.type = "input";

this.createEmptyMovieClip("btn", 4);
btn._y = 50;
drawRect(btn, 0x00FFFF);
btn.onRelease = function() {
trace("btn clicked");
}

function onSetFocus(oldfocus, newfocus) {
if (newfocus == null && oldfocus == null) {
return;
}
if (oldfocus == txt) {
oldfocus = "avm1_child.txt"
}
if (newfocus == txt) {
newfocus = "avm1_child.txt"
}
trace("Selection.onSetFocus: " + oldfocus + " " + newfocus);
}

Selection.addListener(this);

txt.onSetFocus = function(o) {
trace("txt.onSetFocus " + o);
};

txt.onKillFocus = function(o) {
trace("txt.onKillFocus " + o);
};
Binary file added tests/tests/swfs/avm2/focus_mixed_avm/avm1_child.swf
Binary file not shown.
44 changes: 44 additions & 0 deletions tests/tests/swfs/avm2/focus_mixed_avm/input.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
[
{ "type": "Wait" },
{ "type": "Wait" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 1], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 1], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 51], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 51], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 101], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 101], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 151], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 151], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 201], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 201], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 301], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 301], "btn": "Left" },

{ "type": "MouseDown", "pos": [1, 251], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 251], "btn": "Left" },
{ "type": "MouseDown", "pos": [1, 351], "btn": "Left" }, { "type": "MouseUp", "pos": [1, 351], "btn": "Left" }
]
Loading
Loading