core: Edit text sets variables on instantiation

This commit is contained in:
Mike Welsh 2020-06-17 10:37:11 -07:00
parent 12fca71b21
commit 51d66c53f0
2 changed files with 93 additions and 2 deletions

View File

@ -368,6 +368,11 @@ impl<'gc> Avm1<'gc> {
!self.stack_frames.is_empty()
}
/// Remove the current stack frame.
pub fn pop_stack_frame(&mut self) {
self.stack_frames.pop();
}
/// Get the currently executing SWF version.
pub fn current_swf_version(&self) -> u8 {
self.current_stack_frame()
@ -1034,6 +1039,57 @@ impl<'gc> Avm1<'gc> {
Ok(())
}
pub fn resolve_text_field_variable_path<'s>(
&mut self,
context: &mut UpdateContext<'_, 'gc, '_>,
path: &'s str,
) -> Result<Option<(Object<'gc>, &'s str)>, Error<'gc>> {
// Resolve a variable path for a GetVariable action.
let start = self.target_clip_or_root();
// Find the right-most : or . in the path.
// If we have one, we must resolve as a target path.
// We also check for a / to skip some unnecessary work later.
let mut has_slash = false;
let mut var_iter = path.as_bytes().rsplitn(2, |c| match c {
b':' | b'.' => true,
b'/' => {
has_slash = true;
false
}
_ => false,
});
let b = var_iter.next();
let a = var_iter.next();
if let (Some(path), Some(var_name)) = (a, b) {
// We have a . or :, so this is a path to an object plus a variable name.
// We resolve it directly on the targeted object.
let path = unsafe { std::str::from_utf8_unchecked(path) };
let var_name = unsafe { std::str::from_utf8_unchecked(var_name) };
let mut current_scope = Some(self.current_stack_frame().unwrap().read().scope_cell());
while let Some(scope) = current_scope {
if let Some(object) =
self.resolve_target_path(context, start.root(), *scope.read().locals(), path)?
{
return Ok(Some((object, var_name)));
}
current_scope = scope.read().parent_cell();
}
return Ok(None);
}
// Finally! It's a plain old variable name.
// Resolve using scope chain, as normal.
if let Value::Object(object) = start.object() {
Ok(Some((object, path)))
} else {
Ok(None)
}
}
/// Resolve a level by ID.
///
/// If the level does not exist, then it will be created and instantiated

View File

@ -1,6 +1,6 @@
//! `EditText` display object and support code.
use crate::avm1::globals::text_field::attach_virtual_properties;
use crate::avm1::{Avm1, Object, StageObject, Value};
use crate::avm1::{Activation, Avm1, Object, StageObject, TObject, Value};
use crate::context::{RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::drawing::Drawing;
@ -610,7 +610,7 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
fn post_instantiation(
&mut self,
_avm: &mut Avm1<'gc>,
avm: &mut Avm1<'gc>,
context: &mut UpdateContext<'_, 'gc, '_>,
display_object: DisplayObject<'gc>,
_init_object: Option<Object<'gc>>,
@ -642,6 +642,41 @@ impl<'gc> TDisplayObject<'gc> for EditText<'gc> {
for layout_box in text.layout.iter() {
new_layout.push(layout_box.duplicate(context.gc_context));
}
// If this text field has a variable set, initialize text field binding
if let Some(var_path) = self.variable() {
let var_path = (*var_path).to_string();
avm.insert_stack_frame(GcCell::allocate(
context.gc_context,
Activation::from_nothing(
context.swf.header().version,
avm.global_object_cell(),
context.gc_context,
self.parent().unwrap(),
),
));
if let Ok(Some((object, property))) =
avm.resolve_text_field_variable_path(context, &*var_path)
{
// If the property exists on the object, we overwrite the text with the property's value.
if object.has_property(avm, context, property) {
let value = object.get(property, avm, context).unwrap();
let _ = self.set_text(
value
.coerce_to_string(avm, context)
.unwrap_or_default()
.into_owned(),
context,
);
} else {
// Otherwise, we initialize the proprty with the text field's text.
let _ = object.set(property, self.text().clone().into(), avm, context);
}
}
avm.pop_stack_frame();
}
}
fn object(&self) -> Value<'gc> {