diff options
author | Patrick Walton <pcwalton@mimiga.net> | 2014-03-28 13:00:36 -0700 |
---|---|---|
committer | Patrick Walton <pcwalton@mimiga.net> | 2014-04-03 14:50:56 -0700 |
commit | e6665a2f148781d427f845a441c6f3f90d2dfa11 (patch) | |
tree | 2733975589bebe6721cb3288cd3f53560e3ecaed /src | |
parent | 9874d0f9d307482b87949e58e05cb85e19955183 (diff) | |
download | servo-e6665a2f148781d427f845a441c6f3f90d2dfa11.tar.gz servo-e6665a2f148781d427f845a441c6f3f90d2dfa11.zip |
layout: Take padding into account for inline boxes
Diffstat (limited to 'src')
-rw-r--r-- | src/components/main/layout/block.rs | 33 | ||||
-rw-r--r-- | src/components/main/layout/box_.rs | 209 | ||||
-rw-r--r-- | src/components/main/layout/flow.rs | 3 | ||||
-rw-r--r-- | src/components/main/layout/inline.rs | 18 |
4 files changed, 153 insertions, 110 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs index 168ae570c04..03f9256813c 100644 --- a/src/components/main/layout/block.rs +++ b/src/components/main/layout/block.rs @@ -722,8 +722,8 @@ impl BlockFlow { /// This is where we use the preferred widths and minimum widths /// calculated in the bubble-widths traversal. fn get_shrink_to_fit_width(&self, available_width: Au) -> Au { - geometry::min(self.base.pref_width, - geometry::max(self.base.min_width, available_width)) + geometry::min(self.base.intrinsic_widths.preferred_width, + geometry::max(self.base.intrinsic_widths.minimum_width, available_width)) } /// Collect and update static y-offsets bubbled up by kids. @@ -1542,20 +1542,22 @@ impl Flow for BlockFlow { min/pref widths based on child context widths and dimensions of any boxes it is responsible for flowing. */ - /* TODO: absolute contexts */ /* TODO: inline-blocks */ fn bubble_widths(&mut self, _: &mut LayoutContext) { - let mut min_width = Au::new(0); - let mut pref_width = Au::new(0); let mut num_floats = 0; - /* find max width from child block contexts */ + // Find the maximum width from children. + let mut intrinsic_widths = IntrinsicWidths::new(); for child_ctx in self.base.child_iter() { assert!(child_ctx.is_block_flow() || child_ctx.is_inline_flow() || child_ctx.is_table_kind()); let child_base = flow::mut_base(child_ctx); - min_width = geometry::max(min_width, child_base.min_width); - pref_width = geometry::max(pref_width, child_base.pref_width); + intrinsic_widths.minimum_width = + geometry::max(intrinsic_widths.minimum_width, + child_base.intrinsic_widths.total_minimum_width()); + intrinsic_widths.preferred_width = + geometry::max(intrinsic_widths.preferred_width, + child_base.intrinsic_widths.total_preferred_width()); num_floats = num_floats + child_base.num_floats; } @@ -1566,21 +1568,22 @@ impl Flow for BlockFlow { self.base.num_floats = num_floats; } - /* if not an anonymous block context, add in block box's widths. - these widths will not include child elements, just padding etc. */ + // Add in borders, padding, and margins. for box_ in self.box_.iter() { { // Can compute border width here since it doesn't depend on anything. box_.compute_borders(box_.style()) } - let (this_minimum_width, this_preferred_width) = box_.minimum_and_preferred_widths(); - min_width = min_width + this_minimum_width; - pref_width = pref_width + this_preferred_width; + let box_intrinsic_widths = box_.intrinsic_widths(); + intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width, + box_intrinsic_widths.minimum_width); + intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width, + box_intrinsic_widths.preferred_width); + intrinsic_widths.surround_width = box_intrinsic_widths.surround_width } - self.base.min_width = min_width; - self.base.pref_width = pref_width; + self.base.intrinsic_widths = intrinsic_widths } /// Recursively (top-down) determines the actual width of child contexts and boxes. When called diff --git a/src/components/main/layout/box_.rs b/src/components/main/layout/box_.rs index 047d5eb3d77..a3da48433bd 100644 --- a/src/components/main/layout/box_.rs +++ b/src/components/main/layout/box_.rs @@ -4,17 +4,29 @@ //! The `Box` type, which represents the leaves of the layout tree. +use css::node_style::StyledNode; +use layout::construct::FlowConstructor; +use layout::context::LayoutContext; +use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor}; +use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; +use layout::flow::{Flow, FlowFlagsInfo}; +use layout::flow; +use layout::model::{Auto, IntrinsicWidths, MaybeAuto, Specified, specified}; +use layout::model; +use layout::util::OpaqueNodeMethods; +use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; + use extra::url::Url; use sync::{MutexArc, Arc}; use geom::{Point2D, Rect, Size2D, SideOffsets2D}; use geom::approxeq::ApproxEq; use gfx::color::rgb; -use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass}; -use gfx::display_list::{LineDisplayItem, LineDisplayItemClass}; -use gfx::display_list::{ImageDisplayItem, ImageDisplayItemClass}; -use gfx::display_list::{SolidColorDisplayItem, SolidColorDisplayItemClass, TextDisplayItem}; -use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags, ClipDisplayItem}; -use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection}; +use gfx::display_list::{BackgroundAndBorderLevel, BaseDisplayItem, BorderDisplayItem}; +use gfx::display_list::{BorderDisplayItemClass, ClipDisplayItem, ClipDisplayItemClass}; +use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass, LineDisplayItem}; +use gfx::display_list::{LineDisplayItemClass, OpaqueNode, SolidColorDisplayItem}; +use gfx::display_list::{SolidColorDisplayItemClass, StackingContext, TextDisplayItem}; +use gfx::display_list::{TextDisplayItemClass, TextDisplayItemFlags}; use gfx::font::FontStyle; use gfx::text::text_run::TextRun; use servo_msg::constellation_msg::{ConstellationChan, FrameRectMsg, PipelineId, SubpageId}; @@ -24,26 +36,16 @@ use servo_util::geometry::Au; use servo_util::geometry; use servo_util::range::*; use servo_util::namespace; +use servo_util::smallvec::{SmallVec, SmallVec0}; use servo_util::str::is_whitespace; - use std::cast; use std::cell::RefCell; use std::num::Zero; use style::{ComputedValues, TElement, TNode, cascade, initial_values}; use style::computed_values::{LengthOrPercentage, LengthOrPercentageOrAuto, overflow, LPA_Auto}; -use style::computed_values::{border_style, clear, font_family, line_height, position}; -use style::computed_values::{text_align, text_decoration, vertical_align, visibility, white_space}; - -use css::node_style::StyledNode; -use layout::construct::FlowConstructor; -use layout::context::LayoutContext; -use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; -use layout::floats::{ClearBoth, ClearLeft, ClearRight, ClearType}; -use layout::flow::{Flow, FlowFlagsInfo}; -use layout::flow; -use layout::model::{MaybeAuto, specified, Auto, Specified}; -use layout::util::OpaqueNode; -use layout::wrapper::{TLayoutNode, ThreadSafeLayoutNode}; +use style::computed_values::{background_attachment, background_repeat, border_style, clear}; +use style::computed_values::{font_family, line_height, position, text_align, text_decoration}; +use style::computed_values::{vertical_align, visibility, white_space}; /// Boxes (`struct Box`) are the leaves of the layout tree. They cannot position themselves. In /// general, boxes do not have a simple correspondence with CSS boxes in the specification: @@ -288,18 +290,21 @@ pub enum SplitBoxResult { } -/// data for inline boxes +/// Data for inline boxes. +/// +/// FIXME(pcwalton): Copying `InlineParentInfo` vectors all the time is really inefficient. Use +/// atomic reference counting instead. #[deriving(Clone)] pub struct InlineInfo { - parent_info: ~[InlineParentInfo], + parent_info: SmallVec0<InlineParentInfo>, baseline: Au, } impl InlineInfo { pub fn new() -> InlineInfo { InlineInfo { - parent_info: ~[], - baseline: Au::new(0), + parent_info: SmallVec0::new(), + baseline: Au(0), } } } @@ -413,17 +418,37 @@ def_noncontent!(bottom, noncontent_bottom, noncontent_inline_bottom) def_noncontent_horiz!(left, merge_noncontent_inline_left, clear_noncontent_inline_left) def_noncontent_horiz!(right, merge_noncontent_inline_right, clear_noncontent_inline_right) +/// Some DOM nodes can contribute more than one type of box. We call these boxes "sub-boxes". For +/// these nodes, this enum is used to determine which sub-box to construct for that node. +pub enum SubBoxKind { + /// The main box for this node. All DOM nodes that are rendered at all have at least a main + /// box. + MainBoxKind, +} + impl Box { - /// Constructs a new `Box` instance. - pub fn new(constructor: &mut FlowConstructor, node: &ThreadSafeLayoutNode) -> Box { + /// Constructs a new `Box` instance for the given node. + /// + /// Arguments: + /// + /// * `constructor`: The flow constructor. + /// + /// * `node`: The node to create a box for. + /// + /// * `sub_box_kind`: The kind of box to create for the node, in case this node can + /// contribute more than one type of box. See the definition of `SubBoxKind`. + pub fn new(constructor: &mut FlowConstructor, + node: &ThreadSafeLayoutNode, + sub_box_kind: SubBoxKind) + -> Box { Box { - node: OpaqueNode::from_thread_safe_layout_node(node), + node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: node.style().clone(), border_box: RefCell::new(Au::zero_rect()), border: RefCell::new(Zero::zero()), padding: RefCell::new(Zero::zero()), margin: RefCell::new(Zero::zero()), - specific: constructor.build_specific_box_info_for_node(node), + specific: constructor.build_specific_box_info_for_node(node, sub_box_kind), position_offsets: RefCell::new(Zero::zero()), inline_info: RefCell::new(None), new_line_pos: ~[], @@ -433,7 +458,7 @@ impl Box { /// Constructs a new `Box` instance from a specific info. pub fn new_from_specific_info(node: &ThreadSafeLayoutNode, specific: SpecificBoxInfo) -> Box { Box { - node: OpaqueNode::from_thread_safe_layout_node(node), + node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: node.style().clone(), border_box: RefCell::new(Au::zero_rect()), border: RefCell::new(Zero::zero()), @@ -460,7 +485,7 @@ impl Box { let (node_style, _) = cascade(&[], false, Some(node.style().get()), &initial_values(), None); Box { - node: OpaqueNode::from_thread_safe_layout_node(node), + node: OpaqueNodeMethods::from_thread_safe_layout_node(node), style: Arc::new(node_style), border_box: RefCell::new(Au::zero_rect()), border: RefCell::new(Zero::zero()), @@ -589,46 +614,45 @@ impl Box { } } - /// Returns the shared part of the width for computation of minimum and preferred width per - /// CSS 2.1. - fn guess_width(&self) -> Au { + /// Uses the style only to estimate the intrinsic widths. These may be modified for text or + /// replaced elements. + fn style_specified_intrinsic_width(&self) -> IntrinsicWidths { + let (use_margins, use_padding) = match self.specific { + GenericBox | IframeBox(_) | ImageBox(_) => (true, true), + TableBox | TableCellBox => (false, true), + TableWrapperBox => (true, false), + TableRowBox => (false, false), + ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => { + // Styles are irrelevant for these kinds of boxes. + return IntrinsicWidths::new() + } + }; + let style = self.style(); - let mut margin_left = Au::new(0); - let mut margin_right = Au::new(0); - let mut padding_left = Au::new(0); - let mut padding_right = Au::new(0); + let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero(); - match self.specific { - GenericBox | IframeBox(_) | ImageBox(_) => { - margin_left = MaybeAuto::from_style(style.Margin.get().margin_left, - Au::new(0)).specified_or_zero(); - margin_right = MaybeAuto::from_style(style.Margin.get().margin_right, - Au::new(0)).specified_or_zero(); - padding_left = self.compute_padding_length(style.Padding.get().padding_left, - Au::new(0)); - padding_right = self.compute_padding_length(style.Padding.get().padding_right, - Au::new(0)); - } - TableBox | TableCellBox => { - padding_left = self.compute_padding_length(style.Padding.get().padding_left, - Au::new(0)); - padding_right = self.compute_padding_length(style.Padding.get().padding_right, - Au::new(0)); - } - TableWrapperBox => { - margin_left = MaybeAuto::from_style(style.Margin.get().margin_left, - Au::new(0)).specified_or_zero(); - margin_right = MaybeAuto::from_style(style.Margin.get().margin_right, - Au::new(0)).specified_or_zero(); - } - TableRowBox => {} - ScannedTextBox(_) | TableColumnBox(_) | UnscannedTextBox(_) => return Au(0), + let (mut margin_left, mut margin_right) = (Au(0), Au(0)); + if use_margins { + margin_left = MaybeAuto::from_style(style.Margin.get().margin_left, + Au(0)).specified_or_zero(); + margin_right = MaybeAuto::from_style(style.Margin.get().margin_right, + Au(0)).specified_or_zero(); } - let width = MaybeAuto::from_style(style.Box.get().width, Au::new(0)).specified_or_zero(); + let (mut padding_left, mut padding_right) = (Au(0), Au(0)); + if use_padding { + padding_left = self.compute_padding_length(style.Padding.get().padding_left, Au(0)); + padding_right = self.compute_padding_length(style.Padding.get().padding_right, Au(0)); + } + + let surround_width = margin_left + margin_right + padding_left + padding_right + + self.border.get().left + self.border.get().right; - width + margin_left + margin_right + padding_left + padding_right + - self.border.get().left + self.border.get().right + IntrinsicWidths { + minimum_width: width, + preferred_width: width, + surround_width: surround_width, + } } pub fn calculate_line_height(&self, font_size: Au) -> Au { @@ -1315,7 +1339,7 @@ impl Box { let inline_info = self.inline_info.borrow(); match inline_info.get() { &Some(ref info) => { - for data in info.parent_info.rev_iter() { + for data in info.parent_info.as_slice().rev_iter() { let parent_info = FlowFlagsInfo::new(data.style.get()); flow_flags.propagate_text_decoration_from_parent(&parent_info); } @@ -1430,18 +1454,19 @@ impl Box { } _ => {} } - } - /// Returns the *minimum width* and *preferred width* of this box as defined by CSS 2.1. - pub fn minimum_and_preferred_widths(&self) -> (Au, Au) { - let guessed_width = self.guess_width(); - let (additional_minimum, additional_preferred) = match self.specific { - GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | - TableRowBox | TableWrapperBox => (Au(0), Au(0)), + /// Returns the intrinsic widths of this fragment. + pub fn intrinsic_widths(&self) -> IntrinsicWidths { + let mut result = self.style_specified_intrinsic_width(); + + match self.specific { + GenericBox | IframeBox(_) | TableBox | TableCellBox | TableColumnBox(_) | TableRowBox | + TableWrapperBox => {} ImageBox(ref image_box_info) => { let image_width = image_box_info.image_width(); - (image_width, image_width) + result.minimum_width = geometry::max(result.minimum_width, image_width); + result.preferred_width = geometry::max(result.preferred_width, image_width); } ScannedTextBox(ref text_box_info) => { let range = &text_box_info.range; @@ -1453,11 +1478,29 @@ impl Box { max_line_width = Au::max(max_line_width, line_metrics.advance_width); } - (min_line_width, max_line_width) + result.minimum_width = geometry::max(result.minimum_width, min_line_width); + result.preferred_width = geometry::max(result.preferred_width, max_line_width); } UnscannedTextBox(..) => fail!("Unscanned text boxes should have been scanned by now!"), - }; - (guessed_width + additional_minimum, guessed_width + additional_preferred) + } + + // Take borders and padding for parent inline boxes into account. + let inline_info = self.inline_info.get(); + match inline_info { + None => {} + Some(ref inline_info) => { + for inline_parent_info in inline_info.parent_info.iter() { + let border_width = inline_parent_info.border.left + + inline_parent_info.border.right; + let padding_width = inline_parent_info.padding.left + + inline_parent_info.padding.right; + result.minimum_width = result.minimum_width + border_width + padding_width; + result.preferred_width = result.preferred_width + border_width + padding_width; + } + } + } + + result } @@ -1663,16 +1706,16 @@ impl Box { } } - /// Assigns replaced width for this box only if it is replaced content. - /// - /// This assigns only the width, not margin or anything else. - /// CSS 2.1 § 10.3.2. - pub fn assign_replaced_width_if_necessary(&self,container_width: Au) { + /// Assigns replaced width, padding, and margins for this box only if it is replaced content + /// per CSS 2.1 § 10.3.2. + pub fn assign_replaced_width_if_necessary(&self, container_width: Au) { match self.specific { GenericBox | IframeBox(_) | TableBox | TableCellBox | TableRowBox | TableWrapperBox => {} ImageBox(ref image_box_info) => { - // TODO(ksh8281): compute border,margin,padding + self.compute_padding(self.style(), container_width); + + // TODO(ksh8281): compute border,margin let width = ImageBoxInfo::style_length(self.style().Box.get().width, image_box_info.dom_width, container_width); diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs index d0310d28c71..7d92d917943 100644 --- a/src/components/main/layout/flow.rs +++ b/src/components/main/layout/flow.rs @@ -814,8 +814,7 @@ impl BaseFlow { next_sibling: None, prev_sibling: Rawlink::none(), - min_width: Au::new(0), - pref_width: Au::new(0), + intrinsic_widths: IntrinsicWidths::new(), position: Au::zero_rect(), overflow: Au::zero_rect(), diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs index 84786e2d282..26466a33ac5 100644 --- a/src/components/main/layout/inline.rs +++ b/src/components/main/layout/inline.rs @@ -21,7 +21,6 @@ use gfx::display_list::{ContentLevel, StackingContext}; use servo_util::geometry::Au; use servo_util::geometry; use servo_util::range::Range; -use std::cell::RefCell; use std::mem; use std::u16; use style::computed_values::{text_align, vertical_align, white_space}; @@ -630,20 +629,19 @@ impl Flow for InlineFlow { child_base.floats = Floats::new(); } - let mut min_width = Au::new(0); - let mut pref_width = Au::new(0); - + let mut intrinsic_widths = IntrinsicWidths::new(); for box_ in self.boxes.iter() { debug!("Flow: measuring {:s}", box_.debug_str()); box_.compute_borders(box_.style()); - 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); + + let box_intrinsic_widths = box_.intrinsic_widths(); + intrinsic_widths.minimum_width = geometry::max(intrinsic_widths.minimum_width, + box_intrinsic_widths.minimum_width); + intrinsic_widths.preferred_width = geometry::max(intrinsic_widths.preferred_width, + box_intrinsic_widths.preferred_width); } - self.base.min_width = min_width; - self.base.pref_width = pref_width; + self.base.intrinsic_widths = intrinsic_widths; self.base.num_floats = num_floats; } |