diff --git a/core/src/avm1/globals/text_field.rs b/core/src/avm1/globals/text_field.rs index 5a95e2a2d..6f330b877 100644 --- a/core/src/avm1/globals/text_field.rs +++ b/core/src/avm1/globals/text_field.rs @@ -312,6 +312,46 @@ pub fn set_multiline<'gc>( Ok(Value::Undefined.into()) } +fn variable<'gc>( + _avm: &mut Avm1<'gc>, + _context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + _args: &[Value<'gc>], +) -> Result, Error<'gc>> { + if let Some(etext) = this + .as_display_object() + .and_then(|dobj| dobj.as_edit_text()) + { + if let Some(variable) = etext.variable() { + return Ok(variable.to_string().into()); + } + } + + // Unset `variable` retuns null, not undefined + Ok(Value::Null.into()) +} + +fn set_variable<'gc>( + avm: &mut Avm1<'gc>, + context: &mut UpdateContext<'_, 'gc, '_>, + this: Object<'gc>, + args: &[Value<'gc>], +) -> Result, Error<'gc>> { + let variable = match args.get(0) { + None | Some(Value::Undefined) | Some(Value::Null) => None, + Some(v) => Some(v.coerce_to_string(avm, context)?), + }; + + if let Some(etext) = this + .as_display_object() + .and_then(|dobj| dobj.as_edit_text()) + { + etext.set_variable(variable.map(|v| v.into_owned()), context); + } + + Ok(Value::Undefined.into()) +} + pub fn word_wrap<'gc>( _avm: &mut Avm1<'gc>, _context: &mut UpdateContext<'_, 'gc, '_>, @@ -469,6 +509,13 @@ pub fn attach_virtual_properties<'gc>(gc_context: MutationContext<'gc, '_>, obje Some(Executable::Native(set_multiline)), ReadOnly.into(), ); + object.add_property( + gc_context, + "variable", + Executable::Native(variable), + Some(Executable::Native(set_variable)), + DontDelete | ReadOnly | DontEnum, + ); object.add_property( gc_context, "wordWrap", diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index db40e1b03..69244f2f6 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -12,7 +12,7 @@ use crate::tag_utils::SwfMovie; use crate::transform::Transform; use crate::xml::XMLDocument; use gc_arena::{Collect, Gc, GcCell, MutationContext}; -use std::sync::Arc; +use std::{cell::Ref, sync::Arc}; use swf::Twips; /// Boxed error type. @@ -99,6 +99,9 @@ pub struct EditTextData<'gc> { /// The AVM1 object handle object: Option>, + + /// The variable path that this text field is bound to (AVM1 only). + variable: Option, } impl<'gc> EditText<'gc> { @@ -146,6 +149,12 @@ impl<'gc> EditText<'gc> { base.matrix_mut(context.gc_context).tx = bounds.x_min; base.matrix_mut(context.gc_context).ty = bounds.y_min; + let variable = if !swf_tag.variable_name.is_empty() { + Some(swf_tag.variable_name.clone()) + } else { + None + }; + let et = EditText(GcCell::allocate( context.gc_context, EditTextData { @@ -169,6 +178,7 @@ impl<'gc> EditText<'gc> { intrinsic_bounds, bounds, autosize: AutoSizeMode::None, + variable, }, )); @@ -405,6 +415,26 @@ impl<'gc> EditText<'gc> { base_width } + /// Returns the variable that this text field is bound to. + pub fn variable(&self) -> Option> { + let text = self.0.read(); + if text.variable.is_some() { + Some(Ref::map(text, |text| text.variable.as_deref().unwrap())) + } else { + None + } + } + + pub fn set_variable(self, variable: Option, context: &mut UpdateContext<'_, 'gc, '_>) { + self.0.write(context.gc_context).variable = variable + } + + /// Construct a base text transform for this `EditText`, to be used for + /// evaluating fonts. + /// + /// The `text_transform` constitutes the base transform that all text is + /// written into. + /// Redraw the border of this `EditText`. fn redraw_border(self, context: MutationContext<'gc, '_>) { let mut write = self.0.write(context);