auto merge of #2494 : bjz/servo/split-to-width, r=pcwalton

This will make removing the box splitting logic easier to implement. I am also using a `SplitInfo` struct for returning the split results, which should make the code more self-documenting. In the future the `TextRun` should not be returned, and `SplitInfo` should only require a single `CharIndex`, but one thing at a time...

@pcwalton r?
This commit is contained in:
bors-servo 2014-05-27 18:16:09 -04:00
commit 65d91e3950
2 changed files with 85 additions and 50 deletions

View file

@ -241,6 +241,23 @@ impl ScannedTextBoxInfo {
}
}
#[deriving(Show)]
pub struct SplitInfo {
// TODO(bjz): this should only need to be a single character index, but both values are
// currently needed for splitting in the `inline::try_append_*` functions.
pub range: Range<CharIndex>,
pub width: Au,
}
impl SplitInfo {
fn new(range: Range<CharIndex>, info: &ScannedTextBoxInfo) -> SplitInfo {
SplitInfo {
range: range,
width: info.run.advance_for_range(&range),
}
}
}
/// Data for an unscanned text box. Unscanned text boxes are the results of flow construction that
/// have not yet had their width determined.
#[deriving(Clone)]
@ -1087,12 +1104,16 @@ impl Box {
}
}
/// Split box which includes new-line character.
/// Find the split of a box that includes a new-line character.
///
/// A return value of `None` indicates that the box could not be split.
/// Otherwise the split boxes are returned. The right boxe is optional due
/// to the possibility of it being whitespace.
pub fn split_by_new_line(&self) -> Option<(Box, Option<Box>)> {
/// A return value of `None` indicates that the box is not splittable.
/// Otherwise the split information is returned. The right information is
/// optional due to the possibility of it being whitespace.
//
// TODO(bjz): The text run should be removed in the future, but it is currently needed for
// the current method of box splitting in the `inline::try_append_*` functions.
pub fn find_split_info_by_new_line(&self)
-> Option<(SplitInfo, Option<SplitInfo>, Arc<owned::Box<TextRun>> /* TODO(bjz): remove */)> {
match self.specific {
GenericBox | IframeBox(_) | ImageBox(_) | TableBox | TableCellBox |
TableRowBox | TableWrapperBox => None,
@ -1107,36 +1128,32 @@ impl Box {
text_box_info.range.length() - (cur_new_line_pos + CharIndex(1)));
// Left box is for left text of first founded new-line character.
let left_box = {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range);
let new_metrics = new_text_box_info.run.metrics_for_range(&left_range);
let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info));
new_box.new_line_pos = vec!();
new_box
};
let left_box = SplitInfo::new(left_range, text_box_info);
// Right box is for right text of first founded new-line character.
let right_box = if right_range.length() > CharIndex(0) {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range);
let new_metrics = new_text_box_info.run.metrics_for_range(&right_range);
let mut new_box = self.transform(new_metrics.bounding_box.size, ScannedTextBox(new_text_box_info));
new_box.new_line_pos = new_line_pos;
Some(new_box)
Some(SplitInfo::new(right_range, text_box_info))
} else {
None
};
Some((left_box, right_box))
Some((left_box, right_box, text_box_info.run.clone()))
}
}
}
/// Attempts to split this box so that its width is no more than `max_width`.
/// Attempts to find the split positions of a text box so that its width is
/// no more than `max_width`.
///
/// A return value of `None` indicates that the box could not be split.
/// Otherwise the split boxes are returned. The left and right boxes are
/// optional due to the possibility of them being whitespace.
pub fn split_to_width(&self, max_width: Au, starts_line: bool) -> Option<(Option<Box>, Option<Box>)> {
/// Otherwise the information pertaining to the split is returned. The left
/// and right split information are both optional due to the possibility of
/// them being whitespace.
//
// TODO(bjz): The text run should be removed in the future, but it is currently needed for
// the current method of box splitting in the `inline::try_append_*` functions.
pub fn find_split_info_for_width(&self, start: CharIndex, max_width: Au, starts_line: bool)
-> Option<(Option<SplitInfo>, Option<SplitInfo>, Arc<owned::Box<TextRun>> /* TODO(bjz): remove */)> {
match self.specific {
GenericBox | IframeBox(_) | ImageBox(_) | TableBox | TableCellBox |
TableRowBox | TableWrapperBox => None,
@ -1145,7 +1162,7 @@ impl Box {
ScannedTextBox(ref text_box_info) => {
let mut pieces_processed_count: uint = 0;
let mut remaining_width: Au = max_width;
let mut left_range = Range::new(text_box_info.range.begin(), CharIndex(0));
let mut left_range = Range::new(text_box_info.range.begin() + start, CharIndex(0));
let mut right_range: Option<Range<CharIndex>> = None;
debug!("split_to_width: splitting text box (strlen={:u}, range={}, \
@ -1217,25 +1234,14 @@ impl Box {
if (pieces_processed_count == 1 || !left_is_some) && !starts_line {
None
} else {
let left_box = if left_is_some {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), left_range);
let width = new_text_box_info.run.advance_for_range(&left_range);
let height = self.border_box.size.height;
let size = Size2D(width, height);
Some(self.transform(size, ScannedTextBox(new_text_box_info)))
let left = if left_is_some {
Some(SplitInfo::new(left_range, text_box_info))
} else {
None
None
};
let right = right_range.map(|right_range| SplitInfo::new(right_range, text_box_info));
let right_box = right_range.map(|right_range| {
let new_text_box_info = ScannedTextBoxInfo::new(text_box_info.run.clone(), right_range);
let width = new_text_box_info.run.advance_for_range(&right_range);
let height = self.border_box.size.height;
let size = Size2D(width, height);
(self.transform(size, ScannedTextBox(new_text_box_info)))
});
Some((left_box, right_box))
Some((left, right, text_box_info.run.clone()))
}
}
}

View file

@ -3,7 +3,7 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use css::node_style::StyledNode;
use layout::box_::Box;
use layout::box_::{Box, ScannedTextBox, ScannedTextBoxInfo, SplitInfo};
use layout::context::LayoutContext;
use layout::floats::{FloatLeft, Floats, PlacementInfo};
use layout::flow::{BaseFlow, FlowClass, Flow, InlineFlowClass};
@ -419,18 +419,35 @@ impl LineboxScanner {
fn try_append_to_line_by_new_line(&mut self, in_box: Box) -> bool {
if in_box.new_line_pos.len() == 0 {
// In case of box does not include new-line character
debug!("LineboxScanner: Did not find a new-line character, so pushing the box to \
the line without splitting.");
self.push_box_to_line(in_box);
true
} else {
// In case of box includes new-line character
match in_box.split_by_new_line() {
Some((left_box, Some(right_box))) => {
self.push_box_to_line(left_box);
self.work_list.push_front(right_box);
},
Some((left_box, None)) => {
self.push_box_to_line(left_box);
debug!("LineboxScanner: Found a new-line character, so splitting theline.");
match in_box.find_split_info_by_new_line() {
Some((left, right, run)) => {
// TODO(bjz): Remove box splitting
let split_box = |split: SplitInfo| {
let info = ScannedTextBoxInfo::new(run.clone(), split.range);
let specific = ScannedTextBox(info);
let size = Size2D(split.width, in_box.border_box.size.height);
in_box.transform(size, specific)
};
debug!("LineboxScanner: Pushing the box to the left of the new-line character \
to the line.");
let mut left = split_box(left);
left.new_line_pos = vec!();
self.push_box_to_line(left);
for right in right.move_iter() {
debug!("LineboxScanner: Deferring the box to the right of the new-line \
character to the line.");
let mut right = split_box(right);
right.new_line_pos = in_box.new_line_pos.clone();
self.work_list.push_front(right);
}
},
None => {
error!("LineboxScanner: This split case makes no sense!")
@ -492,7 +509,19 @@ impl LineboxScanner {
}
let available_width = green_zone.width - self.pending_line.bounds.size.width;
match in_box.split_to_width(available_width, line_is_empty) {
let split = in_box.find_split_info_for_width(CharIndex(0), available_width, line_is_empty);
match split.map(|(left, right, run)| {
// TODO(bjz): Remove box splitting
let split_box = |split: SplitInfo| {
let info = ScannedTextBoxInfo::new(run.clone(), split.range);
let specific = ScannedTextBox(info);
let size = Size2D(split.width, in_box.border_box.size.height);
in_box.transform(size, specific)
};
(left.map(|x| { debug!("LineboxScanner: Left split {}", x); split_box(x) }),
right.map(|x| { debug!("LineboxScanner: Right split {}", x); split_box(x) }))
}) {
None => {
debug!("LineboxScanner: Tried to split unsplittable render box! Deferring to next \
line. {}", in_box);