From 5c5a7a18666634e28cc002f0d905c94200bd1881 Mon Sep 17 00:00:00 2001 From: "Brian J. Burg" Date: Tue, 16 Oct 2012 21:04:35 -0700 Subject: [PATCH] Hook up linebox scanner, and fix many glitches in the scanning algorithm. --- src/servo/layout/box.rs | 67 +++++++++++++++++++++++--------------- src/servo/layout/inline.rs | 50 ++++++++++++++++++---------- src/servo/layout/text.rs | 2 ++ 3 files changed, 75 insertions(+), 44 deletions(-) diff --git a/src/servo/layout/box.rs b/src/servo/layout/box.rs index b3412f7660f..08010b4af0f 100644 --- a/src/servo/layout/box.rs +++ b/src/servo/layout/box.rs @@ -94,9 +94,10 @@ pub enum RenderBox { pub enum SplitBoxResult { CannotSplit(@RenderBox), - SplitUnnecessary(@RenderBox), - SplitDidFit(@RenderBox, @RenderBox), - SplitDidNotFit(@RenderBox, @RenderBox) + // in general, when splitting the left or right side can + // be zero length, due to leading/trailing trimmable whitespace + SplitDidFit(Option<@RenderBox>, Option<@RenderBox>), + SplitDidNotFit(Option<@RenderBox>, Option<@RenderBox>) } enum InlineSpacerSide { @@ -190,51 +191,65 @@ impl RenderBox : RenderBoxMethods { let mut left_length : uint = 0; let mut right_offset : Option = None; let mut right_length : Option = 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| { + 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 advance = metrics.advance_width; let should_continue : bool; - if advance < remaining_width { - if starts_line && data.run.range_is_trimmable_whitespace(off, len) { + if advance <= remaining_width { + 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; } else { + debug!("split_to_width: case=enlarging span"); remaining_width -= advance; left_length += len; } - should_continue = true; - } 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)); - } + } else { /* advance > remaining_width */ 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; should_continue } - assert left_length > 0; - let left_box = layout::text::adapt_textbox_with_range(self.d(), data.run, - left_offset, left_length); + let left_box = if left_length > 0 { + Some(layout::text::adapt_textbox_with_range(self.d(), data.run, + left_offset, left_length)) + } else { None }; match (right_offset, right_length) { (Some(right_off), Some(right_len)) => { - assert right_len > 0; let right_box = layout::text::adapt_textbox_with_range(self.d(), data.run, right_off, right_len); - if i == 1 { return SplitDidNotFit(left_box, right_box); } - else { return SplitDidFit(left_box, right_box); } + return if i == 1 || left_box.is_none() { + SplitDidNotFit(left_box, Some(right_box)) + } else { + SplitDidFit(left_box, Some(right_box)) + } }, - (None, None) => { return SplitUnnecessary(left_box); }, - (_, _) => fail ~"Must specify right box's offset and length, not one or other." + (_, _) => { return SplitDidFit(left_box, None); }, } }, } diff --git a/src/servo/layout/inline.rs b/src/servo/layout/inline.rs index cb98293c64f..0b33639f35c 100644 --- a/src/servo/layout/inline.rs +++ b/src/servo/layout/inline.rs @@ -307,13 +307,13 @@ impl LineboxScanner { loop { // acquire the next box to lay out from work list or box list - let cur_box = match (self.work_list.pop(), boxes.len()) { - (Some(box), _) => { + let cur_box = match self.work_list.pop() { + Some(box) => { debug!("LineboxScanner: Working with box from work list: b%d", box.d().id); box }, - (None, 0) => { break }, - (None, _) => { + None => { + if i == boxes.len() { break; } let box = boxes[i]; i += 1; debug!("LineboxScanner: Working with box from box list: b%d", box.d().id); box @@ -325,6 +325,8 @@ impl LineboxScanner { debug!("LineboxScanner: Box wasn't appended, because line %u was full.", self.line_spans.len()); 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: %?", self.line_spans.len(), self.pending_line); // set box horizontal offsets - let boxes = &self.flow.inline().boxes; let line_span = copy self.pending_line.span; let mut offset_x = au(0); // 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: %?", self.line_spans.len(), line_span); 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; 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()); 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) => { debug!("LineboxScanner: case=split box did fit; deferring remainder box."); - self.push_box_to_line(left); - self.work_list.push_head(right); + match (left, 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; }, 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.", self.line_spans.len()); // TODO: signal that horizontal overflow happened? - self.push_box_to_line(left); - self.work_list.push_head(right); + match (left, 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; } else { 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 contexts and boxes. When called on this context, the context has 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(); // initialize (content) box widths, if they haven't been @@ -520,8 +534,8 @@ impl FlowContext : InlineLayout { }; } // for boxes.each |box| - //let scanner = LineBoxScanner(self); - //scanner.scan_for_lines(ctx); + let scanner = LineboxScanner(self); + scanner.scan_for_lines(ctx); /* There are no child contexts, so stop here. */ diff --git a/src/servo/layout/text.rs b/src/servo/layout/text.rs index 05f0ec85f16..d5cf5f77505 100644 --- a/src/servo/layout/text.rs +++ b/src/servo/layout/text.rs @@ -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, 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_text_data = TextBoxData(run, offset, length); let metrics = run.metrics_for_range(offset, length);