diff --git a/core/src/font.rs b/core/src/font.rs index 6a252a35a..2463b9639 100644 --- a/core/src/font.rs +++ b/core/src/font.rs @@ -174,24 +174,26 @@ impl<'gc> Font<'gc> { size } - /// Given a line of text, split it into the shortest number of lines that - /// are shorter than `width`. + /// Given a line of text, find the first breakpoint within the text. /// /// This function assumes only `" "` is valid whitespace to split words on, - /// and will not attempt to break words that are longer than `width`. + /// and will not attempt to break words that are longer than `width`, nor + /// will it break at newlines. /// - /// The given `offset` determines the start of the initial line. + /// The given `offset` determines the start of the initial line, while the + /// `width` indicates how long the line is supposed to be. + /// + /// This function yields `None` if the line is not broken. /// /// TODO: This function and, more generally, this entire file will need to /// be internationalized to implement AS3 `flash.text.engine`. - pub fn split_wrapped_lines( + pub fn wrap_line( self, text: &str, font_size: Twips, width: Twips, offset: Twips, - ) -> Vec { - let mut result = vec![]; + ) -> Option { let mut current_width = width .checked_sub(offset) .unwrap_or_else(|| Twips::from_pixels(0.0)); @@ -200,29 +202,19 @@ impl<'gc> Font<'gc> { for word in text.split(' ') { let measure = self.measure(word, font_size); let line_start = current_word.as_ptr() as usize - text.as_ptr() as usize; - let line_end = if (line_start + current_word.len() + 1) < text.len() { - line_start + current_word.len() + 1 - } else { - line_start + current_word.len() - }; + let line_end = line_start + current_word.len(); let word_start = word.as_ptr() as usize - text.as_ptr() as usize; - let word_end = if (word_start + word.len() + 1) < text.len() { - word_start + word.len() + 1 - } else { - word_start + word.len() - }; + let word_end = word_start + word.len(); if measure.0 > current_width && measure.0 > width { //Failsafe for if we get a word wider than the field. if !current_word.is_empty() { - result.push(line_end); + return Some(line_end); } - result.push(word_end); - current_word = &text[word_end..word_end]; - current_width = width; + return Some(word_end); } else if measure.0 > current_width { if !current_word.is_empty() { - result.push(line_end); + return Some(line_end); } current_word = &text[word_start..word_end]; @@ -233,7 +225,7 @@ impl<'gc> Font<'gc> { } } - result + None } pub fn descriptor(self) -> FontDescriptor { diff --git a/core/src/html/layout.rs b/core/src/html/layout.rs index af128bf17..cbb5917ff 100644 --- a/core/src/html/layout.rs +++ b/core/src/html/layout.rs @@ -44,6 +44,12 @@ impl<'gc> LayoutContext<'gc> { &mut self.cursor } + /// Adjust the text layout cursor down to the next line. + fn newline(&mut self, font_size: Twips) { + self.cursor.set_x(Twips::from_pixels(0.0)); + self.cursor += (Twips::from_pixels(0.0), font_size).into(); + } + fn font(&self) -> Option> { self.font } @@ -217,36 +223,48 @@ impl<'gc> LayoutBox<'gc> { for (start, _end, text, span) in fs.iter_spans() { if let Some(font) = layout_context.resolve_font(context, movie.clone(), &span) { let font_size = Twips::from_pixels(span.size); - let breakpoint_list = - font.split_wrapped_lines(&text, font_size, bounds, layout_context.cursor().x()); - - let end = text.len(); - let mut last_breakpoint = 0; - for breakpoint in breakpoint_list { - if last_breakpoint != breakpoint { - Self::append_text_fragment( - context.gc_context, - &mut layout_context, - &text[last_breakpoint..breakpoint], - start + last_breakpoint, - start + breakpoint, - span, - ); + while let Some(breakpoint) = font.wrap_line( + &text[last_breakpoint..], + font_size, + bounds, + layout_context.cursor().x(), + ) { + if breakpoint == last_breakpoint { + last_breakpoint += 1; + continue; } + Self::append_text_fragment( + context.gc_context, + &mut layout_context, + &text[last_breakpoint..breakpoint], + start + last_breakpoint, + start + breakpoint, + span, + ); + last_breakpoint = breakpoint; + if last_breakpoint >= text.len() { + break; + } + + layout_context.newline(font_size); } - Self::append_text_fragment( - context.gc_context, - &mut layout_context, - &text[last_breakpoint..end], - start + last_breakpoint, - start + end, - span, - ); + let span_end = text.len(); + + if last_breakpoint < span_end { + Self::append_text_fragment( + context.gc_context, + &mut layout_context, + &text[last_breakpoint..span_end], + start + last_breakpoint, + start + span_end, + span, + ); + } } }