diff --git a/core/src/display_object/avm2_button.rs b/core/src/display_object/avm2_button.rs index 9f2b2e085..13175f54e 100644 --- a/core/src/display_object/avm2_button.rs +++ b/core/src/display_object/avm2_button.rs @@ -7,6 +7,9 @@ use crate::backend::ui::MouseCursor; use crate::context::{RenderContext, UpdateContext}; use crate::display_object::avm1_button::{ButtonState, ButtonTracking}; use crate::display_object::container::{dispatch_added_event, dispatch_removed_event}; +use crate::display_object::interactive::{ + InteractiveObject, InteractiveObjectBase, TInteractiveObject, +}; use crate::display_object::{DisplayObjectBase, MovieClip, TDisplayObject}; use crate::events::{ClipEvent, ClipEventResult}; use crate::prelude::*; @@ -14,6 +17,7 @@ use crate::tag_utils::{SwfMovie, SwfSlice}; use crate::types::{Degrees, Percent}; use crate::vminterface::Instantiator; use gc_arena::{Collect, GcCell, MutationContext}; +use std::cell::{Ref, RefMut}; use std::sync::Arc; #[derive(Clone, Debug, Collect, Copy)] @@ -24,6 +28,8 @@ pub struct Avm2Button<'gc>(GcCell<'gc, Avm2ButtonData<'gc>>); #[collect(no_drop)] pub struct Avm2ButtonData<'gc> { base: DisplayObjectBase<'gc>, + interactive_base: InteractiveObjectBase, + static_data: GcCell<'gc, ButtonStatic>, /// The current button state to render. @@ -96,6 +102,7 @@ impl<'gc> Avm2Button<'gc> { context.gc_context, Avm2ButtonData { base: Default::default(), + interactive_base: Default::default(), static_data: GcCell::allocate(context.gc_context, static_data), state: self::ButtonState::Up, hit_area: None, @@ -689,6 +696,10 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> { Some(*self) } + fn as_interactive(self) -> Option> { + Some(self.into()) + } + fn allow_as_mask(&self) -> bool { let state = self.0.read().state; let current_state = self.get_state_child(state.into()); @@ -777,6 +788,16 @@ impl<'gc> TDisplayObject<'gc> for Avm2Button<'gc> { } } +impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> { + fn base(&self) -> Ref { + Ref::map(self.0.read(), |r| &r.interactive_base) + } + + fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut { + RefMut::map(self.0.write(mc), |w| &mut w.interactive_base) + } +} + impl<'gc> Avm2ButtonData<'gc> { fn play_sound( &self, diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index f35a79208..8ae606568 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -10,6 +10,9 @@ use crate::avm2::{ }; use crate::backend::ui::MouseCursor; use crate::context::{RenderContext, UpdateContext}; +use crate::display_object::interactive::{ + InteractiveObject, InteractiveObjectBase, TInteractiveObject, +}; use crate::display_object::{DisplayObjectBase, TDisplayObject}; use crate::drawing::Drawing; use crate::events::{ButtonKeyCode, ClipEvent, ClipEventResult, KeyCode}; @@ -25,7 +28,7 @@ use crate::vminterface::{AvmObject, AvmType, Instantiator}; use crate::xml::XmlDocument; use chrono::Utc; use gc_arena::{Collect, Gc, GcCell, MutationContext}; -use std::{cell::Ref, sync::Arc}; +use std::{cell::Ref, cell::RefMut, sync::Arc}; use swf::Twips; /// Boxed error type. @@ -60,6 +63,9 @@ pub struct EditTextData<'gc> { /// DisplayObject common properties. base: DisplayObjectBase<'gc>, + /// InteractiveObject common properties. + interactive_base: InteractiveObjectBase, + /// Static data shared among all instances of this `EditText`. static_data: Gc<'gc, EditTextStatic>, @@ -283,6 +289,7 @@ impl<'gc> EditText<'gc> { context.gc_context, EditTextData { base, + interactive_base: Default::default(), document, text_spans, static_data: gc_arena::Gc::allocate( @@ -1557,6 +1564,10 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { Some(*self) } + fn as_interactive(self) -> Option> { + Some(self.into()) + } + fn post_instantiation( &self, context: &mut UpdateContext<'_, 'gc, '_>, @@ -1881,6 +1892,16 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> { } } +impl<'gc> TInteractiveObject<'gc> for EditText<'gc> { + fn base(&self) -> Ref { + Ref::map(self.0.read(), |r| &r.interactive_base) + } + + fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut { + RefMut::map(self.0.write(mc), |w| &mut w.interactive_base) + } +} + /// Static data shared between all instances of a text object. #[derive(Debug, Clone, Collect)] #[collect(no_drop)] diff --git a/core/src/display_object/interactive.rs b/core/src/display_object/interactive.rs index 5632dc68e..8e8554177 100644 --- a/core/src/display_object/interactive.rs +++ b/core/src/display_object/interactive.rs @@ -1,6 +1,9 @@ //! Interactive object enumtrait +use crate::display_object::avm2_button::Avm2Button; +use crate::display_object::edit_text::EditText; use crate::display_object::movie_clip::MovieClip; +use crate::display_object::stage::Stage; use bitflags::bitflags; use gc_arena::{Collect, MutationContext}; use ruffle_macros::enum_trait_object; @@ -39,7 +42,10 @@ impl Default for InteractiveObjectBase { #[derive(Clone, Collect, Debug, Copy)] #[collect(no_drop)] pub enum InteractiveObject<'gc> { + Stage(Stage<'gc>), + Avm2Button(Avm2Button<'gc>), MovieClip(MovieClip<'gc>), + EditText(EditText<'gc>), } )] pub trait TInteractiveObject<'gc>: diff --git a/core/src/display_object/stage.rs b/core/src/display_object/stage.rs index 2d2d7e371..735be9977 100644 --- a/core/src/display_object/stage.rs +++ b/core/src/display_object/stage.rs @@ -11,12 +11,16 @@ use crate::context::{RenderContext, UpdateContext}; use crate::display_object::container::{ ChildContainer, DisplayObjectContainer, TDisplayObjectContainer, }; +use crate::display_object::interactive::{ + InteractiveObject, InteractiveObjectBase, TInteractiveObject, +}; use crate::display_object::{render_base, DisplayObject, DisplayObjectBase, TDisplayObject}; use crate::prelude::*; use crate::types::{Degrees, Percent}; use crate::vminterface::{AvmType, Instantiator}; use bitflags::bitflags; use gc_arena::{Collect, GcCell, MutationContext}; +use std::cell::{Ref, RefMut}; use std::fmt::{self, Display, Formatter}; use std::str::FromStr; @@ -36,6 +40,9 @@ pub struct StageData<'gc> { /// parent, as the stage does not respect it. base: DisplayObjectBase<'gc>, + /// Base properties for interactive display objects. + interactive_base: InteractiveObjectBase, + /// The list of all children of the stage. /// /// Stage children are exposed to AVM1 as `_level*n*` on all stage objects. @@ -98,6 +105,7 @@ impl<'gc> Stage<'gc> { gc_context, StageData { base: Default::default(), + interactive_base: Default::default(), child: Default::default(), background_color: None, letterbox: Letterbox::Fullscreen, @@ -549,6 +557,10 @@ impl<'gc> TDisplayObject<'gc> for Stage<'gc> { Some(self.into()) } + fn as_interactive(self) -> Option> { + Some(self.into()) + } + fn as_stage(&self) -> Option> { Some(*self) } @@ -588,6 +600,16 @@ impl<'gc> TDisplayObjectContainer<'gc> for Stage<'gc> { impl_display_object_container!(child); } +impl<'gc> TInteractiveObject<'gc> for Stage<'gc> { + fn base(&self) -> Ref { + Ref::map(self.0.read(), |r| &r.interactive_base) + } + + fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut { + RefMut::map(self.0.write(mc), |w| &mut w.interactive_base) + } +} + pub struct ParseEnumError; /// The scale mode of a stage. diff --git a/tests/tests/regression_tests.rs b/tests/tests/regression_tests.rs index 1363eabad..bd36be5f1 100644 --- a/tests/tests/regression_tests.rs +++ b/tests/tests/regression_tests.rs @@ -712,6 +712,9 @@ swf_tests! { (as3_propertyisenumerable_namespaces, "avm2/propertyisenumerable_namespaces", 1), (as3_interface_namespaces, "avm2/interface_namespaces", 1), (as3_interactiveobject_enabled, "avm2/interactiveobject_enabled", 1), + (as3_stage_mouseenabled, "avm2/stage_mouseenabled", 1), + (as3_simplebutton_mouseenabled, "avm2/simplebutton_mouseenabled", 1), + (as3_edittext_mouseenabled, "avm2/edittext_mouseenabled", 1), } // TODO: These tests have some inaccuracies currently, so we use approx_eq to test that numeric values are close enough. diff --git a/tests/tests/swfs/avm2/edittext_mouseenabled/Test.as b/tests/tests/swfs/avm2/edittext_mouseenabled/Test.as new file mode 100644 index 000000000..67117e739 --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouseenabled/Test.as @@ -0,0 +1,54 @@ +package { + public class Test { + + } +} + +import flash.text.TextField; + +trace("///var text = new TextField();"); +var text = new TextField(); + +trace("///(Initial state of event enabled flags...)"); + +trace("///text.mouseEnabled"); +trace(text.mouseEnabled); + +trace("///text.doubleClickEnabled"); +trace(text.doubleClickEnabled); + +trace("///text.doubleClickEnabled = true"); +text.doubleClickEnabled = true; + +trace("///text.mouseEnabled"); +trace(text.mouseEnabled); + +trace("///text.doubleClickEnabled"); +trace(text.doubleClickEnabled); + +trace("///text.mouseEnabled = false"); +text.mouseEnabled = false; + +trace("///text.mouseEnabled"); +trace(text.mouseEnabled); + +trace("///text.doubleClickEnabled"); +trace(text.doubleClickEnabled); + +trace("///text.doubleClickEnabled = false"); +text.doubleClickEnabled = false; + +trace("///text.mouseEnabled"); +trace(text.mouseEnabled); + +trace("///text.doubleClickEnabled"); +trace(text.doubleClickEnabled); + +trace("///text.mouseEnabled = true"); +text.mouseEnabled = true; + +trace("///text.mouseEnabled"); +trace(text.mouseEnabled); + +trace("///text.doubleClickEnabled"); +trace(text.doubleClickEnabled); \ No newline at end of file diff --git a/tests/tests/swfs/avm2/edittext_mouseenabled/output.txt b/tests/tests/swfs/avm2/edittext_mouseenabled/output.txt new file mode 100644 index 000000000..fead21116 --- /dev/null +++ b/tests/tests/swfs/avm2/edittext_mouseenabled/output.txt @@ -0,0 +1,26 @@ +///var text = new TextField(); +///(Initial state of event enabled flags...) +///text.mouseEnabled +true +///text.doubleClickEnabled +false +///text.doubleClickEnabled = true +///text.mouseEnabled +true +///text.doubleClickEnabled +true +///text.mouseEnabled = false +///text.mouseEnabled +false +///text.doubleClickEnabled +true +///text.doubleClickEnabled = false +///text.mouseEnabled +false +///text.doubleClickEnabled +false +///text.mouseEnabled = true +///text.mouseEnabled +true +///text.doubleClickEnabled +false diff --git a/tests/tests/swfs/avm2/edittext_mouseenabled/test.fla b/tests/tests/swfs/avm2/edittext_mouseenabled/test.fla new file mode 100644 index 000000000..f18fc3e7a Binary files /dev/null and b/tests/tests/swfs/avm2/edittext_mouseenabled/test.fla differ diff --git a/tests/tests/swfs/avm2/edittext_mouseenabled/test.swf b/tests/tests/swfs/avm2/edittext_mouseenabled/test.swf new file mode 100644 index 000000000..3253f22c1 Binary files /dev/null and b/tests/tests/swfs/avm2/edittext_mouseenabled/test.swf differ diff --git a/tests/tests/swfs/avm2/simplebutton_mouseenabled/Test.as b/tests/tests/swfs/avm2/simplebutton_mouseenabled/Test.as new file mode 100644 index 000000000..62728f6ea --- /dev/null +++ b/tests/tests/swfs/avm2/simplebutton_mouseenabled/Test.as @@ -0,0 +1,54 @@ +package { + public class Test { + + } +} + +import flash.display.SimpleButton; + +trace("///var btn = new SimpleButton();"); +var btn = new SimpleButton(); + +trace("///(Initial state of event enabled flags...)"); + +trace("///btn.mouseEnabled"); +trace(btn.mouseEnabled); + +trace("///btn.doubleClickEnabled"); +trace(btn.doubleClickEnabled); + +trace("///btn.doubleClickEnabled = true"); +btn.doubleClickEnabled = true; + +trace("///btn.mouseEnabled"); +trace(btn.mouseEnabled); + +trace("///btn.doubleClickEnabled"); +trace(btn.doubleClickEnabled); + +trace("///btn.mouseEnabled = false"); +btn.mouseEnabled = false; + +trace("///btn.mouseEnabled"); +trace(btn.mouseEnabled); + +trace("///btn.doubleClickEnabled"); +trace(btn.doubleClickEnabled); + +trace("///btn.doubleClickEnabled = false"); +btn.doubleClickEnabled = false; + +trace("///btn.mouseEnabled"); +trace(btn.mouseEnabled); + +trace("///btn.doubleClickEnabled"); +trace(btn.doubleClickEnabled); + +trace("///btn.mouseEnabled = true"); +btn.mouseEnabled = true; + +trace("///btn.mouseEnabled"); +trace(btn.mouseEnabled); + +trace("///btn.doubleClickEnabled"); +trace(btn.doubleClickEnabled); \ No newline at end of file diff --git a/tests/tests/swfs/avm2/simplebutton_mouseenabled/output.txt b/tests/tests/swfs/avm2/simplebutton_mouseenabled/output.txt new file mode 100644 index 000000000..204ceddd6 --- /dev/null +++ b/tests/tests/swfs/avm2/simplebutton_mouseenabled/output.txt @@ -0,0 +1,26 @@ +///var btn = new SimpleButton(); +///(Initial state of event enabled flags...) +///btn.mouseEnabled +true +///btn.doubleClickEnabled +false +///btn.doubleClickEnabled = true +///btn.mouseEnabled +true +///btn.doubleClickEnabled +true +///btn.mouseEnabled = false +///btn.mouseEnabled +false +///btn.doubleClickEnabled +true +///btn.doubleClickEnabled = false +///btn.mouseEnabled +false +///btn.doubleClickEnabled +false +///btn.mouseEnabled = true +///btn.mouseEnabled +true +///btn.doubleClickEnabled +false diff --git a/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.fla b/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.fla new file mode 100644 index 000000000..f18fc3e7a Binary files /dev/null and b/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.fla differ diff --git a/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.swf b/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.swf new file mode 100644 index 000000000..2dcf8bee4 Binary files /dev/null and b/tests/tests/swfs/avm2/simplebutton_mouseenabled/test.swf differ diff --git a/tests/tests/swfs/avm2/stage_mouseenabled/output.txt b/tests/tests/swfs/avm2/stage_mouseenabled/output.txt new file mode 100644 index 000000000..3bbcabf35 --- /dev/null +++ b/tests/tests/swfs/avm2/stage_mouseenabled/output.txt @@ -0,0 +1,15 @@ +///(Initial state of event enabled flags...) +///this.stage.mouseEnabled +true +///this.stage.doubleClickEnabled +false +///this.stage.doubleClickEnabled = true +///this.stage.mouseEnabled +true +///this.stage.doubleClickEnabled +true +///this.stage.doubleClickEnabled = false +///this.stage.mouseEnabled +true +///this.stage.doubleClickEnabled +false diff --git a/tests/tests/swfs/avm2/stage_mouseenabled/test.fla b/tests/tests/swfs/avm2/stage_mouseenabled/test.fla new file mode 100644 index 000000000..87a41c2b4 Binary files /dev/null and b/tests/tests/swfs/avm2/stage_mouseenabled/test.fla differ diff --git a/tests/tests/swfs/avm2/stage_mouseenabled/test.swf b/tests/tests/swfs/avm2/stage_mouseenabled/test.swf new file mode 100644 index 000000000..07dfade66 Binary files /dev/null and b/tests/tests/swfs/avm2/stage_mouseenabled/test.swf differ