mirror of
https://github.com/servo/servo.git
synced 2025-06-25 09:34:32 +01:00
More TextRange refactoring.
This commit is contained in:
parent
efca6fedfc
commit
0f050e7835
5 changed files with 44 additions and 105 deletions
|
@ -187,47 +187,47 @@ impl RenderBox : RenderBoxMethods {
|
||||||
|
|
||||||
let mut i : uint = 0;
|
let mut i : uint = 0;
|
||||||
let mut remaining_width : au = max_width;
|
let mut remaining_width : au = max_width;
|
||||||
let mut left_offset : uint = data.offset;
|
let mut left_offset : uint = data.range.begin();
|
||||||
let mut left_length : uint = 0;
|
let mut left_length : uint = 0;
|
||||||
let mut right_offset : Option<uint> = None;
|
let mut right_offset : Option<uint> = None;
|
||||||
let mut right_length : Option<uint> = None;
|
let mut right_length : Option<uint> = None;
|
||||||
debug!("split_to_width: splitting text box (strlen=%u, off=%u, len=%u, avail_width=%?)",
|
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);
|
data.run.text.len(), data.range.begin(), data.range.length(), max_width);
|
||||||
do data.run.iter_indivisible_pieces_for_range(TextRange(data.offset, data.length)) |subrange| {
|
do data.run.iter_indivisible_pieces_for_range(data.range) |piece_range| {
|
||||||
debug!("split_to_width: considering range (off=%u, len=%u, remain_width=%?)",
|
debug!("split_to_width: considering range (off=%u, len=%u, remain_width=%?)",
|
||||||
subrange.begin(), subrange.length(), remaining_width);
|
piece_range.begin(), piece_range.length(), remaining_width);
|
||||||
let metrics = data.run.metrics_for_range(subrange);
|
let metrics = data.run.metrics_for_range(piece_range);
|
||||||
let advance = metrics.advance_width;
|
let advance = metrics.advance_width;
|
||||||
let should_continue : bool;
|
let should_continue : bool;
|
||||||
|
|
||||||
if advance <= remaining_width {
|
if advance <= remaining_width {
|
||||||
should_continue = true;
|
should_continue = true;
|
||||||
if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(subrange) {
|
if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(piece_range) {
|
||||||
debug!("split_to_width: case=skipping leading trimmable whitespace");
|
debug!("split_to_width: case=skipping leading trimmable whitespace");
|
||||||
left_offset += subrange.length();
|
left_offset += piece_range.length();
|
||||||
} else {
|
} else {
|
||||||
debug!("split_to_width: case=enlarging span");
|
debug!("split_to_width: case=enlarging span");
|
||||||
remaining_width -= advance;
|
remaining_width -= advance;
|
||||||
left_length += subrange.length();
|
left_length += piece_range.length();
|
||||||
}
|
}
|
||||||
} else { /* advance > remaining_width */
|
} else { /* advance > remaining_width */
|
||||||
should_continue = false;
|
should_continue = false;
|
||||||
|
|
||||||
if data.run.range_is_trimmable_whitespace(subrange) {
|
if data.run.range_is_trimmable_whitespace(piece_range) {
|
||||||
// if there are still things after the trimmable whitespace, create right chunk
|
// if there are still things after the trimmable whitespace, create right chunk
|
||||||
if subrange.end() < data.offset + data.length {
|
if piece_range.end() < data.range.end() {
|
||||||
debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder");
|
debug!("split_to_width: case=skipping trimmable trailing whitespace, then split remainder");
|
||||||
right_offset = Some(subrange.end());
|
right_offset = Some(piece_range.end());
|
||||||
right_length = Some((data.offset + data.length) - subrange.end());
|
right_length = Some(data.range.end() - piece_range.end());
|
||||||
} else {
|
} else {
|
||||||
debug!("split_to_width: case=skipping trimmable trailing whitespace");
|
debug!("split_to_width: case=skipping trimmable trailing whitespace");
|
||||||
}
|
}
|
||||||
} else if subrange.begin() < data.length + data.offset {
|
} else if piece_range.begin() < data.range.end() {
|
||||||
// still things left, create right chunk
|
// still things left, create right chunk
|
||||||
right_offset = Some(subrange.begin());
|
right_offset = Some(piece_range.begin());
|
||||||
right_length = Some((data.offset + data.length) - subrange.begin());
|
right_length = Some(data.range.end() - piece_range.begin());
|
||||||
debug!("split_to_width: case=splitting remainder with right span: (off=%u, len=%u)",
|
debug!("split_to_width: case=splitting remainder with right span: (off=%u, len=%u)",
|
||||||
subrange.begin(), (data.offset + data.length) - subrange.begin());
|
piece_range.begin(), data.range.end() - piece_range.begin());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
i += 1;
|
i += 1;
|
||||||
|
@ -236,13 +236,13 @@ impl RenderBox : RenderBoxMethods {
|
||||||
|
|
||||||
let left_box = if left_length > 0 {
|
let left_box = if left_length > 0 {
|
||||||
Some(layout::text::adapt_textbox_with_range(self.d(), data.run,
|
Some(layout::text::adapt_textbox_with_range(self.d(), data.run,
|
||||||
left_offset, left_length))
|
TextRange(left_offset, left_length)))
|
||||||
} else { None };
|
} else { None };
|
||||||
|
|
||||||
match (right_offset, right_length) {
|
match (right_offset, right_length) {
|
||||||
(Some(right_off), Some(right_len)) => {
|
(Some(right_off), Some(right_len)) => {
|
||||||
let right_box = layout::text::adapt_textbox_with_range(self.d(), data.run,
|
let right_box = layout::text::adapt_textbox_with_range(self.d(), data.run,
|
||||||
right_off, right_len);
|
TextRange(right_off, right_len));
|
||||||
return if i == 1 || left_box.is_none() {
|
return if i == 1 || left_box.is_none() {
|
||||||
SplitDidNotFit(left_box, Some(right_box))
|
SplitDidNotFit(left_box, Some(right_box))
|
||||||
} else {
|
} else {
|
||||||
|
@ -270,7 +270,7 @@ impl RenderBox : RenderBoxMethods {
|
||||||
// TODO: consult CSS 'width', margin, border.
|
// TODO: consult CSS 'width', margin, border.
|
||||||
// TODO: If image isn't available, consult 'width'.
|
// TODO: If image isn't available, consult 'width'.
|
||||||
ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
|
ImageBox(_,i) => au::from_px(i.get_size().get_default(Size2D(0,0)).width),
|
||||||
TextBox(_,d) => d.run.min_width_for_range(TextRange(d.offset, d.length)),
|
TextBox(_,d) => d.run.min_width_for_range(d.range),
|
||||||
UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here."
|
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.
|
// factor in min/pref widths of any text runs that it owns.
|
||||||
TextBox(_,d) => {
|
TextBox(_,d) => {
|
||||||
let mut max_line_width: au = au(0);
|
let mut max_line_width: au = au(0);
|
||||||
for d.run.iter_natural_lines_for_range(TextRange(d.offset, d.length)) |line_range| {
|
for d.run.iter_natural_lines_for_range(d.range) |line_range| {
|
||||||
// if the line is a single newline, then len will be zero
|
// if the line is a single newline, then len will be zero
|
||||||
if line_range.length() == 0 { loop }
|
if line_range.length() == 0 { loop }
|
||||||
|
|
||||||
|
@ -442,7 +442,7 @@ impl RenderBox : RenderBoxMethods {
|
||||||
UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.",
|
UnscannedTextBox(*) => fail ~"Shouldn't see unscanned boxes here.",
|
||||||
TextBox(_,d) => {
|
TextBox(_,d) => {
|
||||||
list.append_item(~dl::Text(copy abs_box_bounds, text_run::serialize(builder.ctx.font_cache, d.run),
|
list.append_item(~dl::Text(copy abs_box_bounds, text_run::serialize(builder.ctx.font_cache, d.run),
|
||||||
d.offset, d.length))
|
d.range.begin(), d.range.length()))
|
||||||
},
|
},
|
||||||
// TODO: items for background, border, outline
|
// TODO: items for background, border, outline
|
||||||
GenericBox(_) => {
|
GenericBox(_) => {
|
||||||
|
@ -519,7 +519,7 @@ impl RenderBox : BoxedDebugMethods {
|
||||||
let repr = match self {
|
let repr = match self {
|
||||||
@GenericBox(*) => ~"GenericBox",
|
@GenericBox(*) => ~"GenericBox",
|
||||||
@ImageBox(*) => ~"ImageBox",
|
@ImageBox(*) => ~"ImageBox",
|
||||||
@TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.offset, d.length)),
|
@TextBox(_,d) => fmt!("TextBox(text=%s)", str::substr(d.run.text, d.range.begin(), d.range.length())),
|
||||||
@UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s)
|
@UnscannedTextBox(_,s) => fmt!("UnscannedTextBox(%s)", s)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,8 @@ impl TextRunScanner {
|
||||||
let run = @TextRun(ctx.font_cache.get_test_font(), move transformed_text);
|
let run = @TextRun(ctx.font_cache.get_test_font(), move transformed_text);
|
||||||
debug!("TextRunScanner: pushing single text box when start=%u,end=%u",
|
debug!("TextRunScanner: pushing single text box when start=%u,end=%u",
|
||||||
self.clump_start, self.clump_end);
|
self.clump_start, self.clump_end);
|
||||||
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run, 0, run.text.len());
|
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run,
|
||||||
|
TextRange(0, run.text.len()));
|
||||||
out_boxes.push(new_box);
|
out_boxes.push(new_box);
|
||||||
},
|
},
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
|
@ -221,7 +222,8 @@ impl TextRunScanner {
|
||||||
let run = @TextRun(ctx.font_cache.get_test_font(), move run_str);
|
let run = @TextRun(ctx.font_cache.get_test_font(), move run_str);
|
||||||
debug!("TextRunScanner: pushing box(es) when start=%u,end=%u",
|
debug!("TextRunScanner: pushing box(es) when start=%u,end=%u",
|
||||||
self.clump_start, self.clump_end);
|
self.clump_start, self.clump_end);
|
||||||
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run, 0, run.text.len());
|
let new_box = layout::text::adapt_textbox_with_range(in_boxes[self.clump_start].d(), run,
|
||||||
|
TextRange(0, run.text.len()));
|
||||||
out_boxes.push(new_box);
|
out_boxes.push(new_box);
|
||||||
}
|
}
|
||||||
} /* /match */
|
} /* /match */
|
||||||
|
@ -586,7 +588,7 @@ impl FlowContext : InlineLayout {
|
||||||
// adjust bounding box metric to box's horizontal offset
|
// adjust bounding box metric to box's horizontal offset
|
||||||
// TODO: can we trust the leading provided by font metrics?
|
// TODO: can we trust the leading provided by font metrics?
|
||||||
@TextBox(_, data) => {
|
@TextBox(_, data) => {
|
||||||
let text_bounds = data.run.metrics_for_range(TextRange(data.offset, data.length)).bounding_box;
|
let text_bounds = data.run.metrics_for_range(data.range).bounding_box;
|
||||||
text_bounds.translate(&Point2D(cur_box.d().position.origin.x, au(0)))
|
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())
|
_ => fail fmt!("Tried to compute bounding box of unknown Box variant: %s", cur_box.debug_str())
|
||||||
|
|
|
@ -1,34 +1,27 @@
|
||||||
/** Text layout. */
|
/** Text layout. */
|
||||||
|
|
||||||
use au = gfx::geometry;
|
|
||||||
use au::au;
|
|
||||||
use geom::size::Size2D;
|
|
||||||
use servo_text::text_run::{TextRange, TextRun};
|
use servo_text::text_run::{TextRange, TextRun};
|
||||||
use servo_text::font_cache::FontCache;
|
|
||||||
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox};
|
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox};
|
||||||
use layout::context::LayoutContext;
|
|
||||||
|
|
||||||
pub struct TextBoxData {
|
pub struct TextBoxData {
|
||||||
run: @TextRun,
|
run: @TextRun,
|
||||||
offset: uint,
|
range: TextRange,
|
||||||
length: uint
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn TextBoxData(run: @TextRun, offset: uint, length: uint) -> TextBoxData {
|
pub fn TextBoxData(run: @TextRun, range: TextRange) -> TextBoxData {
|
||||||
TextBoxData {
|
TextBoxData {
|
||||||
run: run,
|
run: run,
|
||||||
offset: offset,
|
range: range,
|
||||||
length: length
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun,
|
pub fn adapt_textbox_with_range(box_data: &RenderBoxData, run: @TextRun,
|
||||||
offset: uint, length: uint) -> @RenderBox {
|
range: TextRange) -> @RenderBox {
|
||||||
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
|
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
|
||||||
run.text.len(), offset, length, run.text);
|
run.text.len(), range.begin(), range.length(), run.text);
|
||||||
let new_box_data = copy *box_data;
|
let new_box_data = copy *box_data;
|
||||||
let new_text_data = TextBoxData(run, offset, length);
|
let new_text_data = TextBoxData(run, range);
|
||||||
let metrics = run.metrics_for_range(TextRange(offset, length));
|
let metrics = run.metrics_for_range(range);
|
||||||
new_box_data.position.size = metrics.bounding_box.size;
|
new_box_data.position.size = metrics.bounding_box.size;
|
||||||
@TextBox(move new_box_data, move new_text_data)
|
@TextBox(move new_box_data, move new_text_data)
|
||||||
}
|
}
|
||||||
|
@ -45,58 +38,3 @@ impl RenderBox : UnscannedMethods {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* The main reflow routine for text layout.
|
|
||||||
impl @RenderBox : TextLayout {
|
|
||||||
fn reflow_text(ctx: &LayoutContext) {
|
|
||||||
let d = match self {
|
|
||||||
@TextBox(_,d) => { d }
|
|
||||||
_ => { fail ~"expected text box in reflow_text!" }
|
|
||||||
};
|
|
||||||
|
|
||||||
// TODO: get font from textrun's TextStyle
|
|
||||||
let font = ctx.font_cache.get_test_font();
|
|
||||||
|
|
||||||
// Do line breaking.
|
|
||||||
let mut current = TextRun(font, d.text);
|
|
||||||
let mut lines = dvec::DVec();
|
|
||||||
let mut width_left = au::from_px(800);
|
|
||||||
let mut max_width = au(0);
|
|
||||||
|
|
||||||
while current.size().width > width_left {
|
|
||||||
let min_width = current.min_break_width();
|
|
||||||
|
|
||||||
debug!("line %d, current width %d, width left %d, min width %d",
|
|
||||||
lines.len() as int,
|
|
||||||
*current.size().width as int,
|
|
||||||
*width_left as int,
|
|
||||||
*min_width as int);
|
|
||||||
|
|
||||||
if min_width > width_left {
|
|
||||||
// Too bad, we couldn't break. Overflow.
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
let (prev_line, next_line) = current.split(font, width_left);
|
|
||||||
let prev_width = prev_line.size().width;
|
|
||||||
if max_width < prev_width {
|
|
||||||
max_width = prev_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
lines.push(move prev_line);
|
|
||||||
current = next_line;
|
|
||||||
}
|
|
||||||
|
|
||||||
let remaining_width = current.size().width;
|
|
||||||
if max_width < remaining_width {
|
|
||||||
max_width = remaining_width;
|
|
||||||
}
|
|
||||||
|
|
||||||
let line_count = 1 + (lines.len() as i32);
|
|
||||||
let total_height = au(*current.size().height * line_count);
|
|
||||||
lines.push(move current);
|
|
||||||
|
|
||||||
self.d().position.size = Size2D(max_width, total_height);
|
|
||||||
d.runs = move dvec::unwrap(lines);
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ use glyph::GlyphIndex;
|
||||||
use libc::{ c_int, c_double, c_ulong };
|
use libc::{ c_int, c_double, c_ulong };
|
||||||
use native_font::NativeFont;
|
use native_font::NativeFont;
|
||||||
use ptr::{null, addr_of};
|
use ptr::{null, addr_of};
|
||||||
use text::text_run::TextRun;
|
use text::text_run::{TextRun, TextRange};
|
||||||
use vec_to_ptr = vec::raw::to_ptr;
|
use vec_to_ptr = vec::raw::to_ptr;
|
||||||
|
|
||||||
// Used to abstract over the shaper's choice of fixed int representation.
|
// Used to abstract over the shaper's choice of fixed int representation.
|
||||||
|
@ -41,7 +41,7 @@ struct RunMetrics {
|
||||||
|
|
||||||
// Public API
|
// Public API
|
||||||
pub trait FontMethods {
|
pub trait FontMethods {
|
||||||
fn measure_text(run: &TextRun, offset: uint, length: uint) -> RunMetrics;
|
fn measure_text(&TextRun, TextRange) -> RunMetrics;
|
||||||
|
|
||||||
fn buf(&self) -> @~[u8];
|
fn buf(&self) -> @~[u8];
|
||||||
// these are used to get glyphs and advances in the case that the
|
// these are used to get glyphs and advances in the case that the
|
||||||
|
@ -51,15 +51,14 @@ pub trait FontMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub impl Font : FontMethods {
|
pub impl Font : FontMethods {
|
||||||
fn measure_text(run: &TextRun, offset: uint, length: uint) -> RunMetrics {
|
fn measure_text(run: &TextRun, range: TextRange) -> RunMetrics {
|
||||||
assert offset < run.text.len();
|
assert range.is_valid_for_string(run.text);
|
||||||
assert offset + length <= run.text.len();
|
|
||||||
|
|
||||||
// TODO: alter advance direction for RTL
|
// TODO: alter advance direction for RTL
|
||||||
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
|
// TODO(Issue #98): using inter-char and inter-word spacing settings when measuring text
|
||||||
let mut advance = au(0);
|
let mut advance = au(0);
|
||||||
if length > 0 {
|
if range.length() > 0 {
|
||||||
do run.glyphs.iter_glyphs_for_range(offset, length) |_i, glyph| {
|
do run.glyphs.iter_glyphs_for_range(range.begin(), range.length()) |_i, glyph| {
|
||||||
advance += glyph.advance();
|
advance += glyph.advance();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -75,7 +74,7 @@ pub impl Font : FontMethods {
|
||||||
ascent: self.metrics.ascent,
|
ascent: self.metrics.ascent,
|
||||||
descent: self.metrics.descent,
|
descent: self.metrics.descent,
|
||||||
};
|
};
|
||||||
debug!("Measured text range '%s' with metrics:", run.text.substr(offset, length));
|
debug!("Measured text range '%s' with metrics:", run.text.substr(range.begin(), range.length()));
|
||||||
debug!("%?", metrics);
|
debug!("%?", metrics);
|
||||||
|
|
||||||
return metrics;
|
return metrics;
|
||||||
|
|
|
@ -167,7 +167,7 @@ impl TextRun : TextRunMethods {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn metrics_for_range(&self, range: TextRange) -> RunMetrics {
|
fn metrics_for_range(&self, range: TextRange) -> RunMetrics {
|
||||||
self.font.measure_text(self, range.begin(), range.length())
|
self.font.measure_text(self, range)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn min_width_for_range(&self, range: TextRange) -> au {
|
fn min_width_for_range(&self, range: TextRange) -> au {
|
||||||
|
@ -175,7 +175,7 @@ impl TextRun : TextRunMethods {
|
||||||
|
|
||||||
let mut max_piece_width = au(0);
|
let mut max_piece_width = au(0);
|
||||||
for self.iter_indivisible_pieces_for_range(range) |piece_range| {
|
for self.iter_indivisible_pieces_for_range(range) |piece_range| {
|
||||||
let metrics = self.font.measure_text(self, piece_range.begin(), piece_range.length());
|
let metrics = self.font.measure_text(self, piece_range);
|
||||||
max_piece_width = au::max(max_piece_width, metrics.advance_width);
|
max_piece_width = au::max(max_piece_width, metrics.advance_width);
|
||||||
}
|
}
|
||||||
return max_piece_width;
|
return max_piece_width;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue