diff --git a/core/src/display_object/edit_text.rs b/core/src/display_object/edit_text.rs index eba402256..24dc8568f 100644 --- a/core/src/display_object/edit_text.rs +++ b/core/src/display_object/edit_text.rs @@ -21,6 +21,7 @@ use crate::display_object::{DisplayObjectBase, DisplayObjectPtr}; use crate::drawing::Drawing; use crate::events::{ClipEvent, ClipEventResult, TextControlCode}; use crate::font::{round_down_to_pixel, FontType, Glyph, TextRenderSettings}; +use crate::html; use crate::html::{ BoxBounds, FormatSpans, LayoutBox, LayoutContent, LayoutMetrics, Position, TextFormat, }; @@ -287,7 +288,7 @@ impl<'gc> EditText<'gc> { FontType::Device }; - let (layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans( + let (layout, intrinsic_bounds) = html::lower_from_text_spans( &text_spans, context, swf_movie.clone(), @@ -848,7 +849,7 @@ impl<'gc> EditText<'gc> { FontType::Embedded }; - let (new_layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans( + let (new_layout, intrinsic_bounds) = html::lower_from_text_spans( &edit_text.text_spans, context, movie, diff --git a/core/src/html.rs b/core/src/html.rs index 0e1c7a6a6..58b7f550b 100644 --- a/core/src/html.rs +++ b/core/src/html.rs @@ -7,7 +7,7 @@ mod text_format; pub use dimensions::BoxBounds; pub use dimensions::Position; -pub use layout::{LayoutBox, LayoutContent, LayoutMetrics}; +pub use layout::{lower_from_text_spans, LayoutBox, LayoutContent, LayoutMetrics}; pub use stylesheet::{transform_dashes_to_camel_case, CssStream}; pub use text_format::{FormatSpans, TextDisplay, TextFormat, TextSpan}; diff --git a/core/src/html/layout.rs b/core/src/html/layout.rs index e166edfca..4559a018a 100644 --- a/core/src/html/layout.rs +++ b/core/src/html/layout.rs @@ -807,111 +807,78 @@ impl<'gc> LayoutBox<'gc> { content: LayoutContent::Drawing(drawing), } } +} - /// Construct a new layout hierarchy from text spans. - /// - /// The returned bounds will include both the text bounds itself, as well - /// as left and right margins on any of the lines. - pub fn lower_from_text_spans( - fs: &FormatSpans, - context: &mut UpdateContext<'_, 'gc>, - movie: Arc, - bounds: Twips, - is_word_wrap: bool, - font_type: FontType, - ) -> (Vec>, BoxBounds) { - let mut layout_context = LayoutContext::new(movie, bounds, fs.displayed_text()); +/// Construct a new layout hierarchy from text spans. +/// +/// The returned bounds will include both the text bounds itself, as well +/// as left and right margins on any of the lines. +pub fn lower_from_text_spans<'gc>( + fs: &FormatSpans, + context: &mut UpdateContext<'_, 'gc>, + movie: Arc, + bounds: Twips, + is_word_wrap: bool, + font_type: FontType, +) -> (Vec>, BoxBounds) { + let mut layout_context = LayoutContext::new(movie, bounds, fs.displayed_text()); - for (span_start, _end, span_text, span) in fs.iter_spans() { - if let Some(font) = layout_context.resolve_font(context, span, font_type) { - layout_context.font = Some(font); - layout_context.newspan(span); + for (span_start, _end, span_text, span) in fs.iter_spans() { + if let Some(font) = layout_context.resolve_font(context, span, font_type) { + layout_context.font = Some(font); + layout_context.newspan(span); - let params = EvalParameters::from_span(span); + let params = EvalParameters::from_span(span); - for text in span_text.split(&[b'\n', b'\r', b'\t'][..]) { - let slice_start = text.offset_in(span_text).unwrap(); - let delimiter = if slice_start > 0 { - span_text - .get(slice_start - 1) - .and_then(|c| u8::try_from(c).ok()) - } else { - None - }; + for text in span_text.split(&[b'\n', b'\r', b'\t'][..]) { + let slice_start = text.offset_in(span_text).unwrap(); + let delimiter = if slice_start > 0 { + span_text + .get(slice_start - 1) + .and_then(|c| u8::try_from(c).ok()) + } else { + None + }; - match delimiter { - Some(b'\n' | b'\r') => layout_context.explicit_newline( - context, - fs.displayed_text(), - span_start + slice_start - 1, - span, - font_type, - ), - Some(b'\t') => layout_context.tab(), - _ => {} - } + match delimiter { + Some(b'\n' | b'\r') => layout_context.explicit_newline( + context, + fs.displayed_text(), + span_start + slice_start - 1, + span, + font_type, + ), + Some(b'\t') => layout_context.tab(), + _ => {} + } - let start = span_start + slice_start; + let start = span_start + slice_start; - let mut last_breakpoint = 0; + let mut last_breakpoint = 0; - if is_word_wrap { - let (mut width, mut offset) = layout_context.wrap_dimensions(span); + if is_word_wrap { + let (mut width, mut offset) = layout_context.wrap_dimensions(span); - while let Some(breakpoint) = font.wrap_line( - &text[last_breakpoint..], - params, - width, - offset, - layout_context.is_start_of_line(), - ) { - // This ensures that the space causing the line break - // is included in the line it broke. - let next_breakpoint = string_utils::next_char_boundary( - text, - last_breakpoint + breakpoint, - ); - - // If text doesn't fit at the start of a line, it - // won't fit on the next either, abort and put the - // whole text on the line (will be cut-off). This - // can happen for small text fields with single - // characters. - if breakpoint == 0 && layout_context.is_start_of_line() { - break; - } else if breakpoint == 0 { - layout_context.newline( - context, - fs.displayed_text(), - start + next_breakpoint, - span, - font_type, - ); - - let next_dim = layout_context.wrap_dimensions(span); - - width = next_dim.0; - offset = next_dim.1; - - if last_breakpoint >= text.len() { - break; - } else { - continue; - } - } - - layout_context.append_text( - &text[last_breakpoint..next_breakpoint], - start + last_breakpoint, - start + next_breakpoint, - span, - ); - - last_breakpoint = next_breakpoint; - if last_breakpoint >= text.len() { - break; - } + while let Some(breakpoint) = font.wrap_line( + &text[last_breakpoint..], + params, + width, + offset, + layout_context.is_start_of_line(), + ) { + // This ensures that the space causing the line break + // is included in the line it broke. + let next_breakpoint = + string_utils::next_char_boundary(text, last_breakpoint + breakpoint); + // If text doesn't fit at the start of a line, it + // won't fit on the next either, abort and put the + // whole text on the line (will be cut-off). This + // can happen for small text fields with single + // characters. + if breakpoint == 0 && layout_context.is_start_of_line() { + break; + } else if breakpoint == 0 { layout_context.newline( context, fs.displayed_text(), @@ -919,30 +886,63 @@ impl<'gc> LayoutBox<'gc> { span, font_type, ); + let next_dim = layout_context.wrap_dimensions(span); width = next_dim.0; offset = next_dim.1; + + if last_breakpoint >= text.len() { + break; + } else { + continue; + } } - } - let span_end = text.len(); - - if last_breakpoint < span_end { layout_context.append_text( - &text[last_breakpoint..span_end], + &text[last_breakpoint..next_breakpoint], start + last_breakpoint, - start + span_end, + start + next_breakpoint, span, ); + + last_breakpoint = next_breakpoint; + if last_breakpoint >= text.len() { + break; + } + + layout_context.newline( + context, + fs.displayed_text(), + start + next_breakpoint, + span, + font_type, + ); + let next_dim = layout_context.wrap_dimensions(span); + + width = next_dim.0; + offset = next_dim.1; } } + + let span_end = text.len(); + + if last_breakpoint < span_end { + layout_context.append_text( + &text[last_breakpoint..span_end], + start + last_breakpoint, + start + span_end, + span, + ); + } } } - - layout_context.end_layout(context, fs, font_type) } + layout_context.end_layout(context, fs, font_type) +} + +impl<'gc> LayoutBox<'gc> { pub fn bounds(&self) -> BoxBounds { self.bounds }