Implement TextRunScanner; add explicit @self to all flow-related functions.

This commit is contained in:
Brian J. Burg 2012-10-09 17:39:18 -07:00
parent 2bc9e66257
commit 26d5cfb509
9 changed files with 221 additions and 67 deletions

View file

@ -24,11 +24,11 @@ trait BlockLayout {
pure fn starts_block_flow() -> bool; pure fn starts_block_flow() -> bool;
pure fn with_block_box(fn(box: &@RenderBox) -> ()) -> (); pure fn with_block_box(fn(box: &@RenderBox) -> ()) -> ();
fn bubble_widths_block(ctx: &LayoutContext); fn bubble_widths_block(@self, ctx: &LayoutContext);
fn assign_widths_block(ctx: &LayoutContext); fn assign_widths_block(@self, ctx: &LayoutContext);
fn assign_height_block(ctx: &LayoutContext); fn assign_height_block(@self, ctx: &LayoutContext);
fn build_display_list_block(a: &dl::DisplayListBuilder, b: &Rect<au>, fn build_display_list_block(@self, a: &dl::DisplayListBuilder, b: &Rect<au>,
c: &Point2D<au>, d: &dl::DisplayList); c: &Point2D<au>, d: &dl::DisplayList);
} }
@ -66,14 +66,14 @@ impl FlowContext : BlockLayout {
/* TODO: floats */ /* TODO: floats */
/* TODO: absolute contexts */ /* TODO: absolute contexts */
/* TODO: inline-blocks */ /* TODO: inline-blocks */
fn bubble_widths_block(ctx: &LayoutContext) { fn bubble_widths_block(@self, ctx: &LayoutContext) {
assert self.starts_block_flow(); assert self.starts_block_flow();
let mut min_width = au(0); let mut min_width = au(0);
let mut pref_width = au(0); let mut pref_width = au(0);
/* find max width from child block contexts */ /* find max width from child block contexts */
for FlowTree.each_child(@self) |child_ctx| { for FlowTree.each_child(self) |child_ctx| {
assert child_ctx.starts_block_flow() || child_ctx.starts_inline_flow(); assert child_ctx.starts_block_flow() || child_ctx.starts_inline_flow();
min_width = au::max(min_width, child_ctx.d().min_width); min_width = au::max(min_width, child_ctx.d().min_width);
@ -98,7 +98,7 @@ impl FlowContext : BlockLayout {
Dual boxes consume some width first, and the remainder is assigned to Dual boxes consume some width first, and the remainder is assigned to
all child (block) contexts. */ all child (block) contexts. */
fn assign_widths_block(_ctx: &LayoutContext) { fn assign_widths_block(@self, _ctx: &LayoutContext) {
assert self.starts_block_flow(); assert self.starts_block_flow();
let mut remaining_width = self.d().position.size.width; let mut remaining_width = self.d().position.size.width;
@ -113,19 +113,19 @@ impl FlowContext : BlockLayout {
remaining_width = remaining_width.sub(&left_used.add(&right_used)); remaining_width = remaining_width.sub(&left_used.add(&right_used));
} }
for FlowTree.each_child(@self) |child_ctx| { for FlowTree.each_child(self) |child_ctx| {
assert child_ctx.starts_block_flow() || child_ctx.starts_inline_flow(); assert child_ctx.starts_block_flow() || child_ctx.starts_inline_flow();
child_ctx.d().position.origin.x = left_used; child_ctx.d().position.origin.x = left_used;
child_ctx.d().position.size.width = remaining_width; child_ctx.d().position.size.width = remaining_width;
} }
} }
fn assign_height_block(_ctx: &LayoutContext) { fn assign_height_block(@self, _ctx: &LayoutContext) {
assert self.starts_block_flow(); assert self.starts_block_flow();
let mut cur_y = au(0); let mut cur_y = au(0);
for FlowTree.each_child(@self) |child_ctx| { for FlowTree.each_child(self) |child_ctx| {
child_ctx.d().position.origin.y = cur_y; child_ctx.d().position.origin.y = cur_y;
cur_y = cur_y.add(&child_ctx.d().position.size.height); cur_y = cur_y.add(&child_ctx.d().position.size.height);
} }
@ -142,7 +142,7 @@ impl FlowContext : BlockLayout {
} }
} }
fn build_display_list_block(builder: &dl::DisplayListBuilder, dirty: &Rect<au>, fn build_display_list_block(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) { offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_block_flow(); assert self.starts_block_flow();
@ -155,7 +155,7 @@ impl FlowContext : BlockLayout {
// TODO: handle any out-of-flow elements // TODO: handle any out-of-flow elements
// go deeper into the flow tree // go deeper into the flow tree
for FlowTree.each_child(@self) |child| { for FlowTree.each_child(self) |child| {
self.build_display_list_for_child(builder, child, dirty, offset, list) self.build_display_list_for_child(builder, child, dirty, offset, list)
} }
} }

View file

@ -87,7 +87,7 @@ struct RenderBoxData {
enum RenderBoxType { enum RenderBoxType {
RenderBox_Generic, RenderBox_Generic,
RenderBox_Image, RenderBox_Image,
RenderBox_Text RenderBox_Text,
} }
pub enum RenderBox { pub enum RenderBox {
@ -101,6 +101,8 @@ trait RenderBoxMethods {
pure fn d(&self) -> &self/RenderBoxData; pure fn d(&self) -> &self/RenderBoxData;
pure fn is_replaced() -> bool; pure fn is_replaced() -> bool;
pure fn can_split() -> bool;
pure fn can_merge_with_box(@self, other: @RenderBox) -> bool;
pure fn content_box() -> Rect<au>; pure fn content_box() -> Rect<au>;
pure fn border_box() -> Rect<au>; pure fn border_box() -> Rect<au>;
@ -141,6 +143,23 @@ impl RenderBox : RenderBoxMethods {
} }
} }
pure fn can_split() -> bool {
match self {
TextBox(*) => true,
_ => false
}
}
pure fn can_merge_with_box(@self, other: @RenderBox) -> bool {
assert !core::box::ptr_eq(self, other);
match (self, other) {
(@UnscannedTextBox(*), @UnscannedTextBox(*)) => true,
(@TextBox(_,d1), @TextBox(_,d2)) => { core::box::ptr_eq(d1.run, d2.run) }
(_, _) => false
}
}
/** In general, these functions are transitively impure because they /** In general, these functions are transitively impure because they
* may cause glyphs to be allocated. For now, it's impure because of * may cause glyphs to be allocated. For now, it's impure because of
* holder.get_image() * holder.get_image()

View file

@ -32,19 +32,19 @@ pub struct DisplayListBuilder {
trait FlowDisplayListBuilderMethods { trait FlowDisplayListBuilderMethods {
fn build_display_list(a: &DisplayListBuilder, b: &Rect<au>, c: &dl::DisplayList); fn build_display_list(@self, a: &DisplayListBuilder, b: &Rect<au>, c: &dl::DisplayList);
fn build_display_list_for_child(a: &DisplayListBuilder, b: @FlowContext, fn build_display_list_for_child(@self, a: &DisplayListBuilder, b: @FlowContext,
c: &Rect<au>, d: &Point2D<au>, e: &dl::DisplayList); c: &Rect<au>, d: &Point2D<au>, e: &dl::DisplayList);
} }
impl FlowContext: FlowDisplayListBuilderMethods { impl FlowContext: FlowDisplayListBuilderMethods {
fn build_display_list(builder: &DisplayListBuilder, dirty: &Rect<au>, list: &dl::DisplayList) { fn build_display_list(@self, builder: &DisplayListBuilder, dirty: &Rect<au>, list: &dl::DisplayList) {
let zero = au::zero_point(); let zero = au::zero_point();
self.build_display_list_recurse(builder, dirty, &zero, list); self.build_display_list_recurse(builder, dirty, &zero, list);
} }
fn build_display_list_for_child(builder: &DisplayListBuilder, child: @FlowContext, fn build_display_list_for_child(@self, builder: &DisplayListBuilder, child: @FlowContext,
dirty: &Rect<au>, offset: &Point2D<au>, dirty: &Rect<au>, offset: &Point2D<au>,
list: &dl::DisplayList) { list: &dl::DisplayList) {

View file

@ -130,39 +130,39 @@ fn FlowData(id: int) -> FlowData {
/* Flow context disambiguation methods: the verbose alternative to virtual methods */ /* Flow context disambiguation methods: the verbose alternative to virtual methods */
impl FlowContext { impl FlowContext {
fn bubble_widths(ctx: &LayoutContext) { fn bubble_widths(@self, ctx: &LayoutContext) {
match self { match self {
BlockFlow(*) => self.bubble_widths_block(ctx), @BlockFlow(*) => self.bubble_widths_block(ctx),
InlineFlow(*) => self.bubble_widths_inline(ctx), @InlineFlow(*) => self.bubble_widths_inline(ctx),
RootFlow(*) => self.bubble_widths_root(ctx), @RootFlow(*) => self.bubble_widths_root(ctx),
_ => fail fmt!("Tried to bubble_widths of flow: %?", self) _ => fail fmt!("Tried to bubble_widths of flow: %?", self)
} }
} }
fn assign_widths(ctx: &LayoutContext) { fn assign_widths(@self, ctx: &LayoutContext) {
match self { match self {
BlockFlow(*) => self.assign_widths_block(ctx), @BlockFlow(*) => self.assign_widths_block(ctx),
InlineFlow(*) => self.assign_widths_inline(ctx), @InlineFlow(*) => self.assign_widths_inline(ctx),
RootFlow(*) => self.assign_widths_root(ctx), @RootFlow(*) => self.assign_widths_root(ctx),
_ => fail fmt!("Tried to assign_widths of flow: %?", self) _ => fail fmt!("Tried to assign_widths of flow: %?", self)
} }
} }
fn assign_height(ctx: &LayoutContext) { fn assign_height(@self, ctx: &LayoutContext) {
match self { match self {
BlockFlow(*) => self.assign_height_block(ctx), @BlockFlow(*) => self.assign_height_block(ctx),
InlineFlow(*) => self.assign_height_inline(ctx), @InlineFlow(*) => self.assign_height_inline(ctx),
RootFlow(*) => self.assign_height_root(ctx), @RootFlow(*) => self.assign_height_root(ctx),
_ => fail fmt!("Tried to assign_height of flow: %?", self) _ => fail fmt!("Tried to assign_height of flow: %?", self)
} }
} }
fn build_display_list_recurse(builder: &dl::DisplayListBuilder, dirty: &Rect<au>, fn build_display_list_recurse(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) { offset: &Point2D<au>, list: &dl::DisplayList) {
match self { match self {
RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list), @RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list),
BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list), @BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list),
InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list), @InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list),
_ => fail fmt!("Tried to build_display_list_recurse of flow: %?", self) _ => fail fmt!("Tried to build_display_list_recurse of flow: %?", self)
} }
} }

View file

@ -18,8 +18,6 @@ use std::arc;
use util::tree; use util::tree;
/* /*
Tentative design: (may not line up with reality)
Lineboxes are represented as offsets into the child list, rather than Lineboxes are represented as offsets into the child list, rather than
as an object that "owns" boxes. Choosing a different set of line as an object that "owns" boxes. Choosing a different set of line
breaks requires a new list of offsets, and possibly some splitting and breaks requires a new list of offsets, and possibly some splitting and
@ -43,17 +41,129 @@ hard to try out that alternative.
type BoxRange = {start: u8, len: u8}; type BoxRange = {start: u8, len: u8};
// TODO: flesh out into TextRunScanner // stack-allocated object for scanning an inline flow into
fn build_runs_for_flow(ctx: &LayoutContext, dummy_boxes: &DVec<@RenderBox>) { // TextRun-containing TextBoxes.
for uint::range(0, dummy_boxes.len()) |i| { struct TextRunScanner {
match *dummy_boxes[i] { mut in_clump: bool,
UnscannedTextBox(d, text) => { mut clump_start: uint,
mut clump_end: uint,
flow: @FlowContext,
}
fn TextRunScanner(flow: @FlowContext) -> TextRunScanner {
TextRunScanner {
in_clump: false,
clump_start: 0,
clump_end: 0,
flow: flow,
}
}
impl TextRunScanner {
fn scan_for_runs(ctx: &LayoutContext) {
// if reused, must be reset.
assert !self.in_clump;
let in_boxes = self.flow.inline().boxes;
assert in_boxes.len() > 0;
debug!("scanning %u boxes for text runs...", in_boxes.len());
let temp_boxes = DVec();
let mut prev_box: @RenderBox = in_boxes[0];
for uint::range(0, in_boxes.len()) |i| {
debug!("considering box: %?", in_boxes[i].debug_str());
let can_coalesce_with_prev = i > 0 && boxes_can_be_coalesced(prev_box, in_boxes[i]);
match (self.in_clump, can_coalesce_with_prev) {
// start a new clump
(false, _) => { self.reset_clump_to_index(i); },
// extend clump
(true, true) => { self.clump_end = i; },
// boundary detected; flush and start new clump
(true, false) => {
self.flush_clump_to_list(ctx, &temp_boxes);
self.reset_clump_to_index(i);
}
};
prev_box = in_boxes[i];
}
// handle remaining clumps
if self.in_clump {
self.flush_clump_to_list(ctx, &temp_boxes);
}
debug!("swapping out boxes.");
// swap out old and new box list of flow
self.flow.inline().boxes.set(dvec::unwrap(temp_boxes));
debug!("new inline flow boxes:");
do self.flow.inline().boxes.each |box| {
debug!("%s", box.debug_str()); true
}
// helper functions
pure fn boxes_can_be_coalesced(a: @RenderBox, b: @RenderBox) -> bool {
assert !core::box::ptr_eq(a, b);
match (a, b) {
// TODO: check whether text styles, fonts are the same.
(@UnscannedTextBox(*), @UnscannedTextBox(*)) => a.can_merge_with_box(b),
(_, _) => false
}
}
}
fn reset_clump_to_index(i: uint) {
debug!("resetting clump to %u", i);
self.clump_start = i;
self.clump_end = i;
self.in_clump = true;
}
fn flush_clump_to_list(ctx: &LayoutContext, temp_boxes: &DVec<@RenderBox>) {
assert self.in_clump;
debug!("flushing when start=%?,end=%?", self.clump_start, self.clump_end);
let in_boxes = self.flow.inline().boxes;
let is_singleton = (self.clump_start == self.clump_end);
let is_text_clump = match in_boxes[self.clump_start] {
@UnscannedTextBox(*) => true,
_ => false
};
// TODO: repair the mapping of DOM elements to boxes if it changed.
// (the mapping does not yet exist; see Issue #103)
match (is_singleton, is_text_clump) {
(false, false) => fail ~"WAT: can't coalesce non-text boxes in flush_clump_to_list()!",
(true, false) => { temp_boxes.push(in_boxes[self.clump_start]); }
(true, true) => {
let text = in_boxes[self.clump_start].raw_text();
// TODO: use actual font for corresponding DOM node to create text run.
let run = TextRun(&*ctx.font_cache.get_test_font(), text); let run = TextRun(&*ctx.font_cache.get_test_font(), text);
let box_guts = TextBoxData(@run, 0, text.len()); let box_guts = TextBoxData(@run, 0, text.len());
dummy_boxes.set_elt(i, @TextBox(d, box_guts)); debug!("pushing when start=%?,end=%?", self.clump_start, self.clump_end);
temp_boxes.push(@TextBox(copy *in_boxes[self.clump_start].d(), box_guts));
}, },
_ => {} (false, true) => {
let mut run_str : ~str = ~"";
// TODO: is using ropes to construct the merged text any faster?
do uint::range(self.clump_start, self.clump_end+1) |i| {
run_str = str::append(run_str, in_boxes[i].raw_text()); true
}
// TODO: use actual font for corresponding DOM node to create text run.
let run = TextRun(&*ctx.font_cache.get_test_font(), move run_str);
let box_guts = TextBoxData(@run, 0, run.text.len());
debug!("pushing when start=%?,end=%?", self.clump_start, self.clump_end);
temp_boxes.push(@TextBox(copy *in_boxes[self.clump_start].d(), box_guts));
}
} }
self.in_clump = false;
} }
} }
@ -81,25 +191,30 @@ fn InlineFlowData() -> InlineFlowData {
trait InlineLayout { trait InlineLayout {
pure fn starts_inline_flow() -> bool; pure fn starts_inline_flow() -> bool;
fn bubble_widths_inline(ctx: &LayoutContext); fn bubble_widths_inline(@self, ctx: &LayoutContext);
fn assign_widths_inline(ctx: &LayoutContext); fn assign_widths_inline(@self, ctx: &LayoutContext);
fn assign_height_inline(ctx: &LayoutContext); fn assign_height_inline(@self, ctx: &LayoutContext);
fn build_display_list_inline(a: &dl::DisplayListBuilder, b: &Rect<au>, c: &Point2D<au>, d: &dl::DisplayList); fn build_display_list_inline(@self, a: &dl::DisplayListBuilder, b: &Rect<au>, c: &Point2D<au>, d: &dl::DisplayList);
} }
impl FlowContext : InlineLayout { impl FlowContext : InlineLayout {
pure fn starts_inline_flow() -> bool { match self { InlineFlow(*) => true, _ => false } } pure fn starts_inline_flow() -> bool { match self { InlineFlow(*) => true, _ => false } }
fn bubble_widths_inline(ctx: &LayoutContext) { fn bubble_widths_inline(@self, ctx: &LayoutContext) {
assert self.starts_inline_flow(); assert self.starts_inline_flow();
// TODO: this is a hack debug!("box count: %u", self.inline().boxes.len());
build_runs_for_flow(ctx, &self.inline().boxes);
let scanner = TextRunScanner(self);
scanner.scan_for_runs(ctx);
let mut min_width = au(0); let mut min_width = au(0);
let mut pref_width = au(0); let mut pref_width = au(0);
debug!("/box count: %u", self.inline().boxes.len());
for self.inline().boxes.each |box| { for self.inline().boxes.each |box| {
debug!("measuring box: %s", box.debug_str());
min_width = au::max(min_width, box.get_min_width(ctx)); min_width = au::max(min_width, box.get_min_width(ctx));
pref_width = au::max(pref_width, box.get_pref_width(ctx)); pref_width = au::max(pref_width, box.get_pref_width(ctx));
} }
@ -111,7 +226,7 @@ impl FlowContext : InlineLayout {
/* Recursively (top-down) determines the actual width of child /* Recursively (top-down) determines the actual width of child
contexts and boxes. When called on this context, the context has contexts and boxes. When called on this context, the context has
had its width set by the parent context. */ had its width set by the parent context. */
fn assign_widths_inline(ctx: &LayoutContext) { fn assign_widths_inline(@self, ctx: &LayoutContext) {
assert self.starts_inline_flow(); assert self.starts_inline_flow();
/* Perform inline flow with the available width. */ /* Perform inline flow with the available width. */
@ -170,12 +285,12 @@ impl FlowContext : InlineLayout {
// 'inline-block' box that created this flow. // 'inline-block' box that created this flow.
} }
fn assign_height_inline(_ctx: &LayoutContext) { fn assign_height_inline(@self, _ctx: &LayoutContext) {
// Don't need to set box or ctx heights, since that is done // Don't need to set box or ctx heights, since that is done
// during inline flowing. // during inline flowing.
} }
fn build_display_list_inline(builder: &dl::DisplayListBuilder, dirty: &Rect<au>, fn build_display_list_inline(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) { offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_inline_flow(); assert self.starts_inline_flow();

View file

@ -22,11 +22,12 @@ fn RootFlowData() -> RootFlowData {
trait RootLayout { trait RootLayout {
pure fn starts_root_flow() -> bool; pure fn starts_root_flow() -> bool;
fn bubble_widths_root(ctx: &LayoutContext); fn bubble_widths_root(@self, ctx: &LayoutContext);
fn assign_widths_root(ctx: &LayoutContext); fn assign_widths_root(@self, ctx: &LayoutContext);
fn assign_height_root(ctx: &LayoutContext); fn assign_height_root(@self, ctx: &LayoutContext);
fn build_display_list_root(a: &dl::DisplayListBuilder, b: &Rect<au>, c: &Point2D<au>, d: &dl::DisplayList); fn build_display_list_root(@self, a: &dl::DisplayListBuilder, b: &Rect<au>,
c: &Point2D<au>, d: &dl::DisplayList);
} }
impl FlowContext : RootLayout { impl FlowContext : RootLayout {
@ -39,25 +40,25 @@ impl FlowContext : RootLayout {
} }
/* defer to the block algorithm */ /* defer to the block algorithm */
fn bubble_widths_root(ctx: &LayoutContext) { fn bubble_widths_root(@self, ctx: &LayoutContext) {
assert self.starts_root_flow(); assert self.starts_root_flow();
self.bubble_widths_block(ctx) self.bubble_widths_block(ctx)
} }
fn assign_widths_root(ctx: &LayoutContext) { fn assign_widths_root(@self, ctx: &LayoutContext) {
assert self.starts_root_flow(); assert self.starts_root_flow();
self.d().position = copy ctx.screen_size; self.d().position = copy ctx.screen_size;
self.assign_widths_block(ctx) self.assign_widths_block(ctx)
} }
fn assign_height_root(ctx: &LayoutContext) { fn assign_height_root(@self, ctx: &LayoutContext) {
assert self.starts_root_flow(); assert self.starts_root_flow();
self.assign_height_block(ctx); self.assign_height_block(ctx);
} }
fn build_display_list_root(builder: &dl::DisplayListBuilder, dirty: &Rect<au>, fn build_display_list_root(@self, builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) { offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_root_flow(); assert self.starts_root_flow();

View file

@ -5,7 +5,7 @@ use au::au;
use geom::size::Size2D; use geom::size::Size2D;
use servo_text::text_run::TextRun; use servo_text::text_run::TextRun;
use servo_text::font_cache::FontCache; use servo_text::font_cache::FontCache;
use layout::box::{TextBox, RenderBox}; use layout::box::{TextBox, RenderBox, UnscannedTextBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
pub struct TextBoxData { pub struct TextBoxData {
@ -22,6 +22,19 @@ pub fn TextBoxData(run: @TextRun, offset: uint, length: uint) -> TextBoxData {
} }
} }
trait UnscannedMethods {
pure fn raw_text() -> ~str;
}
impl RenderBox : UnscannedMethods {
pure fn raw_text() -> ~str {
match self {
UnscannedTextBox(_, s) => copy s,
_ => fail ~"unsupported operation: box.raw_text() on non-unscanned text box."
}
}
}
/* The main reflow routine for text layout. /* The main reflow routine for text layout.
impl @RenderBox : TextLayout { impl @RenderBox : TextLayout {
fn reflow_text(ctx: &LayoutContext) { fn reflow_text(ctx: &LayoutContext) {

View file

@ -463,10 +463,10 @@ struct GlyphStore {
// Initializes the glyph store, but doesn't actually shape anything. // Initializes the glyph store, but doesn't actually shape anything.
// Use the set_glyph, set_glyphs() methods to store glyph data. // Use the set_glyph, set_glyphs() methods to store glyph data.
fn GlyphStore(text: &str) -> GlyphStore { fn GlyphStore(length: uint) -> GlyphStore {
assert text.len() > 0; assert length > 0;
let buffer = vec::from_elem(text.len(), InitialGlyphEntry()); let buffer = vec::from_elem(length, InitialGlyphEntry());
GlyphStore { GlyphStore {
entry_buffer: dvec::from_vec(buffer), entry_buffer: dvec::from_vec(buffer),

View file

@ -26,16 +26,19 @@ impl TextRun {
assert offset < self.text.len(); assert offset < self.text.len();
assert offset + length <= self.text.len(); assert offset + length <= self.text.len();
debug!("enter min_width_for_range(o=%?, l=%?)", offset, length);
let mut max_piece_width = au(0); let mut max_piece_width = au(0);
// TODO: use a real font reference // TODO: use a real font reference
let font = ctx.font_cache.get_test_font(); let font = ctx.font_cache.get_test_font();
for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| { for self.iter_indivisible_pieces_for_range(offset, length) |piece_offset, piece_len| {
let metrics = font.measure_text(&self, piece_offset, piece_len); let metrics = font.measure_text(&self, piece_offset, piece_len);
if metrics.advance > max_piece_width { if metrics.advance > max_piece_width {
max_piece_width = metrics.advance; max_piece_width = metrics.advance;
} }
}; };
debug!("exit min_width_for_range(o=%?, l=%?)", offset, length);
return max_piece_width; return max_piece_width;
} }
@ -75,6 +78,8 @@ impl TextRun {
assert offset < self.text.len(); assert offset < self.text.len();
assert offset + length <= self.text.len(); assert offset + length <= self.text.len();
debug!("enter iter_indivisible_pieces_for_range(o=%?, l=%?)", offset, length);
//TODO: need a more sophisticated model of words and possible breaks //TODO: need a more sophisticated model of words and possible breaks
let text = str::view(self.text, offset, length); let text = str::view(self.text, offset, length);
@ -117,11 +122,12 @@ impl TextRun {
} }
} }
} }
debug!("exit iter_indivisible_pieces_for_range(o=%?, l=%?)", offset, length);
} }
} }
fn TextRun(font: &Font, +text: ~str) -> TextRun { fn TextRun(font: &Font, text: ~str) -> TextRun {
let glyph_store = GlyphStore(text); let glyph_store = GlyphStore(text.len());
let run = TextRun { let run = TextRun {
text: text, text: text,
glyphs: glyph_store, glyphs: glyph_store,