avm1: Implement TextField.html
This commit is contained in:
parent
d97a515330
commit
b40f9d4c1a
|
@ -6,7 +6,6 @@ use crate::avm1::return_value::ReturnValue;
|
||||||
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
use crate::avm1::{Avm1, Object, ScriptObject, TObject, UpdateContext, Value};
|
||||||
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
|
use crate::display_object::{AutoSizeMode, EditText, TDisplayObject};
|
||||||
use crate::html::TextFormat;
|
use crate::html::TextFormat;
|
||||||
use crate::xml::XMLDocument;
|
|
||||||
use gc_arena::MutationContext;
|
use gc_arena::MutationContext;
|
||||||
|
|
||||||
/// Implements `TextField`
|
/// Implements `TextField`
|
||||||
|
@ -61,23 +60,23 @@ pub fn get_html<'gc>(
|
||||||
_args: &[Value<'gc>],
|
_args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(_text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
return Ok(true.into());
|
return Ok(text_field.is_html().into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn set_html<'gc>(
|
pub fn set_html<'gc>(
|
||||||
_avm: &mut Avm1<'gc>,
|
avm: &mut Avm1<'gc>,
|
||||||
_context: &mut UpdateContext<'_, 'gc, '_>,
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
this: Object<'gc>,
|
this: Object<'gc>,
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(_text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(_value) = args.get(0) {
|
if let Some(value) = args.get(0) {
|
||||||
//TODO: Do something with this bool value
|
text_field.set_is_html(context, value.as_bool(avm.current_swf_version()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -92,17 +91,9 @@ pub fn get_html_text<'gc>(
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
let html_tree = text_field.html_tree(context).as_node();
|
if let Ok(text) = text_field.html_text(context) {
|
||||||
let html_string_result = html_tree.into_string(&mut |_node| true);
|
return Ok(text.into());
|
||||||
|
|
||||||
if let Err(err) = &html_string_result {
|
|
||||||
log::warn!(
|
|
||||||
"Serialization error when reading TextField.htmlText: {}",
|
|
||||||
err
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Ok(html_string_result.unwrap_or_else(|_| "".to_string()).into());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
@ -115,26 +106,13 @@ pub fn set_html_text<'gc>(
|
||||||
args: &[Value<'gc>],
|
args: &[Value<'gc>],
|
||||||
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
) -> Result<ReturnValue<'gc>, Error<'gc>> {
|
||||||
if let Some(display_object) = this.as_display_object() {
|
if let Some(display_object) = this.as_display_object() {
|
||||||
if let Some(mut text_field) = display_object.as_edit_text() {
|
if let Some(text_field) = display_object.as_edit_text() {
|
||||||
if let Some(value) = args.get(0) {
|
let text = args
|
||||||
let html_string = value
|
.get(0)
|
||||||
.clone()
|
.unwrap_or(&Value::Undefined)
|
||||||
.coerce_to_string(avm, context)?
|
.coerce_to_string(avm, context)?;
|
||||||
.into_owned()
|
let _ = text_field.set_html_text(text.into_owned(), context);
|
||||||
.replace("<sbr>", "\n")
|
// Changing the htmlText does NOT update variable bindings (does not call EditText::propagate_text_binding).
|
||||||
.replace("<br>", "\n");
|
|
||||||
let document = XMLDocument::new(context.gc_context);
|
|
||||||
|
|
||||||
if let Err(err) =
|
|
||||||
document
|
|
||||||
.as_node()
|
|
||||||
.replace_with_str(context.gc_context, &html_string, false)
|
|
||||||
{
|
|
||||||
log::warn!("Parsing error when setting TextField.htmlText: {}", err);
|
|
||||||
}
|
|
||||||
|
|
||||||
text_field.set_html_tree(document, context);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Value::Undefined.into())
|
Ok(Value::Undefined.into())
|
||||||
|
|
|
@ -187,7 +187,7 @@ impl<'gc> TObject<'gc> for StageObject<'gc> {
|
||||||
{
|
{
|
||||||
let _ = binding
|
let _ = binding
|
||||||
.text_field
|
.text_field
|
||||||
.set_text(value.coerce_to_string(avm, context)?.into_owned(), context);
|
.set_html_text(value.coerce_to_string(avm, context)?.into_owned(), context);
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = if obj.base.has_own_property(avm, context, name) {
|
let result = if obj.base.has_own_property(avm, context, name) {
|
||||||
|
|
|
@ -80,6 +80,9 @@ pub struct EditTextData<'gc> {
|
||||||
/// If the text field is required to use device fonts only.
|
/// If the text field is required to use device fonts only.
|
||||||
is_device_font: bool,
|
is_device_font: bool,
|
||||||
|
|
||||||
|
/// If the text field renders as HTML.
|
||||||
|
is_html: bool,
|
||||||
|
|
||||||
/// The current border drawing.
|
/// The current border drawing.
|
||||||
drawing: Drawing,
|
drawing: Drawing,
|
||||||
|
|
||||||
|
@ -119,6 +122,7 @@ impl<'gc> EditText<'gc> {
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let is_multiline = swf_tag.is_multiline;
|
let is_multiline = swf_tag.is_multiline;
|
||||||
let is_word_wrap = swf_tag.is_word_wrap;
|
let is_word_wrap = swf_tag.is_word_wrap;
|
||||||
|
let is_html = swf_tag.is_html;
|
||||||
let document = XMLDocument::new(context.gc_context);
|
let document = XMLDocument::new(context.gc_context);
|
||||||
let text = swf_tag.initial_text.clone().unwrap_or_default();
|
let text = swf_tag.initial_text.clone().unwrap_or_default();
|
||||||
let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context);
|
let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context);
|
||||||
|
@ -126,7 +130,7 @@ impl<'gc> EditText<'gc> {
|
||||||
let mut text_spans = FormatSpans::new();
|
let mut text_spans = FormatSpans::new();
|
||||||
text_spans.set_default_format(default_format.clone());
|
text_spans.set_default_format(default_format.clone());
|
||||||
|
|
||||||
if swf_tag.is_html {
|
if is_html {
|
||||||
document
|
document
|
||||||
.as_node()
|
.as_node()
|
||||||
.replace_with_str(context.gc_context, &text, false)
|
.replace_with_str(context.gc_context, &text, false)
|
||||||
|
@ -178,6 +182,7 @@ impl<'gc> EditText<'gc> {
|
||||||
is_word_wrap,
|
is_word_wrap,
|
||||||
has_border,
|
has_border,
|
||||||
is_device_font,
|
is_device_font,
|
||||||
|
is_html,
|
||||||
drawing: Drawing::new(),
|
drawing: Drawing::new(),
|
||||||
object: None,
|
object: None,
|
||||||
layout,
|
layout,
|
||||||
|
@ -268,6 +273,51 @@ impl<'gc> EditText<'gc> {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn html_text(self, context: &mut UpdateContext<'_, 'gc, '_>) -> Result<String, Error> {
|
||||||
|
if self.is_html() {
|
||||||
|
let html_tree = self.html_tree(context).as_node();
|
||||||
|
let html_string_result = html_tree.into_string(&mut |_node| true);
|
||||||
|
|
||||||
|
if let Err(err) = &html_string_result {
|
||||||
|
log::warn!(
|
||||||
|
"Serialization error when reading TextField.htmlText: {}",
|
||||||
|
err
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(html_string_result.unwrap_or_else(|_| "".to_string()).into());
|
||||||
|
} else {
|
||||||
|
// Non-HTML text fields always return plain text.
|
||||||
|
return Ok(self.text());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_html_text(
|
||||||
|
self,
|
||||||
|
text: String,
|
||||||
|
context: &mut UpdateContext<'_, 'gc, '_>,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
if self.is_html() {
|
||||||
|
let html_string = text.replace("<sbr>", "\n").replace("<br>", "\n");
|
||||||
|
let document = XMLDocument::new(context.gc_context);
|
||||||
|
|
||||||
|
if let Err(err) =
|
||||||
|
document
|
||||||
|
.as_node()
|
||||||
|
.replace_with_str(context.gc_context, &html_string, false)
|
||||||
|
{
|
||||||
|
log::warn!("Parsing error when setting TextField.htmlText: {}", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.set_html_tree(document, context);
|
||||||
|
} else {
|
||||||
|
if let Err(err) = self.set_text(text, context) {
|
||||||
|
log::error!("Error when setting TextField.htmlText: {}", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub fn html_tree(self, context: &mut UpdateContext<'_, 'gc, '_>) -> XMLDocument<'gc> {
|
pub fn html_tree(self, context: &mut UpdateContext<'_, 'gc, '_>) -> XMLDocument<'gc> {
|
||||||
self.0.read().text_spans.raise_to_html(context.gc_context)
|
self.0.read().text_spans.raise_to_html(context.gc_context)
|
||||||
}
|
}
|
||||||
|
@ -282,11 +332,7 @@ impl<'gc> EditText<'gc> {
|
||||||
/// In stylesheet mode, the opposite is true: text spans are an
|
/// In stylesheet mode, the opposite is true: text spans are an
|
||||||
/// intermediate, user-facing text span APIs don't work, and the document
|
/// intermediate, user-facing text span APIs don't work, and the document
|
||||||
/// is retained.
|
/// is retained.
|
||||||
pub fn set_html_tree(
|
pub fn set_html_tree(self, doc: XMLDocument<'gc>, context: &mut UpdateContext<'_, 'gc, '_>) {
|
||||||
&mut self,
|
|
||||||
doc: XMLDocument<'gc>,
|
|
||||||
context: &mut UpdateContext<'_, 'gc, '_>,
|
|
||||||
) {
|
|
||||||
let mut write = self.0.write(context.gc_context);
|
let mut write = self.0.write(context.gc_context);
|
||||||
|
|
||||||
write.document = doc;
|
write.document = doc;
|
||||||
|
@ -379,6 +425,14 @@ impl<'gc> EditText<'gc> {
|
||||||
self.relayout(context);
|
self.relayout(context);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_html(self) -> bool {
|
||||||
|
self.0.read().is_html
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn set_is_html(self, context: &mut UpdateContext<'_, 'gc, '_>, is_html: bool) {
|
||||||
|
self.0.write(context.gc_context).is_html = is_html;
|
||||||
|
}
|
||||||
|
|
||||||
pub fn replace_text(
|
pub fn replace_text(
|
||||||
self,
|
self,
|
||||||
from: usize,
|
from: usize,
|
||||||
|
@ -713,7 +767,7 @@ impl<'gc> EditText<'gc> {
|
||||||
self.parent().unwrap(),
|
self.parent().unwrap(),
|
||||||
&variable_path,
|
&variable_path,
|
||||||
) {
|
) {
|
||||||
let text = if avm.current_swf_version() >= 6 {
|
let text = if self.0.read().is_html {
|
||||||
let html_tree = self.html_tree(context).as_node();
|
let html_tree = self.html_tree(context).as_node();
|
||||||
let html_string_result = html_tree.into_string(&mut |_node| true);
|
let html_string_result = html_tree.into_string(&mut |_node| true);
|
||||||
html_string_result.unwrap_or_default()
|
html_string_result.unwrap_or_default()
|
||||||
|
|
Loading…
Reference in New Issue