Convert offset+length to TextRange in parts of TextRun API.

This commit is contained in:
Brian J. Burg 2012-10-18 10:35:52 -07:00
parent e2c5bcaf37
commit 2cbfde7683
4 changed files with 86 additions and 50 deletions

View file

@ -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<uint> = 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 }

View file

@ -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())

View file

@ -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)
}

View file

@ -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<TextRun> 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;