Replace the existing default format on `EditText` with our brand-new `FormatSpans`.

This also includes code to automatically populate the default format with data from the SWF tag.
This commit is contained in:
David Wendt 2020-05-09 16:10:09 -04:00
parent efdecdea64
commit 4eca2d4bdd
3 changed files with 74 additions and 7 deletions

View File

@ -4,7 +4,7 @@ use crate::avm1::{Avm1, Object, StageObject, Value};
use crate::context::{RenderContext, UpdateContext};
use crate::display_object::{DisplayObjectBase, TDisplayObject};
use crate::font::{Font, Glyph};
use crate::html::TextFormat;
use crate::html::{FormatSpans, TextFormat};
use crate::library::Library;
use crate::prelude::*;
use crate::tag_utils::SwfMovie;
@ -42,10 +42,22 @@ pub struct EditTextData<'gc> {
text: String,
/// The current HTML document displayed by this `EditText`.
///
/// The HTML representation of this `EditText` is lowered into an
/// appropriate set of format spans, which is used for actual rendering.
/// The HTML is only retained if there is also a stylesheet already defined
/// on the `EditText`, else it is discarded during the lowering process.
document: XMLDocument<'gc>,
/// The text formatting for newly inserted text spans.
new_format: TextFormat,
/// The underlying text format spans of the `EditText`.
///
/// This is generated from HTML (with optional CSS) or set directly, and
/// can be directly manipulated by ActionScript. It can also be raised to
/// an equivalent HTML representation, as long as no stylesheet is present.
///
/// It is lowered further into layout boxes, which are used for actual
/// rendering.
text_spans: FormatSpans,
/// If the text is in multi-line mode or single-line mode.
is_multiline: bool,
@ -71,6 +83,7 @@ impl<'gc> EditText<'gc> {
let is_word_wrap = swf_tag.is_word_wrap;
let document = XMLDocument::new(context.gc_context);
let text = swf_tag.initial_text.clone().unwrap_or_default();
let default_format = TextFormat::from_swf_tag(swf_tag.clone(), swf_movie.clone(), context);
if swf_tag.is_html {
document
@ -91,7 +104,7 @@ impl<'gc> EditText<'gc> {
base: Default::default(),
text,
document,
new_format: TextFormat::default(),
text_spans: FormatSpans::from_str_and_format("", default_format),
static_data: gc_arena::Gc::allocate(
context.gc_context,
EditTextStatic {
@ -192,12 +205,12 @@ impl<'gc> EditText<'gc> {
}
pub fn new_text_format(self) -> TextFormat {
self.0.read().new_format.clone()
self.0.read().text_spans.default_format().clone()
}
pub fn set_new_text_format(self, tf: TextFormat, gc_context: MutationContext<'gc, '_>) {
self.0.write(gc_context).cached_break_points = None;
self.0.write(gc_context).new_format = tf;
self.0.write(gc_context).text_spans.set_default_format(tf);
}
pub fn is_multiline(self) -> bool {

View File

@ -3,7 +3,7 @@
mod dimensions;
mod text_format;
pub use text_format::TextFormat;
pub use text_format::{FormatSpans, TextFormat};
#[cfg(test)]
mod test;

View File

@ -1,8 +1,10 @@
//! Classes that store formatting options
use crate::avm1::{Avm1, Object, ScriptObject, TObject, Value};
use crate::context::UpdateContext;
use crate::tag_utils::SwfMovie;
use gc_arena::Collect;
use std::cmp::{min, Ordering};
use std::sync::Arc;
/// A set of text formatting options to be applied to some part, or the whole
/// of, a given text field.
@ -101,6 +103,50 @@ fn getbool_from_avm1_object<'gc>(
}
impl TextFormat {
/// Construct a `TextFormat` from an `EditText`'s SWF tag.
///
/// This requires an `UpdateContext` as we will need to retrieve some font
/// information from the actually-referenced font.
pub fn from_swf_tag<'gc>(
et: swf::EditText,
swf_movie: Arc<SwfMovie>,
context: &mut UpdateContext<'_, 'gc, '_>,
) -> Self {
let movie_library = context.library.library_for_movie_mut(swf_movie);
//TODO: How do we represent fonts referenced by character rather than
//by linkage name?
let font = et.font_class_name;
let align = et.layout.clone().map(|l| l.align);
let left_margin = et.layout.clone().map(|l| l.left_margin.to_pixels());
let right_margin = et.layout.clone().map(|l| l.right_margin.to_pixels());
let indent = et.layout.clone().map(|l| l.indent.to_pixels());
let leading = et.layout.map(|l| l.leading.to_pixels());
Self {
font,
size: et.height.map(|h| h.to_pixels()),
color: et.color,
align,
bold: None, // TODO: Resolve a font and pull this from that font
italic: None, // TODO: Resolve a font and pull this from that font
underline: None, // TODO: Resolve a font and pull this from that font
left_margin,
right_margin,
indent,
block_indent: Some(0.0), // TODO: This isn't specified by the tag itself
kerning: Some(true), // TODO: this isn't specified by the tag itself
leading,
letter_spacing: Some(0.0), // TODO: This isn't specified by the tag itself
tab_stops: Some(vec![]), // TODO: Are there default tab stops?
bullet: Some(false), // TODO: Default tab stops?
// TODO: These are probably empty strings by default
url: Some("".to_string()),
target: Some("".to_string()),
}
}
/// Construct a `TextFormat` from an object that is
pub fn from_avm1_object<'gc>(
object1: Object<'gc>,
@ -643,6 +689,14 @@ impl FormatSpans {
}
}
pub fn default_format(&self) -> &TextFormat {
&self.default_format
}
pub fn set_default_format(&mut self, tf: TextFormat) {
self.default_format = tf;
}
/// Find the index of the span that covers a given search position.
///
/// This function returns both the index of the span which covers the