diff options
Diffstat (limited to 'src/components/main/layout/inline.rs')
-rw-r--r-- | src/components/main/layout/inline.rs | 509 |
1 files changed, 245 insertions, 264 deletions
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index cad2ec09a2f..037b782edf0 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -3,28 +3,27 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use css::node_style::StyledNode; -use std::cell::Cell; use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox}; -use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass}; +use layout::box::{RenderBoxUtils, SplitDidFit, SplitDidNotFit, TextRenderBoxClass}; use layout::context::LayoutContext; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; -use layout::flow::{FlowContext, FlowData, InlineFlow}; +use layout::flow::{FlowClass, FlowContext, FlowData, InlineFlowClass}; +use layout::flow; use layout::float_context::FloatContext; use layout::util::{ElementMapping}; use layout::float_context::{PlacementInfo, FloatLeft}; -use std::u16; -use std::util; +use extra::container::Deque; +use extra::ringbuf::RingBuf; use geom::{Point2D, Rect, Size2D}; use gfx::display_list::DisplayList; -use servo_util::geometry::Au; -use style::computed_values::line_height; use style::computed_values::text_align; use style::computed_values::vertical_align; +use servo_util::geometry::Au; use servo_util::range::Range; -use servo_util::tree::TreeNodeRef; -use extra::container::Deque; -use extra::ringbuf::RingBuf; +use std::cell::Cell; +use std::u16; +use std::util; /* Lineboxes are represented as offsets into the child list, rather than @@ -62,13 +61,15 @@ struct LineBox { struct LineboxScanner { floats: FloatContext, - new_boxes: ~[RenderBox], - work_list: @mut RingBuf<RenderBox>, + new_boxes: ~[@RenderBox], + work_list: @mut RingBuf<@RenderBox>, pending_line: LineBox, lines: ~[LineBox], cur_y: Au, } +local_data_key!(local_linebox_scanner: LineboxScanner) + impl LineboxScanner { pub fn new(float_ctx: FloatContext) -> LineboxScanner { LineboxScanner { @@ -77,33 +78,45 @@ impl LineboxScanner { work_list: @mut RingBuf::new(), pending_line: LineBox { range: Range::empty(), - bounds: Rect(Point2D(Au(0), Au(0)), Size2D(Au(0), Au(0))), - green_zone: Size2D(Au(0), Au(0)) + bounds: Rect(Point2D(Au::new(0), Au::new(0)), Size2D(Au::new(0), Au::new(0))), + green_zone: Size2D(Au::new(0), Au::new(0)) }, lines: ~[], - cur_y: Au(0) + cur_y: Au::new(0) } } + + fn reinitialize(&mut self, float_ctx: FloatContext) { + self.floats = float_ctx; + self.new_boxes.truncate(0); + self.work_list.clear(); + self.pending_line.range = Range::empty(); + self.pending_line.bounds = Rect(Point2D(Au::new(0), Au::new(0)), + Size2D(Au::new(0), Au::new(0))); + self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0)); + self.lines.truncate(0); + self.cur_y = Au::new(0); + } pub fn floats_out(&mut self) -> FloatContext { self.floats.clone() } - fn reset_scanner(&mut self, flow: &mut InlineFlowData) { - debug!("Resetting line box scanner's state for flow f%d.", flow.common.id); + fn reset_scanner(&mut self, flow: &mut InlineFlow) { + debug!("Resetting line box scanner's state for flow f%d.", flow.base.id); self.lines = ~[]; self.new_boxes = ~[]; - self.cur_y = Au(0); + self.cur_y = Au::new(0); self.reset_linebox(); } fn reset_linebox(&mut self) { self.pending_line.range.reset(0,0); - self.pending_line.bounds = Rect(Point2D(Au(0), self.cur_y), Size2D(Au(0), Au(0))); - self.pending_line.green_zone = Size2D(Au(0), Au(0)) + self.pending_line.bounds = Rect(Point2D(Au::new(0), self.cur_y), Size2D(Au::new(0), Au::new(0))); + self.pending_line.green_zone = Size2D(Au::new(0), Au::new(0)) } - pub fn scan_for_lines(&mut self, flow: &mut InlineFlowData) { + pub fn scan_for_lines(&mut self, flow: &mut InlineFlow) { self.reset_scanner(flow); let mut i = 0u; @@ -115,11 +128,11 @@ impl LineboxScanner { break } let box = flow.boxes[i]; i += 1; - debug!("LineboxScanner: Working with box from box list: b%d", box.id()); + debug!("LineboxScanner: Working with box from box list: b%d", box.base().id()); box } else { - let box = self.work_list.pop_front().unwrap(); - debug!("LineboxScanner: Working with box from work list: b%d", box.id()); + let box = self.work_list.pop_front().unwrap(); + debug!("LineboxScanner: Working with box from work list: b%d", box.base().id()); box }; @@ -139,16 +152,15 @@ impl LineboxScanner { self.flush_current_line(); } - flow.elems.repair_for_box_changes(flow.boxes, self.new_boxes); self.swap_out_results(flow); } - fn swap_out_results(&mut self, flow: &mut InlineFlowData) { + fn swap_out_results(&mut self, flow: &mut InlineFlow) { debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d", self.lines.len(), - flow.common.id); + flow.base.id); util::swap(&mut flow.boxes, &mut self.new_boxes); util::swap(&mut flow.lines, &mut self.lines); @@ -165,47 +177,10 @@ impl LineboxScanner { self.reset_linebox(); } - fn calculate_line_height(&self, box: RenderBox, font_size: Au) -> Au { - match box.line_height() { - line_height::Normal => font_size.scale_by(1.14), - line_height::Number(l) => font_size.scale_by(l), - line_height::Length(l) => l - } - } - - fn box_height(&self, box: RenderBox) -> Au { - match box { - ImageRenderBoxClass(image_box) => { - let size = image_box.image.get_size(); - let height = Au::from_px(size.unwrap_or(Size2D(0, 0)).height); - image_box.base.position.size.height = height; - debug!("box_height: found image height: %?", height); - height - } - TextRenderBoxClass(text_box) => { - let range = &text_box.range; - let run = &text_box.run; - - // Compute the height based on the line-height and font size - let text_bounds = run.metrics_for_range(range).bounding_box; - let em_size = text_bounds.size.height; - let line_height = self.calculate_line_height(box, em_size); - - line_height - } - GenericRenderBoxClass(_) => { - Au(0) - } - _ => { - fail!(fmt!("Tried to get height of unknown Box variant: %s", box.debug_str())) - } - } - } - // FIXME(eatkinson): this assumes that the tallest box in the line determines the line height // This might not be the case with some weird text fonts. - fn new_height_for_line(&self, new_box: RenderBox) -> Au { - let box_height = self.box_height(new_box); + fn new_height_for_line(&self, new_box: &RenderBox) -> Au { + let box_height = new_box.box_height(); if box_height > self.pending_line.bounds.size.height { box_height } else { @@ -216,9 +191,12 @@ impl LineboxScanner { /// Computes the position of a line that has only the provided RenderBox. /// Returns: the bounding rect of the line's green zone (whose origin coincides /// with the line's origin) and the actual width of the first box after splitting. - fn initial_line_placement (&self, first_box: RenderBox, ceiling: Au, flow: &mut InlineFlowData) -> (Rect<Au>, Au) { + fn initial_line_placement(&self, first_box: @RenderBox, ceiling: Au, flow: &mut InlineFlow) + -> (Rect<Au>, Au) { debug!("LineboxScanner: Trying to place first box of line %?", self.lines.len()); - debug!("LineboxScanner: box size: %?", first_box.position().size); + + let first_box_size = first_box.base().position.get().size; + debug!("LineboxScanner: box size: %?", first_box_size); let splitable = first_box.can_split(); let line_is_empty: bool = self.pending_line.range.length() == 0; @@ -226,34 +204,36 @@ impl LineboxScanner { // We will move it later if it has nonzero width // and that causes problems. let placement_width = if splitable { - Au(0) + Au::new(0) } else { - first_box.position().size.width + first_box_size.width }; let mut info = PlacementInfo { width: placement_width, - height: first_box.position().size.height, + height: first_box_size.height, ceiling: ceiling, - max_width: flow.common.position.size.width, + max_width: flow.base.position.size.width, f_type: FloatLeft }; let line_bounds = self.floats.place_between_floats(&info); - debug!("LineboxScanner: found position for line: %? using placement_info: %?", line_bounds, info); + debug!("LineboxScanner: found position for line: %? using placement_info: %?", + line_bounds, + info); // Simple case: if the box fits, then we can stop here - if line_bounds.size.width > first_box.position().size.width { + if line_bounds.size.width > first_box_size.width { debug!("LineboxScanner: case=box fits"); - return (line_bounds, first_box.position().size.width); + return (line_bounds, first_box_size.width); } // If not, but we can't split the box, then we'll place // the line here and it will overflow. if !splitable { debug!("LineboxScanner: case=line doesn't fit, but is unsplittable"); - return (line_bounds, first_box.position().size.width); + return (line_bounds, first_box_size.width); } // Otherwise, try and split the box @@ -264,15 +244,15 @@ impl LineboxScanner { CannotSplit(_) => { error!("LineboxScanner: Tried to split unsplittable render box! %s", first_box.debug_str()); - return (line_bounds, first_box.position().size.width); + return (line_bounds, first_box_size.width); } SplitDidFit(left, right) => { debug!("LineboxScanner: case=box split and fit"); let actual_box_width = match (left, right) { - (Some(l_box), Some(_)) => l_box.position().size.width, - (Some(l_box), None) => l_box.position().size.width, - (None, Some(r_box)) => r_box.position().size.width, + (Some(l_box), Some(_)) => l_box.base().position.get().size.width, + (Some(l_box), None) => l_box.base().position.get().size.width, + (None, Some(r_box)) => r_box.base().position.get().size.width, (None, None) => fail!("This case makes no sense.") }; return (line_bounds, actual_box_width); @@ -284,9 +264,9 @@ impl LineboxScanner { debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down"); let actual_box_width = match (left, right) { - (Some(l_box), Some(_)) => l_box.position().size.width, - (Some(l_box), None) => l_box.position().size.width, - (None, Some(r_box)) => r_box.position().size.width, + (Some(l_box), Some(_)) => l_box.base().position.get().size.width, + (Some(l_box), None) => l_box.base().position.get().size.width, + (None, Some(r_box)) => r_box.base().position.get().size.width, (None, None) => fail!("This case makes no sense.") }; @@ -301,7 +281,7 @@ impl LineboxScanner { } /// Returns false only if we should break the line. - fn try_append_to_line(&mut self, in_box: RenderBox, flow: &mut InlineFlowData) -> bool { + fn try_append_to_line(&mut self, in_box: @RenderBox, flow: &mut InlineFlow) -> bool { let line_is_empty: bool = self.pending_line.range.length() == 0; if line_is_empty { @@ -313,7 +293,7 @@ impl LineboxScanner { debug!("LineboxScanner: Trying to append box to line %u (box size: %?, green zone: \ %?): %s", self.lines.len(), - in_box.position().size, + in_box.base().position.get().size, self.pending_line.green_zone, in_box.debug_str()); @@ -326,6 +306,8 @@ impl LineboxScanner { let new_height = self.new_height_for_line(in_box); if new_height > green_zone.height { + debug!("LineboxScanner: entering float collision avoider!"); + // Uh-oh. Adding this box is going to increase the height, // and because of that we will collide with some floats. @@ -367,9 +349,10 @@ impl LineboxScanner { // horizontally. We'll try to place the whole box on this line and break somewhere // if it doesn't fit. - let new_width = self.pending_line.bounds.size.width + in_box.position().size.width; + let new_width = self.pending_line.bounds.size.width + + in_box.base().position.get().size.width; - if(new_width <= green_zone.width){ + if new_width <= green_zone.width { debug!("LineboxScanner: case=box fits without splitting"); self.push_box_to_line(in_box); return true; @@ -441,28 +424,29 @@ impl LineboxScanner { } // unconditional push - fn push_box_to_line(&mut self, box: RenderBox) { - debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.lines.len()); + fn push_box_to_line(&mut self, box: @RenderBox) { + debug!("LineboxScanner: Pushing box b%d to line %u", box.base().id(), self.lines.len()); if self.pending_line.range.length() == 0 { assert!(self.new_boxes.len() <= (u16::max_value as uint)); self.pending_line.range.reset(self.new_boxes.len(), 0); } self.pending_line.range.extend_by(1); - self.pending_line.bounds.size.width = self.pending_line.bounds.size.width + box.position().size.width; + self.pending_line.bounds.size.width = self.pending_line.bounds.size.width + + box.base().position.get().size.width; self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height, - box.position().size.height); + box.base().position.get().size.height); self.new_boxes.push(box); } } -pub struct InlineFlowData { +pub struct InlineFlow { /// Data common to all flows. - common: FlowData, + base: FlowData, // A vec of all inline render boxes. Several boxes may // correspond to one Node/Element. - boxes: ~[RenderBox], + boxes: ~[@RenderBox], // vec of ranges into boxes that represents line positions. // these ranges are disjoint, and are the result of inline layout. // also some metadata used for positioning lines @@ -473,10 +457,10 @@ pub struct InlineFlowData { elems: ElementMapping } -impl InlineFlowData { - pub fn new(common: FlowData) -> InlineFlowData { - InlineFlowData { - common: common, +impl InlineFlow { + pub fn new(base: FlowData) -> InlineFlow { + InlineFlow { + base: base, boxes: ~[], lines: ~[], elems: ElementMapping::new(), @@ -489,87 +473,103 @@ impl InlineFlowData { } self.boxes = ~[]; } -} -pub trait InlineLayout { - fn starts_inline_flow(&self) -> bool; -} + pub fn build_display_list_inline<E:ExtraDisplayListData>(&self, + builder: &DisplayListBuilder, + dirty: &Rect<Au>, + list: &Cell<DisplayList<E>>) + -> bool { -impl InlineLayout for FlowContext { - fn starts_inline_flow(&self) -> bool { - match *self { - InlineFlow(*) => true, - _ => false + //TODO: implement inline iframe size messaging + if self.base.node.is_iframe_element() { + error!("inline iframe size messaging not implemented yet"); } + + let abs_rect = Rect(self.base.abs_position, self.base.position.size); + if !abs_rect.intersects(dirty) { + return true; + } + + // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and + // not recurse on a line if nothing in it can intersect the dirty region. + debug!("FlowContext[%d]: building display list for %u inline boxes", + self.base.id, + self.boxes.len()); + + for box in self.boxes.iter() { + box.build_display_list(builder, dirty, &self.base.abs_position, list) + } + + // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or + // should the flow be nested inside the box somehow? + + // For now, don't traverse the subtree rooted here + true } } -impl InlineFlowData { - pub fn bubble_widths_inline(&mut self, ctx: &mut LayoutContext) { +impl FlowContext for InlineFlow { + fn class(&self) -> FlowClass { + InlineFlowClass + } + + fn as_immutable_inline<'a>(&'a self) -> &'a InlineFlow { + self + } + + fn as_inline<'a>(&'a mut self) -> &'a mut InlineFlow { + self + } + + fn bubble_widths(&mut self, _: &mut LayoutContext) { let mut num_floats = 0; - for kid in self.common.child_iter() { - do kid.with_mut_base |base| { - num_floats += base.num_floats; - base.floats_in = FloatContext::new(base.num_floats); - } + for kid in self.base.child_iter() { + let child_base = flow::mut_base(*kid); + num_floats += child_base.num_floats; + child_base.floats_in = FloatContext::new(child_base.num_floats); } { let this = &mut *self; - let mut min_width = Au(0); - let mut pref_width = Au(0); + let mut min_width = Au::new(0); + let mut pref_width = Au::new(0); for box in this.boxes.iter() { - debug!("FlowContext[%d]: measuring %s", self.common.id, box.debug_str()); - min_width = Au::max(min_width, box.get_min_width(ctx)); - pref_width = Au::max(pref_width, box.get_pref_width(ctx)); + debug!("FlowContext[%d]: measuring %s", self.base.id, box.debug_str()); + let (this_minimum_width, this_preferred_width) = + box.minimum_and_preferred_widths(); + min_width = Au::max(min_width, this_minimum_width); + pref_width = Au::max(pref_width, this_preferred_width); } - this.common.min_width = min_width; - this.common.pref_width = pref_width; - this.common.num_floats = num_floats; + this.base.min_width = min_width; + this.base.pref_width = pref_width; + this.base.num_floats = num_floats; } } /// 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. - pub fn assign_widths_inline(&mut self, _: &LayoutContext) { + fn assign_widths(&mut self, _: &mut LayoutContext) { // Initialize content box widths if they haven't been initialized already. // // TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into // `RenderBox`. - debug!("assign_widths_inline: floats_in: %?", self.common.floats_in); + debug!("assign_widths_inline: floats_in: %?", self.base.floats_in); { let this = &mut *self; for &box in this.boxes.iter() { - match box { - ImageRenderBoxClass(image_box) => { - let width = box.image_width(image_box); - image_box.base.position.size.width = width; - } - TextRenderBoxClass(_) => { - // Text boxes are preinitialized. - } - GenericRenderBoxClass(generic_box) => { - // TODO(#225): There will be different cases here for `inline-block` and - // other replaced content. - // FIXME(pcwalton): This seems clownshoes; can we remove? - generic_box.position.size.width = Au::from_px(45); - } - // FIXME(pcwalton): This isn't very type safe! - _ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)), - } - } // End of for loop. + box.assign_width(); + } } - for kid in self.common.child_iter() { - do kid.with_mut_base |base| { - base.position.size.width = self.common.position.size.width; - base.is_inorder = self.common.is_inorder; - } + for kid in self.base.child_iter() { + let child_base = flow::mut_base(*kid); + child_base.position.size.width = self.base.position.size.width; + child_base.is_inorder = self.base.is_inorder; } // There are no child contexts, so stop here. @@ -580,16 +580,15 @@ impl InlineFlowData { // 'inline-block' box that created this flow before recursing. } - pub fn assign_height_inorder_inline(&mut self, ctx: &mut LayoutContext) { - for kid in self.common.child_iter() { + fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) { + for kid in self.base.child_iter() { kid.assign_height_inorder(ctx); } - self.assign_height_inline(ctx); + self.assign_height(ctx); } - pub fn assign_height_inline(&mut self, _: &LayoutContext) { - - debug!("assign_height_inline: assigning height for flow %?", self.common.id); + fn assign_height(&mut self, _: &mut LayoutContext) { + debug!("assign_height_inline: assigning height for flow %?", self.base.id); // Divide the boxes into lines // TODO(#226): Get the CSS `line-height` property from the containing block's style to @@ -597,33 +596,37 @@ impl InlineFlowData { // // TODO(#226): Get the CSS `line-height` property from each non-replaced inline element to // determine its height for computing linebox height. - debug!("assign_height_inline: floats_in: %?", self.common.floats_in); - let scanner_floats = self.common.floats_in.clone(); + // + // TODO(pcwalton): Cache the linebox scanner? + debug!("assign_height_inline: floats_in: %?", self.base.floats_in); + + let scanner_floats = self.base.floats_in.clone(); let mut scanner = LineboxScanner::new(scanner_floats); + + // Access the linebox scanner. scanner.scan_for_lines(self); - let mut line_height_offset = Au(0); + let mut line_height_offset = Au::new(0); // 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(0) { - slack_width = Au(0); + if slack_width < Au::new(0) { + slack_width = Au::new(0); } - //assert!(slack_width >= Au(0), "Too many boxes on line"); + //assert!(slack_width >= Au::new(0), "Too many boxes on line"); // 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 linebox_align = if line.range.begin() < self.boxes.len() { let first_box = self.boxes[line.range.begin()]; - linebox_align = first_box.text_align(); + first_box.base().nearest_ancestor_element().style().Text.text_align } else { // Nothing to lay out, so assume left alignment. - linebox_align = text_align::left; - } + text_align::left + }; // Set the box x positions let mut offset_x = line.bounds.origin.x; @@ -632,28 +635,25 @@ impl InlineFlowData { // TODO(Issue #213): implement `text-align: justify` text_align::left | text_align::justify => { for i in line.range.eachi() { - do self.boxes[i].with_mut_base |base| { - base.position.origin.x = offset_x; - offset_x = offset_x + base.position.size.width; - } + let box = self.boxes[i].base(); + 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() { - do self.boxes[i].with_mut_base |base| { - base.position.origin.x = offset_x; - offset_x = offset_x + base.position.size.width; - } + let box = self.boxes[i].base(); + 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() { - do self.boxes[i].with_mut_base |base| { - base.position.origin.x = offset_x; - offset_x = offset_x + base.position.size.width; - } + let box = self.boxes[i].base(); + box.position.mutate().ptr.origin.x = offset_x; + offset_x = offset_x + box.position.get().size.width; } } }; @@ -663,46 +663,53 @@ impl InlineFlowData { 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(0); + let mut topmost = Au::new(0); // Calculate the distance from baseline to the bottom of the linebox. - let mut bottommost = Au(0); + let mut bottommost = Au::new(0); // Calculate the biggest height among boxes with 'top' value. - let mut biggest_top = Au(0); + let mut biggest_top = Au::new(0); // Calculate the biggest height among boxes with 'bottom' value. - let mut biggest_bottom = Au(0); + let mut biggest_bottom = Au::new(0); for box_i in line.range.eachi() { let cur_box = self.boxes[box_i]; - let (top_from_base, bottom_from_base, ascent) = match cur_box { - ImageRenderBoxClass(image_box) => { - let mut height = cur_box.image_height(image_box); + let (top_from_base, bottom_from_base, ascent) = match cur_box.class() { + ImageRenderBoxClass => { + let image_box = cur_box.as_image_render_box(); + let mut height = image_box.image_height(); // TODO: margin, border, padding's top and bottom should be calculated in advance, // since baseline of image is bottom margin edge. - let mut top = Au(0); - let mut bottom = Au(0); - do cur_box.with_model |model| { + let mut top; + let mut bottom; + { + let model = image_box.base.model.get(); top = model.border.top + model.padding.top + model.margin.top; - bottom = model.border.bottom + model.padding.bottom + model.margin.bottom; + bottom = model.border.bottom + model.padding.bottom + + model.margin.bottom; } + let noncontent_height = top + bottom; height = height + noncontent_height; - image_box.base.position.size.height = height; - image_box.base.position.translate(&Point2D(Au(0), -height)); + + let position_ref = image_box.base.position.mutate(); + position_ref.ptr.size.height = height; + position_ref.ptr.translate(&Point2D(Au::new(0), -height)); let ascent = height + bottom; - (height, Au(0), ascent) + (height, Au::new(0), ascent) }, - TextRenderBoxClass(text_box) => { + TextRenderBoxClass => { + let text_box = cur_box.as_text_render_box(); let range = &text_box.range; let run = &text_box.run; // Compute the height based on the line-height and font size let text_bounds = run.metrics_for_range(range).bounding_box; let em_size = text_bounds.size.height; - let line_height = scanner.calculate_line_height(cur_box, em_size); + let line_height = text_box.base.calculate_line_height(em_size); // Find the top and bottom of the content area. // Those are used in text-top and text-bottom value of 'vertical-align' @@ -710,12 +717,15 @@ impl InlineFlowData { // Offset from the top of the box is 1/2 of the leading + ascent let text_offset = text_ascent + (line_height - em_size).scale_by(0.5); - text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0))); + text_bounds.translate(&Point2D(text_box.base.position.get().origin.x, + Au::new(0))); (text_offset, line_height - text_offset, text_ascent) }, - GenericRenderBoxClass(generic_box) => { - (generic_box.position.size.height, Au(0), generic_box.position.size.height) + GenericRenderBoxClass => { + let base = cur_box.base(); + let height = base.position.get().size.height; + (height, Au::new(0), height) }, // FIXME(pcwalton): This isn't very type safe! _ => { @@ -734,44 +744,40 @@ impl InlineFlowData { // 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. - let mut parent_text_top = Au(0); + 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(0); - do cur_box.with_mut_base |base| { - // Get parent node - let parent = match base.node.parent_node() { - None => base.node, - Some(parent) => parent, - }; - - let font_size = parent.style().Font.font_size; - parent_text_top = font_size; - } + let parent_text_bottom = Au::new(0); + let cur_box_base = cur_box.base(); + // Get parent node + let parent = cur_box_base.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() { + let offset = match cur_box_base.vertical_align() { vertical_align::baseline => { -ascent }, vertical_align::middle => { // TODO: x-height value should be used from font info. - let xheight = Au(0); - -(xheight + scanner.box_height(cur_box)).scale_by(0.5) + 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(0); + 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(0); + let super_offset = Au::new(0); (-super_offset - ascent) }, vertical_align::text_top => { @@ -808,8 +814,9 @@ impl InlineFlowData { -(length + ascent) }, vertical_align::Percentage(p) => { - let pt_size = cur_box.font_style().pt_size; - let line_height = scanner.calculate_line_height(cur_box, Au::from_pt(pt_size)); + let pt_size = cur_box.base().font_style().pt_size; + let line_height = cur_box.base() + .calculate_line_height(Au::from_pt(pt_size)); let percent_offset = line_height.scale_by(p); -(percent_offset + ascent) } @@ -824,9 +831,7 @@ impl InlineFlowData { bottommost = bottom_from_base; } - do cur_box.with_mut_base |base| { - base.position.origin.y = line.bounds.origin.y + offset; - } + cur_box.base().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. @@ -849,21 +854,15 @@ impl InlineFlowData { // All boxes' y position is updated following the new baseline offset. for box_i in line.range.eachi() { let cur_box = self.boxes[box_i]; - let adjust_offset = match cur_box.vertical_align() { - vertical_align::top => { - Au(0) - }, - vertical_align::bottom => { - baseline_offset + bottommost - }, - _ => { - baseline_offset - } + let cur_box_base = cur_box.base(); + let adjust_offset = match cur_box_base.vertical_align() { + vertical_align::top => Au::new(0), + vertical_align::bottom => baseline_offset + bottommost, + _ => baseline_offset, }; - do cur_box.with_mut_base |base| { - base.position.origin.y = base.position.origin.y + adjust_offset; - } + cur_box_base.position.mutate().ptr.origin.y = + cur_box_base.position.get().origin.y + adjust_offset; } // This is used to set the top y position of the next linebox in the next loop. @@ -871,48 +870,30 @@ impl InlineFlowData { line.bounds.size.height = topmost + bottommost; } // End of `lines.each` loop. - self.common.position.size.height = + self.base.position.size.height = if self.lines.len() > 0 { self.lines.last().bounds.origin.y + self.lines.last().bounds.size.height } else { - Au(0) + Au::new(0) }; - self.common.floats_out = scanner.floats_out().translate(Point2D(Au(0), - -self.common.position.size.height)); + self.base.floats_out = scanner.floats_out() + .translate(Point2D(Au::new(0), + -self.base.position.size.height)); } - pub fn build_display_list_inline<E:ExtraDisplayListData>(&self, - builder: &DisplayListBuilder, - dirty: &Rect<Au>, - list: &Cell<DisplayList<E>>) - -> bool { - - //TODO: implement inline iframe size messaging - if self.common.node.is_iframe_element() { - error!("inline iframe size messaging not implemented yet"); - } - - let abs_rect = Rect(self.common.abs_position, self.common.position.size); - if !abs_rect.intersects(dirty) { - return true; + fn collapse_margins(&mut self, + _: bool, + _: &mut bool, + _: &mut Au, + _: &mut Au, + collapsing: &mut Au, + collapsible: &mut Au) { + *collapsing = Au::new(0); + // Non-empty inline flows prevent collapsing between the previous margion and the next. + if self.base.position.size.height > Au::new(0) { + *collapsible = Au::new(0); } - - // TODO(#228): Once we form line boxes and have their cached bounds, we can be smarter and - // not recurse on a line if nothing in it can intersect the dirty region. - debug!("FlowContext[%d]: building display list for %u inline boxes", - self.common.id, - self.boxes.len()); - - for box in self.boxes.iter() { - box.build_display_list(builder, dirty, &self.common.abs_position, list) - } - - // TODO(#225): Should `inline-block` elements have flows as children of the inline flow or - // should the flow be nested inside the box somehow? - - // For now, don't traverse the subtree rooted here - true } } |