core: Add `InteractiveObject` trait for objects that can receive input events

This commit is contained in:
David Wendt 2021-10-02 17:28:13 -04:00 committed by kmeisthax
parent 89718475df
commit 618c32f859
3 changed files with 106 additions and 0 deletions

View File

@ -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<Bitmap<'gc>> {
None
}
fn as_interactive(self) -> Option<InteractiveObject<'gc>> {
None
}
fn apply_place_object(
&self,

View File

@ -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<InteractiveObject<'gc>>
{
fn base(&self) -> Ref<InteractiveObjectBase>;
fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut<InteractiveObjectBase>;
/// 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)
}
}

View File

@ -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<InteractiveObject<'gc>> {
Some(self.into())
}
fn as_drawing(&self, gc_context: MutationContext<'gc, '_>) -> Option<RefMut<'_, Drawing>> {
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<InteractiveObjectBase> {
Ref::map(self.0.read(), |r| &r.interactive_base)
}
fn base_mut(&self, mc: MutationContext<'gc, '_>) -> RefMut<InteractiveObjectBase> {
RefMut::map(self.0.write(mc), |w| &mut w.interactive_base)
}
}
impl<'gc> MovieClipData<'gc> {
/// Replace the current MovieClipData with a completely new SwfMovie.
///