From 2cbfde76839b39f8a4870576266045d6146e6d55 Mon Sep 17 00:00:00 2001 From: "Brian J. Burg" Date: Thu, 18 Oct 2012 10:35:52 -0700 Subject: [PATCH] Convert offset+length to TextRange in parts of TextRun API. --- src/servo/layout/box.rs | 14 ++--- src/servo/layout/inline.rs | 2 +- src/servo/layout/text.rs | 4 +- src/servo/text/text_run.rs | 116 ++++++++++++++++++++++++------------- 4 files changed, 86 insertions(+), 50 deletions(-) diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index 3328c302498..fddf7c9d105 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -22,7 +22,7 @@ use layout::debug::BoxedDebugMethods; use layout::flow::FlowContext; use layout::text::TextBoxData; use servo_text::text_run; -use servo_text::text_run::TextRun; +use servo_text::text_run::{TextRange, TextRun}; use std::net::url::Url; use task::spawn; use util::color::Color; @@ -193,16 +193,16 @@ impl RenderBox : RenderBoxMethods { let mut right_length : Option = None; debug!("split_to_width: splitting text box (strlen=%u, off=%u, len=%u, avail_width=%?)", data.run.text.len(), data.offset, data.length, max_width); - do data.run.iter_indivisible_pieces_for_range(data.offset, data.length) |off, len| { + do data.run.iter_indivisible_pieces_for_range(TextRange(data.offset, data.length)) |off, len| { debug!("split_to_width: considering range (off=%u, len=%u, remain_width=%?)", off, len, remaining_width); - let metrics = data.run.metrics_for_range(off, len); + let metrics = data.run.metrics_for_range(TextRange(off, len)); let advance = metrics.advance_width; let should_continue : bool; if advance <= remaining_width { should_continue = true; - if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(off, len) { + if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(TextRange(off, len)) { debug!("split_to_width: case=skipping leading trimmable whitespace"); left_offset += len; } else { @@ -213,7 +213,7 @@ impl RenderBox : RenderBoxMethods { } else { /* advance > remaining_width */ should_continue = false; - if data.run.range_is_trimmable_whitespace(off, len) { + if data.run.range_is_trimmable_whitespace(TextRange(off, len)) { // if there are still things after the trimmable whitespace, create right chunk if off + len < data.offset + data.length { debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder"); @@ -270,7 +270,7 @@ impl RenderBox : RenderBoxMethods { // TODO: consult CSS 'width', margin, border. // TODO: If image isn't available, consult 'width'. ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width), - TextBox(_,d) => d.run.min_width_for_range(d.offset, d.length), + TextBox(_,d) => d.run.min_width_for_range(TextRange(d.offset, d.length)), UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here." } } @@ -293,7 +293,7 @@ impl RenderBox : RenderBoxMethods { // factor in min/pref widths of any text runs that it owns. TextBox(_,d) => { let mut max_line_width: au = au(0); - for d.run.iter_natural_lines_for_range(d.offset, d.length) |line_offset, line_len| { + for d.run.iter_natural_lines_for_range(TextRange(d.offset, d.length)) |line_offset, line_len| { // if the line is a single newline, then len will be zero if line_len == 0 { loop } diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs index 00e7d62cb8b..97acba4c6cc 100644 --- a/src/servo/layout/inline.rs +++ b/src/servo/layout/inline.rs @@ -586,7 +586,7 @@ impl FlowContext : InlineLayout { // adjust bounding box metric to box's horizontal offset // TODO: can we trust the leading provided by font metrics? @TextBox(_, data) => { - let text_bounds = data.run.metrics_for_range(data.offset, data.length).bounding_box; + let text_bounds = data.run.metrics_for_range(TextRange(data.offset, data.length)).bounding_box; text_bounds.translate(&Point2D(cur_box.d().position.origin.x, au(0))) }, _ => fail fmt!("Tried to compute bounding box of unknown Box variant: %s", cur_box.debug_str()) diff --git a/src/servo/layout/text.rs b/src/servo/layout/text.rs index d5cf5f77505..b218f4b191e 100644 --- a/src/servo/layout/text.rs +++ b/src/servo/layout/text.rs @@ -3,7 +3,7 @@ use au = gfx::geometry; use au::au; use geom::size::Size2D; -use servo_text::text_run::TextRun; +use servo_text::text_run::{TextRange, TextRun}; use servo_text::font_cache::FontCache; use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox}; use layout::context::LayoutContext; @@ -28,7 +28,7 @@ pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun, run.text.len(), offset, length, run.text); let new_box_data = copy *box_data; let new_text_data = TextBoxData(run, offset, length); - let metrics = run.metrics_for_range(offset, length); + let metrics = run.metrics_for_range(TextRange(offset, length)); new_box_data.position.size = metrics.bounding_box.size; @TextBox(move new_box_data, move new_text_data) } diff --git a/src/servo/text/text_run.rs b/src/servo/text/text_run.rs index 412a8fbd2b2..50fdbc5cf83 100644 --- a/src/servo/text/text_run.rs +++ b/src/servo/text/text_run.rs @@ -19,6 +19,50 @@ pub struct TextRun { priv glyphs: GlyphStore, } +pub struct TextRange { + priv off: u16, + priv len: u16 +} + +pure fn TextRange(off: uint, len :uint) -> TextRange { + assert off <= u16::max_value as uint; + assert len <= u16::max_value as uint; + + TextRange { + off: off as u16, + len: len as u16 + } +} + +impl TextRange { + pub pure fn begin() -> uint { self.off as uint } + pub pure fn length() -> uint { self.len as uint } + pub pure fn end() -> uint { (self.off as uint) + (self.len as uint) } + + pub pure fn eachi(cb: fn&(uint) -> bool) { + do uint::range(self.off as uint, + (self.off as uint) + (self.len as uint)) |i| { + cb(i) + } + } + + pub pure fn is_valid_for_string(s: &str) -> bool { + self.begin() < s.len() && self.end() <= s.len() && self.length() <= s.len() + } + + pub pure fn shift(i: int) -> TextRange { + TextRange(((self.off as int) + i) as uint, self.len as uint) + } + + pub pure fn extend(i: int) -> TextRange { + TextRange(self.off as uint, ((self.len as int) + i) as uint) + } + + pub pure fn adjust(off_i: int, len_i: int) -> TextRange { + TextRange(((self.off as int) + off_i) as uint, ((self.len as int) + len_i) as uint) + } +} + // This is a hack until TextRuns are normally sendable, or // we instead use ARC everywhere. pub struct SendableTextRun { @@ -47,22 +91,23 @@ pub fn deserialize(cache: @FontCache, run: &SendableTextRun) -> @TextRun { trait TextRunMethods { pure fn glyphs(&self) -> &self/GlyphStore; - pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool); + pure fn iter_indivisible_pieces_for_range(&self, range: TextRange, f: fn(uint, uint) -> bool); // TODO: needs to take box style as argument, or move to TextBox. // see Gecko's IsTrimmableSpace methods for details. - pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool; + pure fn range_is_trimmable_whitespace(&self, range: TextRange) -> bool; - fn metrics_for_range(offset: uint, length: uint) -> RunMetrics; - fn min_width_for_range(offset: uint, length: uint) -> au; - fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool); + fn metrics_for_range(&self, range: TextRange) -> RunMetrics; + fn min_width_for_range(&self, range: TextRange) -> au; + fn iter_natural_lines_for_range(&self, range: TextRange, f: fn(uint, uint) -> bool); } impl TextRun : TextRunMethods { pure fn glyphs(&self) -> &self/GlyphStore { &self.glyphs } - pure fn range_is_trimmable_whitespace(&self, offset: uint, length: uint) -> bool { - let mut i = offset; - while i < offset + length { + pure fn range_is_trimmable_whitespace(&self, range: TextRange) -> bool { + let mut i = range.begin(); + while i < range.end() { + // jump i to each new char let {ch, next} = str::char_range_at(self.text, i); match ch { ' ' | '\t' | '\r' => {}, @@ -73,37 +118,30 @@ impl TextRun : TextRunMethods { return true; } - fn metrics_for_range(offset: uint, length: uint) -> RunMetrics { - self.font.measure_text(&self, offset, length) + fn metrics_for_range(&self, range: TextRange) -> RunMetrics { + self.font.measure_text(self, range.begin(), range.length()) } - fn min_width_for_range(offset: uint, length: uint) -> au { - assert length > 0; - assert offset < self.text.len(); - assert offset + length <= self.text.len(); + fn min_width_for_range(&self, range: TextRange) -> au { + assert range.is_valid_for_string(self.text); let mut max_piece_width = au(0); - for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| { - let metrics = self.font.measure_text(&self, piece_offset, piece_len); - if metrics.advance_width > max_piece_width { - max_piece_width = metrics.advance_width; - } - }; - + for self.iter_indivisible_pieces_for_range(range) |piece_offset, piece_len| { + let metrics = self.font.measure_text(self, piece_offset, piece_len); + max_piece_width = au::max(max_piece_width, metrics.advance_width); + } return max_piece_width; } - fn iter_natural_lines_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) { - assert length > 0; - assert offset < self.text.len(); - assert offset + length <= self.text.len(); + fn iter_natural_lines_for_range(&self, range: TextRange, f: fn(uint, uint) -> bool) { + assert range.is_valid_for_string(self.text); - let mut clump_offset = offset; + let mut clump_offset = range.begin(); let mut clump_length = 0; let mut in_clump = false; // clump non-linebreaks of nonzero length - for uint::range(offset, offset + length) |i| { + for range.eachi |i| { match (self.glyphs.char_is_newline(i), in_clump) { (false, true) => { clump_length += 1; } (false, false) => { in_clump = true; clump_offset = i; clump_length = 1; } @@ -119,17 +157,15 @@ impl TextRun : TextRunMethods { // flush any remaining chars as a line if in_clump { - clump_length = (offset + length) - clump_offset; + clump_length = range.end() - clump_offset; f(clump_offset, clump_length); } } - pure fn iter_indivisible_pieces_for_range(&self, offset: uint, length: uint, f: fn(uint, uint) -> bool) { - assert length > 0; - assert offset < self.text.len(); - assert offset + length <= self.text.len(); + pure fn iter_indivisible_pieces_for_range(&self, range: TextRange, f: fn(uint, uint) -> bool) { + assert range.is_valid_for_string(self.text); - let mut clump_offset = offset; + let mut clump_offset = range.begin(); let mut clump_length; loop { @@ -140,12 +176,12 @@ impl TextRun : TextRunMethods { if !f(clump_offset, clump_length) { break } clump_offset += clump_length; // reached end - if clump_offset == offset + length { break } + if clump_offset == range.end() { break } }, None => { // nothing left, flush last piece containing only whitespace - if clump_offset < offset + length { - let clump_length = (offset + length) - clump_offset; + if clump_offset < range.end() { + let clump_length = range.end() - clump_offset; f(clump_offset, clump_length); } break @@ -159,12 +195,12 @@ impl TextRun : TextRunMethods { if !f(clump_offset, clump_length) { break } clump_offset += clump_length; // reached end - if clump_offset == offset + length { break } + if clump_offset == range.end() { break } } None => { // nothing left, flush last piece containing only non-whitespaces - if clump_offset < offset + length { - let clump_length = (offset + length) - clump_offset; + if clump_offset < range.end() { + let clump_length = range.end() - clump_offset; f(clump_offset, clump_length); } break @@ -214,7 +250,7 @@ fn test_iter_indivisible_pieces() { let font = flib.get_test_font(); let run = TextRun(font, copy text); let mut slices : ~[~str] = ~[]; - for run.iter_indivisible_pieces_for_range(0, text.len()) |offset, length| { + for run.iter_indivisible_pieces_for_range(TextRange(0, text.len())) |offset, length| { slices.push(str::slice(text, offset, length)); } assert slices == res;