Hook up linebox scanner, and fix many glitches in the scanning algorithm.

This commit is contained in:
Brian J. Burg 2012-10-16 21:04:35 -07:00
parent 5e7f9fc585
commit 5c5a7a1866
3 changed files with 75 additions and 44 deletions

View file

@ -94,9 +94,10 @@ pub enum RenderBox {
pub enum SplitBoxResult { pub enum SplitBoxResult {
CannotSplit(@RenderBox), CannotSplit(@RenderBox),
SplitUnnecessary(@RenderBox), // in general, when splitting the left or right side can
SplitDidFit(@RenderBox, @RenderBox), // be zero length, due to leading/trailing trimmable whitespace
SplitDidNotFit(@RenderBox, @RenderBox) SplitDidFit(Option<@RenderBox>, Option<@RenderBox>),
SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>)
} }
enum InlineSpacerSide { enum InlineSpacerSide {
@ -190,51 +191,65 @@ impl RenderBox : RenderBoxMethods {
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=%?)",
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(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(off, len);
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 {
if starts_line && data.run.range_is_trimmable_whitespace(off, len) { should_continue = true;
if starts_line && i == 0 && data.run.range_is_trimmable_whitespace(off, len) {
debug!("split_to_width: case=skipping leading trimmable whitespace");
left_offset += len; left_offset += len;
} else { } else {
debug!("split_to_width: case=enlarging span");
remaining_width -= advance; remaining_width -= advance;
left_length += len; left_length += len;
} }
should_continue = true; } else { /* advance > remaining_width */
} else if advance > remaining_width
&& data.run.range_is_trimmable_whitespace(off, len) {
// if there are still things after the trimmable whitespace, create right chunk
if off + len < data.length {
right_offset = Some(off + len);
right_length = Some(data.length - (off + len));
}
should_continue = false; should_continue = false;
} else {
right_offset = Some(off);
right_length = Some(data.length - off);
should_continue = false;
}
if data.run.range_is_trimmable_whitespace(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");
right_offset = Some(off + len);
right_length = Some((data.offset + data.length) - (off + len));
} else {
debug!("split_to_width: case=skipping trimmable trailing whitespace");
}
} else if off < data.length + data.offset {
// still things left, create right chunk
right_offset = Some(off);
right_length = Some((data.offset + data.length) - off);
debug!("split_to_width: case=splitting remainder with right span: (off=%u, len=%u)",
off, (data.offset + data.length) - off);
}
}
i += 1; i += 1;
should_continue should_continue
} }
assert left_length > 0; let left_box = if left_length > 0 {
let left_box = 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); left_offset, left_length))
} else { None };
match (right_offset, right_length) { match (right_offset, right_length) {
(Some(right_off), Some(right_len)) => { (Some(right_off), Some(right_len)) => {
assert right_len > 0;
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); right_off, right_len);
if i == 1 { return SplitDidNotFit(left_box, right_box); } return if i == 1 || left_box.is_none() {
else { return SplitDidFit(left_box, right_box); } SplitDidNotFit(left_box, Some(right_box))
} else {
SplitDidFit(left_box, Some(right_box))
}
}, },
(None, None) => { return SplitUnnecessary(left_box); }, (_, _) => { return SplitDidFit(left_box, None); },
(_, _) => fail ~"Must specify right box's offset and length, not one or other."
} }
}, },
} }

View file

@ -307,13 +307,13 @@ impl LineboxScanner {
loop { loop {
// acquire the next box to lay out from work list or box list // acquire the next box to lay out from work list or box list
let cur_box = match (self.work_list.pop(), boxes.len()) { let cur_box = match self.work_list.pop() {
(Some(box), _) => { Some(box) => {
debug!("LineboxScanner: Working with box from work list: b%d", box.d().id); debug!("LineboxScanner: Working with box from work list: b%d", box.d().id);
box box
}, },
(None, 0) => { break }, None => {
(None, _) => { if i == boxes.len() { break; }
let box = boxes[i]; i += 1; let box = boxes[i]; i += 1;
debug!("LineboxScanner: Working with box from box list: b%d", box.d().id); debug!("LineboxScanner: Working with box from box list: b%d", box.d().id);
box box
@ -325,6 +325,8 @@ impl LineboxScanner {
debug!("LineboxScanner: Box wasn't appended, because line %u was full.", debug!("LineboxScanner: Box wasn't appended, because line %u was full.",
self.line_spans.len()); self.line_spans.len());
self.flush_current_line(); self.flush_current_line();
} else {
debug!("LineboxScanner: appended a box to line %u", self.line_spans.len());
} }
} }
@ -352,7 +354,6 @@ impl LineboxScanner {
debug!("LineboxScanner: Flushing line %u: %?", debug!("LineboxScanner: Flushing line %u: %?",
self.line_spans.len(), self.pending_line); self.line_spans.len(), self.pending_line);
// set box horizontal offsets // set box horizontal offsets
let boxes = &self.flow.inline().boxes;
let line_span = copy self.pending_line.span; let line_span = copy self.pending_line.span;
let mut offset_x = au(0); let mut offset_x = au(0);
// TODO: interpretation of CSS 'text-direction' and 'text-align' // TODO: interpretation of CSS 'text-direction' and 'text-align'
@ -360,7 +361,7 @@ impl LineboxScanner {
debug!("LineboxScanner: Setting horizontal offsets for boxes in line %u range: %?", debug!("LineboxScanner: Setting horizontal offsets for boxes in line %u range: %?",
self.line_spans.len(), line_span); self.line_spans.len(), line_span);
for uint::range(line_span.start as uint, (line_span.start + line_span.len) as uint) |i| { for uint::range(line_span.start as uint, (line_span.start + line_span.len) as uint) |i| {
let box_data = &boxes[i].d(); let box_data = &self.new_boxes[i].d();
box_data.position.origin.x = offset_x; box_data.position.origin.x = offset_x;
offset_x += box_data.position.size.width; offset_x += box_data.position.size.width;
} }
@ -406,15 +407,17 @@ impl LineboxScanner {
error!("LineboxScanner: Tried to split unsplittable render box! %s", in_box.debug_str()); error!("LineboxScanner: Tried to split unsplittable render box! %s", in_box.debug_str());
return false; return false;
}, },
SplitUnnecessary(_) => {
error!("LineboxScanner: Tried to split when un-split piece was small enough! %s", in_box.debug_str());
self.push_box_to_line(in_box);
return true;
},
SplitDidFit(left, right) => { SplitDidFit(left, right) => {
debug!("LineboxScanner: case=split box did fit; deferring remainder box."); debug!("LineboxScanner: case=split box did fit; deferring remainder box.");
self.push_box_to_line(left); match (left, right) {
self.work_list.push_head(right); (Some(left_box), Some(right_box)) => {
self.push_box_to_line(left_box);
self.work_list.push_head(right_box);
},
(Some(left_box), None) => { self.push_box_to_line(left_box); }
(None, Some(right_box)) => { self.push_box_to_line(right_box); }
(None, None) => { fail ~"This split case makes no sense!" }
}
return true; return true;
}, },
SplitDidNotFit(left, right) => { SplitDidNotFit(left, right) => {
@ -422,8 +425,19 @@ impl LineboxScanner {
debug!("LineboxScanner: case=split box didn't fit and line %u is empty, so overflowing and deferring remainder box.", debug!("LineboxScanner: case=split box didn't fit and line %u is empty, so overflowing and deferring remainder box.",
self.line_spans.len()); self.line_spans.len());
// TODO: signal that horizontal overflow happened? // TODO: signal that horizontal overflow happened?
self.push_box_to_line(left); match (left, right) {
self.work_list.push_head(right); (Some(left_box), Some(right_box)) => {
self.push_box_to_line(left_box);
self.work_list.push_head(right_box);
},
(Some(left_box), None) => {
self.push_box_to_line(left_box);
}
(None, Some(right_box)) => {
self.push_box_to_line(right_box);
},
(None, None) => { fail ~"This split case makes no sense!" }
}
return true; return true;
} else { } else {
debug!("LineboxScanner: case=split box didn't fit, not appending and deferring original box."); debug!("LineboxScanner: case=split box didn't fit, not appending and deferring original box.");
@ -503,7 +517,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(@self, _ctx: &LayoutContext) { fn assign_widths_inline(@self, ctx: &LayoutContext) {
assert self.starts_inline_flow(); assert self.starts_inline_flow();
// initialize (content) box widths, if they haven't been // initialize (content) box widths, if they haven't been
@ -520,8 +534,8 @@ impl FlowContext : InlineLayout {
}; };
} // for boxes.each |box| } // for boxes.each |box|
//let scanner = LineBoxScanner(self); let scanner = LineboxScanner(self);
//scanner.scan_for_lines(ctx); scanner.scan_for_lines(ctx);
/* There are no child contexts, so stop here. */ /* There are no child contexts, so stop here. */

View file

@ -24,6 +24,8 @@ pub fn TextBoxData(run: @TextRun, offset: uint, length: uint) -> TextBoxData {
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 { offset: uint, length: uint) -> @RenderBox {
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
run.text.len(), offset, 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, offset, length);
let metrics = run.metrics_for_range(offset, length); let metrics = run.metrics_for_range(offset, length);