diff --git a/core/src/avm2/globals/flash/display/sprite.rs b/core/src/avm2/globals/flash/display/sprite.rs index 47f1ebdea..40df9edca 100644 --- a/core/src/avm2/globals/flash/display/sprite.rs +++ b/core/src/avm2/globals/flash/display/sprite.rs @@ -113,6 +113,44 @@ pub fn set_sound_transform<'gc>( Ok(Value::Undefined) } +/// Implements `buttonMode`'s getter +pub fn button_mode<'gc>( + _activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + _args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mc) = this + .and_then(|o| o.as_display_object()) + .and_then(|o| o.as_movie_clip()) + { + return Ok(mc.forced_button_mode().into()); + } + + Ok(Value::Undefined) +} + +/// Implements `buttonMode`'s setter +pub fn set_button_mode<'gc>( + activation: &mut Activation<'_, 'gc, '_>, + this: Option>, + args: &[Value<'gc>], +) -> Result, Error> { + if let Some(mc) = this + .and_then(|o| o.as_display_object()) + .and_then(|o| o.as_movie_clip()) + { + let forced_button_mode = args + .get(0) + .cloned() + .unwrap_or(Value::Undefined) + .coerce_to_boolean(); + + mc.set_forced_button_mode(&mut activation.context, forced_button_mode); + } + + Ok(Value::Undefined) +} + /// Construct `Sprite`'s class. pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc>> { let class = Class::new( @@ -144,6 +182,7 @@ pub fn create_class<'gc>(mc: MutationContext<'gc, '_>) -> GcCell<'gc, Class<'gc> Some(sound_transform), Some(set_sound_transform), ), + ("buttonMode", Some(button_mode), Some(set_button_mode)), ]; write.define_public_builtin_instance_properties(mc, PUBLIC_INSTANCE_PROPERTIES); diff --git a/core/src/display_object/movie_clip.rs b/core/src/display_object/movie_clip.rs index d5c4ac823..be4cd2644 100644 --- a/core/src/display_object/movie_clip.rs +++ b/core/src/display_object/movie_clip.rs @@ -87,7 +87,13 @@ pub struct MovieClipData<'gc> { is_focusable: bool, has_focus: bool, enabled: bool, + + /// Show a hand cursor when the clip is in button mode. use_hand_cursor: bool, + + /// Force enable button mode, which causes all mouse-related events to + /// trigger on this clip rather than any input-eligible children. + button_mode: bool, last_queued_script_frame: Option, queued_script_frame: Option, queued_goto_frame: Option, @@ -116,6 +122,7 @@ impl<'gc> MovieClip<'gc> { has_focus: false, enabled: true, use_hand_cursor: true, + button_mode: false, last_queued_script_frame: None, queued_script_frame: None, queued_goto_frame: None, @@ -150,6 +157,7 @@ impl<'gc> MovieClip<'gc> { has_focus: false, enabled: true, use_hand_cursor: true, + button_mode: false, last_queued_script_frame: None, queued_script_frame: None, queued_goto_frame: None, @@ -187,6 +195,7 @@ impl<'gc> MovieClip<'gc> { has_focus: false, enabled: true, use_hand_cursor: true, + button_mode: false, last_queued_script_frame: None, queued_script_frame: None, queued_goto_frame: None, @@ -221,6 +230,7 @@ impl<'gc> MovieClip<'gc> { has_focus: false, enabled: true, use_hand_cursor: true, + button_mode: false, last_queued_script_frame: None, queued_script_frame: None, queued_goto_frame: None, @@ -1675,12 +1685,25 @@ impl<'gc> MovieClip<'gc> { self.0.read().tag_stream_len() } + pub fn forced_button_mode(self) -> bool { + self.0.read().button_mode + } + + pub fn set_forced_button_mode( + self, + context: &mut UpdateContext<'_, 'gc, '_>, + button_mode: bool, + ) { + self.0.write(context.gc_context).button_mode = button_mode; + } + pub fn is_button_mode(&self, context: &mut UpdateContext<'_, 'gc, '_>) -> bool { - if self - .0 - .read() - .clip_event_flags - .intersects(ClipEvent::BUTTON_EVENT_FLAGS) + if self.forced_button_mode() + || self + .0 + .read() + .clip_event_flags + .intersects(ClipEvent::BUTTON_EVENT_FLAGS) { true } else {