mirror of
https://github.com/servo/servo.git
synced 2025-06-24 17:14:33 +01:00
layout: Implement enough of automatic table layout to pass Acid2.
This commit is contained in:
parent
10aed5bc1f
commit
901c4483b2
9 changed files with 576 additions and 557 deletions
|
@ -1432,6 +1432,111 @@ impl BlockFlow {
|
||||||
fn get_hypothetical_top_edge(&self) -> Au {
|
fn get_hypothetical_top_edge(&self) -> Au {
|
||||||
self.base.position.origin.y
|
self.base.position.origin.y
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn set_containing_width_if_float(&mut self, containing_block_width: Au) {
|
||||||
|
if self.is_float() {
|
||||||
|
self.float.get_mut_ref().containing_width = containing_block_width;
|
||||||
|
|
||||||
|
// Parent usually sets this, but floats are never inorder
|
||||||
|
self.base.flags_info.flags.set_inorder(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn propagate_assigned_width_to_children(&mut self,
|
||||||
|
left_content_edge: Au,
|
||||||
|
content_width: Au,
|
||||||
|
opt_col_widths: Option<~[Au]>) {
|
||||||
|
let has_inorder_children = if self.is_float() {
|
||||||
|
self.base.num_floats > 0
|
||||||
|
} else {
|
||||||
|
self.base.flags_info.flags.inorder() || self.base.num_floats > 0
|
||||||
|
};
|
||||||
|
|
||||||
|
let kid_abs_cb_x_offset;
|
||||||
|
if self.is_positioned() {
|
||||||
|
match self.box_ {
|
||||||
|
Some(ref box_) => {
|
||||||
|
// Pass yourself as a new Containing Block
|
||||||
|
// The static x offset for any immediate kid flows will be the
|
||||||
|
// left padding
|
||||||
|
kid_abs_cb_x_offset = box_.padding.get().left;
|
||||||
|
}
|
||||||
|
None => fail!("BlockFlow: no principal box found"),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// For kids, the left margin edge will be at our left content edge.
|
||||||
|
// The current static offset is at our left margin
|
||||||
|
// edge. So move in to the left content edge.
|
||||||
|
kid_abs_cb_x_offset = self.base.absolute_static_x_offset + left_content_edge;
|
||||||
|
}
|
||||||
|
let kid_fixed_cb_x_offset = self.base.fixed_static_x_offset + left_content_edge;
|
||||||
|
|
||||||
|
// This value is used only for table cells.
|
||||||
|
let mut kid_left_margin_edge = left_content_edge;
|
||||||
|
|
||||||
|
// FIXME(ksh8281): avoid copy
|
||||||
|
let flags_info = self.base.flags_info.clone();
|
||||||
|
for (i, kid) in self.base.child_iter().enumerate() {
|
||||||
|
if kid.is_block_flow() {
|
||||||
|
let kid_block = kid.as_block();
|
||||||
|
kid_block.base.absolute_static_x_offset = kid_abs_cb_x_offset;
|
||||||
|
kid_block.base.fixed_static_x_offset = kid_fixed_cb_x_offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
let child_base = flow::mut_base(kid);
|
||||||
|
// Left margin edge of kid flow is at our left content edge
|
||||||
|
child_base.position.origin.x = left_content_edge;
|
||||||
|
// Width of kid flow is our content width
|
||||||
|
child_base.position.size.width = content_width;
|
||||||
|
child_base.flags_info.flags.set_inorder(has_inorder_children);
|
||||||
|
|
||||||
|
if !child_base.flags_info.flags.inorder() {
|
||||||
|
child_base.floats = Floats::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle tables.
|
||||||
|
match opt_col_widths {
|
||||||
|
Some(ref col_widths) => {
|
||||||
|
// If kid is table_rowgroup or table_row, the column widths info should be
|
||||||
|
// copied from its parent.
|
||||||
|
let kid_width;
|
||||||
|
if kid.is_table() || kid.is_table_rowgroup() || kid.is_table_row() {
|
||||||
|
*kid.col_widths() = col_widths.clone();
|
||||||
|
|
||||||
|
// Width of kid flow is our content width.
|
||||||
|
kid_width = content_width
|
||||||
|
} else if kid.is_table_cell() {
|
||||||
|
// If kid is table_cell, the x offset and width for each cell should be
|
||||||
|
// calculated from parent's column widths info.
|
||||||
|
kid_left_margin_edge = if i == 0 {
|
||||||
|
Au(0)
|
||||||
|
} else {
|
||||||
|
kid_left_margin_edge + col_widths[i-1]
|
||||||
|
};
|
||||||
|
|
||||||
|
kid_width = col_widths[i]
|
||||||
|
} else {
|
||||||
|
// Width of kid flow is our content width.
|
||||||
|
kid_width = content_width
|
||||||
|
}
|
||||||
|
|
||||||
|
let kid_base = flow::mut_base(kid);
|
||||||
|
kid_base.position.origin.x = kid_left_margin_edge;
|
||||||
|
kid_base.position.size.width = kid_width;
|
||||||
|
}
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per CSS 2.1 § 16.3.1, text decoration propagates to all children in flow.
|
||||||
|
//
|
||||||
|
// TODO(pcwalton): When we have out-of-flow children, don't unconditionally propagate.
|
||||||
|
let child_base = flow::mut_base(kid);
|
||||||
|
child_base.flags_info.propagate_text_decoration_from_parent(&flags_info);
|
||||||
|
child_base.flags_info.propagate_text_alignment_from_parent(&flags_info)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Flow for BlockFlow {
|
impl Flow for BlockFlow {
|
||||||
|
|
|
@ -26,14 +26,16 @@
|
||||||
/// similar methods.
|
/// similar methods.
|
||||||
|
|
||||||
use css::node_style::StyledNode;
|
use css::node_style::StyledNode;
|
||||||
use layout::block::{BlockFlow};
|
use layout::block::BlockFlow;
|
||||||
use layout::box_::{Box, TableRowBox, TableCellBox};
|
use layout::box_::{Box, TableRowBox, TableCellBox};
|
||||||
use layout::context::LayoutContext;
|
|
||||||
use layout::construct::OptVector;
|
use layout::construct::OptVector;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::context::LayoutContext;
|
||||||
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo, ToGfxColor};
|
||||||
use layout::floats::Floats;
|
use layout::floats::Floats;
|
||||||
|
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
|
||||||
use layout::incremental::RestyleDamage;
|
use layout::incremental::RestyleDamage;
|
||||||
use layout::inline::InlineFlow;
|
use layout::inline::InlineFlow;
|
||||||
|
use layout::model::{CollapsibleMargins, IntrinsicWidths, MarginCollapseInfo};
|
||||||
use layout::parallel::FlowParallelInfo;
|
use layout::parallel::FlowParallelInfo;
|
||||||
use layout::parallel;
|
use layout::parallel;
|
||||||
use layout::table_wrapper::TableWrapperFlow;
|
use layout::table_wrapper::TableWrapperFlow;
|
||||||
|
@ -44,24 +46,22 @@ use layout::table_row::TableRowFlow;
|
||||||
use layout::table_caption::TableCaptionFlow;
|
use layout::table_caption::TableCaptionFlow;
|
||||||
use layout::table_cell::TableCellFlow;
|
use layout::table_cell::TableCellFlow;
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
use layout::flow_list::{FlowList, Link, Rawlink, FlowListIterator, MutFlowListIterator};
|
|
||||||
|
|
||||||
use collections::Deque;
|
use collections::Deque;
|
||||||
use geom::point::Point2D;
|
|
||||||
use geom::Size2D;
|
use geom::Size2D;
|
||||||
|
use geom::point::Point2D;
|
||||||
use geom::rect::Rect;
|
use geom::rect::Rect;
|
||||||
use gfx::display_list::{ClipDisplayItemClass, DisplayListCollection, DisplayList};
|
|
||||||
use layout::display_list_builder::ToGfxColor;
|
|
||||||
use gfx::color::Color;
|
use gfx::color::Color;
|
||||||
use servo_util::smallvec::{SmallVec, SmallVec0};
|
use gfx::display_list::StackingContext;
|
||||||
|
use servo_msg::compositor_msg::LayerId;
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::smallvec::{SmallVec, SmallVec0};
|
||||||
use std::cast;
|
use std::cast;
|
||||||
use std::cell::RefCell;
|
use std::iter::Zip;
|
||||||
use std::sync::atomics::Relaxed;
|
use std::sync::atomics::Relaxed;
|
||||||
use std::vec::MutItems;
|
use std::vec::MutItems;
|
||||||
use std::iter::Zip;
|
|
||||||
use style::ComputedValues;
|
use style::ComputedValues;
|
||||||
use style::computed_values::{text_align, position};
|
use style::computed_values::{clear, position, text_align};
|
||||||
|
|
||||||
/// Virtual methods that make up a float context.
|
/// Virtual methods that make up a float context.
|
||||||
///
|
///
|
||||||
|
@ -126,6 +126,24 @@ pub trait Flow {
|
||||||
fail!("called as_table_cell() on a non-tablecell flow")
|
fail!("called as_table_cell() on a non-tablecell flow")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// If this is a table row or table rowgroup or table flow, returns column widths.
|
||||||
|
/// Fails otherwise.
|
||||||
|
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
|
||||||
|
fail!("called col_widths() on an other flow than table-row/table-rowgroup/table")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
|
||||||
|
/// Fails otherwise.
|
||||||
|
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
fail!("called col_min_widths() on an other flow than table-row/table-rowgroup/table")
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If this is a table row flow or table rowgroup flow or table flow, returns column min widths.
|
||||||
|
/// Fails otherwise.
|
||||||
|
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
fail!("called col_pref_widths() on an other flow than table-row/table-rowgroup/table")
|
||||||
|
}
|
||||||
|
|
||||||
// Main methods
|
// Main methods
|
||||||
|
|
||||||
/// Pass 1 of reflow: computes minimum and preferred widths.
|
/// Pass 1 of reflow: computes minimum and preferred widths.
|
||||||
|
@ -1094,183 +1112,62 @@ impl<'a> MutableFlowUtils for &'a mut Flow {
|
||||||
None => fail!("empty Rawlink to a descendant")
|
None => fail!("empty Rawlink to a descendant")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_root() {
|
|
||||||
for fixed_descendant_link in mut_base(self).fixed_descendants.iter() {
|
|
||||||
match fixed_descendant_link.resolve() {
|
|
||||||
Some(flow) => {
|
|
||||||
let mut kid_overflow = base(flow).overflow;
|
|
||||||
kid_overflow = kid_overflow.translate(&my_position.origin);
|
|
||||||
overflow = overflow.union(&kid_overflow)
|
|
||||||
}
|
|
||||||
None => fail!("empty Rawlink to a descendant")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
mut_base(self).overflow = overflow;
|
mut_base(self).overflow = overflow;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Push display items for current flow and its children onto `list`.
|
/// Push display items for current flow and its descendants onto the appropriate display lists
|
||||||
|
/// of the given stacking context.
|
||||||
///
|
///
|
||||||
/// For InlineFlow, add display items for all its boxes onto list`.
|
/// Arguments:
|
||||||
/// For BlockFlow, add a ClipDisplayItemClass for itself and its children,
|
|
||||||
/// plus any other display items like border.
|
|
||||||
///
|
///
|
||||||
/// `container_block_size`: Size of the Containing Block for the current
|
/// * `stacking_context`: The parent stacking context that this flow belongs to and to which
|
||||||
/// flow. This is used for relative positioning (which resolves percentage
|
/// display items will be added.
|
||||||
/// values for 'top', etc. after all Containing Block heights have been computed.)
|
///
|
||||||
/// `absolute_cb_abs_position`: Absolute position of the Containing Block
|
/// * `builder`: The display list builder, which contains information used during the entire
|
||||||
/// for the flow if it is absolutely positioned.
|
/// display list building pass.
|
||||||
fn build_display_lists<E:ExtraDisplayListData>(
|
///
|
||||||
self,
|
/// * `info`: Per-flow display list building information.
|
||||||
builder: &DisplayListBuilder,
|
fn build_display_list(self,
|
||||||
container_block_size: &Size2D<Au>,
|
stacking_context: &mut StackingContext,
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
builder: &mut DisplayListBuilder,
|
||||||
dirty: &Rect<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
mut index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> bool {
|
|
||||||
debug!("Flow: building display list");
|
debug!("Flow: building display list");
|
||||||
index = match self.class() {
|
match self.class() {
|
||||||
BlockFlowClass => self.as_block().build_display_list_block(builder,
|
BlockFlowClass => {
|
||||||
container_block_size,
|
self.as_block().build_display_list_block(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
}
|
||||||
dirty,
|
InlineFlowClass => {
|
||||||
index,
|
self.as_inline().build_display_list_inline(stacking_context, builder, info)
|
||||||
lists),
|
}
|
||||||
InlineFlowClass => self.as_inline().build_display_list_inline(builder, container_block_size, dirty, index, lists),
|
TableWrapperFlowClass => {
|
||||||
TableWrapperFlowClass => self.as_table_wrapper().build_display_list_table_wrapper(builder,
|
self.as_table_wrapper().build_display_list_table_wrapper(stacking_context,
|
||||||
container_block_size,
|
builder,
|
||||||
absolute_cb_abs_position,
|
info)
|
||||||
dirty,
|
}
|
||||||
index,
|
TableFlowClass => {
|
||||||
lists),
|
self.as_table().build_display_list_table(stacking_context, builder, info)
|
||||||
TableFlowClass => self.as_table().build_display_list_table(builder,
|
}
|
||||||
container_block_size,
|
TableRowGroupFlowClass => {
|
||||||
absolute_cb_abs_position,
|
self.as_table_rowgroup().build_display_list_table_rowgroup(stacking_context,
|
||||||
dirty,
|
builder,
|
||||||
index,
|
info)
|
||||||
lists),
|
}
|
||||||
TableRowGroupFlowClass => self.as_table_rowgroup().build_display_list_table_rowgroup(builder,
|
TableRowFlowClass => {
|
||||||
container_block_size,
|
self.as_table_row().build_display_list_table_row(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
}
|
||||||
dirty,
|
TableCaptionFlowClass => {
|
||||||
index,
|
self.as_table_caption().build_display_list_table_caption(stacking_context,
|
||||||
lists),
|
builder,
|
||||||
TableRowFlowClass => self.as_table_row().build_display_list_table_row(builder,
|
info)
|
||||||
container_block_size,
|
}
|
||||||
absolute_cb_abs_position,
|
TableCellFlowClass => {
|
||||||
dirty,
|
self.as_table_cell().build_display_list_table_cell(stacking_context, builder, info)
|
||||||
index,
|
}
|
||||||
lists),
|
TableColGroupFlowClass => {
|
||||||
TableCaptionFlowClass => self.as_table_caption().build_display_list_table_caption(builder,
|
// Nothing to do here, I guess? --pcwalton
|
||||||
container_block_size,
|
}
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty,
|
|
||||||
index,
|
|
||||||
lists),
|
|
||||||
TableCellFlowClass => self.as_table_cell().build_display_list_table_cell(builder,
|
|
||||||
container_block_size,
|
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty,
|
|
||||||
index,
|
|
||||||
lists),
|
|
||||||
TableColGroupFlowClass => index,
|
|
||||||
};
|
|
||||||
|
|
||||||
if lists.with_mut(|lists| lists.lists[index].list.len() == 0) {
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if self.is_block_container() || self.is_table_kind() {
|
|
||||||
let block = self.as_block();
|
|
||||||
let mut child_lists = DisplayListCollection::new();
|
|
||||||
child_lists.add_list(DisplayList::new());
|
|
||||||
let child_lists = RefCell::new(child_lists);
|
|
||||||
let container_block_size;
|
|
||||||
let abs_cb_position;
|
|
||||||
// TODO(pradeep): Move this into a generated CB function and stuff in Flow.
|
|
||||||
match block.box_ {
|
|
||||||
Some(ref box_) => {
|
|
||||||
// The Containing Block formed by a Block for relatively
|
|
||||||
// positioned descendants is the content box.
|
|
||||||
container_block_size = box_.content_box_size();
|
|
||||||
|
|
||||||
abs_cb_position = if block.is_positioned() {
|
|
||||||
block.base.abs_position + block.generated_cb_position()
|
|
||||||
} else {
|
|
||||||
absolute_cb_abs_position
|
|
||||||
};
|
|
||||||
}
|
|
||||||
None => fail!("Flow: block container should have a box_")
|
|
||||||
}
|
|
||||||
|
|
||||||
for kid in block.base.child_iter() {
|
|
||||||
if kid.is_absolutely_positioned() {
|
|
||||||
// All absolute flows will be handled by their CB.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
kid.build_display_lists(builder, &container_block_size,
|
|
||||||
abs_cb_position,
|
|
||||||
dirty, 0u, &child_lists);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Maybe we should handle position 'absolute' and 'fixed'
|
|
||||||
// descendants before normal descendants just in case there is a
|
|
||||||
// problem when display-list building is parallel and both the
|
|
||||||
// original parent and this flow access the same absolute flow.
|
|
||||||
// Note that this can only be done once we have paint order
|
|
||||||
// working cos currently the later boxes paint over the absolute
|
|
||||||
// and fixed boxes :|
|
|
||||||
for abs_descendant_link in block.base.abs_descendants.iter() {
|
|
||||||
match abs_descendant_link.resolve() {
|
|
||||||
Some(flow) => {
|
|
||||||
// TODO(pradeep): Send in your abs_position directly.
|
|
||||||
flow.build_display_lists(builder, &container_block_size,
|
|
||||||
abs_cb_position,
|
|
||||||
dirty, 0u, &child_lists);
|
|
||||||
}
|
|
||||||
None => fail!("empty Rawlink to a descendant")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if block.is_root() {
|
|
||||||
for fixed_descendant_link in block.base.fixed_descendants.iter() {
|
|
||||||
match fixed_descendant_link.resolve() {
|
|
||||||
Some(flow) => {
|
|
||||||
flow.build_display_lists(builder, &container_block_size,
|
|
||||||
abs_cb_position,
|
|
||||||
dirty, 0u, &child_lists);
|
|
||||||
}
|
|
||||||
None => fail!("empty Rawlink to a descendant")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut child_lists = Some(child_lists.unwrap());
|
|
||||||
// Find parent ClipDisplayItemClass and push all child display items
|
|
||||||
// under it
|
|
||||||
lists.with_mut(|lists| {
|
|
||||||
let mut child_lists = child_lists.take_unwrap();
|
|
||||||
let result = lists.lists[index].list.mut_rev_iter().position(|item| {
|
|
||||||
match *item {
|
|
||||||
ClipDisplayItemClass(ref mut item) => {
|
|
||||||
item.child_list.push_all_move(child_lists.lists.shift().unwrap().list);
|
|
||||||
true
|
|
||||||
},
|
|
||||||
_ => false,
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if result.is_none() {
|
|
||||||
fail!("fail to find parent item");
|
|
||||||
}
|
|
||||||
|
|
||||||
lists.lists.push_all_move(child_lists.lists);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Destroys the flow.
|
/// Destroys the flow.
|
||||||
|
|
|
@ -5,22 +5,21 @@
|
||||||
//! CSS table formatting contexts.
|
//! CSS table formatting contexts.
|
||||||
|
|
||||||
use layout::box_::Box;
|
use layout::box_::Box;
|
||||||
use layout::block::BlockFlow;
|
use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
|
||||||
use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution};
|
use layout::block::{WidthConstraintInput, WidthConstraintSolution};
|
||||||
use layout::construct::FlowConstructor;
|
use layout::construct::FlowConstructor;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::floats::{FloatKind};
|
use layout::floats::{FloatKind};
|
||||||
use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use layout::flow::{TableFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
use layout::table_wrapper::{TableLayout, FixedLayout, AutoLayout};
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use style::computed_values::table_layout;
|
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::DisplayListCollection;
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::geometry;
|
||||||
|
use style::computed_values::table_layout;
|
||||||
|
|
||||||
/// A table flow corresponded to the table's internal table box under a table wrapper flow.
|
/// A table flow corresponded to the table's internal table box under a table wrapper flow.
|
||||||
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper box,
|
/// The properties `position`, `float`, and `margin-*` are used on the table wrapper box,
|
||||||
|
@ -31,6 +30,12 @@ pub struct TableFlow {
|
||||||
/// Column widths
|
/// Column widths
|
||||||
col_widths: ~[Au],
|
col_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column min widths.
|
||||||
|
col_min_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column pref widths.
|
||||||
|
col_pref_widths: ~[Au],
|
||||||
|
|
||||||
/// Table-layout property
|
/// Table-layout property
|
||||||
table_layout: TableLayout,
|
table_layout: TableLayout,
|
||||||
}
|
}
|
||||||
|
@ -49,6 +54,8 @@ impl TableFlow {
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -66,6 +73,8 @@ impl TableFlow {
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -84,6 +93,8 @@ impl TableFlow {
|
||||||
TableFlow {
|
TableFlow {
|
||||||
block_flow: block_flow,
|
block_flow: block_flow,
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
table_layout: table_layout
|
table_layout: table_layout
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,61 +102,46 @@ impl TableFlow {
|
||||||
pub fn teardown(&mut self) {
|
pub fn teardown(&mut self) {
|
||||||
self.block_flow.teardown();
|
self.block_flow.teardown();
|
||||||
self.col_widths = ~[];
|
self.col_widths = ~[];
|
||||||
|
self.col_min_widths = ~[];
|
||||||
|
self.col_pref_widths = ~[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Update the corresponding value of self_widths if a value of kid_widths has larger value
|
||||||
|
/// than one of self_widths.
|
||||||
|
pub fn update_col_widths(self_widths: &mut ~[Au], kid_widths: &~[Au]) -> Au {
|
||||||
|
let mut sum_widths = Au(0);
|
||||||
|
let mut kid_widths_it = kid_widths.iter();
|
||||||
|
for self_width in self_widths.mut_iter() {
|
||||||
|
match kid_widths_it.next() {
|
||||||
|
Some(kid_width) => {
|
||||||
|
if *self_width < *kid_width {
|
||||||
|
*self_width = *kid_width;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {}
|
||||||
|
}
|
||||||
|
sum_widths = sum_widths + *self_width;
|
||||||
|
}
|
||||||
|
sum_widths
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Assign height for table flow.
|
/// Assign height for table flow.
|
||||||
///
|
///
|
||||||
|
/// TODO(pcwalton): This probably doesn't handle margin collapse right.
|
||||||
|
///
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods
|
/// methods
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_height_table_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
|
fn assign_height_table_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
|
||||||
|
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
|
||||||
let (_, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(true);
|
|
||||||
|
|
||||||
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
|
|
||||||
left_offset, top_offset);
|
|
||||||
|
|
||||||
let mut cur_y = top_offset;
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
|
||||||
let child_node = flow::mut_base(kid);
|
|
||||||
child_node.position.origin.y = cur_y;
|
|
||||||
cur_y = cur_y + child_node.position.size.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
let height = cur_y - top_offset;
|
|
||||||
|
|
||||||
let mut noncontent_height = Au::new(0);
|
|
||||||
for box_ in self.block_flow.box_.iter() {
|
|
||||||
let mut position = box_.border_box.get();
|
|
||||||
|
|
||||||
// noncontent_height = border_top/bottom + padding_top/bottom of box
|
|
||||||
noncontent_height = box_.noncontent_height();
|
|
||||||
|
|
||||||
position.origin.y = Au(0);
|
|
||||||
position.size.height = height + noncontent_height;
|
|
||||||
|
|
||||||
box_.border_box.set(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.block_flow.base.position.size.height = height + noncontent_height;
|
|
||||||
|
|
||||||
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
|
|
||||||
top_offset, bottom_offset, left_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table<E:ExtraDisplayListData>(
|
pub fn build_display_list_table(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table: same process as block flow");
|
debug!("build_display_list_table: same process as block flow");
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
self.block_flow.build_display_list_block(stacking_context, builder, info);
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -162,54 +158,95 @@ impl Flow for TableFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This function finds the specified column widths from column group and the first row.
|
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
|
||||||
/// Those are used in fixed table layout calculation.
|
&mut self.col_widths
|
||||||
/* FIXME: automatic table layout calculation */
|
}
|
||||||
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
|
||||||
let mut did_first_row = false;
|
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_min_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_pref_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
/// The specified column widths are set from column group and the first row for the fixed
|
||||||
|
/// table layout calculation.
|
||||||
|
/// The maximum min/pref widths of each column are set from the rows for the automatic
|
||||||
|
/// table layout calculation.
|
||||||
|
fn bubble_widths(&mut self, _: &mut LayoutContext) {
|
||||||
|
let mut min_width = Au(0);
|
||||||
|
let mut pref_width = Au(0);
|
||||||
|
let mut did_first_row = false;
|
||||||
|
let mut num_floats = 0;
|
||||||
|
|
||||||
/* find max width from child block contexts */
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
assert!(kid.is_proper_table_child());
|
assert!(kid.is_proper_table_child());
|
||||||
|
|
||||||
if kid.is_table_colgroup() {
|
if kid.is_table_colgroup() {
|
||||||
self.col_widths.push_all(kid.as_table_colgroup().widths);
|
self.col_widths.push_all(kid.as_table_colgroup().widths);
|
||||||
|
self.col_min_widths = self.col_widths.clone();
|
||||||
|
self.col_pref_widths = self.col_widths.clone();
|
||||||
} else if kid.is_table_rowgroup() || kid.is_table_row() {
|
} else if kid.is_table_rowgroup() || kid.is_table_row() {
|
||||||
// read column widths from table-row-group/table-row, and assign
|
// read column widths from table-row-group/table-row, and assign
|
||||||
// width=0 for the columns not defined in column-group
|
// width=0 for the columns not defined in column-group
|
||||||
// FIXME: need to read widths from either table-header-group OR
|
// FIXME: need to read widths from either table-header-group OR
|
||||||
// first table-row
|
// first table-row
|
||||||
let kid_col_widths = if kid.is_table_rowgroup() {
|
|
||||||
&kid.as_table_rowgroup().col_widths
|
|
||||||
} else {
|
|
||||||
&kid.as_table_row().col_widths
|
|
||||||
};
|
|
||||||
match self.table_layout {
|
match self.table_layout {
|
||||||
FixedLayout if !did_first_row => {
|
FixedLayout => {
|
||||||
did_first_row = true;
|
let kid_col_widths = kid.col_widths();
|
||||||
let mut child_widths = kid_col_widths.iter();
|
if !did_first_row {
|
||||||
for col_width in self.col_widths.mut_iter() {
|
did_first_row = true;
|
||||||
match child_widths.next() {
|
let mut child_widths = kid_col_widths.iter();
|
||||||
Some(child_width) => {
|
for col_width in self.col_widths.mut_iter() {
|
||||||
if *col_width == Au::new(0) {
|
match child_widths.next() {
|
||||||
*col_width = *child_width;
|
Some(child_width) => {
|
||||||
}
|
if *col_width == Au::new(0) {
|
||||||
},
|
*col_width = *child_width;
|
||||||
None => break
|
}
|
||||||
|
},
|
||||||
|
None => break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let num_child_cols = kid_col_widths.len();
|
||||||
|
let num_cols = self.col_widths.len();
|
||||||
|
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
|
||||||
|
num_cols, num_child_cols);
|
||||||
|
for i in range(num_cols, num_child_cols) {
|
||||||
|
self.col_widths.push( kid_col_widths[i] );
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => {}
|
AutoLayout => {
|
||||||
}
|
min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
|
||||||
let num_child_cols = kid_col_widths.len();
|
pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
|
||||||
let num_cols = self.col_widths.len();
|
|
||||||
debug!("colgroup has {} column(s) and child has {} column(s)", num_cols, num_child_cols);
|
// update the number of column widths from table-rows.
|
||||||
for i in range(num_cols, num_child_cols) {
|
let num_cols = self.col_min_widths.len();
|
||||||
self.col_widths.push( kid_col_widths[i] );
|
let num_child_cols = kid.col_min_widths().len();
|
||||||
|
debug!("table until the previous row has {} column(s) and this row has {} column(s)",
|
||||||
|
num_cols, num_child_cols);
|
||||||
|
for i in range(num_cols, num_child_cols) {
|
||||||
|
self.col_widths.push(Au::new(0));
|
||||||
|
let new_kid_min = kid.col_min_widths()[i];
|
||||||
|
self.col_min_widths.push( new_kid_min );
|
||||||
|
let new_kid_pref = kid.col_pref_widths()[i];
|
||||||
|
self.col_pref_widths.push( new_kid_pref );
|
||||||
|
min_width = min_width + new_kid_min;
|
||||||
|
pref_width = pref_width + new_kid_pref;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let child_base = flow::mut_base(kid);
|
||||||
|
num_floats = num_floats + child_base.num_floats;
|
||||||
}
|
}
|
||||||
self.block_flow.bubble_widths(ctx);
|
for box_ in self.block_flow.box_.iter() {
|
||||||
|
box_.compute_borders(box_.style());
|
||||||
|
}
|
||||||
|
self.block_flow.base.num_floats = num_floats;
|
||||||
|
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
|
||||||
|
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width, pref_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
||||||
|
@ -242,20 +279,25 @@ impl Flow for TableFlow {
|
||||||
content_width = box_.border_box.get().size.width - padding_and_borders;
|
content_width = box_.border_box.get().size.width - padding_and_borders;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In fixed table layout, we distribute extra space among the unspecified columns if there are
|
match self.table_layout {
|
||||||
// any, or among all the columns if all are specified.
|
FixedLayout => {
|
||||||
if (total_column_width < content_width) && (num_unspecified_widths == 0) {
|
// In fixed table layout, we distribute extra space among the unspecified columns if there are
|
||||||
let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
|
// any, or among all the columns if all are specified.
|
||||||
for col_width in self.col_widths.mut_iter() {
|
if (total_column_width < content_width) && (num_unspecified_widths == 0) {
|
||||||
*col_width = (*col_width).scale_by(ratio);
|
let ratio = content_width.to_f64().unwrap() / total_column_width.to_f64().unwrap();
|
||||||
}
|
for col_width in self.col_widths.mut_iter() {
|
||||||
} else if num_unspecified_widths != 0 {
|
*col_width = (*col_width).scale_by(ratio);
|
||||||
let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
|
}
|
||||||
for col_width in self.col_widths.mut_iter() {
|
} else if num_unspecified_widths != 0 {
|
||||||
if *col_width == Au(0) {
|
let extra_column_width = (content_width - total_column_width) / Au::new(num_unspecified_widths);
|
||||||
*col_width = extra_column_width;
|
for col_width in self.col_widths.mut_iter() {
|
||||||
|
if *col_width == Au(0) {
|
||||||
|
*col_width = extra_column_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
|
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, Some(self.col_widths.clone()));
|
||||||
|
@ -275,20 +317,6 @@ impl Flow for TableFlow {
|
||||||
self.assign_height_table_base(ctx, false);
|
self.assign_height_table_base(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS Section 8.3.1 - Collapsing Margins
|
|
||||||
// Since `margin` is not used on table box, `collapsing` and `collapsible` are set to 0
|
|
||||||
fn collapse_margins(&mut self,
|
|
||||||
_: bool,
|
|
||||||
_: &mut bool,
|
|
||||||
_: &mut Au,
|
|
||||||
_: &mut Au,
|
|
||||||
collapsing: &mut Au,
|
|
||||||
collapsible: &mut Au) {
|
|
||||||
// `margin` is not used on table box.
|
|
||||||
*collapsing = Au::new(0);
|
|
||||||
*collapsible = Au::new(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableFlow: ";
|
let txt = ~"TableFlow: ";
|
||||||
txt.append(match self.block_flow.box_ {
|
txt.append(match self.block_flow.box_ {
|
||||||
|
|
|
@ -7,14 +7,11 @@
|
||||||
use layout::block::BlockFlow;
|
use layout::block::BlockFlow;
|
||||||
use layout::construct::FlowConstructor;
|
use layout::construct::FlowConstructor;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
|
use layout::flow::{TableCaptionFlowClass, FlowClass, Flow};
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::DisplayListCollection;
|
|
||||||
use servo_util::geometry::Au;
|
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
pub struct TableCaptionFlow {
|
pub struct TableCaptionFlow {
|
||||||
|
@ -34,19 +31,12 @@ impl TableCaptionFlow {
|
||||||
self.block_flow.teardown();
|
self.block_flow.teardown();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table_caption<E:ExtraDisplayListData>(
|
pub fn build_display_list_table_caption(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table_caption: same process as block flow");
|
debug!("build_display_list_table_caption: same process as block flow");
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
self.block_flow.build_display_list_block(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,13 +76,6 @@ impl Flow for TableCaptionFlow {
|
||||||
self.block_flow.assign_height(ctx);
|
self.block_flow.assign_height(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// table-caption has margins but is not collapsed with a sibling(table)
|
|
||||||
/// or its parents(table-wrapper).
|
|
||||||
/// Therefore, margins to be collapsed do not exist.
|
|
||||||
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
|
|
||||||
_: &mut Au, _: &mut Au, _: &mut Au) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableCaptionFlow: ";
|
let txt = ~"TableCaptionFlow: ";
|
||||||
txt.append(match self.block_flow.box_ {
|
txt.append(match self.block_flow.box_ {
|
||||||
|
|
|
@ -5,16 +5,15 @@
|
||||||
//! CSS table formatting contexts.
|
//! CSS table formatting contexts.
|
||||||
|
|
||||||
use layout::box_::Box;
|
use layout::box_::Box;
|
||||||
use layout::block::{BlockFlow, WidthAndMarginsComputer};
|
use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::flow::{TableCellFlowClass, FlowClass, Flow};
|
use layout::flow::{TableCellFlowClass, FlowClass, Flow};
|
||||||
|
use layout::model::{MaybeAuto};
|
||||||
use layout::table::InternalTable;
|
use layout::table::InternalTable;
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::{DisplayListCollection};
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
|
@ -42,62 +41,23 @@ impl TableCellFlow {
|
||||||
|
|
||||||
/// Assign height for table-cell flow.
|
/// Assign height for table-cell flow.
|
||||||
///
|
///
|
||||||
|
/// TODO(pcwalton): This doesn't handle floats right.
|
||||||
|
///
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods
|
/// methods
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_height_table_cell_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
|
fn assign_height_table_cell_base(&mut self,
|
||||||
let (_, mut top_offset, bottom_offset, left_offset) = self.block_flow
|
layout_context: &mut LayoutContext,
|
||||||
.initialize_offsets(true);
|
inorder: bool) {
|
||||||
|
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse)
|
||||||
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
|
|
||||||
left_offset, top_offset);
|
|
||||||
let mut cur_y = top_offset;
|
|
||||||
// Since table cell does not have `margin`, the first child's top margin and
|
|
||||||
// the last child's bottom margin do not collapse.
|
|
||||||
self.block_flow.compute_margin_collapse(&mut cur_y,
|
|
||||||
&mut top_offset,
|
|
||||||
&mut Au(0),
|
|
||||||
&mut Au(0),
|
|
||||||
false,
|
|
||||||
false);
|
|
||||||
|
|
||||||
// CSS 2.1 § 17.5.3. Table cell box height is the minimum height required by the content.
|
|
||||||
let height = cur_y - top_offset;
|
|
||||||
|
|
||||||
// TODO(june0cho): vertical-align of table-cell should be calculated.
|
|
||||||
let mut noncontent_height = Au::new(0);
|
|
||||||
for box_ in self.block_flow.box_.iter() {
|
|
||||||
let mut position = box_.border_box.get();
|
|
||||||
|
|
||||||
// noncontent_height = border_top/bottom + padding_top/bottom of box
|
|
||||||
noncontent_height = box_.noncontent_height();
|
|
||||||
|
|
||||||
position.origin.y = Au(0);
|
|
||||||
position.size.height = height + noncontent_height;
|
|
||||||
|
|
||||||
box_.border_box.set(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.block_flow.base.position.size.height = height + noncontent_height;
|
|
||||||
|
|
||||||
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y, top_offset,
|
|
||||||
bottom_offset, left_offset);
|
|
||||||
self.block_flow.assign_height_absolute_flows(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table_cell<E:ExtraDisplayListData>(
|
pub fn build_display_list_table_cell(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
debug!("build_display_list_table: same process as block flow");
|
||||||
dirty: &Rect<Au>,
|
self.block_flow.build_display_list_block(stacking_context, builder, info)
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table_cell: same process as block flow");
|
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -117,6 +77,18 @@ impl Flow for TableCellFlow {
|
||||||
/// Minimum/preferred widths set by this function are used in automatic table layout calculation.
|
/// Minimum/preferred widths set by this function are used in automatic table layout calculation.
|
||||||
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
||||||
self.block_flow.bubble_widths(ctx);
|
self.block_flow.bubble_widths(ctx);
|
||||||
|
for box_ in self.block_flow.box_.iter() {
|
||||||
|
let specified_width = MaybeAuto::from_style(box_.style().Box.get().width,
|
||||||
|
Au::new(0)).specified_or_zero();
|
||||||
|
if self.block_flow.base.intrinsic_widths.minimum_width < specified_width {
|
||||||
|
self.block_flow.base.intrinsic_widths.minimum_width = specified_width;
|
||||||
|
}
|
||||||
|
if self.block_flow.base.intrinsic_widths.preferred_width <
|
||||||
|
self.block_flow.base.intrinsic_widths.minimum_width {
|
||||||
|
self.block_flow.base.intrinsic_widths.preferred_width =
|
||||||
|
self.block_flow.base.intrinsic_widths.minimum_width;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
||||||
|
@ -156,12 +128,6 @@ impl Flow for TableCellFlow {
|
||||||
self.assign_height_table_cell_base(ctx, false);
|
self.assign_height_table_cell_base(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TableCellBox and their parents(TableRowBox) do not have margins.
|
|
||||||
/// Therefore, margins to be collapsed do not exist.
|
|
||||||
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
|
|
||||||
_: &mut Au, _: &mut Au, _: &mut Au) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableCellFlow: ";
|
let txt = ~"TableCellFlow: ";
|
||||||
txt.append(match self.block_flow.box_ {
|
txt.append(match self.block_flow.box_ {
|
||||||
|
|
|
@ -82,12 +82,6 @@ impl Flow for TableColGroupFlow {
|
||||||
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
|
fn assign_height(&mut self, _ctx: &mut LayoutContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TableColumnBox and their parents(TableBox) do not have margins.
|
|
||||||
/// Therefore, margins to be collapsed do not exist.
|
|
||||||
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
|
|
||||||
_: &mut Au, _: &mut Au, _: &mut Au) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableColGroupFlow: ";
|
let txt = ~"TableColGroupFlow: ";
|
||||||
txt.append(match self.box_ {
|
txt.append(match self.box_ {
|
||||||
|
|
|
@ -9,16 +9,14 @@ use layout::block::BlockFlow;
|
||||||
use layout::block::WidthAndMarginsComputer;
|
use layout::block::WidthAndMarginsComputer;
|
||||||
use layout::construct::FlowConstructor;
|
use layout::construct::FlowConstructor;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use layout::flow::{TableRowFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::table::InternalTable;
|
use layout::table::InternalTable;
|
||||||
use layout::model::{MaybeAuto, Specified, Auto};
|
use layout::model::{MaybeAuto, Specified, Auto};
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::DisplayListCollection;
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
|
|
||||||
|
@ -28,6 +26,12 @@ pub struct TableRowFlow {
|
||||||
|
|
||||||
/// Column widths.
|
/// Column widths.
|
||||||
col_widths: ~[Au],
|
col_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column min widths.
|
||||||
|
col_min_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column pref widths.
|
||||||
|
col_pref_widths: ~[Au],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableRowFlow {
|
impl TableRowFlow {
|
||||||
|
@ -37,6 +41,8 @@ impl TableRowFlow {
|
||||||
TableRowFlow {
|
TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node_and_box(node, box_),
|
block_flow: BlockFlow::from_node_and_box(node, box_),
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,12 +52,16 @@ impl TableRowFlow {
|
||||||
TableRowFlow {
|
TableRowFlow {
|
||||||
block_flow: BlockFlow::from_node(constructor, node),
|
block_flow: BlockFlow::from_node(constructor, node),
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn teardown(&mut self) {
|
pub fn teardown(&mut self) {
|
||||||
self.block_flow.teardown();
|
self.block_flow.teardown();
|
||||||
self.col_widths = ~[];
|
self.col_widths = ~[];
|
||||||
|
self.col_min_widths = ~[];
|
||||||
|
self.col_pref_widths = ~[];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
|
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
|
||||||
|
@ -66,19 +76,23 @@ impl TableRowFlow {
|
||||||
|
|
||||||
/// Assign height for table-row flow.
|
/// Assign height for table-row flow.
|
||||||
///
|
///
|
||||||
|
/// TODO(pcwalton): This doesn't handle floats and positioned elements right.
|
||||||
|
///
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods
|
/// methods
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_height_table_row_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
|
fn assign_height_table_row_base(&mut self, layout_context: &mut LayoutContext, inorder: bool) {
|
||||||
let (top_offset, bottom_offset, left_offset) = self.initialize_offsets();
|
let (top_offset, _, _) = self.initialize_offsets();
|
||||||
|
|
||||||
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
|
|
||||||
left_offset, top_offset);
|
|
||||||
let mut cur_y = top_offset;
|
let mut cur_y = top_offset;
|
||||||
|
|
||||||
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
|
// Per CSS 2.1 § 17.5.3, find max_y = max( computed `height`, minimum height of all cells )
|
||||||
let mut max_y = Au::new(0);
|
let mut max_y = Au::new(0);
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
|
if inorder {
|
||||||
|
kid.assign_height_inorder(layout_context)
|
||||||
|
}
|
||||||
|
|
||||||
for child_box in kid.as_table_cell().box_().iter() {
|
for child_box in kid.as_table_cell().box_().iter() {
|
||||||
// TODO: Percentage height
|
// TODO: Percentage height
|
||||||
let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height,
|
let child_specified_height = MaybeAuto::from_style(child_box.style().Box.get().height,
|
||||||
|
@ -101,6 +115,8 @@ impl TableRowFlow {
|
||||||
cur_y = cur_y + height;
|
cur_y = cur_y + height;
|
||||||
|
|
||||||
// Assign the height of own box
|
// Assign the height of own box
|
||||||
|
//
|
||||||
|
// FIXME(pcwalton): Take `cur_y` into account.
|
||||||
for box_ in self.block_flow.box_.iter() {
|
for box_ in self.block_flow.box_.iter() {
|
||||||
let mut position = box_.border_box.get();
|
let mut position = box_.border_box.get();
|
||||||
position.size.height = height;
|
position.size.height = height;
|
||||||
|
@ -118,24 +134,14 @@ impl TableRowFlow {
|
||||||
let child_node = flow::mut_base(kid);
|
let child_node = flow::mut_base(kid);
|
||||||
child_node.position.size.height = height;
|
child_node.position.size.height = height;
|
||||||
}
|
}
|
||||||
|
|
||||||
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
|
|
||||||
top_offset, bottom_offset, left_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table_row<E:ExtraDisplayListData>(
|
pub fn build_display_list_table_row(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table_row: same process as block flow");
|
debug!("build_display_list_table_row: same process as block flow");
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
self.block_flow.build_display_list_block(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,27 +158,51 @@ impl Flow for TableRowFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
|
||||||
|
&mut self.col_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_min_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_pref_widths
|
||||||
|
}
|
||||||
|
|
||||||
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
|
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
|
||||||
/// on this context, all child contexts have had their min/pref widths set. This function must
|
/// on this context, all child contexts have had their min/pref widths set. This function must
|
||||||
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
|
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
|
||||||
/// responsible for flowing.
|
/// responsible for flowing.
|
||||||
/// Min/pref widths set by this function are used in automatic table layout calculation.
|
/// Min/pref widths set by this function are used in automatic table layout calculation.
|
||||||
/// Also, this function collects the specified column widths of children cells. Those are used
|
/// The specified column widths of children cells are used in fixed table layout calculation.
|
||||||
/// in fixed table layout calculation
|
fn bubble_widths(&mut self, _: &mut LayoutContext) {
|
||||||
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
let mut min_width = Au(0);
|
||||||
|
let mut pref_width = Au(0);
|
||||||
|
let mut num_floats = 0;
|
||||||
/* find the specified widths from child table-cell contexts */
|
/* find the specified widths from child table-cell contexts */
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
assert!(kid.is_table_cell());
|
assert!(kid.is_table_cell());
|
||||||
|
|
||||||
|
// collect the specified column widths of cells. These are used in fixed table layout calculation.
|
||||||
for child_box in kid.as_table_cell().box_().iter() {
|
for child_box in kid.as_table_cell().box_().iter() {
|
||||||
let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width,
|
let child_specified_width = MaybeAuto::from_style(child_box.style().Box.get().width,
|
||||||
Au::new(0)).specified_or_zero();
|
Au::new(0)).specified_or_zero();
|
||||||
self.col_widths.push(child_specified_width);
|
self.col_widths.push(child_specified_width);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: calculate min_width & pref_width for automatic table layout calculation
|
// collect min_width & pref_width of children cells for automatic table layout calculation.
|
||||||
self.block_flow.bubble_widths(ctx);
|
let child_base = flow::mut_base(kid);
|
||||||
|
self.col_min_widths.push(child_base.intrinsic_widths.minimum_width);
|
||||||
|
self.col_pref_widths.push(child_base.intrinsic_widths.preferred_width);
|
||||||
|
min_width = min_width + child_base.intrinsic_widths.minimum_width;
|
||||||
|
pref_width = pref_width + child_base.intrinsic_widths.preferred_width;
|
||||||
|
num_floats = num_floats + child_base.num_floats;
|
||||||
|
}
|
||||||
|
self.block_flow.base.num_floats = num_floats;
|
||||||
|
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
|
||||||
|
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
|
||||||
|
pref_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
||||||
|
@ -205,12 +235,6 @@ impl Flow for TableRowFlow {
|
||||||
self.assign_height_table_row_base(ctx, false);
|
self.assign_height_table_row_base(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TableRowBox and their parents(TableBox) do not have margins.
|
|
||||||
/// Therefore, margins to be collapsed do not exist.
|
|
||||||
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
|
|
||||||
_: &mut Au, _: &mut Au, _: &mut Au) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableRowFlow: ";
|
let txt = ~"TableRowFlow: ";
|
||||||
txt.append(match self.block_flow.box_ {
|
txt.append(match self.block_flow.box_ {
|
||||||
|
|
|
@ -9,16 +9,15 @@ use layout::block::BlockFlow;
|
||||||
use layout::block::WidthAndMarginsComputer;
|
use layout::block::WidthAndMarginsComputer;
|
||||||
use layout::construct::FlowConstructor;
|
use layout::construct::FlowConstructor;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use layout::flow::{TableRowGroupFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use layout::flow;
|
use layout::flow;
|
||||||
use layout::table::InternalTable;
|
use layout::table::{InternalTable, TableFlow};
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::DisplayListCollection;
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
|
use servo_util::geometry;
|
||||||
|
|
||||||
/// A table formatting context.
|
/// A table formatting context.
|
||||||
pub struct TableRowGroupFlow {
|
pub struct TableRowGroupFlow {
|
||||||
|
@ -26,6 +25,12 @@ pub struct TableRowGroupFlow {
|
||||||
|
|
||||||
/// Column widths
|
/// Column widths
|
||||||
col_widths: ~[Au],
|
col_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column min widths.
|
||||||
|
col_min_widths: ~[Au],
|
||||||
|
|
||||||
|
/// Column pref widths.
|
||||||
|
col_pref_widths: ~[Au],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TableRowGroupFlow {
|
impl TableRowGroupFlow {
|
||||||
|
@ -35,6 +40,8 @@ impl TableRowGroupFlow {
|
||||||
TableRowGroupFlow {
|
TableRowGroupFlow {
|
||||||
block_flow: BlockFlow::from_node_and_box(node, box_),
|
block_flow: BlockFlow::from_node_and_box(node, box_),
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,12 +51,16 @@ impl TableRowGroupFlow {
|
||||||
TableRowGroupFlow {
|
TableRowGroupFlow {
|
||||||
block_flow: BlockFlow::from_node(constructor, node),
|
block_flow: BlockFlow::from_node(constructor, node),
|
||||||
col_widths: ~[],
|
col_widths: ~[],
|
||||||
|
col_min_widths: ~[],
|
||||||
|
col_pref_widths: ~[],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn teardown(&mut self) {
|
pub fn teardown(&mut self) {
|
||||||
self.block_flow.teardown();
|
self.block_flow.teardown();
|
||||||
self.col_widths = ~[];
|
self.col_widths = ~[];
|
||||||
|
self.col_min_widths = ~[];
|
||||||
|
self.col_pref_widths = ~[];
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
|
pub fn box_<'a>(&'a mut self) -> &'a Option<Box>{
|
||||||
|
@ -64,14 +75,14 @@ impl TableRowGroupFlow {
|
||||||
|
|
||||||
/// Assign height for table-rowgroup flow.
|
/// Assign height for table-rowgroup flow.
|
||||||
///
|
///
|
||||||
|
/// FIXME(pcwalton): This doesn't handle floats right.
|
||||||
|
///
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods
|
/// methods
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_height_table_rowgroup_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
|
fn assign_height_table_rowgroup_base(&mut self, _: &mut LayoutContext, _: bool) {
|
||||||
let (top_offset, bottom_offset, left_offset) = self.initialize_offsets();
|
let (top_offset, _, _) = self.initialize_offsets();
|
||||||
|
|
||||||
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
|
|
||||||
left_offset, top_offset);
|
|
||||||
let mut cur_y = top_offset;
|
let mut cur_y = top_offset;
|
||||||
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
|
@ -88,24 +99,14 @@ impl TableRowGroupFlow {
|
||||||
box_.border_box.set(position);
|
box_.border_box.set(position);
|
||||||
}
|
}
|
||||||
self.block_flow.base.position.size.height = height;
|
self.block_flow.base.position.size.height = height;
|
||||||
|
|
||||||
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
|
|
||||||
top_offset, bottom_offset, left_offset);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table_rowgroup<E:ExtraDisplayListData>(
|
pub fn build_display_list_table_rowgroup(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table_rowgroup: same process as block flow");
|
debug!("build_display_list_table_rowgroup: same process as block flow");
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
self.block_flow.build_display_list_block(stacking_context, builder, info)
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +123,18 @@ impl Flow for TableRowGroupFlow {
|
||||||
&mut self.block_flow
|
&mut self.block_flow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn col_widths<'a>(&'a mut self) -> &'a mut ~[Au] {
|
||||||
|
&mut self.col_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_min_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_min_widths
|
||||||
|
}
|
||||||
|
|
||||||
|
fn col_pref_widths<'a>(&'a self) -> &'a ~[Au] {
|
||||||
|
&self.col_pref_widths
|
||||||
|
}
|
||||||
|
|
||||||
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
|
/// Recursively (bottom-up) determines the context's preferred and minimum widths. When called
|
||||||
/// on this context, all child contexts have had their min/pref widths set. This function must
|
/// on this context, all child contexts have had their min/pref widths set. This function must
|
||||||
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
|
/// decide min/pref widths based on child context widths and dimensions of any boxes it is
|
||||||
|
@ -129,24 +142,48 @@ impl Flow for TableRowGroupFlow {
|
||||||
/// Min/pref widths set by this function are used in automatic table layout calculation.
|
/// Min/pref widths set by this function are used in automatic table layout calculation.
|
||||||
/// Also, this function finds the specified column widths from the first row.
|
/// Also, this function finds the specified column widths from the first row.
|
||||||
/// Those are used in fixed table layout calculation
|
/// Those are used in fixed table layout calculation
|
||||||
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
fn bubble_widths(&mut self, _: &mut LayoutContext) {
|
||||||
/* find the specified column widths from the first table-row.
|
let mut min_width = Au(0);
|
||||||
update the number of column widths from other table-rows. */
|
let mut pref_width = Au(0);
|
||||||
|
let mut num_floats = 0;
|
||||||
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
assert!(kid.is_table_row());
|
assert!(kid.is_table_row());
|
||||||
if self.col_widths.is_empty() {
|
|
||||||
self.col_widths = kid.as_table_row().col_widths.clone();
|
// calculate min_width & pref_width for automatic table layout calculation
|
||||||
|
// 'self.col_min_widths' collects the maximum value of cells' min-widths for each column.
|
||||||
|
// 'self.col_pref_widths' collects the maximum value of cells' pref-widths for each column.
|
||||||
|
if self.col_widths.is_empty() { // First Row
|
||||||
|
assert!(self.col_min_widths.is_empty() && self.col_pref_widths.is_empty());
|
||||||
|
// 'self.col_widths' collects the specified column widths from the first table-row for fixed table layout calculation.
|
||||||
|
self.col_widths = kid.col_widths().clone();
|
||||||
|
self.col_min_widths = kid.col_min_widths().clone();
|
||||||
|
self.col_pref_widths = kid.col_pref_widths().clone();
|
||||||
} else {
|
} else {
|
||||||
|
min_width = TableFlow::update_col_widths(&mut self.col_min_widths, kid.col_min_widths());
|
||||||
|
pref_width = TableFlow::update_col_widths(&mut self.col_pref_widths, kid.col_pref_widths());
|
||||||
|
|
||||||
|
// update the number of column widths from table-rows.
|
||||||
let num_cols = self.col_widths.len();
|
let num_cols = self.col_widths.len();
|
||||||
let num_child_cols = kid.as_table_row().col_widths.len();
|
let num_child_cols = kid.col_min_widths().len();
|
||||||
for _ in range(num_cols, num_child_cols) {
|
for i in range(num_cols, num_child_cols) {
|
||||||
self.col_widths.push(Au::new(0));
|
self.col_widths.push(Au::new(0));
|
||||||
|
let new_kid_min = kid.col_min_widths()[i];
|
||||||
|
self.col_min_widths.push(kid.col_min_widths()[i]);
|
||||||
|
let new_kid_pref = kid.col_pref_widths()[i];
|
||||||
|
self.col_pref_widths.push(kid.col_pref_widths()[i]);
|
||||||
|
min_width = min_width + new_kid_min;
|
||||||
|
pref_width = pref_width + new_kid_pref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
let child_base = flow::mut_base(kid);
|
||||||
|
num_floats = num_floats + child_base.num_floats;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: calculate min_width & pref_width for automatic table layout calculation
|
self.block_flow.base.num_floats = num_floats;
|
||||||
self.block_flow.bubble_widths(ctx);
|
self.block_flow.base.intrinsic_widths.minimum_width = min_width;
|
||||||
|
self.block_flow.base.intrinsic_widths.preferred_width = geometry::max(min_width,
|
||||||
|
pref_width);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
||||||
|
@ -180,12 +217,6 @@ impl Flow for TableRowGroupFlow {
|
||||||
self.assign_height_table_rowgroup_base(ctx, false);
|
self.assign_height_table_rowgroup_base(ctx, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// TableRowBox and their parents(TableBox) do not have margins.
|
|
||||||
/// Therefore, margins to be collapsed do not exist.
|
|
||||||
fn collapse_margins(&mut self, _: bool, _: &mut bool, _: &mut Au,
|
|
||||||
_: &mut Au, _: &mut Au, _: &mut Au) {
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = ~"TableRowGroupFlow: ";
|
let txt = ~"TableRowGroupFlow: ";
|
||||||
txt.append(match self.block_flow.box_ {
|
txt.append(match self.block_flow.box_ {
|
||||||
|
|
|
@ -5,23 +5,20 @@
|
||||||
//! CSS table formatting contexts.
|
//! CSS table formatting contexts.
|
||||||
|
|
||||||
use layout::box_::Box;
|
use layout::box_::Box;
|
||||||
use layout::block::BlockFlow;
|
use layout::block::{BlockFlow, MarginsMayNotCollapse, WidthAndMarginsComputer};
|
||||||
use layout::block::{WidthAndMarginsComputer, WidthConstraintInput, WidthConstraintSolution};
|
use layout::block::{WidthConstraintInput, WidthConstraintSolution};
|
||||||
use layout::construct::FlowConstructor;
|
use layout::construct::FlowConstructor;
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
use layout::display_list_builder::{DisplayListBuilder, DisplayListBuildingInfo};
|
||||||
use layout::floats::{FloatKind};
|
use layout::floats::FloatKind;
|
||||||
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
use layout::flow::{TableWrapperFlowClass, FlowClass, Flow, ImmutableFlowUtils};
|
||||||
use layout::flow;
|
use layout::model::{Specified, Auto, specified};
|
||||||
use layout::model::{MaybeAuto, Specified, Auto, specified};
|
|
||||||
use layout::wrapper::ThreadSafeLayoutNode;
|
use layout::wrapper::ThreadSafeLayoutNode;
|
||||||
|
|
||||||
use std::cell::RefCell;
|
use gfx::display_list::StackingContext;
|
||||||
use style::computed_values::table_layout;
|
|
||||||
use geom::{Point2D, Rect, Size2D};
|
|
||||||
use gfx::display_list::DisplayListCollection;
|
|
||||||
use servo_util::geometry::Au;
|
use servo_util::geometry::Au;
|
||||||
use servo_util::geometry;
|
use servo_util::geometry;
|
||||||
|
use style::computed_values::table_layout;
|
||||||
|
|
||||||
pub enum TableLayout {
|
pub enum TableLayout {
|
||||||
FixedLayout,
|
FixedLayout,
|
||||||
|
@ -103,77 +100,27 @@ impl TableWrapperFlow {
|
||||||
|
|
||||||
/// Assign height for table-wrapper flow.
|
/// Assign height for table-wrapper flow.
|
||||||
/// `Assign height` of table-wrapper flow follows a similar process to that of block flow.
|
/// `Assign height` of table-wrapper flow follows a similar process to that of block flow.
|
||||||
/// However, table-wrapper flow doesn't consider collapsing margins for flow's children
|
///
|
||||||
/// and calculating padding/border.
|
|
||||||
///
|
|
||||||
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
/// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||||
/// methods
|
/// methods
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn assign_height_table_wrapper_base(&mut self, ctx: &mut LayoutContext, inorder: bool) {
|
fn assign_height_table_wrapper_base(&mut self,
|
||||||
|
layout_context: &mut LayoutContext,
|
||||||
// Note: Ignoring clearance for absolute flows as of now.
|
inorder: bool) {
|
||||||
let ignore_clear = self.is_absolutely_positioned();
|
self.block_flow.assign_height_block_base(layout_context, inorder, MarginsMayNotCollapse);
|
||||||
let (clearance, top_offset, bottom_offset, left_offset) = self.block_flow.initialize_offsets(ignore_clear);
|
|
||||||
|
|
||||||
self.block_flow.handle_children_floats_if_necessary(ctx, inorder,
|
|
||||||
left_offset, top_offset);
|
|
||||||
|
|
||||||
// Table wrapper flow has margin but is not collapsed with kids(table caption and table).
|
|
||||||
let (margin_top, margin_bottom, _, _) = self.block_flow.precompute_margin();
|
|
||||||
|
|
||||||
let mut cur_y = top_offset;
|
|
||||||
|
|
||||||
for kid in self.block_flow.base.child_iter() {
|
|
||||||
let child_node = flow::mut_base(kid);
|
|
||||||
child_node.position.origin.y = cur_y;
|
|
||||||
cur_y = cur_y + child_node.position.size.height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// top_offset: top margin-edge of the topmost child.
|
|
||||||
// hence, height = content height
|
|
||||||
let mut height = cur_y - top_offset;
|
|
||||||
|
|
||||||
// For an absolutely positioned element, store the content height and stop the function.
|
|
||||||
if self.block_flow.store_content_height_if_absolutely_positioned(height) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
for box_ in self.block_flow.box_.iter() {
|
|
||||||
let style = box_.style();
|
|
||||||
|
|
||||||
// At this point, `height` is the height of the containing block, so passing `height`
|
|
||||||
// as the second argument here effectively makes percentages relative to the containing
|
|
||||||
// block per CSS 2.1 § 10.5.
|
|
||||||
height = match MaybeAuto::from_style(style.Box.get().height, height) {
|
|
||||||
Auto => height,
|
|
||||||
Specified(value) => geometry::max(value, height)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
self.block_flow.compute_height_position(&mut height,
|
|
||||||
Au(0),
|
|
||||||
margin_top,
|
|
||||||
margin_bottom,
|
|
||||||
clearance);
|
|
||||||
|
|
||||||
self.block_flow.set_floats_out_if_inorder(inorder, height, cur_y,
|
|
||||||
top_offset, bottom_offset, left_offset);
|
|
||||||
self.block_flow.assign_height_absolute_flows(ctx);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_display_list_table_wrapper<E:ExtraDisplayListData>(
|
pub fn build_display_list_table_wrapper(&mut self,
|
||||||
&mut self,
|
stacking_context: &mut StackingContext,
|
||||||
builder: &DisplayListBuilder,
|
builder: &mut DisplayListBuilder,
|
||||||
container_block_size: &Size2D<Au>,
|
info: &DisplayListBuildingInfo) {
|
||||||
absolute_cb_abs_position: Point2D<Au>,
|
|
||||||
dirty: &Rect<Au>,
|
|
||||||
index: uint,
|
|
||||||
lists: &RefCell<DisplayListCollection<E>>)
|
|
||||||
-> uint {
|
|
||||||
debug!("build_display_list_table_wrapper: same process as block flow");
|
debug!("build_display_list_table_wrapper: same process as block flow");
|
||||||
self.block_flow.build_display_list_block(builder, container_block_size,
|
self.block_flow.build_display_list_block(stacking_context, builder, info);
|
||||||
absolute_cb_abs_position,
|
|
||||||
dirty, index, lists)
|
for kid in self.block_flow.base.child_iter() {
|
||||||
|
kid.as_table().block_flow.base.position.size.height =
|
||||||
|
self.block_flow.base.position.size.height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -197,7 +144,7 @@ impl Flow for TableWrapperFlow {
|
||||||
any boxes it is responsible for flowing. */
|
any boxes it is responsible for flowing. */
|
||||||
|
|
||||||
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
fn bubble_widths(&mut self, ctx: &mut LayoutContext) {
|
||||||
/* find max width from child block contexts */
|
// get column widths info from table flow
|
||||||
for kid in self.block_flow.base.child_iter() {
|
for kid in self.block_flow.base.child_iter() {
|
||||||
assert!(kid.is_table_caption() || kid.is_table());
|
assert!(kid.is_table_caption() || kid.is_table());
|
||||||
|
|
||||||
|
@ -227,7 +174,7 @@ impl Flow for TableWrapperFlow {
|
||||||
let mut left_content_edge = Au::new(0);
|
let mut left_content_edge = Au::new(0);
|
||||||
let mut content_width = containing_block_width;
|
let mut content_width = containing_block_width;
|
||||||
|
|
||||||
self.block_flow.set_containing_width_if_float(containing_block_width);
|
// self.block_flow.set_containing_width_if_float(containing_block_width);
|
||||||
|
|
||||||
let width_computer = TableWrapper;
|
let width_computer = TableWrapper;
|
||||||
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
|
width_computer.compute_used_width_table_wrapper(self, ctx, containing_block_width);
|
||||||
|
@ -243,7 +190,12 @@ impl Flow for TableWrapperFlow {
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, None);
|
// In case of fixed layout, column widths are calculated in table flow.
|
||||||
|
let assigned_col_widths = match self.table_layout {
|
||||||
|
FixedLayout => None,
|
||||||
|
AutoLayout => Some(self.col_widths.clone())
|
||||||
|
};
|
||||||
|
self.block_flow.propagate_assigned_width_to_children(left_content_edge, content_width, assigned_col_widths);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// This is called on kid flows by a parent.
|
/// This is called on kid flows by a parent.
|
||||||
|
@ -270,26 +222,6 @@ impl Flow for TableWrapperFlow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// CSS Section 8.3.1 - Collapsing Margins
|
|
||||||
// `self`: the Flow whose margins we want to collapse.
|
|
||||||
// `collapsing`: value to be set by this function. This tells us how much
|
|
||||||
// of the top margin has collapsed with a previous margin.
|
|
||||||
// `collapsible`: Potential collapsible margin at the bottom of this flow's box.
|
|
||||||
fn collapse_margins(&mut self,
|
|
||||||
top_margin_collapsible: bool,
|
|
||||||
first_in_flow: &mut bool,
|
|
||||||
margin_top: &mut Au,
|
|
||||||
top_offset: &mut Au,
|
|
||||||
collapsing: &mut Au,
|
|
||||||
collapsible: &mut Au) {
|
|
||||||
self.block_flow.collapse_margins(top_margin_collapsible,
|
|
||||||
first_in_flow,
|
|
||||||
margin_top,
|
|
||||||
top_offset,
|
|
||||||
collapsing,
|
|
||||||
collapsible);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug_str(&self) -> ~str {
|
fn debug_str(&self) -> ~str {
|
||||||
let txt = if self.is_float() {
|
let txt = if self.is_float() {
|
||||||
~"TableWrapperFlow(Float): "
|
~"TableWrapperFlow(Float): "
|
||||||
|
@ -327,10 +259,12 @@ impl TableWrapper {
|
||||||
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow,
|
let mut input = self.compute_width_constraint_inputs(&mut table_wrapper.block_flow,
|
||||||
parent_flow_width,
|
parent_flow_width,
|
||||||
ctx);
|
ctx);
|
||||||
match table_wrapper.table_layout {
|
let computed_width = match table_wrapper.table_layout {
|
||||||
FixedLayout => {
|
FixedLayout => {
|
||||||
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0),
|
let fixed_cells_width = table_wrapper.col_widths.iter().fold(Au(0),
|
||||||
|sum, width| sum.add(width));
|
|sum, width| sum.add(width));
|
||||||
|
|
||||||
|
let mut computed_width = input.computed_width.specified_or_zero();
|
||||||
for box_ in table_wrapper.block_flow.box_.iter() {
|
for box_ in table_wrapper.block_flow.box_.iter() {
|
||||||
let style = box_.style();
|
let style = box_.style();
|
||||||
|
|
||||||
|
@ -344,15 +278,72 @@ impl TableWrapper {
|
||||||
let border_left = style.Border.get().border_left_width;
|
let border_left = style.Border.get().border_left_width;
|
||||||
let border_right = style.Border.get().border_right_width;
|
let border_right = style.Border.get().border_right_width;
|
||||||
let padding_and_borders = padding_left + padding_right + border_left + border_right;
|
let padding_and_borders = padding_left + padding_right + border_left + border_right;
|
||||||
let mut computed_width = input.computed_width.specified_or_zero();
|
|
||||||
// Compare border-edge widths. Because fixed_cells_width indicates content-width,
|
// Compare border-edge widths. Because fixed_cells_width indicates content-width,
|
||||||
// padding and border values are added to fixed_cells_width.
|
// padding and border values are added to fixed_cells_width.
|
||||||
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width);
|
computed_width = geometry::max(fixed_cells_width + padding_and_borders, computed_width);
|
||||||
input.computed_width = Specified(computed_width);
|
|
||||||
}
|
}
|
||||||
|
computed_width
|
||||||
},
|
},
|
||||||
_ => {}
|
AutoLayout => {
|
||||||
}
|
// Automatic table layout is calculated according to CSS 2.1 § 17.5.2.2.
|
||||||
|
// But, this spec is not specified. Since the new spec is specified, it may be modified. See #1687.
|
||||||
|
let mut cap_min = Au(0);
|
||||||
|
let mut cols_min = Au(0);
|
||||||
|
let mut cols_max = Au(0);
|
||||||
|
let mut col_min_widths = &~[];
|
||||||
|
let mut col_pref_widths = &~[];
|
||||||
|
for kid in table_wrapper.block_flow.base.child_iter() {
|
||||||
|
if kid.is_table_caption() {
|
||||||
|
cap_min = kid.as_block().base.intrinsic_widths.minimum_width;
|
||||||
|
} else {
|
||||||
|
assert!(kid.is_table());
|
||||||
|
cols_min = kid.as_block().base.intrinsic_widths.minimum_width;
|
||||||
|
cols_max = kid.as_block().base.intrinsic_widths.preferred_width;
|
||||||
|
col_min_widths = kid.col_min_widths();
|
||||||
|
col_pref_widths = kid.col_pref_widths();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// 'extra_width': difference between the calculated table width and minimum width required by all columns.
|
||||||
|
// It will be distributed over the columns
|
||||||
|
let (width, extra_width) = match input.computed_width {
|
||||||
|
Auto => {
|
||||||
|
if input.available_width > geometry::max(cols_max, cap_min) {
|
||||||
|
if cols_max > cap_min {
|
||||||
|
table_wrapper.col_widths = col_pref_widths.clone();
|
||||||
|
(cols_max, Au(0))
|
||||||
|
} else {
|
||||||
|
(cap_min, cap_min - cols_min)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let max = if cols_min >= input.available_width && cols_min >= cap_min {
|
||||||
|
table_wrapper.col_widths = col_min_widths.clone();
|
||||||
|
cols_min
|
||||||
|
} else {
|
||||||
|
geometry::max(input.available_width, cap_min)
|
||||||
|
};
|
||||||
|
(max, max - cols_min)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Specified(width) => {
|
||||||
|
let max = if cols_min >= width && cols_min >= cap_min {
|
||||||
|
table_wrapper.col_widths = col_min_widths.clone();
|
||||||
|
cols_min
|
||||||
|
} else {
|
||||||
|
geometry::max(width, cap_min)
|
||||||
|
};
|
||||||
|
(max, max - cols_min)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// The extra width is distributed over the columns
|
||||||
|
if extra_width > Au(0) {
|
||||||
|
let cell_len = table_wrapper.col_widths.len() as f64;
|
||||||
|
table_wrapper.col_widths = col_min_widths.map(|width|
|
||||||
|
width + extra_width.scale_by(1.0/cell_len));
|
||||||
|
}
|
||||||
|
width
|
||||||
|
}
|
||||||
|
};
|
||||||
|
input.computed_width = Specified(computed_width);
|
||||||
input
|
input
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue