avm1: Allow `button.enabled` to hold any value

This commit is contained in:
Toad06 2023-04-30 00:43:14 +02:00 committed by relrelb
parent 86c27078d3
commit ee7403952a
16 changed files with 122 additions and 98 deletions

View File

@ -38,10 +38,10 @@ macro_rules! button_setter {
} }
const PROTO_DECLS: &[Declaration] = declare_properties! { const PROTO_DECLS: &[Declaration] = declare_properties! {
"enabled" => property(button_getter!(enabled), button_setter!(set_enabled)); "enabled" => bool(true);
"getDepth" => method(globals::get_depth; DONT_ENUM | DONT_DELETE | READ_ONLY | VERSION_6); "getDepth" => method(globals::get_depth; DONT_DELETE | READ_ONLY | VERSION_6);
"useHandCursor" => property(button_getter!(use_hand_cursor), button_setter!(set_use_hand_cursor)); "useHandCursor" => property(button_getter!(use_hand_cursor), button_setter!(set_use_hand_cursor));
"blendMode" => property(button_getter!(blend_mode), button_setter!(set_blend_mode); DONT_DELETE | DONT_ENUM); "blendMode" => property(button_getter!(blend_mode), button_setter!(set_blend_mode); DONT_DELETE);
}; };
pub fn create_proto<'gc>( pub fn create_proto<'gc>(
@ -63,28 +63,6 @@ pub fn constructor<'gc>(
Ok(this.into()) Ok(this.into())
} }
fn enabled<'gc>(
this: Avm1Button<'gc>,
_activation: &mut Activation<'_, 'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
// TODO: This property should return the value set by the user.
Ok(this.enabled().into())
}
fn set_enabled<'gc>(
this: Avm1Button<'gc>,
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
) -> Result<(), Error<'gc>> {
let enabled = if matches!(value, Value::Undefined) {
true
} else {
value.as_bool(activation.swf_version())
};
this.set_enabled(&mut activation.context, enabled);
Ok(())
}
fn use_hand_cursor<'gc>( fn use_hand_cursor<'gc>(
this: Avm1Button<'gc>, this: Avm1Button<'gc>,
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,

View File

@ -107,7 +107,7 @@ const PROTO_DECLS: &[Declaration] = declare_properties! {
"attachBitmap" => method(mc_method!(attach_bitmap); DONT_ENUM | DONT_DELETE | VERSION_8); "attachBitmap" => method(mc_method!(attach_bitmap); DONT_ENUM | DONT_DELETE | VERSION_8);
"removeMovieClip" => method(remove_movie_clip; DONT_ENUM | DONT_DELETE); "removeMovieClip" => method(remove_movie_clip; DONT_ENUM | DONT_DELETE);
"transform" => property(mc_getter!(transform), mc_setter!(set_transform); DONT_ENUM | VERSION_8); "transform" => property(mc_getter!(transform), mc_setter!(set_transform); DONT_ENUM | VERSION_8);
"enabled" => property(mc_getter!(enabled), mc_setter!(set_enabled); DONT_DELETE | DONT_ENUM); "enabled" => bool(true; DONT_ENUM);
"_lockroot" => property(mc_getter!(lock_root), mc_setter!(set_lock_root); DONT_DELETE | DONT_ENUM); "_lockroot" => property(mc_getter!(lock_root), mc_setter!(set_lock_root); DONT_DELETE | DONT_ENUM);
"useHandCursor" => property(mc_getter!(use_hand_cursor), mc_setter!(set_use_hand_cursor); DONT_DELETE | DONT_ENUM); "useHandCursor" => property(mc_getter!(use_hand_cursor), mc_setter!(set_use_hand_cursor); DONT_DELETE | DONT_ENUM);
"blendMode" => property(mc_getter!(blend_mode), mc_setter!(set_blend_mode); DONT_DELETE | DONT_ENUM); "blendMode" => property(mc_getter!(blend_mode), mc_setter!(set_blend_mode); DONT_DELETE | DONT_ENUM);
@ -1409,28 +1409,6 @@ fn set_transform<'gc>(
Ok(()) Ok(())
} }
fn enabled<'gc>(
this: MovieClip<'gc>,
_activation: &mut Activation<'_, 'gc>,
) -> Result<Value<'gc>, Error<'gc>> {
// TODO: This property should return the value set by the user.
Ok(this.enabled().into())
}
fn set_enabled<'gc>(
this: MovieClip<'gc>,
activation: &mut Activation<'_, 'gc>,
value: Value<'gc>,
) -> Result<(), Error<'gc>> {
let enabled = if matches!(value, Value::Undefined) {
true
} else {
value.as_bool(activation.swf_version())
};
this.set_enabled(&mut activation.context, enabled);
Ok(())
}
fn lock_root<'gc>( fn lock_root<'gc>(
this: MovieClip<'gc>, this: MovieClip<'gc>,
_activation: &mut Activation<'_, 'gc>, _activation: &mut Activation<'_, 'gc>,

View File

@ -204,7 +204,7 @@ pub fn get_enabled<'gc>(
.and_then(|o| o.as_display_object()) .and_then(|o| o.as_display_object())
.and_then(|dobj| dobj.as_movie_clip()) .and_then(|dobj| dobj.as_movie_clip())
{ {
return Ok(mc.enabled().into()); return Ok(mc.avm2_enabled().into());
} }
Ok(Value::Undefined) Ok(Value::Undefined)
@ -221,7 +221,7 @@ pub fn set_enabled<'gc>(
{ {
let enabled = args.get_bool(0); let enabled = args.get_bool(0);
mc.set_enabled(&mut activation.context, enabled); mc.set_avm2_enabled(&mut activation.context, enabled);
} }
Ok(Value::Undefined) Ok(Value::Undefined)

View File

@ -1,4 +1,4 @@
use crate::avm1::{Object, StageObject, Value}; use crate::avm1::{Activation, ActivationIdentifier, Object, StageObject, TObject, Value};
use crate::backend::ui::MouseCursor; use crate::backend::ui::MouseCursor;
use crate::context::{ActionType, RenderContext, UpdateContext}; use crate::context::{ActionType, RenderContext, UpdateContext};
use crate::display_object::container::{ use crate::display_object::container::{
@ -43,7 +43,6 @@ pub struct Avm1ButtonData<'gc> {
object: Option<Object<'gc>>, object: Option<Object<'gc>>,
initialized: bool, initialized: bool,
has_focus: bool, has_focus: bool,
enabled: bool,
use_hand_cursor: bool, use_hand_cursor: bool,
} }
@ -90,7 +89,6 @@ impl<'gc> Avm1Button<'gc> {
ButtonTracking::Push ButtonTracking::Push
}, },
has_focus: false, has_focus: false,
enabled: true,
use_hand_cursor: true, use_hand_cursor: true,
}, },
)) ))
@ -197,12 +195,23 @@ impl<'gc> Avm1Button<'gc> {
} }
} }
pub fn enabled(self) -> bool { pub fn enabled(self, context: &mut UpdateContext<'_, 'gc>) -> bool {
self.0.read().enabled if let Some(object) = self.0.read().object {
} let mut activation = Activation::from_stub(
context.reborrow(),
pub fn set_enabled(self, context: &mut UpdateContext<'_, 'gc>, enabled: bool) { ActivationIdentifier::root("[AVM1 Button Enabled]"),
self.0.write(context.gc_context).enabled = enabled; );
if let Ok(enabled) = object.get("enabled", &mut activation) {
match enabled {
Value::Undefined => true,
_ => enabled.as_bool(activation.swf_version()),
}
} else {
true
}
} else {
false
}
} }
pub fn use_hand_cursor(self) -> bool { pub fn use_hand_cursor(self) -> bool {
@ -421,7 +430,11 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, _event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
_event: ClipEvent,
) -> ClipEventResult {
// An invisible button can still run its `rollOut` or `releaseOutside` event. // An invisible button can still run its `rollOut` or `releaseOutside` event.
// A disabled button doesn't run its events (`KeyPress` being the exception) but // A disabled button doesn't run its events (`KeyPress` being the exception) but
// its state can still change. This is tested at "avm1/mouse_events_visible_enabled". // its state can still change. This is tested at "avm1/mouse_events_visible_enabled".
@ -438,6 +451,8 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> {
event: ClipEvent, event: ClipEvent,
) -> ClipEventResult { ) -> ClipEventResult {
let self_display_object = self.into(); let self_display_object = self.into();
let is_enabled = self.enabled(context);
let mut write = self.0.write(context.gc_context); let mut write = self.0.write(context.gc_context);
// Translate the clip event to a button event, based on how the button state changes. // Translate the clip event to a button event, based on how the button state changes.
@ -489,7 +504,7 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> {
_ => return ClipEventResult::NotHandled, _ => return ClipEventResult::NotHandled,
}; };
let (update_state, new_state) = if write.enabled { let (update_state, new_state) = if is_enabled {
write.run_actions(context, condition, None); write.run_actions(context, condition, None);
write.play_sound(context, sound); write.play_sound(context, sound);
@ -557,8 +572,8 @@ impl<'gc> TInteractiveObject<'gc> for Avm1Button<'gc> {
None None
} }
fn mouse_cursor(self, _context: &mut UpdateContext<'_, 'gc>) -> MouseCursor { fn mouse_cursor(self, context: &mut UpdateContext<'_, 'gc>) -> MouseCursor {
if self.use_hand_cursor() && self.enabled() { if self.use_hand_cursor() && self.enabled(context) {
MouseCursor::Hand MouseCursor::Hand
} else { } else {
MouseCursor::Arrow MouseCursor::Arrow

View File

@ -703,7 +703,11 @@ impl<'gc> TInteractiveObject<'gc> for Avm2Button<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
event: ClipEvent,
) -> ClipEventResult {
if !self.visible() { if !self.visible() {
return ClipEventResult::NotHandled; return ClipEventResult::NotHandled;
} }

View File

@ -1797,7 +1797,11 @@ impl<'gc> TInteractiveObject<'gc> for EditText<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
event: ClipEvent,
) -> ClipEventResult {
if event != ClipEvent::Press { if event != ClipEvent::Press {
return ClipEventResult::NotHandled; return ClipEventResult::NotHandled;
} }

View File

@ -168,7 +168,11 @@ pub trait TInteractiveObject<'gc>:
/// machinery should run. Otherwise, the event will not be handled, neither /// machinery should run. Otherwise, the event will not be handled, neither
/// by this interactive object nor it's children. The event will be passed /// by this interactive object nor it's children. The event will be passed
/// onto other siblings of the display object instead. /// onto other siblings of the display object instead.
fn filter_clip_event(self, event: ClipEvent) -> ClipEventResult; fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
event: ClipEvent,
) -> ClipEventResult;
/// Propagate the event to children. /// Propagate the event to children.
/// ///
@ -424,7 +428,7 @@ pub trait TInteractiveObject<'gc>:
return ClipEventResult::NotHandled; return ClipEventResult::NotHandled;
} }
if self.filter_clip_event(event) == ClipEventResult::NotHandled { if self.filter_clip_event(context, event) == ClipEventResult::NotHandled {
return ClipEventResult::NotHandled; return ClipEventResult::NotHandled;
} }

View File

@ -159,7 +159,11 @@ impl<'gc> TInteractiveObject<'gc> for LoaderDisplay<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, _event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
_event: ClipEvent,
) -> ClipEventResult {
ClipEventResult::NotHandled ClipEventResult::NotHandled
} }
fn event_dispatch( fn event_dispatch(

View File

@ -111,7 +111,7 @@ pub struct MovieClipData<'gc> {
avm2_class: Option<Avm2ClassObject<'gc>>, avm2_class: Option<Avm2ClassObject<'gc>>,
drawing: Drawing, drawing: Drawing,
has_focus: bool, has_focus: bool,
enabled: bool, avm2_enabled: bool,
/// Show a hand cursor when the clip is in button mode. /// Show a hand cursor when the clip is in button mode.
use_hand_cursor: bool, use_hand_cursor: bool,
@ -154,7 +154,7 @@ impl<'gc> MovieClip<'gc> {
avm2_class: None, avm2_class: None,
drawing: Drawing::new(), drawing: Drawing::new(),
has_focus: false, has_focus: false,
enabled: true, avm2_enabled: true,
use_hand_cursor: true, use_hand_cursor: true,
button_mode: false, button_mode: false,
last_queued_script_frame: None, last_queued_script_frame: None,
@ -193,7 +193,7 @@ impl<'gc> MovieClip<'gc> {
avm2_class: Some(class), avm2_class: Some(class),
drawing: Drawing::new(), drawing: Drawing::new(),
has_focus: false, has_focus: false,
enabled: true, avm2_enabled: true,
use_hand_cursor: true, use_hand_cursor: true,
button_mode: false, button_mode: false,
last_queued_script_frame: None, last_queued_script_frame: None,
@ -236,7 +236,7 @@ impl<'gc> MovieClip<'gc> {
avm2_class: None, avm2_class: None,
drawing: Drawing::new(), drawing: Drawing::new(),
has_focus: false, has_focus: false,
enabled: true, avm2_enabled: true,
use_hand_cursor: true, use_hand_cursor: true,
button_mode: false, button_mode: false,
last_queued_script_frame: None, last_queued_script_frame: None,
@ -301,7 +301,7 @@ impl<'gc> MovieClip<'gc> {
avm2_class: None, avm2_class: None,
drawing: Drawing::new(), drawing: Drawing::new(),
has_focus: false, has_focus: false,
enabled: true, avm2_enabled: true,
use_hand_cursor: true, use_hand_cursor: true,
button_mode: false, button_mode: false,
last_queued_script_frame: None, last_queued_script_frame: None,
@ -2208,12 +2208,40 @@ impl<'gc> MovieClip<'gc> {
Ok(()) Ok(())
} }
pub fn enabled(self) -> bool { fn enabled(self, context: &mut UpdateContext<'_, 'gc>) -> bool {
self.0.read().enabled if !context.is_action_script_3() {
self.avm1_enabled(context)
} else {
self.avm2_enabled()
}
} }
pub fn set_enabled(self, context: &mut UpdateContext<'_, 'gc>, enabled: bool) { fn avm1_enabled(self, context: &mut UpdateContext<'_, 'gc>) -> bool {
self.0.write(context.gc_context).enabled = enabled; let object = self.object();
if let Avm1Value::Object(object) = object {
let mut activation = Avm1Activation::from_stub(
context.reborrow(),
ActivationIdentifier::root("[AVM1 MovieClip Enabled]"),
);
if let Ok(enabled) = object.get("enabled", &mut activation) {
match enabled {
Avm1Value::Undefined => true,
_ => enabled.as_bool(activation.swf_version()),
}
} else {
true
}
} else {
false
}
}
pub fn avm2_enabled(self) -> bool {
self.0.read().avm2_enabled
}
pub fn set_avm2_enabled(self, context: &mut UpdateContext<'_, 'gc>, enabled: bool) {
self.0.write(context.gc_context).avm2_enabled = enabled;
} }
pub fn use_hand_cursor(self) -> bool { pub fn use_hand_cursor(self) -> bool {
@ -2783,17 +2811,19 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
if event.is_button_event() && !self.visible() && !matches!(event, ClipEvent::ReleaseOutside) self,
{ context: &mut UpdateContext<'_, 'gc>,
return ClipEventResult::NotHandled; event: ClipEvent,
} ) -> ClipEventResult {
if event.is_button_event() {
if !self.visible() && !matches!(event, ClipEvent::ReleaseOutside) {
return ClipEventResult::NotHandled;
}
if !self.enabled() if !self.enabled(context) && !matches!(event, ClipEvent::KeyPress { .. }) {
&& event.is_button_event() return ClipEventResult::NotHandled;
&& !matches!(event, ClipEvent::KeyPress { .. }) }
{
return ClipEventResult::NotHandled;
} }
ClipEventResult::Handled ClipEventResult::Handled
@ -3090,7 +3120,7 @@ impl<'gc> TInteractiveObject<'gc> for MovieClip<'gc> {
} }
fn mouse_cursor(self, context: &mut UpdateContext<'_, 'gc>) -> MouseCursor { fn mouse_cursor(self, context: &mut UpdateContext<'_, 'gc>) -> MouseCursor {
if self.use_hand_cursor() && self.enabled() && self.is_button_mode(context) { if self.use_hand_cursor() && self.enabled(context) && self.is_button_mode(context) {
MouseCursor::Hand MouseCursor::Hand
} else { } else {
MouseCursor::Arrow MouseCursor::Arrow

View File

@ -867,7 +867,11 @@ impl<'gc> TInteractiveObject<'gc> for Stage<'gc> {
self.into() self.into()
} }
fn filter_clip_event(self, _event: ClipEvent) -> ClipEventResult { fn filter_clip_event(
self,
_context: &mut UpdateContext<'_, 'gc>,
_event: ClipEvent,
) -> ClipEventResult {
ClipEventResult::Handled ClipEventResult::Handled
} }

View File

@ -1,19 +1,21 @@
Step 1: rollOver Step 1: rollOver
MovieClip.prototype.hasOwnProperty('enabled'): true MovieClip.prototype.hasOwnProperty('enabled'): true
button.hasOwnProperty('enabled'): false
'enabled' in button: true 'enabled' in button: true
button.enabled: true button.enabled: true
// button.enabled = undefined // button.enabled = undefined
button.hasOwnProperty('enabled'): true
'enabled' in button: true 'enabled' in button: true
`button.enabled` is now a falsy value. `button.enabled` is now a falsy value.
`button.enabled` is undefined. `button.enabled` is undefined.
Step 2: press Step 2: press
'enabled' in button: true
`button.enabled` is still a falsy value. `button.enabled` is still a falsy value.
`button.enabled` is still undefined. `button.enabled` is still undefined.
Step 3: release Step 3: release
// delete button.enabled // delete button.enabled
button.hasOwnProperty('enabled'): false
'enabled' in button: true 'enabled' in button: true
`button.enabled` is now true. `button.enabled` is now true.

View File

@ -3,9 +3,9 @@
if(_root.step === undefined) { if(_root.step === undefined) {
_root.step = 1; _root.step = 1;
_root.enabledInButton = function() { _root.propInButton = function(property) {
for(var i in _root.doin) { for(var k in _root.doin) {
if(i === "enabled") { return true; } if(k === property) { return true; }
} }
return false; return false;
}; };
@ -16,12 +16,14 @@ if(_root.step === undefined) {
case 1: case 1:
// The mouse moved inside of the button area, this triggers the rollOver event. // The mouse moved inside of the button area, this triggers the rollOver event.
trace("MovieClip.prototype.hasOwnProperty('enabled'): " + MovieClip.prototype.hasOwnProperty("enabled")); trace("MovieClip.prototype.hasOwnProperty('enabled'): " + MovieClip.prototype.hasOwnProperty("enabled"));
trace("'enabled' in button: " + enabledInButton()); trace("button.hasOwnProperty('enabled'): " + button.hasOwnProperty("enabled"));
trace("'enabled' in button: " + propInButton("enabled"));
trace("button.enabled: " + button.enabled); trace("button.enabled: " + button.enabled);
button.enabled = false; button.enabled = false;
button.enabled = undefined; button.enabled = undefined;
trace("// button.enabled = undefined"); trace("// button.enabled = undefined");
trace("'enabled' in button: " + enabledInButton()); trace("button.hasOwnProperty('enabled'): " + button.hasOwnProperty("enabled"));
trace("'enabled' in button: " + propInButton("enabled"));
if(!button.enabled) { if(!button.enabled) {
trace("`button.enabled` is now a falsy value."); trace("`button.enabled` is now a falsy value.");
if(button.enabled === undefined) { if(button.enabled === undefined) {
@ -31,7 +33,6 @@ if(_root.step === undefined) {
break; break;
case 2: case 2:
// The mouse left button was pressed, this triggers the press event. // The mouse left button was pressed, this triggers the press event.
trace("'enabled' in button: " + enabledInButton());
if(!button.enabled) { if(!button.enabled) {
trace("`button.enabled` is still a falsy value."); trace("`button.enabled` is still a falsy value.");
if(button.enabled === undefined) { if(button.enabled === undefined) {
@ -44,7 +45,8 @@ if(_root.step === undefined) {
button.enabled = false; button.enabled = false;
delete button.enabled; delete button.enabled;
trace("// delete button.enabled"); trace("// delete button.enabled");
trace("'enabled' in button: " + enabledInButton()); trace("button.hasOwnProperty('enabled'): " + button.hasOwnProperty("enabled"));
trace("'enabled' in button: " + propInButton("enabled"));
if(button.enabled === true) { if(button.enabled === true) {
trace("`button.enabled` is now true."); trace("`button.enabled` is now true.");
} }