Refactor TextRenderBox to handle range and run data and let a RenderBox's style handle font size

This commit is contained in:
JJ Weber 2013-05-26 14:21:54 -07:00
parent 5be7bfa14f
commit 5c8c776f0a
3 changed files with 56 additions and 80 deletions

View file

@ -10,7 +10,6 @@ use layout::context::LayoutContext;
use layout::debug::DebugMethods; use layout::debug::DebugMethods;
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor}; use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
use layout::flow::FlowContext; use layout::flow::FlowContext;
use layout::text::TextBoxData;
use layout::text; use layout::text;
use core::cell::Cell; use core::cell::Cell;
@ -20,9 +19,10 @@ use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{DisplayItem, DisplayList}; use gfx::display_list::{DisplayItem, DisplayList};
use gfx::font::{FontStyle, FontWeight300}; use gfx::font::{FontStyle, FontWeight300};
use gfx::geometry::Au; use gfx::geometry::Au;
use gfx::text::text_run::TextRun;
use newcss::color::rgb; use newcss::color::rgb;
use newcss::complete::CompleteStyle; use newcss::complete::CompleteStyle;
use newcss::units::{Cursive, Em, Fantasy, Length, Monospace, Pt, Px, SansSerif, Serif}; use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium}; use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily}; use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal}; use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
@ -94,14 +94,13 @@ impl ImageRenderBox {
/// `TextRun` object. /// `TextRun` object.
pub struct TextRenderBox { pub struct TextRenderBox {
base: RenderBoxBase, base: RenderBoxBase,
run: @TextRun,
// TODO: Flatten `TextBoxData` into this type. range: Range,
text_data: TextBoxData,
} }
impl TextRenderBox { impl TextRenderBox {
fn teardown(&self) { fn teardown(&self) {
self.text_data.teardown(); self.run.teardown();
} }
} }
@ -154,11 +153,6 @@ pub struct RenderBoxBase {
/// The position of this box relative to its owning flow. /// The position of this box relative to its owning flow.
position: Rect<Au>, position: Rect<Au>,
/// The font size.
///
/// FIXME(pcwalton): Why is this present on non-text-boxes?
font_size: Length,
/// A debug ID. /// A debug ID.
/// ///
/// TODO(#87) Make this only present in debug builds. /// TODO(#87) Make this only present in debug builds.
@ -172,7 +166,6 @@ impl RenderBoxBase {
node: node, node: node,
ctx: flow_context, ctx: flow_context,
position: Au::zero_rect(), position: Au::zero_rect(),
font_size: Px(0.0),
id: id, id: id,
} }
} }
@ -261,7 +254,7 @@ pub impl RenderBox {
self.font_style() == other.font_style() && self.text_decoration() == other.text_decoration() self.font_style() == other.font_style() && self.text_decoration() == other.text_decoration()
}, },
(&TextRenderBoxClass(text_box_a), &TextRenderBoxClass(text_box_b)) => { (&TextRenderBoxClass(text_box_a), &TextRenderBoxClass(text_box_b)) => {
managed::ptr_eq(text_box_a.text_data.run, text_box_b.text_data.run) managed::ptr_eq(text_box_a.run, text_box_b.run)
} }
(_, _) => false, (_, _) => false,
} }
@ -280,21 +273,21 @@ pub impl RenderBox {
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
let mut pieces_processed_count: uint = 0; let mut pieces_processed_count: uint = 0;
let mut remaining_width: Au = max_width; let mut remaining_width: Au = max_width;
let mut left_range = Range::new(text_box.text_data.range.begin(), 0); let mut left_range = Range::new(text_box.range.begin(), 0);
let mut right_range: Option<Range> = None; let mut right_range: Option<Range> = None;
debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)", debug!("split_to_width: splitting text box (strlen=%u, range=%?, avail_width=%?)",
text_box.text_data.run.text.len(), text_box.run.text.len(),
text_box.text_data.range, text_box.range,
max_width); max_width);
for text_box.text_data.run.iter_indivisible_pieces_for_range( for text_box.run.iter_indivisible_pieces_for_range(
&text_box.text_data.range) |piece_range| { &text_box.range) |piece_range| {
debug!("split_to_width: considering piece (range=%?, remain_width=%?)", debug!("split_to_width: considering piece (range=%?, remain_width=%?)",
piece_range, piece_range,
remaining_width); remaining_width);
let metrics = text_box.text_data.run.metrics_for_range(piece_range); let metrics = text_box.run.metrics_for_range(piece_range);
let advance = metrics.advance_width; let advance = metrics.advance_width;
let should_continue: bool; let should_continue: bool;
@ -303,7 +296,7 @@ pub impl RenderBox {
if starts_line && if starts_line &&
pieces_processed_count == 0 && pieces_processed_count == 0 &&
text_box.text_data.run.range_is_trimmable_whitespace(piece_range) { text_box.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_range.shift_by(piece_range.length() as int); left_range.shift_by(piece_range.length() as int);
} else { } else {
@ -314,24 +307,24 @@ pub impl RenderBox {
} else { // The advance is more than the remaining width. } else { // The advance is more than the remaining width.
should_continue = false; should_continue = false;
if text_box.text_data.run.range_is_trimmable_whitespace(piece_range) { if text_box.run.range_is_trimmable_whitespace(piece_range) {
// If there are still things after the trimmable whitespace, create the // If there are still things after the trimmable whitespace, create the
// right chunk. // right chunk.
if piece_range.end() < text_box.text_data.range.end() { if piece_range.end() < text_box.range.end() {
debug!("split_to_width: case=skipping trimmable trailing \ debug!("split_to_width: case=skipping trimmable trailing \
whitespace, then split remainder"); whitespace, then split remainder");
let right_range_end = let right_range_end =
text_box.text_data.range.end() - piece_range.end(); text_box.range.end() - piece_range.end();
right_range = Some(Range::new(piece_range.end(), right_range_end)); right_range = Some(Range::new(piece_range.end(), right_range_end));
} else { } else {
debug!("split_to_width: case=skipping trimmable trailing \ debug!("split_to_width: case=skipping trimmable trailing \
whitespace"); whitespace");
} }
} else if piece_range.begin() < text_box.text_data.range.end() { } else if piece_range.begin() < text_box.range.end() {
// There are still some things left over at the end of the line. Create // There are still some things left over at the end of the line. Create
// the right chunk. // the right chunk.
let right_range_end = let right_range_end =
text_box.text_data.range.end() - piece_range.begin(); text_box.range.end() - piece_range.begin();
right_range = Some(Range::new(piece_range.begin(), right_range_end)); right_range = Some(Range::new(piece_range.begin(), right_range_end));
debug!("split_to_width: case=splitting remainder with right range=%?", debug!("split_to_width: case=splitting remainder with right range=%?",
right_range); right_range);
@ -347,7 +340,7 @@ pub impl RenderBox {
let left_box = if left_range.length() > 0 { let left_box = if left_range.length() > 0 {
let new_text_box = @mut text::adapt_textbox_with_range(text_box.base, let new_text_box = @mut text::adapt_textbox_with_range(text_box.base,
text_box.text_data.run, text_box.run,
left_range); left_range);
Some(TextRenderBoxClass(new_text_box)) Some(TextRenderBoxClass(new_text_box))
} else { } else {
@ -356,7 +349,7 @@ pub impl RenderBox {
let right_box = do right_range.map_default(None) |range: &Range| { let right_box = do right_range.map_default(None) |range: &Range| {
let new_text_box = @mut text::adapt_textbox_with_range(text_box.base, let new_text_box = @mut text::adapt_textbox_with_range(text_box.base,
text_box.text_data.run, text_box.run,
*range); *range);
Some(TextRenderBoxClass(new_text_box)) Some(TextRenderBoxClass(new_text_box))
}; };
@ -386,7 +379,7 @@ pub impl RenderBox {
} }
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
text_box.text_data.run.min_width_for_range(&text_box.text_data.range) text_box.run.min_width_for_range(&text_box.range)
} }
UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.") UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
@ -414,10 +407,10 @@ pub impl RenderBox {
// report nothing and the parent flow can factor in minimum/preferred widths of any // report nothing and the parent flow can factor in minimum/preferred widths of any
// text runs that it owns. // text runs that it owns.
let mut max_line_width = Au(0); let mut max_line_width = Au(0);
for text_box.text_data.run.iter_natural_lines_for_range(&text_box.text_data.range) for text_box.run.iter_natural_lines_for_range(&text_box.range)
|line_range| { |line_range| {
let mut line_width: Au = Au(0); let mut line_width: Au = Au(0);
for text_box.text_data.run.glyphs.iter_glyphs_for_char_range(line_range) for text_box.run.glyphs.iter_glyphs_for_char_range(line_range)
|_, glyph| { |_, glyph| {
line_width += glyph.advance() line_width += glyph.advance()
} }
@ -577,8 +570,8 @@ pub impl RenderBox {
// FIXME: This should use `with_mut_ref` when that appears. // FIXME: This should use `with_mut_ref` when that appears.
let mut this_list = list.take(); let mut this_list = list.take();
this_list.append_item(~DisplayItem::new_Text(&absolute_box_bounds, this_list.append_item(~DisplayItem::new_Text(&absolute_box_bounds,
~text_box.text_data.run.serialize(), ~text_box.run.serialize(),
text_box.text_data.range, text_box.range,
color)); color));
list.put_back(this_list); list.put_back(this_list);
@ -598,8 +591,8 @@ pub impl RenderBox {
// Draw a rectangle representing the baselines. // Draw a rectangle representing the baselines.
// //
// TODO(Issue #221): Create and use a Line display item for the baseline. // TODO(Issue #221): Create and use a Line display item for the baseline.
let ascent = text_box.text_data.run.metrics_for_range( let ascent = text_box.run.metrics_for_range(
&text_box.text_data.range).ascent; &text_box.range).ascent;
let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent), let baseline = Rect(absolute_box_bounds.origin + Point2D(Au(0), ascent),
Size2D(absolute_box_bounds.size.width, Au(0))); Size2D(absolute_box_bounds.size.width, Au(0)));
@ -825,9 +818,9 @@ impl DebugMethods for RenderBox {
GenericRenderBoxClass(*) => ~"GenericRenderBox", GenericRenderBoxClass(*) => ~"GenericRenderBox",
ImageRenderBoxClass(*) => ~"ImageRenderBox", ImageRenderBoxClass(*) => ~"ImageRenderBox",
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
fmt!("TextRenderBox(text=%s)", str::substr(text_box.text_data.run.text, fmt!("TextRenderBox(text=%s)", str::substr(text_box.run.text,
text_box.text_data.range.begin(), text_box.range.begin(),
text_box.text_data.range.length())) text_box.range.length()))
} }
UnscannedTextRenderBoxClass(text_box) => { UnscannedTextRenderBoxClass(text_box) => {
fmt!("UnscannedTextRenderBox(%s)", text_box.text) fmt!("UnscannedTextRenderBox(%s)", text_box.text)

View file

@ -807,8 +807,8 @@ impl InlineFlowData {
// TODO: We can use font metrics directly instead of re-measuring for the // TODO: We can use font metrics directly instead of re-measuring for the
// bounding box. // bounding box.
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
let range = &text_box.text_data.range; let range = &text_box.range;
let run = &text_box.text_data.run; let run = &text_box.run;
let text_bounds = run.metrics_for_range(range).bounding_box; let text_bounds = run.metrics_for_range(range).bounding_box;
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0))) text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
}, },
@ -840,7 +840,7 @@ impl InlineFlowData {
// according to the `vertical-align` property of the containing block. // according to the `vertical-align` property of the containing block.
let halfleading = match cur_box { let halfleading = match cur_box {
TextRenderBoxClass(text_box) => { TextRenderBoxClass(text_box) => {
(text_box.text_data.run.font.metrics.em_size - line_height).scale_by(0.5) (text_box.run.font.metrics.em_size - line_height).scale_by(0.5)
}, },
_ => Au(0), _ => Au(0),
}; };

View file

@ -5,28 +5,11 @@
//! Text layout. //! Text layout.
use layout::box::{RenderBox, RenderBoxBase, TextRenderBox, UnscannedTextRenderBoxClass}; use layout::box::{RenderBox, RenderBoxBase, TextRenderBox, UnscannedTextRenderBoxClass};
use gfx::text::text_run::TextRun; use gfx::text::text_run::TextRun;
use servo_util::range::Range; use servo_util::range::Range;
pub struct TextBoxData {
run: @TextRun,
range: Range,
}
impl TextBoxData {
pub fn new(run: @TextRun, range: Range) -> TextBoxData {
TextBoxData {
run: run,
range: range,
}
}
pub fn teardown(&self) {
self.run.teardown();
}
}
/// Creates a TextRenderBox from a range and a text run.
pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range) pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)
-> TextRenderBox { -> TextRenderBox {
assert!(range.begin() < run.char_len()); assert!(range.begin() < run.char_len());
@ -38,14 +21,14 @@ pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: R
range.begin(), range.begin(),
range.length(), range.length(),
run.text); run.text);
let new_text_data = TextBoxData::new(run, range);
let metrics = run.metrics_for_range(&range);
let metrics = run.metrics_for_range(&range);
base.position.size = metrics.bounding_box.size; base.position.size = metrics.bounding_box.size;
TextRenderBox { TextRenderBox {
base: base, base: base,
text_data: new_text_data, run: run,
range: range,
} }
} }