text: Move lower_from_text_spans to module level

This commit is contained in:
Kamil Jarosz 2024-06-19 20:53:20 +02:00 committed by Nathan Adams
parent 43b2cced0a
commit d2f5b78503
3 changed files with 107 additions and 106 deletions

View File

@ -21,6 +21,7 @@ use crate::display_object::{DisplayObjectBase, DisplayObjectPtr};
use crate::drawing::Drawing; use crate::drawing::Drawing;
use crate::events::{ClipEvent, ClipEventResult, TextControlCode}; use crate::events::{ClipEvent, ClipEventResult, TextControlCode};
use crate::font::{round_down_to_pixel, FontType, Glyph, TextRenderSettings}; use crate::font::{round_down_to_pixel, FontType, Glyph, TextRenderSettings};
use crate::html;
use crate::html::{ use crate::html::{
BoxBounds, FormatSpans, LayoutBox, LayoutContent, LayoutMetrics, Position, TextFormat, BoxBounds, FormatSpans, LayoutBox, LayoutContent, LayoutMetrics, Position, TextFormat,
}; };
@ -287,7 +288,7 @@ impl<'gc> EditText<'gc> {
FontType::Device FontType::Device
}; };
let (layout, intrinsic_bounds) = LayoutBox::lower_from_text_spans( let (layout, intrinsic_bounds) = html::lower_from_text_spans(
&text_spans, &text_spans,
context, context,
swf_movie.clone(), swf_movie.clone(),
@ -848,7 +849,7 @@ impl<'gc> EditText<'gc> {
FontType::Embedded 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, &edit_text.text_spans,
context, context,
movie, movie,

View File

@ -7,7 +7,7 @@ mod text_format;
pub use dimensions::BoxBounds; pub use dimensions::BoxBounds;
pub use dimensions::Position; 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 stylesheet::{transform_dashes_to_camel_case, CssStream};
pub use text_format::{FormatSpans, TextDisplay, TextFormat, TextSpan}; pub use text_format::{FormatSpans, TextDisplay, TextFormat, TextSpan};

View File

@ -807,111 +807,78 @@ impl<'gc> LayoutBox<'gc> {
content: LayoutContent::Drawing(drawing), content: LayoutContent::Drawing(drawing),
} }
} }
}
/// Construct a new layout hierarchy from text spans. /// Construct a new layout hierarchy from text spans.
/// ///
/// The returned bounds will include both the text bounds itself, as well /// The returned bounds will include both the text bounds itself, as well
/// as left and right margins on any of the lines. /// as left and right margins on any of the lines.
pub fn lower_from_text_spans( pub fn lower_from_text_spans<'gc>(
fs: &FormatSpans, fs: &FormatSpans,
context: &mut UpdateContext<'_, 'gc>, context: &mut UpdateContext<'_, 'gc>,
movie: Arc<SwfMovie>, movie: Arc<SwfMovie>,
bounds: Twips, bounds: Twips,
is_word_wrap: bool, is_word_wrap: bool,
font_type: FontType, font_type: FontType,
) -> (Vec<LayoutBox<'gc>>, BoxBounds<Twips>) { ) -> (Vec<LayoutBox<'gc>>, BoxBounds<Twips>) {
let mut layout_context = LayoutContext::new(movie, bounds, fs.displayed_text()); let mut layout_context = LayoutContext::new(movie, bounds, fs.displayed_text());
for (span_start, _end, span_text, span) in fs.iter_spans() { for (span_start, _end, span_text, span) in fs.iter_spans() {
if let Some(font) = layout_context.resolve_font(context, span, font_type) { if let Some(font) = layout_context.resolve_font(context, span, font_type) {
layout_context.font = Some(font); layout_context.font = Some(font);
layout_context.newspan(span); 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'][..]) { for text in span_text.split(&[b'\n', b'\r', b'\t'][..]) {
let slice_start = text.offset_in(span_text).unwrap(); let slice_start = text.offset_in(span_text).unwrap();
let delimiter = if slice_start > 0 { let delimiter = if slice_start > 0 {
span_text span_text
.get(slice_start - 1) .get(slice_start - 1)
.and_then(|c| u8::try_from(c).ok()) .and_then(|c| u8::try_from(c).ok())
} else { } else {
None None
}; };
match delimiter { match delimiter {
Some(b'\n' | b'\r') => layout_context.explicit_newline( Some(b'\n' | b'\r') => layout_context.explicit_newline(
context, context,
fs.displayed_text(), fs.displayed_text(),
span_start + slice_start - 1, span_start + slice_start - 1,
span, span,
font_type, font_type,
), ),
Some(b'\t') => layout_context.tab(), 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 { if is_word_wrap {
let (mut width, mut offset) = layout_context.wrap_dimensions(span); let (mut width, mut offset) = layout_context.wrap_dimensions(span);
while let Some(breakpoint) = font.wrap_line( while let Some(breakpoint) = font.wrap_line(
&text[last_breakpoint..], &text[last_breakpoint..],
params, params,
width, width,
offset, offset,
layout_context.is_start_of_line(), layout_context.is_start_of_line(),
) { ) {
// This ensures that the space causing the line break // This ensures that the space causing the line break
// is included in the line it broke. // is included in the line it broke.
let next_breakpoint = string_utils::next_char_boundary( let next_breakpoint =
text, string_utils::next_char_boundary(text, last_breakpoint + breakpoint);
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;
}
// 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( layout_context.newline(
context, context,
fs.displayed_text(), fs.displayed_text(),
@ -919,30 +886,63 @@ impl<'gc> LayoutBox<'gc> {
span, span,
font_type, font_type,
); );
let next_dim = layout_context.wrap_dimensions(span); let next_dim = layout_context.wrap_dimensions(span);
width = next_dim.0; width = next_dim.0;
offset = next_dim.1; 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( layout_context.append_text(
&text[last_breakpoint..span_end], &text[last_breakpoint..next_breakpoint],
start + last_breakpoint, start + last_breakpoint,
start + span_end, start + next_breakpoint,
span, 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<Twips> { pub fn bounds(&self) -> BoxBounds<Twips> {
self.bounds self.bounds
} }