From 4b0e29e98c1ab181f9dfe028590e7833ea6bec7c Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Tue, 4 Jun 2024 10:44:48 +0200 Subject: [PATCH 1/2] avm1: Drop focus on EditText when something else is clicked In AVM1 text fields are somewhat special when handling focus. When a text field is clicked, it gains focus, when something else is clicked, it loses the focus. However, this logic only applies to text fields, other objects (buttons, movie clips) neither gain focus nor lose it upon press. --- core/src/player.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/core/src/player.rs b/core/src/player.rs index 57fe5963e162..05aec51e4fbe 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -1542,6 +1542,9 @@ impl Player { for (object, event) in events { let display_object = object.as_displayobject(); if !display_object.avm1_removed() { + if event == ClipEvent::Press { + Self::update_focus_on_mouse_press(context, display_object); + } object.handle_clip_event(context, event); if display_object.movie().is_action_script_3() { object.event_dispatch_to_avm2(context, event); @@ -1575,6 +1578,23 @@ impl Player { needs_render } + fn update_focus_on_mouse_press(context: &mut UpdateContext, pressed_object: DisplayObject) { + let is_avm2 = context.swf.is_action_script_3(); + + // Update AVM1 focus + if !is_avm2 { + let tracker = context.focus_tracker; + // In AVM1 text fields are somewhat special when handling focus. + // When a text field is clicked, it gains focus, + // when something else is clicked, it loses the focus. + // However, this logic only applies to text fields, other objects + // (buttons, movie clips) neither gain focus nor lose it upon press. + if tracker.get_as_edit_text().is_some() && pressed_object.as_edit_text().is_none() { + tracker.set(None, context); + } + } + } + //Checks if two displayObjects have the same depth and id and accur in the same movie.s fn check_display_object_equality(object1: DisplayObject, object2: DisplayObject) -> bool { object1.depth() == object2.depth() From 6a951cd4be84794e652f06d1811fc42b6065cee5 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Tue, 4 Jun 2024 10:45:35 +0200 Subject: [PATCH 2/2] tests: Add avm1/focus_mouse test This test verifies how FP handles focus changes on mouse clicks. --- tests/tests/swfs/avm1/focus_mouse/input.json | 34 ++++++++++++++ tests/tests/swfs/avm1/focus_mouse/output.txt | 45 +++++++++++++++++++ tests/tests/swfs/avm1/focus_mouse/test.as | 20 +++++++++ tests/tests/swfs/avm1/focus_mouse/test.swf | Bin 0 -> 11863 bytes tests/tests/swfs/avm1/focus_mouse/test.toml | 1 + 5 files changed, 100 insertions(+) create mode 100644 tests/tests/swfs/avm1/focus_mouse/input.json create mode 100644 tests/tests/swfs/avm1/focus_mouse/output.txt create mode 100644 tests/tests/swfs/avm1/focus_mouse/test.as create mode 100644 tests/tests/swfs/avm1/focus_mouse/test.swf create mode 100644 tests/tests/swfs/avm1/focus_mouse/test.toml diff --git a/tests/tests/swfs/avm1/focus_mouse/input.json b/tests/tests/swfs/avm1/focus_mouse/input.json new file mode 100644 index 000000000000..100832d87fc3 --- /dev/null +++ b/tests/tests/swfs/avm1/focus_mouse/input.json @@ -0,0 +1,34 @@ +[ + { "type": "MouseMove", "pos": [5, 5] }, + { "type": "MouseDown", "pos": [5, 5], "btn": "Left" }, { "type": "MouseUp", "pos": [5, 5], "btn": "Left" }, + + { "type": "MouseMove", "pos": [70, 175] }, + { "type": "MouseDown", "pos": [70, 175], "btn": "Left" }, { "type": "MouseUp", "pos": [70, 175], "btn": "Left" }, + { "type": "MouseMove", "pos": [5, 5] }, + { "type": "MouseDown", "pos": [5, 5], "btn": "Left" }, { "type": "MouseUp", "pos": [5, 5], "btn": "Left" }, + + { "type": "MouseMove", "pos": [230, 175] }, + { "type": "MouseDown", "pos": [230, 175], "btn": "Left" }, { "type": "MouseUp", "pos": [230, 175], "btn": "Left" }, + { "type": "MouseMove", "pos": [5, 5] }, + { "type": "MouseDown", "pos": [5, 5], "btn": "Left" }, { "type": "MouseUp", "pos": [5, 5], "btn": "Left" }, + + { "type": "MouseMove", "pos": [360, 190] }, + { "type": "MouseDown", "pos": [360, 190], "btn": "Left" }, { "type": "MouseUp", "pos": [360, 190], "btn": "Left" }, + { "type": "MouseMove", "pos": [5, 5] }, + { "type": "MouseDown", "pos": [5, 5], "btn": "Left" }, { "type": "MouseUp", "pos": [5, 5], "btn": "Left" }, + + { "type": "MouseMove", "pos": [360, 190] }, + { "type": "MouseDown", "pos": [360, 190], "btn": "Left" }, { "type": "MouseUp", "pos": [360, 190], "btn": "Left" }, + { "type": "MouseMove", "pos": [230, 175] }, + { "type": "MouseDown", "pos": [230, 175], "btn": "Left" }, { "type": "MouseUp", "pos": [230, 175], "btn": "Left" }, + + { "type": "MouseMove", "pos": [360, 190] }, + { "type": "MouseDown", "pos": [360, 190], "btn": "Left" }, { "type": "MouseUp", "pos": [360, 190], "btn": "Left" }, + { "type": "MouseMove", "pos": [70, 175] }, + { "type": "MouseDown", "pos": [70, 175], "btn": "Left" }, { "type": "MouseUp", "pos": [70, 175], "btn": "Left" }, + + { "type": "MouseMove", "pos": [360, 190] }, + { "type": "MouseDown", "pos": [360, 190], "btn": "Left" }, { "type": "MouseUp", "pos": [360, 190], "btn": "Left" }, + { "type": "MouseMove", "pos": [360, 190] }, + { "type": "MouseDown", "pos": [360, 190], "btn": "Left" }, { "type": "MouseUp", "pos": [360, 190], "btn": "Left" } +] diff --git a/tests/tests/swfs/avm1/focus_mouse/output.txt b/tests/tests/swfs/avm1/focus_mouse/output.txt new file mode 100644 index 000000000000..87c64f62c9a4 --- /dev/null +++ b/tests/tests/swfs/avm1/focus_mouse/output.txt @@ -0,0 +1,45 @@ +_level0.clip.onMouseDown +_level0.clip.onMouseUp +_level0.clip.onMouseDown +_level0.clip.onPress +_level0.clip.onMouseUp +_level0.clip.onRelease +_level0.clip.onMouseDown +_level0.clip.onMouseUp +_level0.clip.onMouseDown +press +_level0.button.onPress +_level0.clip.onMouseUp +release +_level0.button.onRelease +_level0.clip.onMouseDown +_level0.clip.onMouseUp +_level0.clip.onMouseDown +Focus at: _level0.text +_level0.clip.onMouseUp +_level0.clip.onMouseDown +Focus at: null +_level0.clip.onMouseUp +_level0.clip.onMouseDown +Focus at: _level0.text +_level0.clip.onMouseUp +_level0.clip.onMouseDown +Focus at: null +press +_level0.button.onPress +_level0.clip.onMouseUp +release +_level0.button.onRelease +_level0.clip.onMouseDown +Focus at: _level0.text +_level0.clip.onMouseUp +_level0.clip.onMouseDown +Focus at: null +_level0.clip.onPress +_level0.clip.onMouseUp +_level0.clip.onRelease +_level0.clip.onMouseDown +Focus at: _level0.text +_level0.clip.onMouseUp +_level0.clip.onMouseDown +_level0.clip.onMouseUp diff --git a/tests/tests/swfs/avm1/focus_mouse/test.as b/tests/tests/swfs/avm1/focus_mouse/test.as new file mode 100644 index 000000000000..4ba65cfe90a0 --- /dev/null +++ b/tests/tests/swfs/avm1/focus_mouse/test.as @@ -0,0 +1,20 @@ +var listener = new Object(); +listener.onSetFocus = function(oldFocus, newFocus) { + trace("Focus at: " + newFocus); +}; +Selection.addListener(listener); + +function setHandlers(obj) { + obj.onRelease = function () { trace(obj + ".onRelease"); } + obj.onKeyDown = function () { trace(obj + ".onKeyDown"); } + obj.onKeyUp = function () { trace(obj + ".onKeyUp"); } + obj.onPress = function () { trace(obj + ".onPress"); } + obj.onReleaseOutside = function () { trace(obj + ".onReleaseOutside"); } + obj.onMouseDown = function () { trace(obj + ".onMouseDown") } + obj.onMouseUp = function () { trace(obj + ".onMouseUp") } +} + +clip.focusEnabled = true; +setHandlers(clip); +setHandlers(button); +setHandlers(text); diff --git a/tests/tests/swfs/avm1/focus_mouse/test.swf b/tests/tests/swfs/avm1/focus_mouse/test.swf new file mode 100644 index 0000000000000000000000000000000000000000..8d86d3bc5503cfd2ec9ab57ff201a7b7e87176b3 GIT binary patch literal 11863 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 RMwI+dA~M+l{ug6KvRdwGM