From 9d18bcdd06565a975571fd9bb81f26b05fd9bf18 Mon Sep 17 00:00:00 2001 From: Kamil Jarosz Date: Tue, 12 Mar 2024 16:23:30 +0100 Subject: [PATCH] avm1: Add support for tabEnabled and tabIndex --- core/src/avm1/globals/button.rs | 35 ++++++++++++++++++++++++++++ core/src/avm1/globals/movie_clip.rs | 36 +++++++++++++++++++++++++++++ core/src/avm1/globals/text_field.rs | 31 +++++++++++++++++++++++++ 3 files changed, 102 insertions(+) diff --git a/core/src/avm1/globals/button.rs b/core/src/avm1/globals/button.rs index 9010deafc..18fbc9f20 100644 --- a/core/src/avm1/globals/button.rs +++ b/core/src/avm1/globals/button.rs @@ -47,6 +47,8 @@ const PROTO_DECLS: &[Declaration] = declare_properties! { "scale9Grid" => property(button_getter!(scale_9_grid), button_setter!(set_scale_9_grid); DONT_DELETE | DONT_ENUM | VERSION_8); "filters" => property(button_getter!(filters), button_setter!(set_filters); DONT_DELETE | DONT_ENUM | VERSION_8); "cacheAsBitmap" => property(button_getter!(cache_as_bitmap), button_setter!(set_cache_as_bitmap); DONT_DELETE | DONT_ENUM | VERSION_8); + // NOTE: `tabEnabled` is not a built-in property of Button. + "tabIndex" => property(button_getter!(tab_index), button_setter!(set_tab_index); VERSION_6); }; pub fn create_proto<'gc>( @@ -173,3 +175,36 @@ fn set_scale_9_grid<'gc>( }; Ok(()) } + +fn tab_index<'gc>( + this: Avm1Button<'gc>, + _activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + if let Some(index) = this.tab_index_value() { + Ok(index.into()) + } else { + Ok(Value::Undefined) + } +} + +fn set_tab_index<'gc>( + this: Avm1Button<'gc>, + activation: &mut Activation<'_, 'gc>, + value: Value<'gc>, +) -> Result<(), Error<'gc>> { + match value { + Value::Undefined | Value::Null => { + this.set_tab_index_value(&mut activation.context, None); + } + Value::Bool(_) | Value::Number(_) => { + // FIXME This coercion is not perfect, as it wraps + // instead of falling back to MIN, as FP does + let i32_value = value.coerce_to_i32(activation)?; + this.set_tab_index_value(&mut activation.context, Some(i32_value)); + } + _ => { + this.set_tab_index_value(&mut activation.context, Some(i32::MIN)); + } + }; + Ok(()) +} diff --git a/core/src/avm1/globals/movie_clip.rs b/core/src/avm1/globals/movie_clip.rs index 2bd7dd665..db100ab3e 100644 --- a/core/src/avm1/globals/movie_clip.rs +++ b/core/src/avm1/globals/movie_clip.rs @@ -116,6 +116,9 @@ const PROTO_DECLS: &[Declaration] = declare_properties! { "transform" => property(mc_getter!(transform), mc_setter!(set_transform); DONT_ENUM | VERSION_8); "useHandCursor" => bool(true; DONT_ENUM); // NOTE: `focusEnabled` is not a built-in property of MovieClip. + // NOTE: `tabEnabled` is not a built-in property of MovieClip. + // NOTE: `tabIndex` is not enumerable in MovieClip, contrary to Button and TextField + "tabIndex" => property(mc_getter!(tab_index), mc_setter!(set_tab_index); DONT_ENUM | VERSION_6); }; /// Implements `MovieClip` @@ -1819,3 +1822,36 @@ fn set_filters<'gc>( this.set_filters(activation.context.gc_context, filters); Ok(()) } + +fn tab_index<'gc>( + this: MovieClip<'gc>, + _activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + if let Some(index) = this.tab_index_value() { + Ok(index.into()) + } else { + Ok(Value::Undefined) + } +} + +fn set_tab_index<'gc>( + this: MovieClip<'gc>, + activation: &mut Activation<'_, 'gc>, + value: Value<'gc>, +) -> Result<(), Error<'gc>> { + match value { + Value::Undefined | Value::Null => { + this.set_tab_index_value(&mut activation.context, None); + } + Value::Bool(_) | Value::Number(_) => { + // FIXME This coercion is not perfect, as it wraps + // instead of falling back to MIN, as FP does + let i32_value = value.coerce_to_i32(activation)?; + this.set_tab_index_value(&mut activation.context, Some(i32_value)); + } + _ => { + this.set_tab_index_value(&mut activation.context, Some(i32::MIN)); + } + }; + Ok(()) +} diff --git a/core/src/avm1/globals/text_field.rs b/core/src/avm1/globals/text_field.rs index 7ef761932..d93d0cae2 100644 --- a/core/src/avm1/globals/text_field.rs +++ b/core/src/avm1/globals/text_field.rs @@ -93,6 +93,8 @@ const PROTO_DECLS: &[Declaration] = declare_properties! { "gridFitType" => property(tf_getter!(grid_fit_type), tf_setter!(set_grid_fit_type)); "sharpness" => property(tf_getter!(sharpness), tf_setter!(set_sharpness)); "thickness" => property(tf_getter!(thickness), tf_setter!(set_thickness)); + // NOTE: `tabEnabled` is not a built-in property of TextField. + "tabIndex" => property(tf_getter!(tab_index), tf_setter!(set_tab_index); VERSION_6); }; /// Implements `TextField` @@ -113,6 +115,7 @@ pub fn create_proto<'gc>( define_properties_on(PROTO_DECLS, context, object, fn_proto); object.into() } + pub fn password<'gc>( this: EditText<'gc>, _activation: &mut Activation<'_, 'gc>, @@ -911,3 +914,31 @@ fn set_restrict<'gc>( }; Ok(()) } + +pub fn tab_index<'gc>( + this: EditText<'gc>, + _activation: &mut Activation<'_, 'gc>, +) -> Result, Error<'gc>> { + if let Some(index) = this.tab_index_value() { + Ok(index.into()) + } else { + Ok(Value::Undefined) + } +} + +pub fn set_tab_index<'gc>( + this: EditText<'gc>, + activation: &mut Activation<'_, 'gc>, + value: Value<'gc>, +) -> Result<(), Error<'gc>> { + match value { + Value::Undefined | Value::Null => { + this.set_tab_index_value(&mut activation.context, None); + } + _ => { + let u32_value = value.coerce_to_u32(activation)?; + this.set_tab_index_value(&mut activation.context, Some(u32_value)); + } + }; + Ok(()) +}