mirror of
https://github.com/servo/servo.git
synced 2025-08-06 06:00:15 +01:00
layout: Refactor InlineFlow::assign_height
to pull out horizontal
position calculation into a separate function.
This commit is contained in:
parent
7148a28611
commit
2624b937aa
1 changed files with 154 additions and 139 deletions
|
@ -510,6 +510,114 @@ impl InlineFlow {
|
|||
// For now, don't traverse the subtree rooted here
|
||||
true
|
||||
}
|
||||
|
||||
/// Returns the relative offset from the baseline for this box, taking into account the value
|
||||
/// of the CSS `vertical-align` property.
|
||||
///
|
||||
/// The extra boolean is set if and only if `biggest_top` and/or `biggest_bottom` were updated.
|
||||
/// That is, if the box has a `top` or `bottom` value, true is returned.
|
||||
fn relative_offset_from_baseline(cur_box: @Box,
|
||||
ascent: Au,
|
||||
parent_text_top: Au,
|
||||
parent_text_bottom: Au,
|
||||
top_from_base: &mut Au,
|
||||
bottom_from_base: &mut Au,
|
||||
biggest_top: &mut Au,
|
||||
biggest_bottom: &mut Au)
|
||||
-> (Au, bool) {
|
||||
match cur_box.vertical_align() {
|
||||
vertical_align::baseline => (-ascent, false),
|
||||
vertical_align::middle => {
|
||||
// TODO: x-height value should be used from font info.
|
||||
let xheight = Au::new(0);
|
||||
(-(xheight + cur_box.box_height()).scale_by(0.5), false)
|
||||
},
|
||||
vertical_align::sub => {
|
||||
// TODO: The proper position for subscripts should be used.
|
||||
// Lower the baseline to the proper position for subscripts
|
||||
let sub_offset = Au::new(0);
|
||||
(sub_offset - ascent, false)
|
||||
},
|
||||
vertical_align::super_ => {
|
||||
// TODO: The proper position for superscripts should be used.
|
||||
// Raise the baseline to the proper position for superscripts
|
||||
let super_offset = Au::new(0);
|
||||
(-super_offset - ascent, false)
|
||||
},
|
||||
vertical_align::text_top => {
|
||||
let box_height = *top_from_base + *bottom_from_base;
|
||||
let prev_bottom_from_base = *bottom_from_base;
|
||||
*top_from_base = parent_text_top;
|
||||
*bottom_from_base = box_height - *top_from_base;
|
||||
(*bottom_from_base - prev_bottom_from_base - ascent, false)
|
||||
},
|
||||
vertical_align::text_bottom => {
|
||||
let box_height = *top_from_base + *bottom_from_base;
|
||||
let prev_bottom_from_base = *bottom_from_base;
|
||||
*bottom_from_base = parent_text_bottom;
|
||||
*top_from_base = box_height - *bottom_from_base;
|
||||
(*bottom_from_base - prev_bottom_from_base - ascent, false)
|
||||
},
|
||||
vertical_align::top => {
|
||||
if *biggest_top < (*top_from_base + *bottom_from_base) {
|
||||
*biggest_top = *top_from_base + *bottom_from_base;
|
||||
}
|
||||
let offset_top = *top_from_base - ascent;
|
||||
(offset_top, true)
|
||||
},
|
||||
vertical_align::bottom => {
|
||||
if *biggest_bottom < (*top_from_base + *bottom_from_base) {
|
||||
*biggest_bottom = *top_from_base + *bottom_from_base;
|
||||
}
|
||||
let offset_bottom = -(*bottom_from_base + ascent);
|
||||
(offset_bottom, true)
|
||||
},
|
||||
vertical_align::Length(length) => (-(length + ascent), false),
|
||||
vertical_align::Percentage(p) => {
|
||||
let pt_size = cur_box.font_style().pt_size;
|
||||
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
|
||||
let percent_offset = line_height.scale_by(p);
|
||||
(-(percent_offset + ascent), false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets box X positions based on alignment for one line.
|
||||
fn set_horizontal_box_positions(boxes: &[@Box], line: &LineBox) {
|
||||
// Figure out how much width we have.
|
||||
let slack_width = Au::max(Au(0), line.green_zone.width - line.bounds.size.width);
|
||||
|
||||
// Get the text alignment.
|
||||
//
|
||||
// TODO(burg, issue #222): use 'text-align' property from `InlineFlow`'s block container,
|
||||
// not from the style of the first box child.
|
||||
let linebox_align = if line.range.begin() < boxes.len() {
|
||||
let first_box = boxes[line.range.begin()];
|
||||
first_box.nearest_ancestor_element().style().Text.text_align
|
||||
} else {
|
||||
// Nothing to lay out, so assume left alignment.
|
||||
text_align::left
|
||||
};
|
||||
|
||||
// Set the box x positions based on that alignment.
|
||||
let mut offset_x = line.bounds.origin.x;
|
||||
offset_x = offset_x + match linebox_align {
|
||||
// So sorry, but justified text is more complicated than shuffling linebox
|
||||
// coordinates.
|
||||
//
|
||||
// TODO(burg, issue #213): Implement `text-align: justify`.
|
||||
text_align::left | text_align::justify => Au(0),
|
||||
text_align::center => slack_width.scale_by(0.5),
|
||||
text_align::right => slack_width,
|
||||
};
|
||||
|
||||
for i in line.range.eachi() {
|
||||
let box = &boxes[i];
|
||||
let size = box.position.get().size;
|
||||
box.position.set(Rect(Point2D(offset_x, box.position.get().origin.y), size));
|
||||
offset_x = offset_x + size.width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for InlineFlow {
|
||||
|
@ -589,7 +697,8 @@ impl Flow for InlineFlow {
|
|||
fn assign_height(&mut self, _: &mut LayoutContext) {
|
||||
debug!("assign_height_inline: assigning height for flow {}", self.base.id);
|
||||
|
||||
// Divide the boxes into lines
|
||||
// Divide the boxes into lines.
|
||||
//
|
||||
// TODO(#226): Get the CSS `line-height` property from the containing block's style to
|
||||
// determine minimum linebox height.
|
||||
//
|
||||
|
@ -607,68 +716,20 @@ impl Flow for InlineFlow {
|
|||
|
||||
let mut line_height_offset = Au::new(0);
|
||||
|
||||
// Now, go through each line and lay out the boxes inside
|
||||
// Now, go through each line and lay out the boxes inside.
|
||||
for line in self.lines.mut_iter() {
|
||||
// We need to distribute extra width based on text-align.
|
||||
let mut slack_width = line.green_zone.width - line.bounds.size.width;
|
||||
if slack_width < Au::new(0) {
|
||||
slack_width = Au::new(0);
|
||||
}
|
||||
|
||||
// Get the text alignment.
|
||||
// TODO(Issue #222): use 'text-align' property from InlineFlow's
|
||||
// block container, not from the style of the first box child.
|
||||
let linebox_align = if line.range.begin() < self.boxes.len() {
|
||||
let first_box = self.boxes[line.range.begin()];
|
||||
first_box.nearest_ancestor_element().style().Text.text_align
|
||||
} else {
|
||||
// Nothing to lay out, so assume left alignment.
|
||||
text_align::left
|
||||
};
|
||||
|
||||
// Set the box x positions
|
||||
let mut offset_x = line.bounds.origin.x;
|
||||
match linebox_align {
|
||||
// So sorry, but justified text is more complicated than shuffling linebox coordinates.
|
||||
// TODO(Issue #213): implement `text-align: justify`
|
||||
text_align::left | text_align::justify => {
|
||||
for i in line.range.eachi() {
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
}
|
||||
text_align::center => {
|
||||
offset_x = offset_x + slack_width.scale_by(0.5);
|
||||
for i in line.range.eachi() {
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
}
|
||||
text_align::right => {
|
||||
offset_x = offset_x + slack_width;
|
||||
for i in line.range.eachi() {
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
}
|
||||
};
|
||||
// Lay out boxes horizontally.
|
||||
InlineFlow::set_horizontal_box_positions(self.boxes, line);
|
||||
|
||||
// Set the top y position of the current linebox.
|
||||
// `line_height_offset` is updated at the end of the previous loop.
|
||||
line.bounds.origin.y = line.bounds.origin.y + line_height_offset;
|
||||
|
||||
// Calculate the distance from baseline to the top of the linebox.
|
||||
let mut topmost = Au::new(0);
|
||||
// Calculate the distance from baseline to the bottom of the linebox.
|
||||
let mut bottommost = Au::new(0);
|
||||
|
||||
// Calculate the biggest height among boxes with 'top' value.
|
||||
let mut biggest_top = Au::new(0);
|
||||
// Calculate the biggest height among boxes with 'bottom' value.
|
||||
let mut biggest_bottom = Au::new(0);
|
||||
// Calculate the distance from baseline to the top and bottom of the linebox.
|
||||
let (mut topmost, mut bottommost) = (Au(0), Au(0));
|
||||
// Calculate the biggest height among boxes with 'top' and 'bottom' values
|
||||
// respectively.
|
||||
let (mut biggest_top, mut biggest_bottom) = (Au(0), Au(0));
|
||||
|
||||
for box_i in line.range.eachi() {
|
||||
let cur_box = self.boxes[box_i];
|
||||
|
@ -678,8 +739,8 @@ impl Flow for InlineFlow {
|
|||
ImageBox(ref image_box) => {
|
||||
let mut height = image_box.image_height(cur_box);
|
||||
|
||||
// TODO: margin, border, padding's top and bottom should be calculated in advance,
|
||||
// since baseline of image is bottom margin edge.
|
||||
// TODO: margin, border, padding's top and bottom should be calculated in
|
||||
// advance, since baseline of image is bottom margin edge.
|
||||
let mut top;
|
||||
let mut bottom;
|
||||
{
|
||||
|
@ -726,92 +787,45 @@ impl Flow for InlineFlow {
|
|||
fail!("Unscanned text boxes should have been scanned by now.")
|
||||
}
|
||||
};
|
||||
|
||||
let mut top_from_base = top_from_base;
|
||||
let mut bottom_from_base = bottom_from_base;
|
||||
|
||||
// To calculate text-top and text-bottom value of 'vertical-align',
|
||||
// we should find the top and bottom of the content area of parent box.
|
||||
// The content area is defined in "http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced".
|
||||
// TODO: We should extract em-box info from font size of parent
|
||||
// and calcuate the distances from baseline to the top and the bottom of parent's content area.
|
||||
// The content area is defined in:
|
||||
// http://www.w3.org/TR/CSS2/visudet.html#inline-non-replaced
|
||||
//
|
||||
// TODO: We should extract em-box info from the font size of the parent and
|
||||
// calculate the distances from the baseline to the top and the bottom of the
|
||||
// parent's content area.
|
||||
|
||||
// It should calculate the distance from baseline to the top of parent's content area.
|
||||
// But, it is assumed now as font size of parent.
|
||||
// We should calculate the distance from baseline to the top of parent's content
|
||||
// area. But for now we assume it's the parent's font size.
|
||||
let mut parent_text_top;
|
||||
// It should calculate the distance from baseline to the bottom of parent's content area.
|
||||
// But, it is assumed now as 0.
|
||||
let parent_text_bottom = Au::new(0);
|
||||
// Get parent node
|
||||
let parent = cur_box.node.parent_node();
|
||||
|
||||
// We should calculate the distance from baseline to the bottom of the parent's
|
||||
// content area. But for now we assume it's zero.
|
||||
let parent_text_bottom = Au::new(0);
|
||||
|
||||
let parent = cur_box.node.parent_node();
|
||||
let font_size = parent.unwrap().style().Font.font_size;
|
||||
parent_text_top = font_size;
|
||||
|
||||
// This flag decides whether topmost and bottommost are updated or not.
|
||||
// That is, if the box has top or bottom value, no_update_flag becomes true.
|
||||
let mut no_update_flag = false;
|
||||
// Calculate a relative offset from baseline.
|
||||
let offset = match cur_box.vertical_align() {
|
||||
vertical_align::baseline => {
|
||||
-ascent
|
||||
},
|
||||
vertical_align::middle => {
|
||||
// TODO: x-height value should be used from font info.
|
||||
let xheight = Au::new(0);
|
||||
-(xheight + cur_box.box_height()).scale_by(0.5)
|
||||
},
|
||||
vertical_align::sub => {
|
||||
// TODO: The proper position for subscripts should be used.
|
||||
// Lower the baseline to the proper position for subscripts
|
||||
let sub_offset = Au::new(0);
|
||||
(sub_offset - ascent)
|
||||
},
|
||||
vertical_align::super_ => {
|
||||
// TODO: The proper position for superscripts should be used.
|
||||
// Raise the baseline to the proper position for superscripts
|
||||
let super_offset = Au::new(0);
|
||||
(-super_offset - ascent)
|
||||
},
|
||||
vertical_align::text_top => {
|
||||
let box_height = top_from_base + bottom_from_base;
|
||||
let prev_bottom_from_base = bottom_from_base;
|
||||
top_from_base = parent_text_top;
|
||||
bottom_from_base = box_height - top_from_base;
|
||||
(bottom_from_base - prev_bottom_from_base - ascent)
|
||||
},
|
||||
vertical_align::text_bottom => {
|
||||
let box_height = top_from_base + bottom_from_base;
|
||||
let prev_bottom_from_base = bottom_from_base;
|
||||
bottom_from_base = parent_text_bottom;
|
||||
top_from_base = box_height - bottom_from_base;
|
||||
(bottom_from_base - prev_bottom_from_base - ascent)
|
||||
},
|
||||
vertical_align::top => {
|
||||
if biggest_top < (top_from_base + bottom_from_base) {
|
||||
biggest_top = top_from_base + bottom_from_base;
|
||||
}
|
||||
let offset_top = top_from_base - ascent;
|
||||
no_update_flag = true;
|
||||
offset_top
|
||||
},
|
||||
vertical_align::bottom => {
|
||||
if biggest_bottom < (top_from_base + bottom_from_base) {
|
||||
biggest_bottom = top_from_base + bottom_from_base;
|
||||
}
|
||||
let offset_bottom = -(bottom_from_base + ascent);
|
||||
no_update_flag = true;
|
||||
offset_bottom
|
||||
},
|
||||
vertical_align::Length(length) => {
|
||||
-(length + ascent)
|
||||
},
|
||||
vertical_align::Percentage(p) => {
|
||||
let pt_size = cur_box.font_style().pt_size;
|
||||
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
|
||||
let percent_offset = line_height.scale_by(p);
|
||||
-(percent_offset + ascent)
|
||||
}
|
||||
};
|
||||
// Calculate a relative offset from the baseline.
|
||||
//
|
||||
// The no-update flag decides whether `biggest_top` and `biggest_bottom` are
|
||||
// updated or not. That is, if the box has a `top` or `bottom` value,
|
||||
// `no_update_flag` becomes true.
|
||||
let (offset, no_update_flag) =
|
||||
InlineFlow::relative_offset_from_baseline(cur_box,
|
||||
ascent,
|
||||
parent_text_top,
|
||||
parent_text_bottom,
|
||||
&mut top_from_base,
|
||||
&mut bottom_from_base,
|
||||
&mut biggest_top,
|
||||
&mut biggest_bottom);
|
||||
|
||||
// If the current box has 'top' or 'bottom' value, no_update_flag is true.
|
||||
// Otherwise, topmost and bottomost are updated.
|
||||
|
@ -825,15 +839,15 @@ impl Flow for InlineFlow {
|
|||
cur_box.position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
|
||||
}
|
||||
|
||||
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
|
||||
// Then, if necessary, update the topmost.
|
||||
// Calculate the distance from baseline to the top of the biggest box with 'bottom'
|
||||
// value. Then, if necessary, update the topmost.
|
||||
let topmost_of_bottom = biggest_bottom - bottommost;
|
||||
if topmost_of_bottom > topmost {
|
||||
topmost = topmost_of_bottom;
|
||||
}
|
||||
|
||||
// Calculate the distance from baseline to the bottom of the biggest box with 'top' value.
|
||||
// Then, if necessary, update the bottommost.
|
||||
// Calculate the distance from baseline to the bottom of the biggest box with 'top'
|
||||
// value. Then, if necessary, update the bottommost.
|
||||
let bottommost_of_top = biggest_top - topmost;
|
||||
if bottommost_of_top > bottommost {
|
||||
bottommost = bottommost_of_top;
|
||||
|
@ -856,7 +870,8 @@ impl Flow for InlineFlow {
|
|||
}
|
||||
|
||||
// This is used to set the top y position of the next linebox in the next loop.
|
||||
line_height_offset = line_height_offset + topmost + bottommost - line.bounds.size.height;
|
||||
line_height_offset = line_height_offset + topmost + bottommost -
|
||||
line.bounds.size.height;
|
||||
line.bounds.size.height = topmost + bottommost;
|
||||
} // End of `lines.each` loop.
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue