diff --git a/core/src/events.rs b/core/src/events.rs index ac0afae4f..9a60a0916 100644 --- a/core/src/events.rs +++ b/core/src/events.rs @@ -1,4 +1,5 @@ use crate::display_object::InteractiveObject; +use serde::Deserialize; use swf::ClipEventFlag; #[derive(Debug, Clone, Copy)] @@ -334,7 +335,7 @@ impl<'gc> ClipEvent<'gc> { } /// Control inputs to a text field -#[derive(Debug, Copy, Clone, PartialEq, Eq)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Deserialize)] pub enum TextControlCode { // TODO: Add control codes for Ctrl+Arrows and Home/End keys MoveLeft, diff --git a/tests/input-format/src/format.rs b/tests/input-format/src/format.rs index ebc510710..853ac38a5 100644 --- a/tests/input-format/src/format.rs +++ b/tests/input-format/src/format.rs @@ -19,6 +19,23 @@ pub enum MouseButton { Right, } +/// Control inputs to a text field +#[derive(Debug, Copy, Clone, PartialEq, Eq, Serialize, Deserialize)] +pub enum TextControlCode { + // TODO: Add control codes for Ctrl+Arrows and Home/End keys + MoveLeft, + MoveRight, + SelectLeft, + SelectRight, + SelectAll, + Copy, + Paste, + Cut, + Backspace, + Enter, + Delete, +} + /// All automated event types supported by FlashTAS. /// /// A FlashTAS input file consists of a string of `AutomatedEvent`s which are @@ -47,4 +64,10 @@ pub enum AutomatedEvent { /// Press a key KeyDown { key_code: u8 }, + + /// Input a character code + TextInput { codepoint: char }, + + /// Input a control character code + TextControl { code: TextControlCode }, } diff --git a/tests/input-format/src/injector.rs b/tests/input-format/src/injector.rs index 859152bae..33d8e7bdd 100644 --- a/tests/input-format/src/injector.rs +++ b/tests/input-format/src/injector.rs @@ -81,7 +81,10 @@ impl InputInjector { match event { AutomatedEvent::Wait => break, - AutomatedEvent::MouseMove { .. } | AutomatedEvent::KeyDown { .. } => {} + AutomatedEvent::MouseMove { .. } + | AutomatedEvent::KeyDown { .. } + | AutomatedEvent::TextInput { .. } + | AutomatedEvent::TextControl { .. } => {} AutomatedEvent::MouseDown { btn, .. } => { self.buttons |= (*btn).into(); } diff --git a/tests/input-format/src/lib.rs b/tests/input-format/src/lib.rs index f2c431267..7091e06ad 100644 --- a/tests/input-format/src/lib.rs +++ b/tests/input-format/src/lib.rs @@ -1,5 +1,5 @@ mod format; mod injector; -pub use format::{AutomatedEvent, MouseButton}; +pub use format::{AutomatedEvent, MouseButton, TextControlCode}; pub use injector::{InputInjector, MouseButtons}; diff --git a/tests/tests/swfs/avm2/textfield_change/Main.as b/tests/tests/swfs/avm2/textfield_change/Main.as new file mode 100755 index 000000000..7e2be79cf --- /dev/null +++ b/tests/tests/swfs/avm2/textfield_change/Main.as @@ -0,0 +1,14 @@ +package { + import flash.display.MovieClip; + import flash.display.DisplayObject; + import flash.display.InteractiveObject; + + public class Main extends MovieClip { + public function Main() { + this.getChildAt(0).addEventListener("change", function(e) { + trace("New text: " + e.target.text); + }); + this.stage.focus = this.getChildAt(0) as InteractiveObject; + } + } +} \ No newline at end of file diff --git a/tests/tests/swfs/avm2/textfield_change/input.json b/tests/tests/swfs/avm2/textfield_change/input.json new file mode 100644 index 000000000..bb924370a --- /dev/null +++ b/tests/tests/swfs/avm2/textfield_change/input.json @@ -0,0 +1,34 @@ +[ + { + "type": "TextInput", + "codepoint": "R" + }, + { + "type": "TextInput", + "codepoint": "u" + }, + { + "type": "TextInput", + "codepoint": "f" + }, + { + "type": "TextInput", + "codepoint": "f" + }, + { + "type": "TextInput", + "codepoint": "l" + }, + { + "type": "TextInput", + "codepoint": "e" + }, + { + "type": "TextControl", + "code": "Backspace" + }, + { + "type": "TextInput", + "codepoint": "!" + } +] \ No newline at end of file diff --git a/tests/tests/swfs/avm2/textfield_change/output.txt b/tests/tests/swfs/avm2/textfield_change/output.txt new file mode 100644 index 000000000..894528cca --- /dev/null +++ b/tests/tests/swfs/avm2/textfield_change/output.txt @@ -0,0 +1,8 @@ +New text: R +New text: Ru +New text: Ruf +New text: Ruff +New text: Ruffl +New text: Ruffle +New text: Ruffl +New text: Ruffl! diff --git a/tests/tests/swfs/avm2/textfield_change/test.fla b/tests/tests/swfs/avm2/textfield_change/test.fla new file mode 100755 index 000000000..b821f8dd5 Binary files /dev/null and b/tests/tests/swfs/avm2/textfield_change/test.fla differ diff --git a/tests/tests/swfs/avm2/textfield_change/test.swf b/tests/tests/swfs/avm2/textfield_change/test.swf new file mode 100755 index 000000000..842a10cea Binary files /dev/null and b/tests/tests/swfs/avm2/textfield_change/test.swf differ diff --git a/tests/tests/swfs/avm2/textfield_change/test.toml b/tests/tests/swfs/avm2/textfield_change/test.toml new file mode 100644 index 000000000..67f15e863 --- /dev/null +++ b/tests/tests/swfs/avm2/textfield_change/test.toml @@ -0,0 +1 @@ +num_ticks = 1 \ No newline at end of file diff --git a/tests/tests/util/runner.rs b/tests/tests/util/runner.rs index 60a466dc0..c41226394 100644 --- a/tests/tests/util/runner.rs +++ b/tests/tests/util/runner.rs @@ -7,13 +7,16 @@ use ruffle_core::backend::audio::{ }; use ruffle_core::backend::log::LogBackend; use ruffle_core::backend::navigator::NullExecutor; -use ruffle_core::events::KeyCode; use ruffle_core::events::MouseButton as RuffleMouseButton; +use ruffle_core::events::{KeyCode, TextControlCode as RuffleTextControlCode}; use ruffle_core::impl_audio_mixer_backend; use ruffle_core::limits::ExecutionLimit; use ruffle_core::tag_utils::SwfMovie; use ruffle_core::{Player, PlayerBuilder, PlayerEvent}; -use ruffle_input_format::{AutomatedEvent, InputInjector, MouseButton as InputMouseButton}; +use ruffle_input_format::{ + AutomatedEvent, InputInjector, MouseButton as InputMouseButton, + TextControlCode as InputTextControlCode, +}; use std::cell::RefCell; use std::path::Path; use std::rc::Rc; @@ -194,6 +197,24 @@ pub fn run_swf( key_code: KeyCode::from_u8(*key_code).expect("Invalid keycode in test"), key_char: None, }, + AutomatedEvent::TextInput { codepoint } => PlayerEvent::TextInput { + codepoint: *codepoint, + }, + AutomatedEvent::TextControl { code } => PlayerEvent::TextControl { + code: match code { + InputTextControlCode::MoveLeft => RuffleTextControlCode::Backspace, + InputTextControlCode::MoveRight => RuffleTextControlCode::Delete, + InputTextControlCode::SelectLeft => RuffleTextControlCode::SelectLeft, + InputTextControlCode::SelectRight => RuffleTextControlCode::SelectRight, + InputTextControlCode::SelectAll => RuffleTextControlCode::SelectAll, + InputTextControlCode::Copy => RuffleTextControlCode::Copy, + InputTextControlCode::Paste => RuffleTextControlCode::Paste, + InputTextControlCode::Cut => RuffleTextControlCode::Cut, + InputTextControlCode::Backspace => RuffleTextControlCode::Backspace, + InputTextControlCode::Enter => RuffleTextControlCode::Enter, + InputTextControlCode::Delete => RuffleTextControlCode::Delete, + }, + }, AutomatedEvent::Wait => unreachable!(), }); });