core: Edit text sets variables on instantiation
This commit is contained in:
parent
12fca71b21
commit
51d66c53f0
|
@ -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
|
||||
|
|
|
@ -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> {
|
||||
|
|
Loading…
Reference in New Issue