From 618c32f859aed31b680486a9cb9795e1ff26dc60 Mon Sep 17 00:00:00 2001 From: David Wendt Date: Sat, 2 Oct 2021 17:28:13 -0400 Subject: [PATCH] core: Add `InteractiveObject` trait for objects that can receive input events --- core/src/display_object.rs | 5 ++ core/src/display_object/interactive.rs | 79 ++++++++++++++++++++++++++ core/src/display_object/movie_clip.rs | 22 +++++++ 3 files changed, 106 insertions(+) create mode 100644 core/src/display_object/interactive.rs diff --git a/core/src/display_object.rs b/core/src/display_object.rs index 8e72f22a7..8cbf98178 100644 --- a/core/src/display_object.rs +++ b/core/src/display_object.rs @@ -29,6 +29,7 @@ mod bitmap; mod container; mod edit_text; mod graphic; +mod interactive; mod morph_shape; mod movie_clip; mod stage; @@ -46,6 +47,7 @@ pub use avm2_button::Avm2Button; pub use bitmap::Bitmap; pub use edit_text::{AutoSizeMode, EditText, TextSelection}; pub use graphic::Graphic; +pub use interactive::{InteractiveObject, TInteractiveObject}; pub use morph_shape::{MorphShape, MorphShapeStatic}; pub use movie_clip::{MovieClip, Scene}; pub use stage::{Stage, StageAlign, StageQuality, StageScaleMode}; @@ -1077,6 +1079,9 @@ pub trait TDisplayObject<'gc>: fn as_bitmap(self) -> Option> { None } + fn as_interactive(self) -> Option> { + None + } fn apply_place_object( &self, diff --git a/core/src/display_object/interactive.rs b/core/src/display_object/interactive.rs new file mode 100644 index 000000000..5632dc68e --- /dev/null +++ b/core/src/display_object/interactive.rs @@ -0,0 +1,79 @@ +//! Interactive object enumtrait + +use crate::display_object::movie_clip::MovieClip; +use bitflags::bitflags; +use gc_arena::{Collect, MutationContext}; +use ruffle_macros::enum_trait_object; +use std::cell::{Ref, RefMut}; +use std::fmt::Debug; + +bitflags! { + /// Boolean state flags used by `InteractiveObject`. + #[derive(Collect)] + #[collect(require_static)] + struct InteractiveObjectFlags: u8 { + /// Whether this `InteractiveObject` accepts mouse and other user + /// events. + const MOUSE_ENABLED = 1 << 0; + + /// Whether this `InteractiveObject` accepts double-clicks. + const DOUBLE_CLICK_ENABLED = 1 << 1; + } +} + +#[derive(Collect, Clone, Debug)] +#[collect(no_drop)] +pub struct InteractiveObjectBase { + flags: InteractiveObjectFlags, +} + +impl Default for InteractiveObjectBase { + fn default() -> Self { + Self { + flags: InteractiveObjectFlags::MOUSE_ENABLED, + } + } +} + +#[enum_trait_object( + #[derive(Clone, Collect, Debug, Copy)] + #[collect(no_drop)] + pub enum InteractiveObject<'gc> { + MovieClip(MovieClip<'gc>), + } +)] +pub trait TInteractiveObject<'gc>: + 'gc + Clone + Copy + Collect + Debug + Into> +{ + fn base(&self) -> Ref; + + fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut; + + /// Check if the interactive object accepts user input. + fn mouse_enabled(self) -> bool { + self.base() + .flags + .contains(InteractiveObjectFlags::MOUSE_ENABLED) + } + + /// Set if the interactive object accepts user input. + fn set_mouse_enabled(self, mc: MutationContext<'gc, '_>, value: bool) { + self.base_mut(mc) + .flags + .set(InteractiveObjectFlags::MOUSE_ENABLED, value) + } + + /// Check if the interactive object accepts double-click events. + fn double_click_enabled(self) -> bool { + self.base() + .flags + .contains(InteractiveObjectFlags::DOUBLE_CLICK_ENABLED) + } + + // Set if the interactive object accepts double-click events. + fn set_double_click_enabled(self, mc: MutationContext<'gc, '_>, value: bool) { + self.base_mut(mc) + .flags + .set(InteractiveObjectFlags::DOUBLE_CLICK_ENABLED, value) + } +} diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index e1c87729e..af990ee3d 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -20,6 +20,9 @@ use crate::display_object::container::{ dispatch_added_event_only, dispatch_added_to_stage_event_only, dispatch_removed_event, ChildContainer, TDisplayObjectContainer, }; +use crate::display_object::interactive::{ + InteractiveObject, InteractiveObjectBase, TInteractiveObject, +}; use crate::display_object::{ Avm1Button, Avm2Button, Bitmap, DisplayObjectBase, EditText, Graphic, MorphShapeStatic, TDisplayObject, Text, Video, @@ -68,6 +71,7 @@ pub struct MovieClip<'gc>(GcCell<'gc, MovieClipData<'gc>>); #[collect(no_drop)] pub struct MovieClipData<'gc> { base: DisplayObjectBase<'gc>, + interactive_base: InteractiveObjectBase, static_data: Gc<'gc, MovieClipStatic>, tag_stream_pos: u64, current_frame: FrameNumber, @@ -98,6 +102,7 @@ impl<'gc> MovieClip<'gc> { gc_context, MovieClipData { base: Default::default(), + interactive_base: Default::default(), static_data: Gc::allocate(gc_context, MovieClipStatic::empty(movie)), tag_stream_pos: 0, current_frame: 0, @@ -132,6 +137,7 @@ impl<'gc> MovieClip<'gc> { gc_context, MovieClipData { base: Default::default(), + interactive_base: Default::default(), static_data: Gc::allocate(gc_context, MovieClipStatic::empty(movie)), tag_stream_pos: 0, current_frame: 0, @@ -166,6 +172,7 @@ impl<'gc> MovieClip<'gc> { gc_context, MovieClipData { base: Default::default(), + interactive_base: Default::default(), static_data: Gc::allocate( gc_context, MovieClipStatic::with_data(id, swf, num_frames), @@ -200,6 +207,7 @@ impl<'gc> MovieClip<'gc> { gc_context, MovieClipData { base: Default::default(), + interactive_base: Default::default(), static_data: Gc::allocate( gc_context, MovieClipStatic::with_data(0, movie.into(), num_frames), @@ -2029,6 +2037,10 @@ impl<'gc> TDisplayObject<'gc> for MovieClip<'gc> { Some(self.into()) } + fn as_interactive(self) -> Option> { + Some(self.into()) + } + fn as_drawing(&self, gc_context: MutationContext<'gc, '_>) -> Option> { Some(RefMut::map(self.0.write(gc_context), |s| &mut s.drawing)) } @@ -2129,6 +2141,16 @@ impl<'gc> TDisplayObjectContainer<'gc> for MovieClip<'gc> { impl_display_object_container!(container); } +impl<'gc> TInteractiveObject<'gc> for MovieClip<'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> MovieClipData<'gc> { /// Replace the current MovieClipData with a completely new SwfMovie. ///