diff --git a/core/src/backend/ui.rs b/core/src/backend/ui.rs index b864475172b1..2a01a5c0a29e 100644 --- a/core/src/backend/ui.rs +++ b/core/src/backend/ui.rs @@ -1,7 +1,7 @@ use crate::backend::navigator::OwnedFuture; use crate::events::{KeyCode, PlayerEvent, TextControlCode}; pub use crate::loader::Error as DialogLoaderError; -use chrono::{DateTime, Utc}; +use chrono::{DateTime, TimeDelta, Utc}; use downcast_rs::Downcast; use fluent_templates::loader::langid; pub use fluent_templates::LanguageIdentifier; @@ -140,7 +140,7 @@ pub enum MouseCursor { /// Equivalent to AS3 `MouseCursor.ARROW`. Arrow, - /// The hand icon incdicating a button or link. + /// The hand icon indicating a button or link. /// Equivalent to AS3 `MouseCursor.BUTTON`. Hand, @@ -153,12 +153,28 @@ pub enum MouseCursor { Grab, } +struct ClickEventData { + x: f64, + y: f64, + time: DateTime, + index: usize, +} + +impl ClickEventData { + fn distance_squared_to(&self, x: f64, y: f64) -> f64 { + let dx = x - self.x; + let dy = y - self.y; + dx * dx + dy * dy + } +} + pub struct InputManager { keys_down: HashSet, keys_toggled: HashSet, last_key: KeyCode, last_char: Option, last_text_control: Option, + last_click: Option, } impl InputManager { @@ -169,6 +185,7 @@ impl InputManager { last_key: KeyCode::Unknown, last_char: None, last_text_control: None, + last_click: None, } } @@ -212,15 +229,40 @@ impl InputManager { PlayerEvent::TextControl { code } => { self.last_text_control = Some(code); } - PlayerEvent::MouseDown { button, .. } => { + PlayerEvent::MouseDown { + x, + y, + button, + index, + } => { self.toggle_key(button.into()); - self.add_key(button.into()) + self.add_key(button.into()); + self.update_last_click(x, y, index); } PlayerEvent::MouseUp { button, .. } => self.remove_key(button.into()), _ => {} } } + fn update_last_click(&mut self, x: f64, y: f64, index: Option) { + let time = Utc::now(); + let index = index.unwrap_or_else(|| { + let Some(last_click) = self.last_click.as_ref() else { + return 0; + }; + + // TODO Make this configurable as "double click delay" and "double click distance" + if (time - last_click.time).abs() < TimeDelta::milliseconds(500) + && last_click.distance_squared_to(x, y) < 4.0 + { + last_click.index + 1 + } else { + 0 + } + }); + self.last_click = Some(ClickEventData { x, y, time, index }); + } + pub fn is_key_down(&self, key: KeyCode) -> bool { self.keys_down.contains(&key) } @@ -241,6 +283,13 @@ impl InputManager { self.last_text_control } + pub fn last_click_index(&self) -> usize { + self.last_click + .as_ref() + .map(|lc| lc.index) + .unwrap_or_default() + } + pub fn is_mouse_down(&self) -> bool { self.is_key_down(KeyCode::MouseLeft) } diff --git a/core/src/display_object/avm1_button.rs b/core/src/display_object/avm1_button.rs index 6ec9ccb7b4c4..d9611a996f06 100644 --- a/core/src/display_object/avm1_button.rs +++ b/core/src/display_object/avm1_button.rs @@ -476,7 +476,7 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> { Some(ButtonActionCondition::OUT_DOWN_TO_OVER_DOWN), None, ), - ClipEvent::Press => ( + ClipEvent::Press { .. } => ( ButtonState::Down, Some(ButtonActionCondition::OVER_UP_TO_OVER_DOWN), static_data.over_to_down_sound.as_ref(), diff --git a/core/src/display_object/avm2_button.rs b/core/src/display_object/avm2_button.rs index b4f4ff4e70ae..a2e94c733639 100644 --- a/core/src/display_object/avm2_button.rs +++ b/core/src/display_object/avm2_button.rs @@ -752,7 +752,7 @@ impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> { let (new_state, sound) = match event { ClipEvent::DragOut { .. } => (ButtonState::Over, None), ClipEvent::DragOver { .. } => (ButtonState::Down, None), - ClipEvent::Press => (ButtonState::Down, static_data.over_to_down_sound.as_ref()), + ClipEvent::Press { .. } => (ButtonState::Down, static_data.over_to_down_sound.as_ref()), ClipEvent::Release => (ButtonState::Over, static_data.down_to_over_sound.as_ref()), ClipEvent::ReleaseOutside => (ButtonState::Up, static_data.over_to_up_sound.as_ref()), ClipEvent::MouseUpInside => (ButtonState::Up, static_data.over_to_up_sound.as_ref()), diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index 259f187423a3..a31f5978512d 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -169,6 +169,10 @@ pub struct EditTextData<'gc> { /// Restrict what characters the user may input. #[collect(require_static)] restrict: EditTextRestrict, + + /// Information related to the last click event inside this text field. + #[collect(require_static)] + last_click: Option, } impl<'gc> EditTextData<'gc> { @@ -299,6 +303,7 @@ impl<'gc> EditText<'gc> { mouse_wheel_enabled: true, is_tlf: false, restrict: EditTextRestrict::allow_all(), + last_click: None, }, )); @@ -1173,6 +1178,24 @@ impl<'gc> EditText<'gc> { } } + /// Calculate and return the [`TextSelection`] at the given position + /// using the given selection mode. + fn calculate_selection_at(self, position: usize, mode: TextSelectionMode) -> TextSelection { + match mode { + TextSelectionMode::Character => TextSelection::for_position(position), + TextSelectionMode::Word => { + let from = self.find_prev_word_boundary(position, true); + let to = self.find_next_word_boundary(position, true); + TextSelection::for_range(from, to) + } + TextSelectionMode::Line => { + let from = self.find_prev_line_boundary(position); + let to = self.find_next_line_boundary(position); + TextSelection::for_range(from, to) + } + } + } + pub fn reset_selection_blinking(self, gc_context: &Mutation<'gc>) { if let Some(selection) = self.0.write(gc_context).selection.as_mut() { selection.reset_blinking(); @@ -1558,10 +1581,10 @@ impl<'gc> EditText<'gc> { } TextControlCode::SelectRightWord | TextControlCode::MoveRightWord - | TextControlCode::DeleteWord => self.find_next_word_boundary(current_pos), + | TextControlCode::DeleteWord => self.find_next_word_boundary(current_pos, false), TextControlCode::SelectLeftWord | TextControlCode::MoveLeftWord - | TextControlCode::BackspaceWord => self.find_prev_word_boundary(current_pos), + | TextControlCode::BackspaceWord => self.find_prev_word_boundary(current_pos, false), TextControlCode::SelectRightLine | TextControlCode::MoveRightLine => { self.find_next_line_boundary(current_pos) } @@ -1576,12 +1599,17 @@ impl<'gc> EditText<'gc> { } } - /// Find the nearest word boundary before `pos`, + /// Find the nearest word boundary before (or exceptionally at) `pos`, /// which is applicable for selection. /// + /// When `stop_on_space` is true, `pos` will be returned if there's space before it. + /// /// This algorithm is based on [UAX #29](https://unicode.org/reports/tr29/). - fn find_prev_word_boundary(self, pos: usize) -> usize { + fn find_prev_word_boundary(self, pos: usize, stop_on_space: bool) -> usize { let head = &self.text()[..pos]; + if stop_on_space && head.ends_with(ruffle_wstr::utils::swf_is_whitespace) { + return pos; + } let to_utf8 = WStrToUtf8::new(head); WordBoundIndices::new(&to_utf8.to_utf8_lossy()) .rev() @@ -1591,12 +1619,17 @@ impl<'gc> EditText<'gc> { .unwrap_or(0) } - /// Find the nearest word boundary after `pos`, + /// Find the nearest word boundary after (or exceptionally at) `pos`, /// which is applicable for selection. /// + /// When `stop_on_space` is true, `pos` will be returned if there's space after it. + /// /// This algorithm is based on [UAX #29](https://unicode.org/reports/tr29/). - fn find_next_word_boundary(self, pos: usize) -> usize { + fn find_next_word_boundary(self, pos: usize, stop_on_space: bool) -> usize { let tail = &self.text()[pos..]; + if stop_on_space && tail.starts_with(ruffle_wstr::utils::swf_is_whitespace) { + return pos; + } let to_utf8 = WStrToUtf8::new(tail); WordBoundIndices::new(&to_utf8.to_utf8_lossy()) .skip_while(|(_, span)| span.trim().is_empty()) @@ -1950,6 +1983,52 @@ impl<'gc> EditText<'gc> { .contains(Position::from((position.x, position.y))) }) } + + fn handle_click( + self, + click_index: usize, + position: usize, + context: &mut UpdateContext<'_, 'gc>, + ) { + if !self.is_selectable() { + return; + } + + let this_click = ClickEventData { + position, + click_index, + }; + let selection_mode = this_click.selection_mode(); + self.0.write(context.gc()).last_click = Some(this_click); + + // Update selection + let selection = self.calculate_selection_at(position, selection_mode); + self.set_selection(Some(selection), context.gc()); + } + + fn handle_drag(self, position: usize, context: &mut UpdateContext<'_, 'gc>) { + if !self.is_selectable() { + return; + } + + let Some((last_position, selection_mode)) = self + .0 + .read() + .last_click + .as_ref() + .map(|last_click| (last_click.position, last_click.selection_mode())) + else { + // No last click, so no drag + return; + }; + + // We have to calculate selections at the first and the current position, + // because the user may be selecting words or lines. + let first_selection = self.calculate_selection_at(last_position, selection_mode); + let current_selection = self.calculate_selection_at(position, selection_mode); + let new_selection = TextSelection::span_across(first_selection, current_selection); + self.set_selection(Some(new_selection), context.gc()); + } } impl<'gc> TDisplayObject<'gc> for EditText<'gc> { @@ -2410,7 +2489,7 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { event: ClipEvent, ) -> ClipEventResult { match event { - ClipEvent::Press | ClipEvent::MouseWheel { .. } | ClipEvent::MouseMove => { + ClipEvent::Press { .. } | ClipEvent::MouseWheel { .. } | ClipEvent::MouseMove => { ClipEventResult::Handled } _ => ClipEventResult::NotHandled, @@ -2437,7 +2516,7 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { return ClipEventResult::Handled; } - if let ClipEvent::Press = event { + if let ClipEvent::Press { index } = event { if self.is_editable() || self.is_selectable() { let tracker = context.focus_tracker; tracker.set(Some(self.into()), context); @@ -2447,7 +2526,7 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { let mut link_to_open = None; if let Some(position) = self.screen_position_to_index(*context.mouse_position) { - self.set_selection(Some(TextSelection::for_position(position)), context.gc()); + self.handle_click(index, position, context); if let Some((span_index, _)) = self.0.read().text_spans.resolve_position_as_span(position) @@ -2471,6 +2550,8 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { // TODO: This fires on mouse DOWN but it should be mouse UP... // but only if it went down in the same span. // Needs more advanced focus handling than we have at time of writing this comment. + // TODO This also needs to fire only if the user clicked on the link, + // currently it fires when the cursor position resolves to one in the link. self.open_url(context, &url, &target); } } @@ -2481,11 +2562,8 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { if let ClipEvent::MouseMove = event { // If a mouse has moved and this EditTest is pressed, we need to update the selection. if InteractiveObject::option_ptr_eq(context.mouse_data.pressed, self.as_interactive()) { - if let Some(mut selection) = self.selection() { - if let Some(position) = self.screen_position_to_index(*context.mouse_position) { - selection.to = position; - self.set_selection(Some(selection), context.gc()); - } + if let Some(position) = self.screen_position_to_index(*context.mouse_position) { + self.handle_drag(position, context); } } } @@ -2617,6 +2695,49 @@ struct EditTextStatic { initial_text: Option, } +#[derive(Clone, Debug)] +struct ClickEventData { + /// The position in text resolved from click coordinates. + position: usize, + + click_index: usize, +} + +impl ClickEventData { + /// Selection mode that results from this click index. + fn selection_mode(&self) -> TextSelectionMode { + TextSelectionMode::from_click_index(self.click_index) + } +} + +#[derive(Copy, Clone, Debug)] +enum TextSelectionMode { + /// Specifies that text should be selected at char boundaries. + /// + /// Used when e.g. clicking or clicking and dragging. + Character, + + /// Specifies that text should be selected at word boundaries. + /// + /// Used when e.g. double-clicking or double-clicking and dragging. + Word, + + /// Specifies that text should be selected at line boundaries. + /// + /// Used when e.g. triple-clicking or triple-clicking and dragging. + Line, +} + +impl TextSelectionMode { + fn from_click_index(click_index: usize) -> Self { + match click_index { + 0 => Self::Character, + 1 => Self::Word, + _ => Self::Line, + } + } +} + #[derive(Copy, Clone, Debug)] pub struct TextSelection { from: usize, @@ -2653,6 +2774,21 @@ impl TextSelection { } } + /// Create a new selection spanning across the given selections. + pub fn span_across(from: Self, to: Self) -> Self { + let from_start = from.start(); + let from_end = from.end(); + let to_start = to.start(); + let to_end = to.end(); + if from_start < to_start && from_end < to_end { + Self::for_range(from_start, to_end) + } else if to_start < from_start && to_end < from_end { + Self::for_range(from_end, to_start) + } else { + Self::for_range(from_start.min(to_start), from_end.max(to_end)) + } + } + /// The "from" part of the range is where the user started the selection. /// It may be greater than "to", for example if the user dragged a selection box from right to /// left. diff --git a/core/src/display_object/interactive.rs b/core/src/display_object/interactive.rs index 5107795f147d..385115fd1e9f 100644 --- a/core/src/display_object/interactive.rs +++ b/core/src/display_object/interactive.rs @@ -281,7 +281,7 @@ pub trait TInteractiveObject<'gc>: let mut activation = Avm2Activation::from_nothing(context.reborrow()); match event { - ClipEvent::Press => { + ClipEvent::Press { .. } => { let avm2_event = Avm2EventObject::mouse_event( &mut activation, "mouseDown", diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index c26453414bf8..f95bf816fd8b 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -3000,7 +3000,9 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> { ClipEvent::RollOver { .. } | ClipEvent::Release | ClipEvent::DragOut { .. } => { Some(WStr::from_units(b"_over")) } - ClipEvent::Press | ClipEvent::DragOver { .. } => Some(WStr::from_units(b"_down")), + ClipEvent::Press { .. } | ClipEvent::DragOver { .. } => { + Some(WStr::from_units(b"_down")) + } _ => None, }; diff --git a/core/src/events.rs b/core/src/events.rs index 0d619104444a..3b6846cf5929 100644 --- a/core/src/events.rs +++ b/core/src/events.rs @@ -24,6 +24,7 @@ pub enum PlayerEvent { x: f64, y: f64, button: MouseButton, + index: Option, }, MouseLeave, MouseWheel { @@ -176,7 +177,13 @@ pub enum ClipEvent<'gc> { /// This is a targeted equivalent to `MouseDown` and is available in both /// AVM1 and AVM2. The target of this event is determined by the position /// of the mouse cursor. - Press, + Press { + /// The index of this click in a click sequence performed in a quick succession. + /// + /// For instance the value of 0 indicates it's a single click, + /// the number of 1 indicates it's a double click, etc. + index: usize, + }, /// Mouse moved out of a display object. /// @@ -267,7 +274,7 @@ impl<'gc> ClipEvent<'gc> { ClipEvent::MouseDown => Some(ClipEventFlag::MOUSE_DOWN), ClipEvent::MouseMove => Some(ClipEventFlag::MOUSE_MOVE), ClipEvent::MouseUp => Some(ClipEventFlag::MOUSE_UP), - ClipEvent::Press => Some(ClipEventFlag::PRESS), + ClipEvent::Press { .. } => Some(ClipEventFlag::PRESS), ClipEvent::RollOut { .. } => Some(ClipEventFlag::ROLL_OUT), ClipEvent::RollOver { .. } => Some(ClipEventFlag::ROLL_OVER), ClipEvent::Release => Some(ClipEventFlag::RELEASE), @@ -326,7 +333,7 @@ impl<'gc> ClipEvent<'gc> { ClipEvent::MouseDown => Some("onMouseDown"), ClipEvent::MouseMove => Some("onMouseMove"), ClipEvent::MouseUp => Some("onMouseUp"), - ClipEvent::Press => Some("onPress"), + ClipEvent::Press { .. } => Some("onPress"), ClipEvent::RollOut { .. } => Some("onRollOut"), ClipEvent::RollOver { .. } => Some("onRollOver"), ClipEvent::Release => Some("onRelease"), diff --git a/core/src/player.rs b/core/src/player.rs index e67e7c3910cd..f91f27d5ada2 100644 --- a/core/src/player.rs +++ b/core/src/player.rs @@ -1153,7 +1153,7 @@ impl Player { ) { // The button/clip is pressed and then immediately released. // We do not have to wait for KeyUp. - focus.handle_clip_event(context, ClipEvent::Press); + focus.handle_clip_event(context, ClipEvent::Press { index: 0 }); focus.handle_clip_event(context, ClipEvent::Release); } } @@ -1168,6 +1168,7 @@ impl Player { x, y, button: MouseButton::Left, + .. } | PlayerEvent::MouseUp { x, @@ -1476,12 +1477,13 @@ impl Player { // Handle presses and releases. if is_mouse_button_changed { if context.input.is_mouse_down() { + let index = context.input.last_click_index(); // Pressed on a hovered object. if let Some(over_object) = context.mouse_data.hovered { - events.push((over_object, ClipEvent::Press)); + events.push((over_object, ClipEvent::Press { index })); context.mouse_data.pressed = context.mouse_data.hovered; } else { - events.push((context.stage.into(), ClipEvent::Press)); + events.push((context.stage.into(), ClipEvent::Press { index })); } } else { if let Some(over_object) = context.mouse_data.hovered { @@ -1548,7 +1550,7 @@ impl Player { if display_object.movie().is_action_script_3() { object.event_dispatch_to_avm2(context, event); } - if event == ClipEvent::Press { + if matches!(event, ClipEvent::Press { .. }) { Self::update_focus_on_mouse_press(context, display_object); } } diff --git a/desktop/src/app.rs b/desktop/src/app.rs index ec507d880065..9e21dfc1aa92 100644 --- a/desktop/src/app.rs +++ b/desktop/src/app.rs @@ -251,7 +251,14 @@ impl App { _ => RuffleMouseButton::Unknown, }; let event = match state { - ElementState::Pressed => PlayerEvent::MouseDown { x, y, button }, + // TODO We should get information about click index from the OS, + // but winit does not support that yet. + ElementState::Pressed => PlayerEvent::MouseDown { + x, + y, + button, + index: None, + }, ElementState::Released => PlayerEvent::MouseUp { x, y, button }, }; if state == ElementState::Released && button == RuffleMouseButton::Right diff --git a/tests/framework/src/runner.rs b/tests/framework/src/runner.rs index 401337d9ed0c..686e0ca9326c 100644 --- a/tests/framework/src/runner.rs +++ b/tests/framework/src/runner.rs @@ -207,7 +207,7 @@ impl TestRunner { } self.player.lock().unwrap().handle_event(match evt { - AutomatedEvent::MouseDown { pos, btn } => PlayerEvent::MouseDown { + AutomatedEvent::MouseDown { pos, btn, index } => PlayerEvent::MouseDown { x: pos.0, y: pos.1, button: match btn { @@ -215,6 +215,9 @@ impl TestRunner { InputMouseButton::Middle => RuffleMouseButton::Middle, InputMouseButton::Right => RuffleMouseButton::Right, }, + // None here means that the core will compute index automatically, + // however we do not want that in tests. + index: Some(index.unwrap_or_default()), }, AutomatedEvent::MouseMove { pos } => PlayerEvent::MouseMove { x: pos.0, y: pos.1 }, AutomatedEvent::MouseUp { pos, btn } => PlayerEvent::MouseUp { diff --git a/tests/input-format/src/format.rs b/tests/input-format/src/format.rs index 14f818789df6..136aa6b6b831 100644 --- a/tests/input-format/src/format.rs +++ b/tests/input-format/src/format.rs @@ -65,6 +65,7 @@ pub enum AutomatedEvent { MouseDown { pos: MousePosition, btn: MouseButton, + index: Option, }, /// Release a mouse button. diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/NotoSans.ttf b/tests/tests/swfs/avm2/edittext_mouse_selection/NotoSans.ttf new file mode 100644 index 000000000000..fa4cff505eb6 Binary files /dev/null and b/tests/tests/swfs/avm2/edittext_mouse_selection/NotoSans.ttf differ diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/Test.as b/tests/tests/swfs/avm2/edittext_mouse_selection/Test.as new file mode 100644 index 000000000000..8a27b7592255 --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouse_selection/Test.as @@ -0,0 +1,77 @@ +package { +import flash.display.Sprite; +import flash.events.KeyboardEvent; +import flash.events.MouseEvent; +import flash.events.TextEvent; +import flash.text.TextField; +import flash.text.TextFormat; +import flash.ui.Keyboard; + +[SWF(width="400", height="200")] +public class Test extends Sprite { + [Embed(source="NotoSans.ttf", fontName="Noto Sans", embedAsCFF="false", unicodeRange="U+0020-U+007E")] + private var notoSans:Class; + + private var text:TextField; + + public function Test() { + text = new TextField(); + text.border = true; + text.width = 400; + text.height = 200; + text.type = "input"; + text.wordWrap = true; + text.multiline = true; + var tf = new TextFormat(); + tf.font = "Noto Sans"; + tf.size = 24; + text.defaultTextFormat = tf; + addChild(text); + + stage.focus = text; + stage.addEventListener(TextEvent.TEXT_INPUT, textInput); + stage.addEventListener(KeyboardEvent.KEY_DOWN, keyPressedDown); + stage.addEventListener(MouseEvent.MOUSE_DOWN, mouseDown); + stage.addEventListener(MouseEvent.MOUSE_UP, mouseUp); + stage.addEventListener(MouseEvent.MOUSE_MOVE, mouseMove); + } + + private function keyPressedDown(event:KeyboardEvent):void { + if (event.keyCode == Keyboard.ESCAPE) { + trace("Selected: " + text.selectedText.replace(/\r/g, "\\n").replace(/\n/g, "\\n")); + text.setSelection(0, 0); + } else if (event.keyCode == Keyboard.NUMBER_1) { + text.text = "word1_word2_word3"; + } else if (event.keyCode == Keyboard.NUMBER_2) { + text.text = "word1 word2 word3"; + } else if (event.keyCode == Keyboard.NUMBER_3) { + text.text = "word1 word2 word3 word4 word5 word6 word7 word8 word9\nword10 word11 word12"; + } else if (event.keyCode == Keyboard.NUMBER_4) { + text.selectable = false; + } else if (event.keyCode == Keyboard.NUMBER_5) { + text.selectable = true; + } else if (event.keyCode == Keyboard.NUMBER_6) { + text.text = "word1 word2"; + } + trace("{ \"type\": \"KeyDown\", \"key_code\": " + event.keyCode + " },") + } + + private function mouseDown(event:MouseEvent):void { + trace("{ \"type\": \"MouseDown\", \"pos\": [" + event.stageX + ", " + event.stageY + "], \"btn\": \"Left\" },"); + } + + private function mouseUp(event:MouseEvent):void { + trace("{ \"type\": \"MouseUp\", \"pos\": [" + event.stageX + ", " + event.stageY + "], \"btn\": \"Left\" },"); + } + + private function mouseMove(event:MouseEvent):void { + trace("{ \"type\": \"MouseMove\", \"pos\": [" + event.stageX + ", " + event.stageY + "] },"); + } + + private function textInput(event:TextEvent):void { + if (event.text.search(/[0-9]/g) != -1) { + event.preventDefault(); + } + } +} +} diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/input.json b/tests/tests/swfs/avm2/edittext_mouse_selection/input.json new file mode 100644 index 000000000000..e401288c8941 --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouse_selection/input.json @@ -0,0 +1,330 @@ +[ + { "type": "MouseMove", "pos": [174, 50] }, + { "type": "MouseMove", "pos": [174, 51] }, + { "type": "KeyDown", "key_code": 49 }, + { "type": "MouseMove", "pos": [35, 23] }, + { "type": "MouseMove", "pos": [34, 23] }, + { "type": "MouseDown", "pos": [34, 23], "btn": "Left" }, + { "type": "MouseMove", "pos": [201, 23] }, + { "type": "MouseMove", "pos": [202, 23] }, + { "type": "MouseUp", "pos": [202, 23], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [121, 23] }, + { "type": "MouseMove", "pos": [120, 23] }, + { "type": "MouseDown", "pos": [120, 23], "btn": "Left" }, + { "type": "MouseUp", "pos": [120, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [120, 23], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [120, 23], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [34, 22] }, + { "type": "MouseMove", "pos": [34, 21] }, + { "type": "MouseDown", "pos": [34, 21], "btn": "Left" }, + { "type": "MouseUp", "pos": [34, 21], "btn": "Left" }, + { "type": "MouseDown", "pos": [34, 21], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [34, 21], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [216, 22] }, + { "type": "MouseMove", "pos": [216, 21] }, + { "type": "MouseDown", "pos": [216, 21], "btn": "Left" }, + { "type": "MouseUp", "pos": [216, 21], "btn": "Left" }, + { "type": "MouseDown", "pos": [216, 21], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [216, 21], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [215, 21] }, + { "type": "KeyDown", "key_code": 50 }, + { "type": "MouseMove", "pos": [38, 24] }, + { "type": "MouseMove", "pos": [37, 24] }, + { "type": "MouseDown", "pos": [37, 24], "btn": "Left" }, + { "type": "MouseUp", "pos": [37, 24], "btn": "Left" }, + { "type": "MouseDown", "pos": [37, 24], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [37, 24], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [117, 22] }, + { "type": "MouseMove", "pos": [116, 22] }, + { "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, + { "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [116, 22], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [193, 19] }, + { "type": "MouseMove", "pos": [194, 19] }, + { "type": "MouseDown", "pos": [194, 19], "btn": "Left" }, + { "type": "MouseUp", "pos": [194, 19], "btn": "Left" }, + { "type": "MouseDown", "pos": [194, 19], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [194, 19], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [37, 20] }, + { "type": "MouseMove", "pos": [36, 20] }, + { "type": "MouseDown", "pos": [36, 20], "btn": "Left" }, + { "type": "MouseUp", "pos": [36, 20], "btn": "Left" }, + { "type": "MouseDown", "pos": [36, 20], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [115, 23] }, + { "type": "MouseMove", "pos": [116, 23] }, + { "type": "MouseUp", "pos": [116, 23], "btn": "Left" }, + { "type": "MouseMove", "pos": [115, 23] }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseDown", "pos": [115, 23], "btn": "Left" }, + { "type": "MouseUp", "pos": [115, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [115, 23], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [192, 22] }, + { "type": "MouseMove", "pos": [193, 22] }, + { "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseDown", "pos": [193, 22], "btn": "Left" }, + { "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [193, 22], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [40, 23] }, + { "type": "MouseMove", "pos": [39, 23] }, + { "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 49 }, + { "type": "MouseMove", "pos": [40, 23] }, + { "type": "MouseMove", "pos": [39, 23] }, + { "type": "MouseMove", "pos": [38, 23] }, + { "type": "MouseMove", "pos": [37, 23] }, + { "type": "MouseDown", "pos": [37, 23], "btn": "Left" }, + { "type": "MouseUp", "pos": [37, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [37, 23], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [113, 25] }, + { "type": "MouseMove", "pos": [114, 25] }, + { "type": "KeyDown", "key_code": 50 }, + { "type": "MouseMove", "pos": [115, 25] }, + { "type": "MouseMove", "pos": [116, 25] }, + { "type": "MouseUp", "pos": [116, 25], "btn": "Left" }, + { "type": "MouseMove", "pos": [111, 25] }, + { "type": "MouseMove", "pos": [110, 25] }, + { "type": "KeyDown", "key_code": 49 }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [116, 23] }, + { "type": "MouseMove", "pos": [117, 23] }, + { "type": "MouseDown", "pos": [117, 23], "btn": "Left" }, + { "type": "MouseUp", "pos": [117, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [117, 23], "btn": "Left", "index": 1 }, + { "type": "KeyDown", "key_code": 50 }, + { "type": "MouseMove", "pos": [192, 22] }, + { "type": "MouseMove", "pos": [193, 22] }, + { "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 49 }, + { "type": "MouseMove", "pos": [199, 22] }, + { "type": "MouseMove", "pos": [200, 22] }, + { "type": "MouseDown", "pos": [200, 22], "btn": "Left" }, + { "type": "MouseUp", "pos": [200, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [200, 22], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [121, 21] }, + { "type": "MouseMove", "pos": [120, 21] }, + { "type": "KeyDown", "key_code": 50 }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseUp", "pos": [120, 21], "btn": "Left" }, + { "type": "KeyDown", "key_code": 49 }, + { "type": "MouseMove", "pos": [130, 26] }, + { "type": "MouseMove", "pos": [130, 25] }, + { "type": "MouseDown", "pos": [130, 25], "btn": "Left" }, + { "type": "MouseUp", "pos": [130, 25], "btn": "Left" }, + { "type": "MouseMove", "pos": [120, 24] }, + { "type": "MouseMove", "pos": [119, 24] }, + { "type": "MouseDown", "pos": [119, 24], "btn": "Left" }, + { "type": "MouseUp", "pos": [119, 24], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [238, 96] }, + { "type": "MouseMove", "pos": [238, 95] }, + { "type": "KeyDown", "key_code": 51 }, + { "type": "MouseMove", "pos": [97, 14] }, + { "type": "MouseMove", "pos": [98, 14] }, + { "type": "MouseDown", "pos": [98, 14], "btn": "Left" }, + { "type": "MouseUp", "pos": [98, 14], "btn": "Left" }, + { "type": "MouseDown", "pos": [98, 14], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [143, 103] }, + { "type": "MouseMove", "pos": [143, 104] }, + { "type": "MouseUp", "pos": [143, 104], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [193, 28] }, + { "type": "MouseMove", "pos": [193, 27] }, + { "type": "MouseDown", "pos": [193, 27], "btn": "Left" }, + { "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, + { "type": "MouseDown", "pos": [193, 27], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, + { "type": "MouseDown", "pos": [193, 27], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [145, 99] }, + { "type": "MouseMove", "pos": [145, 100] }, + { "type": "MouseDown", "pos": [145, 100], "btn": "Left" }, + { "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, + { "type": "MouseDown", "pos": [145, 100], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, + { "type": "MouseDown", "pos": [145, 100], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 51 }, + { "type": "MouseMove", "pos": [39, 24] }, + { "type": "MouseMove", "pos": [39, 23] }, + { "type": "MouseDown", "pos": [39, 23], "btn": "Left" }, + { "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [39, 23], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, + { "type": "MouseDown", "pos": [39, 23], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [37, 49] }, + { "type": "MouseMove", "pos": [37, 50] }, + { "type": "MouseDown", "pos": [37, 50], "btn": "Left" }, + { "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, + { "type": "MouseDown", "pos": [37, 50], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, + { "type": "MouseDown", "pos": [37, 50], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [35, 93] }, + { "type": "MouseMove", "pos": [35, 94] }, + { "type": "MouseDown", "pos": [35, 94], "btn": "Left" }, + { "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, + { "type": "MouseDown", "pos": [35, 94], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, + { "type": "MouseDown", "pos": [35, 94], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [231, 93] }, + { "type": "MouseMove", "pos": [232, 93] }, + { "type": "MouseDown", "pos": [232, 93], "btn": "Left" }, + { "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, + { "type": "MouseDown", "pos": [232, 93], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, + { "type": "MouseDown", "pos": [232, 93], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, + { "type": "MouseMove", "pos": [231, 86] }, + { "type": "MouseMove", "pos": [231, 85] }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [234, 57] }, + { "type": "MouseMove", "pos": [234, 56] }, + { "type": "MouseDown", "pos": [234, 56], "btn": "Left" }, + { "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, + { "type": "MouseDown", "pos": [234, 56], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, + { "type": "MouseDown", "pos": [234, 56], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, + { "type": "MouseMove", "pos": [234, 55] }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [233, 31] }, + { "type": "MouseMove", "pos": [233, 30] }, + { "type": "MouseDown", "pos": [233, 30], "btn": "Left" }, + { "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, + { "type": "MouseDown", "pos": [233, 30], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, + { "type": "MouseDown", "pos": [233, 30], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [147, 22] }, + { "type": "MouseMove", "pos": [146, 22] }, + { "type": "MouseDown", "pos": [146, 22], "btn": "Left" }, + { "type": "MouseUp", "pos": [146, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [146, 22], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [146, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [146, 22], "btn": "Left", "index": 2 }, + { "type": "MouseMove", "pos": [138, 81] }, + { "type": "MouseMove", "pos": [138, 82] }, + { "type": "MouseUp", "pos": [138, 82], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [135, 86] }, + { "type": "MouseMove", "pos": [135, 87] }, + { "type": "MouseDown", "pos": [135, 87], "btn": "Left" }, + { "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, + { "type": "MouseDown", "pos": [135, 87], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, + { "type": "MouseDown", "pos": [135, 87], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, + { "type": "MouseDown", "pos": [135, 87], "btn": "Left", "index": 3 }, + { "type": "MouseMove", "pos": [129, 45] }, + { "type": "MouseMove", "pos": [129, 44] }, + { "type": "MouseUp", "pos": [129, 44], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [246, 61] }, + { "type": "MouseMove", "pos": [246, 60] }, + { "type": "MouseDown", "pos": [246, 60], "btn": "Left" }, + { "type": "MouseUp", "pos": [246, 60], "btn": "Left" }, + { "type": "MouseDown", "pos": [246, 60], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [246, 60], "btn": "Left" }, + { "type": "MouseDown", "pos": [246, 60], "btn": "Left", "index": 2 }, + { "type": "MouseMove", "pos": [230, 90] }, + { "type": "MouseMove", "pos": [229, 91] }, + { "type": "MouseUp", "pos": [229, 91], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [241, 60] }, + { "type": "MouseMove", "pos": [242, 59] }, + { "type": "MouseDown", "pos": [242, 59], "btn": "Left" }, + { "type": "MouseUp", "pos": [242, 59], "btn": "Left" }, + { "type": "MouseDown", "pos": [242, 59], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [242, 59], "btn": "Left" }, + { "type": "MouseDown", "pos": [242, 59], "btn": "Left", "index": 2 }, + { "type": "MouseMove", "pos": [239, 23] }, + { "type": "MouseMove", "pos": [239, 22] }, + { "type": "MouseUp", "pos": [239, 22], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 52 }, + { "type": "KeyDown", "key_code": 51 }, + { "type": "MouseMove", "pos": [99, 24] }, + { "type": "MouseMove", "pos": [99, 23] }, + { "type": "MouseDown", "pos": [99, 23], "btn": "Left" }, + { "type": "MouseMove", "pos": [150, 96] }, + { "type": "MouseMove", "pos": [150, 97] }, + { "type": "MouseUp", "pos": [150, 97], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [116, 23] }, + { "type": "MouseMove", "pos": [116, 22] }, + { "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, + { "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [116, 22], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, + { "type": "MouseDown", "pos": [116, 22], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [112, 95] }, + { "type": "MouseMove", "pos": [112, 94] }, + { "type": "MouseDown", "pos": [112, 94], "btn": "Left" }, + { "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, + { "type": "MouseDown", "pos": [112, 94], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, + { "type": "MouseDown", "pos": [112, 94], "btn": "Left", "index": 2 }, + { "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [112, 59] }, + { "type": "MouseMove", "pos": [112, 58] }, + { "type": "MouseDown", "pos": [112, 58], "btn": "Left" }, + { "type": "MouseUp", "pos": [112, 58], "btn": "Left" }, + { "type": "MouseDown", "pos": [112, 58], "btn": "Left", "index": 1 }, + { "type": "MouseMove", "pos": [129, 93] }, + { "type": "MouseMove", "pos": [130, 94] }, + { "type": "MouseUp", "pos": [130, 94], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 53 }, + { "type": "KeyDown", "key_code": 50 }, + { "type": "MouseMove", "pos": [164, 19] }, + { "type": "MouseMove", "pos": [163, 19] }, + { "type": "MouseDown", "pos": [163, 19], "btn": "Left" }, + { "type": "MouseUp", "pos": [163, 19], "btn": "Left" }, + { "type": "MouseDown", "pos": [163, 19], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [163, 19], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [85, 18] }, + { "type": "MouseMove", "pos": [84, 18] }, + { "type": "MouseDown", "pos": [84, 18], "btn": "Left" }, + { "type": "MouseUp", "pos": [84, 18], "btn": "Left" }, + { "type": "MouseDown", "pos": [84, 18], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [84, 18], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "MouseMove", "pos": [72, 17] }, + { "type": "MouseMove", "pos": [71, 17] }, + { "type": "MouseDown", "pos": [71, 17], "btn": "Left" }, + { "type": "MouseUp", "pos": [71, 17], "btn": "Left" }, + { "type": "MouseDown", "pos": [71, 17], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [71, 17], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 }, + { "type": "KeyDown", "key_code": 54 }, + { "type": "MouseMove", "pos": [87, 19] }, + { "type": "MouseMove", "pos": [87, 18] }, + { "type": "MouseDown", "pos": [87, 18], "btn": "Left" }, + { "type": "MouseUp", "pos": [87, 18], "btn": "Left" }, + { "type": "MouseDown", "pos": [87, 18], "btn": "Left", "index": 1 }, + { "type": "MouseUp", "pos": [87, 18], "btn": "Left" }, + { "type": "KeyDown", "key_code": 27 } +] diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/output.txt b/tests/tests/swfs/avm2/edittext_mouse_selection/output.txt new file mode 100644 index 000000000000..36d0f3b6602a --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouse_selection/output.txt @@ -0,0 +1,363 @@ +{ "type": "MouseMove", "pos": [174, 50] }, +{ "type": "MouseMove", "pos": [174, 51] }, +{ "type": "KeyDown", "key_code": 49 }, +{ "type": "MouseMove", "pos": [35, 23] }, +{ "type": "MouseMove", "pos": [34, 23] }, +{ "type": "MouseDown", "pos": [34, 23], "btn": "Left" }, +{ "type": "MouseMove", "pos": [201, 23] }, +{ "type": "MouseMove", "pos": [202, 23] }, +{ "type": "MouseUp", "pos": [202, 23], "btn": "Left" }, +Selected: rd1_word2_wo +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [121, 23] }, +{ "type": "MouseMove", "pos": [120, 23] }, +{ "type": "MouseDown", "pos": [120, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [120, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [120, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [120, 23], "btn": "Left" }, +Selected: word1_word2_word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [34, 22] }, +{ "type": "MouseMove", "pos": [34, 21] }, +{ "type": "MouseDown", "pos": [34, 21], "btn": "Left" }, +{ "type": "MouseUp", "pos": [34, 21], "btn": "Left" }, +{ "type": "MouseDown", "pos": [34, 21], "btn": "Left" }, +{ "type": "MouseUp", "pos": [34, 21], "btn": "Left" }, +Selected: word1_word2_word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [216, 22] }, +{ "type": "MouseMove", "pos": [216, 21] }, +{ "type": "MouseDown", "pos": [216, 21], "btn": "Left" }, +{ "type": "MouseUp", "pos": [216, 21], "btn": "Left" }, +{ "type": "MouseDown", "pos": [216, 21], "btn": "Left" }, +{ "type": "MouseUp", "pos": [216, 21], "btn": "Left" }, +Selected: word1_word2_word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [215, 21] }, +{ "type": "KeyDown", "key_code": 50 }, +{ "type": "MouseMove", "pos": [38, 24] }, +{ "type": "MouseMove", "pos": [37, 24] }, +{ "type": "MouseDown", "pos": [37, 24], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 24], "btn": "Left" }, +{ "type": "MouseDown", "pos": [37, 24], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 24], "btn": "Left" }, +Selected: word1 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [117, 22] }, +{ "type": "MouseMove", "pos": [116, 22] }, +{ "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, +Selected: word2 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [193, 19] }, +{ "type": "MouseMove", "pos": [194, 19] }, +{ "type": "MouseDown", "pos": [194, 19], "btn": "Left" }, +{ "type": "MouseUp", "pos": [194, 19], "btn": "Left" }, +{ "type": "MouseDown", "pos": [194, 19], "btn": "Left" }, +{ "type": "MouseUp", "pos": [194, 19], "btn": "Left" }, +Selected: word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [37, 20] }, +{ "type": "MouseMove", "pos": [36, 20] }, +{ "type": "MouseDown", "pos": [36, 20], "btn": "Left" }, +{ "type": "MouseUp", "pos": [36, 20], "btn": "Left" }, +{ "type": "MouseDown", "pos": [36, 20], "btn": "Left" }, +{ "type": "MouseMove", "pos": [115, 23] }, +{ "type": "MouseMove", "pos": [116, 23] }, +{ "type": "MouseUp", "pos": [116, 23], "btn": "Left" }, +{ "type": "MouseMove", "pos": [115, 23] }, +Selected: word1 word2 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseDown", "pos": [115, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [115, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [115, 23], "btn": "Left" }, +{ "type": "MouseMove", "pos": [192, 22] }, +{ "type": "MouseMove", "pos": [193, 22] }, +{ "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, +Selected: word2 word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseDown", "pos": [193, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [193, 22], "btn": "Left" }, +{ "type": "MouseMove", "pos": [40, 23] }, +{ "type": "MouseMove", "pos": [39, 23] }, +{ "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, +Selected: word1 word2 word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 49 }, +{ "type": "MouseMove", "pos": [40, 23] }, +{ "type": "MouseMove", "pos": [39, 23] }, +{ "type": "MouseMove", "pos": [38, 23] }, +{ "type": "MouseMove", "pos": [37, 23] }, +{ "type": "MouseDown", "pos": [37, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [37, 23], "btn": "Left" }, +{ "type": "MouseMove", "pos": [113, 25] }, +{ "type": "MouseMove", "pos": [114, 25] }, +{ "type": "KeyDown", "key_code": 50 }, +{ "type": "MouseMove", "pos": [115, 25] }, +{ "type": "MouseMove", "pos": [116, 25] }, +{ "type": "MouseUp", "pos": [116, 25], "btn": "Left" }, +{ "type": "MouseMove", "pos": [111, 25] }, +{ "type": "MouseMove", "pos": [110, 25] }, +{ "type": "KeyDown", "key_code": 49 }, +Selected: word1_word2 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [116, 23] }, +{ "type": "MouseMove", "pos": [117, 23] }, +{ "type": "MouseDown", "pos": [117, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [117, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [117, 23], "btn": "Left" }, +{ "type": "KeyDown", "key_code": 50 }, +{ "type": "MouseMove", "pos": [192, 22] }, +{ "type": "MouseMove", "pos": [193, 22] }, +{ "type": "MouseUp", "pos": [193, 22], "btn": "Left" }, +Selected: word2 word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 49 }, +{ "type": "MouseMove", "pos": [199, 22] }, +{ "type": "MouseMove", "pos": [200, 22] }, +{ "type": "MouseDown", "pos": [200, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [200, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [200, 22], "btn": "Left" }, +{ "type": "MouseMove", "pos": [121, 21] }, +{ "type": "MouseMove", "pos": [120, 21] }, +{ "type": "KeyDown", "key_code": 50 }, +Selected: word1 word2 word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseUp", "pos": [120, 21], "btn": "Left" }, +{ "type": "KeyDown", "key_code": 49 }, +{ "type": "MouseMove", "pos": [130, 26] }, +{ "type": "MouseMove", "pos": [130, 25] }, +{ "type": "MouseDown", "pos": [130, 25], "btn": "Left" }, +{ "type": "MouseUp", "pos": [130, 25], "btn": "Left" }, +{ "type": "MouseMove", "pos": [120, 24] }, +{ "type": "MouseMove", "pos": [119, 24] }, +{ "type": "MouseDown", "pos": [119, 24], "btn": "Left" }, +{ "type": "MouseUp", "pos": [119, 24], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [238, 96] }, +{ "type": "MouseMove", "pos": [238, 95] }, +{ "type": "KeyDown", "key_code": 51 }, +{ "type": "MouseMove", "pos": [97, 14] }, +{ "type": "MouseMove", "pos": [98, 14] }, +{ "type": "MouseDown", "pos": [98, 14], "btn": "Left" }, +{ "type": "MouseUp", "pos": [98, 14], "btn": "Left" }, +{ "type": "MouseDown", "pos": [98, 14], "btn": "Left" }, +{ "type": "MouseMove", "pos": [143, 103] }, +{ "type": "MouseMove", "pos": [143, 104] }, +{ "type": "MouseUp", "pos": [143, 104], "btn": "Left" }, +Selected: word2 word3 word4 word5 word6 word7 word8 word9\nword10 word11 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [193, 28] }, +{ "type": "MouseMove", "pos": [193, 27] }, +{ "type": "MouseDown", "pos": [193, 27], "btn": "Left" }, +{ "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, +{ "type": "MouseDown", "pos": [193, 27], "btn": "Left" }, +{ "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, +{ "type": "MouseDown", "pos": [193, 27], "btn": "Left" }, +{ "type": "MouseUp", "pos": [193, 27], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [145, 99] }, +{ "type": "MouseMove", "pos": [145, 100] }, +{ "type": "MouseDown", "pos": [145, 100], "btn": "Left" }, +{ "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, +{ "type": "MouseDown", "pos": [145, 100], "btn": "Left" }, +{ "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, +{ "type": "MouseDown", "pos": [145, 100], "btn": "Left" }, +{ "type": "MouseUp", "pos": [145, 100], "btn": "Left" }, +Selected: word10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 51 }, +{ "type": "MouseMove", "pos": [39, 24] }, +{ "type": "MouseMove", "pos": [39, 23] }, +{ "type": "MouseDown", "pos": [39, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [39, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, +{ "type": "MouseDown", "pos": [39, 23], "btn": "Left" }, +{ "type": "MouseUp", "pos": [39, 23], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [37, 49] }, +{ "type": "MouseMove", "pos": [37, 50] }, +{ "type": "MouseDown", "pos": [37, 50], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, +{ "type": "MouseDown", "pos": [37, 50], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, +{ "type": "MouseDown", "pos": [37, 50], "btn": "Left" }, +{ "type": "MouseUp", "pos": [37, 50], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [35, 93] }, +{ "type": "MouseMove", "pos": [35, 94] }, +{ "type": "MouseDown", "pos": [35, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, +{ "type": "MouseDown", "pos": [35, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, +{ "type": "MouseDown", "pos": [35, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [35, 94], "btn": "Left" }, +Selected: word10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [231, 93] }, +{ "type": "MouseMove", "pos": [232, 93] }, +{ "type": "MouseDown", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseDown", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseDown", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseUp", "pos": [232, 93], "btn": "Left" }, +{ "type": "MouseMove", "pos": [231, 86] }, +{ "type": "MouseMove", "pos": [231, 85] }, +Selected: word10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [234, 57] }, +{ "type": "MouseMove", "pos": [234, 56] }, +{ "type": "MouseDown", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseDown", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseDown", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseUp", "pos": [234, 56], "btn": "Left" }, +{ "type": "MouseMove", "pos": [234, 55] }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [233, 31] }, +{ "type": "MouseMove", "pos": [233, 30] }, +{ "type": "MouseDown", "pos": [233, 30], "btn": "Left" }, +{ "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, +{ "type": "MouseDown", "pos": [233, 30], "btn": "Left" }, +{ "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, +{ "type": "MouseDown", "pos": [233, 30], "btn": "Left" }, +{ "type": "MouseUp", "pos": [233, 30], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [147, 22] }, +{ "type": "MouseMove", "pos": [146, 22] }, +{ "type": "MouseDown", "pos": [146, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [146, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [146, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [146, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [146, 22], "btn": "Left" }, +{ "type": "MouseMove", "pos": [138, 81] }, +{ "type": "MouseMove", "pos": [138, 82] }, +{ "type": "MouseUp", "pos": [138, 82], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9\nword10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [135, 86] }, +{ "type": "MouseMove", "pos": [135, 87] }, +{ "type": "MouseDown", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseDown", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseDown", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseUp", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseDown", "pos": [135, 87], "btn": "Left" }, +{ "type": "MouseMove", "pos": [129, 45] }, +{ "type": "MouseMove", "pos": [129, 44] }, +{ "type": "MouseUp", "pos": [129, 44], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9\nword10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [246, 61] }, +{ "type": "MouseMove", "pos": [246, 60] }, +{ "type": "MouseDown", "pos": [246, 60], "btn": "Left" }, +{ "type": "MouseUp", "pos": [246, 60], "btn": "Left" }, +{ "type": "MouseDown", "pos": [246, 60], "btn": "Left" }, +{ "type": "MouseUp", "pos": [246, 60], "btn": "Left" }, +{ "type": "MouseDown", "pos": [246, 60], "btn": "Left" }, +{ "type": "MouseMove", "pos": [230, 90] }, +{ "type": "MouseMove", "pos": [229, 91] }, +{ "type": "MouseUp", "pos": [229, 91], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9\nword10 word11 word12 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [241, 60] }, +{ "type": "MouseMove", "pos": [242, 59] }, +{ "type": "MouseDown", "pos": [242, 59], "btn": "Left" }, +{ "type": "MouseUp", "pos": [242, 59], "btn": "Left" }, +{ "type": "MouseDown", "pos": [242, 59], "btn": "Left" }, +{ "type": "MouseUp", "pos": [242, 59], "btn": "Left" }, +{ "type": "MouseDown", "pos": [242, 59], "btn": "Left" }, +{ "type": "MouseMove", "pos": [239, 23] }, +{ "type": "MouseMove", "pos": [239, 22] }, +{ "type": "MouseUp", "pos": [239, 22], "btn": "Left" }, +Selected: word1 word2 word3 word4 word5 word6 word7 word8 word9 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 52 }, +{ "type": "KeyDown", "key_code": 51 }, +{ "type": "MouseMove", "pos": [99, 24] }, +{ "type": "MouseMove", "pos": [99, 23] }, +{ "type": "MouseDown", "pos": [99, 23], "btn": "Left" }, +{ "type": "MouseMove", "pos": [150, 96] }, +{ "type": "MouseMove", "pos": [150, 97] }, +{ "type": "MouseUp", "pos": [150, 97], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [116, 23] }, +{ "type": "MouseMove", "pos": [116, 22] }, +{ "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseDown", "pos": [116, 22], "btn": "Left" }, +{ "type": "MouseUp", "pos": [116, 22], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [112, 95] }, +{ "type": "MouseMove", "pos": [112, 94] }, +{ "type": "MouseDown", "pos": [112, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, +{ "type": "MouseDown", "pos": [112, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, +{ "type": "MouseDown", "pos": [112, 94], "btn": "Left" }, +{ "type": "MouseUp", "pos": [112, 94], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [112, 59] }, +{ "type": "MouseMove", "pos": [112, 58] }, +{ "type": "MouseDown", "pos": [112, 58], "btn": "Left" }, +{ "type": "MouseUp", "pos": [112, 58], "btn": "Left" }, +{ "type": "MouseDown", "pos": [112, 58], "btn": "Left" }, +{ "type": "MouseMove", "pos": [129, 93] }, +{ "type": "MouseMove", "pos": [130, 94] }, +{ "type": "MouseUp", "pos": [130, 94], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 53 }, +{ "type": "KeyDown", "key_code": 50 }, +{ "type": "MouseMove", "pos": [164, 19] }, +{ "type": "MouseMove", "pos": [163, 19] }, +{ "type": "MouseDown", "pos": [163, 19], "btn": "Left" }, +{ "type": "MouseUp", "pos": [163, 19], "btn": "Left" }, +{ "type": "MouseDown", "pos": [163, 19], "btn": "Left" }, +{ "type": "MouseUp", "pos": [163, 19], "btn": "Left" }, +Selected: word3 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [85, 18] }, +{ "type": "MouseMove", "pos": [84, 18] }, +{ "type": "MouseDown", "pos": [84, 18], "btn": "Left" }, +{ "type": "MouseUp", "pos": [84, 18], "btn": "Left" }, +{ "type": "MouseDown", "pos": [84, 18], "btn": "Left" }, +{ "type": "MouseUp", "pos": [84, 18], "btn": "Left" }, +Selected: word2 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "MouseMove", "pos": [72, 17] }, +{ "type": "MouseMove", "pos": [71, 17] }, +{ "type": "MouseDown", "pos": [71, 17], "btn": "Left" }, +{ "type": "MouseUp", "pos": [71, 17], "btn": "Left" }, +{ "type": "MouseDown", "pos": [71, 17], "btn": "Left" }, +{ "type": "MouseUp", "pos": [71, 17], "btn": "Left" }, +Selected: word1 +{ "type": "KeyDown", "key_code": 27 }, +{ "type": "KeyDown", "key_code": 54 }, +{ "type": "MouseMove", "pos": [87, 19] }, +{ "type": "MouseMove", "pos": [87, 18] }, +{ "type": "MouseDown", "pos": [87, 18], "btn": "Left" }, +{ "type": "MouseUp", "pos": [87, 18], "btn": "Left" }, +{ "type": "MouseDown", "pos": [87, 18], "btn": "Left" }, +{ "type": "MouseUp", "pos": [87, 18], "btn": "Left" }, +Selected: +{ "type": "KeyDown", "key_code": 27 }, diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/test.swf b/tests/tests/swfs/avm2/edittext_mouse_selection/test.swf new file mode 100644 index 000000000000..5f5f7d16d746 Binary files /dev/null and b/tests/tests/swfs/avm2/edittext_mouse_selection/test.swf differ diff --git a/tests/tests/swfs/avm2/edittext_mouse_selection/test.toml b/tests/tests/swfs/avm2/edittext_mouse_selection/test.toml new file mode 100644 index 000000000000..cf6123969a1d --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouse_selection/test.toml @@ -0,0 +1 @@ +num_ticks = 1 diff --git a/web/src/lib.rs b/web/src/lib.rs index a56e3544995a..e00d614c6d65 100644 --- a/web/src/lib.rs +++ b/web/src/lib.rs @@ -575,6 +575,8 @@ impl RuffleHandle { 2 => MouseButton::Right, _ => MouseButton::Unknown, }, + // TODO The index should be provided by the browser, not calculated. + index: None, }; let _ = instance.with_core_mut(|core| { core.handle_event(event);