Added floats to the flow tree

This commit is contained in:
Eric Atkinson 2013-06-23 15:48:02 -07:00
parent d3fe4f4e3a
commit 94e7a86b7e
10 changed files with 398 additions and 51 deletions

View file

@ -8,9 +8,10 @@ use layout::box::{RenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::display_list_builder::{FlowDisplayListBuilderMethods}; use layout::display_list_builder::{FlowDisplayListBuilderMethods};
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow}; use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow, FloatFlow};
use layout::inline::InlineLayout; use layout::inline::InlineLayout;
use layout::model::{MaybeAuto, Specified, Auto}; use layout::model::{MaybeAuto, Specified, Auto};
use layout::float_context::FloatContext;
use core::cell::Cell; use core::cell::Cell;
use geom::point::Point2D; use geom::point::Point2D;
@ -72,7 +73,7 @@ impl BlockLayout for FlowContext {
fn starts_block_flow(&self) -> bool { fn starts_block_flow(&self) -> bool {
match *self { match *self {
BlockFlow(*) | InlineBlockFlow(*) => true, BlockFlow(*) | InlineBlockFlow(*) | FloatFlow(*) => true,
_ => false _ => false
} }
} }
@ -91,14 +92,17 @@ impl BlockFlowData {
pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) { pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) {
let mut min_width = Au(0); let mut min_width = Au(0);
let mut pref_width = Au(0); let mut pref_width = Au(0);
let mut num_floats = 0;
/* find max width from child block contexts */ /* find max width from child block contexts */
for BlockFlow(self).each_child |child_ctx| { for BlockFlow(self).each_child |child_ctx| {
assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow()); assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_base |child_node| { do child_ctx.with_mut_base |child_node| {
min_width = geometry::max(min_width, child_node.min_width); min_width = geometry::max(min_width, child_node.min_width);
pref_width = geometry::max(pref_width, child_node.pref_width); pref_width = geometry::max(pref_width, child_node.pref_width);
num_floats = num_floats + child_node.num_floats;
} }
} }
@ -116,6 +120,7 @@ impl BlockFlowData {
self.common.min_width = min_width; self.common.min_width = min_width;
self.common.pref_width = pref_width; self.common.pref_width = pref_width;
self.common.num_floats = num_floats;
} }
/// Computes left and right margins and width based on CSS 2.1 secion 10.3.3. /// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
@ -180,6 +185,7 @@ impl BlockFlowData {
debug!("Setting root position"); debug!("Setting root position");
self.common.position.origin = Au::zero_point(); self.common.position.origin = Au::zero_point();
self.common.position.size.width = ctx.screen_size.size.width; self.common.position.size.width = ctx.screen_size.size.width;
self.common.floats_in = FloatContext::new(self.common.num_floats);
} }
//position was set to the containing block by the flow's parent //position was set to the containing block by the flow's parent
@ -240,7 +246,7 @@ impl BlockFlowData {
} }
} }
pub fn assign_height_block(@mut self, ctx: &LayoutContext) { pub fn assign_height_block(@mut self, ctx: &mut LayoutContext) {
let mut cur_y = Au(0); let mut cur_y = Au(0);
let mut top_offset = Au(0); let mut top_offset = Au(0);
@ -251,6 +257,28 @@ impl BlockFlowData {
} }
} }
// TODO(eatkinson): the translation here is probably
// totally wrong. We need to do it right or pages
// with floats will look very strange.
// Floats for blocks work like this:
// self.floats_in -> child[0].floats_in
// visit child[0]
// child[i-1].floats_out -> child[i].floats_in
// visit child[i]
// repeat until all children are visited.
// last_child.floats_out -> self.floats_out (done at the end of this function)
let mut float_ctx = self.common.floats_in.clone();
for BlockFlow(self).each_child |kid| {
do kid.with_mut_base |child_node| {
child_node.floats_in = float_ctx.clone();
}
kid.assign_height(ctx);
do kid.with_mut_base |child_node| {
float_ctx = child_node.floats_out.translate(Point2D(Au(0), -child_node.position.size.height));
}
}
for BlockFlow(self).each_child |kid| { for BlockFlow(self).each_child |kid| {
do kid.with_mut_base |child_node| { do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y; child_node.position.origin.y = cur_y;
@ -281,6 +309,8 @@ impl BlockFlowData {
//TODO(eatkinson): compute heights using the 'height' property. //TODO(eatkinson): compute heights using the 'height' property.
self.common.position.size.height = height + noncontent_height; self.common.position.size.height = height + noncontent_height;
self.common.floats_out = float_ctx.translate(Point2D(Au(0), self.common.position.size.height));
} }
pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self, pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self,

View file

@ -8,7 +8,7 @@ use css::node_style::StyledNode;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
use layout::flow::FlowContext; use layout::flow::FlowContext;
use layout::model::BoxModel; use layout::model::{BoxModel, MaybeAuto};
use layout::text; use layout::text;
use core::cell::Cell; use core::cell::Cell;
@ -370,11 +370,41 @@ pub impl RenderBox {
} }
} }
/// Guess the intrinsic width of this box for
/// computation of min and preferred widths.
//
// TODO(eatkinson): this is unspecified in
// CSS 2.1, but we need to do something reasonable
// here. What this function does currently is
// NOT reasonable.
//
// TODO(eatkinson): integrate with
// get_min_width and get_pref_width?
priv fn guess_width (&self) -> Au {
do self.with_base |base| {
if(!base.node.is_element()) {
Au(0)
} else {
let w = MaybeAuto::from_width(self.style().width(), Au(0)).spec_or_default(Au(0));
let ml = MaybeAuto::from_margin(self.style().margin_left(), Au(0)).spec_or_default(Au(0));
let mr = MaybeAuto::from_margin(self.style().margin_right(), Au(0)).spec_or_default(Au(0));
let pl = base.model.compute_padding_length(self.style().padding_left(), Au(0));
let pr = base.model.compute_padding_length(self.style().padding_right(), Au(0));
let bl = base.model.compute_border_width(self.style().border_left_width());
let br = base.model.compute_border_width(self.style().border_right_width());
w + ml + mr + pl + pr + bl + br
}
}
}
/// Returns the *minimum width* of this render box as defined by the CSS specification. /// Returns the *minimum width* of this render box as defined by the CSS specification.
fn get_min_width(&self, _: &LayoutContext) -> Au { fn get_min_width(&self, _: &LayoutContext) -> Au {
// FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS // FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
// needs to be restyled. // needs to be restyled.
match *self {
self.guess_width() + match *self {
// TODO: This should account for the minimum width of the box element in isolation. // TODO: This should account for the minimum width of the box element in isolation.
// That includes borders, margins, and padding, but not child widths. The block // That includes borders, margins, and padding, but not child widths. The block
// `FlowContext` will combine the width of this element and that of its children to // `FlowContext` will combine the width of this element and that of its children to
@ -397,7 +427,7 @@ pub impl RenderBox {
/// Returns the *preferred width* of this render box as defined by the CSS specification. /// Returns the *preferred width* of this render box as defined by the CSS specification.
fn get_pref_width(&self, _: &LayoutContext) -> Au { fn get_pref_width(&self, _: &LayoutContext) -> Au {
match *self { self.guess_width() + match *self {
// TODO: This should account for the preferred width of the box element in isolation. // TODO: This should account for the preferred width of the box element in isolation.
// That includes borders, margins, and padding, but not child widths. The block // That includes borders, margins, and padding, but not child widths. The block
// `FlowContext` will combine the width of this element and that of its children to // `FlowContext` will combine the width of this element and that of its children to

View file

@ -6,6 +6,7 @@
use layout::aux::LayoutAuxMethods; use layout::aux::LayoutAuxMethods;
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::float::FloatFlowData;
use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox}; use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image}; use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image};
use layout::box::{RenderBox_Text, UnscannedTextRenderBox, UnscannedTextRenderBoxClass}; use layout::box::{RenderBox_Text, UnscannedTextRenderBox, UnscannedTextRenderBoxClass};
@ -22,6 +23,7 @@ use newcss::values::{CSSDisplayTableRowGroup, CSSDisplayTableHeaderGroup, CSSDis
use newcss::values::{CSSDisplayTableRow, CSSDisplayTableColumnGroup, CSSDisplayTableColumn}; use newcss::values::{CSSDisplayTableRow, CSSDisplayTableColumnGroup, CSSDisplayTableColumn};
use newcss::values::{CSSDisplayTableCell, CSSDisplayTableCaption}; use newcss::values::{CSSDisplayTableCell, CSSDisplayTableCaption};
use newcss::values::{CSSDisplayNone}; use newcss::values::{CSSDisplayNone};
use newcss::values::{CSSFloatNone};
use script::dom::element::*; use script::dom::element::*;
use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId}; use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId}; use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId};
@ -350,7 +352,22 @@ pub impl LayoutTreeBuilder {
Some(gen) => Some(gen.flow) Some(gen) => Some(gen.flow)
}; };
// TODO(eatkinson): use the value of the float property to
// determine whether to float left or right.
let is_float = if (node.is_element()) {
match node.style().float() {
CSSFloatNone => false,
_ => true
}
} else {
false
};
let new_generator = match (display, parent_generator.flow, sibling_flow) { let new_generator = match (display, parent_generator.flow, sibling_flow) {
(CSSDisplayBlock, BlockFlow(_), _) if is_float => {
self.create_child_generator(node, parent_generator, Flow_Float)
},
(CSSDisplayBlock, BlockFlow(info), _) => match (info.is_root, node.parent_node()) { (CSSDisplayBlock, BlockFlow(info), _) => match (info.is_root, node.parent_node()) {
// If this is the root node, then use the root flow's // If this is the root node, then use the root flow's
// context. Otherwise, make a child block context. // context. Otherwise, make a child block context.
@ -383,8 +400,6 @@ pub impl LayoutTreeBuilder {
// TODO(eatkinson): blocks that are children of inlines need // TODO(eatkinson): blocks that are children of inlines need
// to split their parent flows. // to split their parent flows.
//
// TODO(eatkinson): floats and positioned elements.
_ => parent_generator _ => parent_generator
}; };
@ -520,7 +535,7 @@ pub impl LayoutTreeBuilder {
let result = match ty { let result = match ty {
Flow_Absolute => AbsoluteFlow(@mut info), Flow_Absolute => AbsoluteFlow(@mut info),
Flow_Block => BlockFlow(@mut BlockFlowData::new(info)), Flow_Block => BlockFlow(@mut BlockFlowData::new(info)),
Flow_Float => FloatFlow(@mut info), Flow_Float => FloatFlow(@mut FloatFlowData::new(info)),
Flow_InlineBlock => InlineBlockFlow(@mut info), Flow_InlineBlock => InlineBlockFlow(@mut info),
Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)), Flow_Inline => InlineFlow(@mut InlineFlowData::new(info)),
Flow_Root => BlockFlow(@mut BlockFlowData::new_root(info)), Flow_Root => BlockFlow(@mut BlockFlowData::new_root(info)),

View file

@ -0,0 +1,223 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::display_list_builder::{FlowDisplayListBuilderMethods};
use layout::flow::{FloatFlow, FlowData};
use layout::model::{MaybeAuto};
use layout::float_context::{FloatContext, PlacementInfo, FloatLeft};
use core::cell::Cell;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::display_list::DisplayList;
use gfx::geometry::Au;
use gfx::geometry;
use servo_util::tree::{TreeNodeRef, TreeUtils};
pub struct FloatFlowData {
/// Data common to all flows.
common: FlowData,
/// The associated render box.
box: Option<RenderBox>,
containing_width: Au,
/// Index into the box list for inline floats
index: Option<uint>,
}
impl FloatFlowData {
pub fn new(common: FlowData) -> FloatFlowData {
FloatFlowData {
common: common,
containing_width: Au(0),
box: None,
index: None,
}
}
pub fn teardown(&mut self) {
self.common.teardown();
for self.box.each |box| {
box.teardown();
}
self.box = None;
self.index = None;
}
}
impl FloatFlowData {
pub fn bubble_widths_float(@mut self, ctx: &LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
self.common.num_floats = 1;
for FloatFlow(self).each_child |child_ctx| {
//assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
do child_ctx.with_mut_base |child_node| {
min_width = geometry::max(min_width, child_node.min_width);
pref_width = geometry::max(pref_width, child_node.pref_width);
child_node.floats_in = FloatContext::new(child_node.num_floats);
}
}
self.box.map(|&box| {
let style = box.style();
do box.with_model |model| {
model.compute_borders(style)
}
min_width = min_width.add(&box.get_min_width(ctx));
pref_width = pref_width.add(&box.get_pref_width(ctx));
});
self.common.min_width = min_width;
self.common.pref_width = pref_width;
}
pub fn assign_widths_float(@mut self, _: &LayoutContext) {
debug!("assign_widths_block: assigning width for flow %?", self.common.id);
// position.size.width is set by parent even though we don't know
// position.origin yet.
let mut remaining_width = self.common.position.size.width;
self.containing_width = remaining_width;
let mut x_offset = Au(0);
for self.box.each |&box| {
let style = box.style();
do box.with_model |model| {
// Can compute padding here since we know containing block width.
model.compute_padding(style, remaining_width);
// Margins for floats are 0 if auto.
let margin_top = MaybeAuto::from_margin(style.margin_top(),
remaining_width).spec_or_default(Au(0));
let margin_bottom = MaybeAuto::from_margin(style.margin_bottom(),
remaining_width).spec_or_default(Au(0));
let margin_left = MaybeAuto::from_margin(style.margin_left(),
remaining_width).spec_or_default(Au(0));
let margin_right = MaybeAuto::from_margin(style.margin_right(),
remaining_width).spec_or_default(Au(0));
let shrink_to_fit = geometry::min(self.common.pref_width,
geometry::max(self.common.min_width,
remaining_width));
let width = MaybeAuto::from_width(style.width(),
remaining_width).spec_or_default(shrink_to_fit);
model.margin.top = margin_top;
model.margin.right = margin_right;
model.margin.bottom = margin_bottom;
model.margin.left = margin_left;
self.common.position.size.width = width;
x_offset = model.offset();
remaining_width = width;
}
do box.with_mut_base |base| {
//The associated box is the border box of this flow
base.position.origin.x = base.model.margin.left;
let pb = base.model.padding.left + base.model.padding.right +
base.model.border.left + base.model.border.right;
base.position.size.width = remaining_width + pb;
}
}
for FloatFlow(self).each_child |kid| {
//assert!(kid.starts_block_flow() || kid.starts_inline_flow());
do kid.with_mut_base |child_node| {
child_node.position.origin.x = x_offset;
child_node.position.size.width = remaining_width;
}
}
}
pub fn assign_height_float(@mut self, ctx: &mut LayoutContext) {
for FloatFlow(self).each_child |kid| {
kid.assign_height(ctx);
}
let mut cur_y = Au(0);
let mut top_offset = Au(0);
for self.box.each |&box| {
do box.with_model |model| {
top_offset = model.margin.top + model.border.top + model.padding.top;
cur_y += top_offset;
}
}
for FloatFlow(self).each_child |kid| {
do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y;
cur_y += child_node.position.size.height;
}
}
let height = cur_y - top_offset;
let mut noncontent_height = Au(0);
self.box.map(|&box| {
do box.with_mut_base |base| {
//The associated box is the border box of this flow
base.position.origin.y = base.model.margin.top;
noncontent_height = base.model.padding.top + base.model.padding.bottom +
base.model.border.top + base.model.border.bottom;
base.position.size.height = height + noncontent_height;
noncontent_height += base.model.margin.top + base.model.margin.bottom;
}
});
//TODO(eatkinson): compute heights using the 'height' property.
self.common.position.size.height = height + noncontent_height;
let info = PlacementInfo {
width: self.common.position.size.width,
height: self.common.position.size.height,
ceiling: Au(0),
max_width: self.containing_width,
f_type: FloatLeft,
};
self.common.floats_out = self.common.floats_in.add_float(&info);
}
pub fn build_display_list_float<E:ExtraDisplayListData>(@mut self,
builder: &DisplayListBuilder,
dirty: &Rect<Au>,
offset: &Point2D<Au>,
list: &Cell<DisplayList<E>>) {
self.box.map(|&box| {
box.build_display_list(builder, dirty, offset, list)
});
// go deeper into the flow tree
let flow = FloatFlow(self);
for flow.each_child |child| {
flow.build_display_list_for_child(builder, child, dirty, offset, list)
}
}
}

View file

@ -1,28 +1,32 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use geom::point::Point2D; use geom::point::Point2D;
use geom::size::Size2D; use geom::size::Size2D;
use geom::rect::Rect; use geom::rect::Rect;
use gfx::geometry::{Au, max, min}; use gfx::geometry::{Au, max, min};
use core::util::replace; use core::util::replace;
enum FloatType{ pub enum FloatType{
FloatLeft, FloatLeft,
FloatRight FloatRight
} }
priv struct FloatContextBase{ struct FloatContextBase{
float_data: ~[Option<FloatData>], float_data: ~[Option<FloatData>],
floats_used: uint, floats_used: uint,
max_y : Au, max_y : Au,
offset: Point2D<Au> offset: Point2D<Au>
} }
priv struct FloatData{ struct FloatData{
bounds: Rect<Au>, bounds: Rect<Au>,
f_type: FloatType f_type: FloatType
} }
/// All information necessary to place a float /// All information necessary to place a float
struct PlacementInfo{ pub struct PlacementInfo{
width: Au, // The dimensions of the float width: Au, // The dimensions of the float
height: Au, height: Au,
ceiling: Au, // The minimum top of the float, as determined by earlier elements ceiling: Au, // The minimum top of the float, as determined by earlier elements
@ -32,18 +36,26 @@ struct PlacementInfo{
/// Wrappers around float methods. To avoid allocating data we'll never use, /// Wrappers around float methods. To avoid allocating data we'll never use,
/// destroy the context on modification. /// destroy the context on modification.
enum FloatContext { pub enum FloatContext {
Invalid, Invalid,
Valid(FloatContextBase) Valid(FloatContextBase)
} }
impl FloatContext { impl FloatContext {
fn new(num_floats: uint) -> FloatContext { pub fn new(num_floats: uint) -> FloatContext {
Valid(FloatContextBase::new(num_floats)) Valid(FloatContextBase::new(num_floats))
} }
#[inline(always)] #[inline(always)]
priv fn with_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R { pub fn clone(&mut self) -> FloatContext {
match *self {
Invalid => fail!("Can't clone an invalid float context"),
Valid(_) => replace(self, Invalid)
}
}
#[inline(always)]
fn with_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), Invalid => fail!("Float context no longer available"),
Valid(ref mut base) => callback(base) Valid(ref mut base) => callback(base)
@ -51,7 +63,7 @@ impl FloatContext {
} }
#[inline(always)] #[inline(always)]
fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R { pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R {
match *self { match *self {
Invalid => fail!("Float context no longer available"), Invalid => fail!("Float context no longer available"),
Valid(ref base) => callback(base) Valid(ref base) => callback(base)
@ -59,7 +71,7 @@ impl FloatContext {
} }
#[inline(always)] #[inline(always)]
fn translate(&mut self, trans: Point2D<Au>) -> FloatContext { pub fn translate(&mut self, trans: Point2D<Au>) -> FloatContext {
do self.with_base |base| { do self.with_base |base| {
base.translate(trans); base.translate(trans);
} }
@ -67,14 +79,14 @@ impl FloatContext {
} }
#[inline(always)] #[inline(always)]
fn available_rect(&mut self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> { pub fn available_rect(&mut self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> {
do self.with_base |base| { do self.with_base |base| {
base.available_rect(top, height, max_x) base.available_rect(top, height, max_x)
} }
} }
#[inline(always)] #[inline(always)]
fn add_float(&mut self, info: &PlacementInfo) -> FloatContext{ pub fn add_float(&mut self, info: &PlacementInfo) -> FloatContext{
do self.with_base |base| { do self.with_base |base| {
base.add_float(info); base.add_float(info);
} }
@ -84,9 +96,8 @@ impl FloatContext {
impl FloatContextBase{ impl FloatContextBase{
fn new(num_floats: uint) -> FloatContextBase { fn new(num_floats: uint) -> FloatContextBase {
let new_data = do vec::build_sized(num_floats) |push_fun| { debug!("Creating float context of size %?", num_floats);
push_fun(None); let new_data = vec::from_elem(num_floats, None);
};
FloatContextBase { FloatContextBase {
float_data: new_data, float_data: new_data,
floats_used: 0, floats_used: 0,
@ -166,6 +177,7 @@ impl FloatContextBase{
} }
fn add_float(&mut self, info: &PlacementInfo) { fn add_float(&mut self, info: &PlacementInfo) {
debug!("Floats_used: %?, Floats available: %?", self.floats_used, self.float_data.len());
assert!(self.floats_used < self.float_data.len() && assert!(self.floats_used < self.float_data.len() &&
self.float_data[self.floats_used].is_none()); self.float_data[self.floats_used].is_none());
@ -177,6 +189,7 @@ impl FloatContextBase{
f_type: info.f_type f_type: info.f_type
}; };
self.float_data[self.floats_used] = Some(new_float); self.float_data[self.floats_used] = Some(new_float);
self.floats_used += 1;
} }
/// Given necessary info, finds the position of the float in /// Given necessary info, finds the position of the float in

View file

@ -26,10 +26,12 @@
/// similar methods. /// similar methods.
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::float::FloatFlowData;
use layout::box::RenderBox; use layout::box::RenderBox;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::inline::{InlineFlowData}; use layout::inline::{InlineFlowData};
use layout::float_context::{FloatContext, Invalid};
use core::cell::Cell; use core::cell::Cell;
use geom::point::Point2D; use geom::point::Point2D;
@ -44,7 +46,7 @@ use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
pub enum FlowContext { pub enum FlowContext {
AbsoluteFlow(@mut FlowData), AbsoluteFlow(@mut FlowData),
BlockFlow(@mut BlockFlowData), BlockFlow(@mut BlockFlowData),
FloatFlow(@mut FlowData), FloatFlow(@mut FloatFlowData),
InlineBlockFlow(@mut FlowData), InlineBlockFlow(@mut FlowData),
InlineFlow(@mut InlineFlowData), InlineFlow(@mut InlineFlowData),
TableFlow(@mut FlowData), TableFlow(@mut FlowData),
@ -70,10 +72,10 @@ impl FlowContext {
pub fn teardown(&self) { pub fn teardown(&self) {
match *self { match *self {
AbsoluteFlow(data) | AbsoluteFlow(data) |
FloatFlow(data) |
InlineBlockFlow(data) | InlineBlockFlow(data) |
TableFlow(data) => data.teardown(), TableFlow(data) => data.teardown(),
BlockFlow(data) => data.teardown(), BlockFlow(data) => data.teardown(),
FloatFlow(data) => data.teardown(),
InlineFlow(data) => data.teardown() InlineFlow(data) => data.teardown()
} }
} }
@ -110,7 +112,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
BlockFlow(info) => { BlockFlow(info) => {
callback(&info.common) callback(&info.common)
} }
FloatFlow(info) => callback(info), FloatFlow(info) => callback(&info.common),
InlineBlockFlow(info) => callback(info), InlineBlockFlow(info) => callback(info),
InlineFlow(info) => { InlineFlow(info) => {
callback(&info.common) callback(&info.common)
@ -124,7 +126,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
BlockFlow(info) => { BlockFlow(info) => {
callback(&mut info.common) callback(&mut info.common)
} }
FloatFlow(info) => callback(info), FloatFlow(info) => callback(&mut info.common),
InlineBlockFlow(info) => callback(info), InlineBlockFlow(info) => callback(info),
InlineFlow(info) => { InlineFlow(info) => {
callback(&mut info.common) callback(&mut info.common)
@ -156,6 +158,9 @@ pub struct FlowData {
min_width: Au, min_width: Au,
pref_width: Au, pref_width: Au,
position: Rect<Au>, position: Rect<Au>,
floats_in: FloatContext,
floats_out: FloatContext,
num_floats: uint,
} }
impl TreeNode<FlowContext> for FlowData { impl TreeNode<FlowContext> for FlowData {
@ -216,6 +221,9 @@ impl FlowData {
min_width: Au(0), min_width: Au(0),
pref_width: Au(0), pref_width: Au(0),
position: Au::zero_rect(), position: Au::zero_rect(),
floats_in: Invalid,
floats_out: Invalid,
num_floats: 0,
} }
} }
} }
@ -264,6 +272,7 @@ impl<'self> FlowContext {
match *self { match *self {
BlockFlow(info) => info.bubble_widths_block(ctx), BlockFlow(info) => info.bubble_widths_block(ctx),
InlineFlow(info) => info.bubble_widths_inline(ctx), InlineFlow(info) => info.bubble_widths_inline(ctx),
FloatFlow(info) => info.bubble_widths_float(ctx),
_ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id())) _ => fail!(fmt!("Tried to bubble_widths of flow: f%d", self.id()))
} }
} }
@ -272,6 +281,7 @@ impl<'self> FlowContext {
match *self { match *self {
BlockFlow(info) => info.assign_widths_block(ctx), BlockFlow(info) => info.assign_widths_block(ctx),
InlineFlow(info) => info.assign_widths_inline(ctx), InlineFlow(info) => info.assign_widths_inline(ctx),
FloatFlow(info) => info.assign_widths_float(ctx),
_ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id())) _ => fail!(fmt!("Tried to assign_widths of flow: f%d", self.id()))
} }
} }
@ -280,6 +290,7 @@ impl<'self> FlowContext {
match *self { match *self {
BlockFlow(info) => info.assign_height_block(ctx), BlockFlow(info) => info.assign_height_block(ctx),
InlineFlow(info) => info.assign_height_inline(ctx), InlineFlow(info) => info.assign_height_inline(ctx),
FloatFlow(info) => info.assign_height_float(ctx),
_ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id())) _ => fail!(fmt!("Tried to assign_height of flow: f%d", self.id()))
} }
} }
@ -296,6 +307,7 @@ impl<'self> FlowContext {
match *self { match *self {
BlockFlow(info) => info.build_display_list_block(builder, dirty, offset, list), BlockFlow(info) => info.build_display_list_block(builder, dirty, offset, list),
InlineFlow(info) => info.build_display_list_inline(builder, dirty, offset, list), InlineFlow(info) => info.build_display_list_inline(builder, dirty, offset, list),
FloatFlow(info) => info.build_display_list_float(builder, dirty, offset, list),
_ => fail!(fmt!("Tried to build_display_list_recurse of flow: %?", self)) _ => fail!(fmt!("Tried to build_display_list_recurse of flow: %?", self))
} }
} }
@ -412,7 +424,7 @@ impl<'self> FlowContext {
}; };
do self.with_base |base| { do self.with_base |base| {
fmt!("f%? %? size %?", base.id, repr, base.position) fmt!("f%? %? floats %?", base.id, repr, base.num_floats)
} }
} }
} }

View file

@ -10,6 +10,7 @@ use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData}; use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FlowContext, FlowData, InlineFlow}; use layout::flow::{FlowContext, FlowData, InlineFlow};
use layout::text::{UnscannedMethods, adapt_textbox_with_range}; use layout::text::{UnscannedMethods, adapt_textbox_with_range};
use layout::float_context::FloatContext;
use core::util; use core::util;
use geom::{Point2D, Rect, Size2D}; use geom::{Point2D, Rect, Size2D};
@ -25,6 +26,7 @@ use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLeng
use servo_util::range::Range; use servo_util::range::Range;
use std::deque::Deque; use std::deque::Deque;
use servo_util::tree::{TreeNodeRef, TreeUtils};
/* /*
Lineboxes are represented as offsets into the child list, rather than Lineboxes are represented as offsets into the child list, rather than
@ -394,7 +396,7 @@ impl TextRunScanner {
struct PendingLine { struct PendingLine {
range: Range, range: Range,
width: Au bounds: Rect<Au>
} }
struct LineboxScanner { struct LineboxScanner {
@ -413,8 +415,8 @@ impl LineboxScanner {
flow: inline, flow: inline,
new_boxes: ~[], new_boxes: ~[],
work_list: @mut Deque::new(), work_list: @mut Deque::new(),
pending_line: PendingLine {mut range: Range::empty(), mut width: Au(0)}, pending_line: PendingLine {mut range: Range::empty(), mut bounds: Rect(Point2D(Au(0), Au(0)), Size2D(Au(0), Au(0)))},
line_spans: ~[] line_spans: ~[],
} }
} }
@ -427,7 +429,7 @@ impl LineboxScanner {
fn reset_linebox(&mut self) { fn reset_linebox(&mut self) {
self.pending_line.range.reset(0,0); self.pending_line.range.reset(0,0);
self.pending_line.width = Au(0); self.pending_line.bounds = Rect(Point2D(Au(0), Au(0)), Size2D(Au(0), Au(0)));
} }
pub fn scan_for_lines(&mut self, ctx: &LayoutContext) { pub fn scan_for_lines(&mut self, ctx: &LayoutContext) {
@ -508,7 +510,7 @@ impl LineboxScanner {
linebox_align = CSSTextAlignLeft; linebox_align = CSSTextAlignLeft;
} }
let slack_width = self.flow.position().size.width - self.pending_line.width; let slack_width = self.flow.position().size.width - self.pending_line.bounds.size.width;
match linebox_align { match linebox_align {
// So sorry, but justified text is more complicated than shuffling linebox coordinates. // So sorry, but justified text is more complicated than shuffling linebox coordinates.
// TODO(Issue #213): implement `text-align: justify` // TODO(Issue #213): implement `text-align: justify`
@ -548,7 +550,7 @@ impl LineboxScanner {
// return value: whether any box was appended. // return value: whether any box was appended.
fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: RenderBox) -> bool { fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: RenderBox) -> bool {
let remaining_width = self.flow.position().size.width - self.pending_line.width; let remaining_width = self.flow.position().size.width - self.pending_line.bounds.size.width;
let in_box_width = in_box.position().size.width; let in_box_width = in_box.position().size.width;
let line_is_empty: bool = self.pending_line.range.length() == 0; let line_is_empty: bool = self.pending_line.range.length() == 0;
@ -639,7 +641,7 @@ impl LineboxScanner {
self.pending_line.range.reset(self.new_boxes.len(), 0); self.pending_line.range.reset(self.new_boxes.len(), 0);
} }
self.pending_line.range.extend_by(1); self.pending_line.range.extend_by(1);
self.pending_line.width += box.position().size.width; self.pending_line.bounds.size.width += box.position().size.width;
self.new_boxes.push(box); self.new_boxes.push(box);
} }
} }
@ -696,6 +698,15 @@ impl InlineFlowData {
pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) { pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) {
let mut scanner = TextRunScanner::new(); let mut scanner = TextRunScanner::new();
scanner.scan_for_runs(ctx, InlineFlow(self)); scanner.scan_for_runs(ctx, InlineFlow(self));
let mut num_floats = 0;
for InlineFlow(self).each_child |kid| {
do kid.with_mut_base |base| {
num_floats += base.num_floats;
base.floats_in = FloatContext::new(base.num_floats);
}
}
{ {
let this = &mut *self; let this = &mut *self;
@ -711,12 +722,13 @@ impl InlineFlowData {
this.common.min_width = min_width; this.common.min_width = min_width;
this.common.pref_width = pref_width; this.common.pref_width = pref_width;
this.common.num_floats = num_floats;
} }
} }
/// 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
/// on this context, the context has had its width set by the parent context. /// on this context, the context has had its width set by the parent context.
pub fn assign_widths_inline(@mut self, ctx: &mut LayoutContext) { pub fn assign_widths_inline(@mut self, _: &mut LayoutContext) {
// Initialize content box widths if they haven't been initialized already. // Initialize content box widths if they haven't been initialized already.
// //
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into // TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into
@ -745,9 +757,11 @@ impl InlineFlowData {
} // End of for loop. } // End of for loop.
} }
let mut scanner = LineboxScanner::new(InlineFlow(self)); for InlineFlow(self).each_child |kid| {
scanner.scan_for_lines(ctx); do kid.with_mut_base |base| {
base.position.size.width = self.common.position.size.width;
}
}
// There are no child contexts, so stop here. // There are no child contexts, so stop here.
// TODO(Issue #225): once there are 'inline-block' elements, this won't be // TODO(Issue #225): once there are 'inline-block' elements, this won't be
@ -757,7 +771,18 @@ impl InlineFlowData {
// 'inline-block' box that created this flow before recursing. // 'inline-block' box that created this flow before recursing.
} }
pub fn assign_height_inline(&mut self, _: &mut LayoutContext) { pub fn assign_height_inline(@mut self, ctx: &mut LayoutContext) {
for InlineFlow(self).each_child |kid| {
kid.assign_height(ctx);
}
// TODO(eatkinson): line boxes need to shrink if there are floats
let mut scanner = LineboxScanner::new(InlineFlow(self));
scanner.scan_for_lines(ctx);
self.common.floats_out = self.common.floats_in.clone();
// TODO(#226): Get the CSS `line-height` property from the containing block's style to // TODO(#226): Get the CSS `line-height` property from the containing block's style to
// determine minimum linebox height. // determine minimum linebox height.
// //
@ -774,10 +799,8 @@ impl InlineFlowData {
let mut linebox_height = Au(0); let mut linebox_height = Au(0);
let mut baseline_offset = Au(0); let mut baseline_offset = Au(0);
let boxes = &mut self.boxes;
for line_span.eachi |box_i| { for line_span.eachi |box_i| {
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround let cur_box = self.boxes[box_i];
// Compute the height and bounding box of each box. // Compute the height and bounding box of each box.
let bounding_box = match cur_box { let bounding_box = match cur_box {
@ -849,7 +872,7 @@ impl InlineFlowData {
// Now go back and adjust the Y coordinates to match the baseline we determined. // Now go back and adjust the Y coordinates to match the baseline we determined.
for line_span.eachi |box_i| { for line_span.eachi |box_i| {
let cur_box = boxes[box_i]; let cur_box = self.boxes[box_i];
// TODO(#226): This is completely wrong. We need to use the element's `line-height` // TODO(#226): This is completely wrong. We need to use the element's `line-height`
// when calculating line box height. Then we should go back over and set Y offsets // when calculating line box height. Then we should go back over and set Y offsets

View file

@ -225,9 +225,9 @@ impl Layout {
for layout_root.traverse_preorder |flow| { for layout_root.traverse_preorder |flow| {
flow.assign_widths(&mut layout_ctx); flow.assign_widths(&mut layout_ctx);
}; };
for layout_root.traverse_postorder |flow| {
flow.assign_height(&mut layout_ctx); // For now, this is an inorder traversal
}; layout_root.assign_height(&mut layout_ctx);
} }
// Build the display list if necessary, and send it to the renderer. // Build the display list if necessary, and send it to the renderer.

View file

@ -112,7 +112,7 @@ impl BoxModel {
} }
/// Helper function to compute the border width in app units from the CSS border width. /// Helper function to compute the border width in app units from the CSS border width.
priv fn compute_border_width(&self, width: CSSBorderWidth) -> Au { pub fn compute_border_width(&self, width: CSSBorderWidth) -> Au {
match width { match width {
CSSBorderWidthLength(Px(v)) | CSSBorderWidthLength(Px(v)) |
CSSBorderWidthLength(Em(v)) | CSSBorderWidthLength(Em(v)) |
@ -126,7 +126,7 @@ impl BoxModel {
} }
} }
fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au) -> Au { pub fn compute_padding_length(&self, padding: CSSPadding, content_box_width: Au) -> Au {
match padding { match padding {
CSSPaddingLength(Px(v)) | CSSPaddingLength(Px(v)) |
CSSPaddingLength(Pt(v)) | CSSPaddingLength(Pt(v)) |

View file

@ -73,7 +73,8 @@ pub mod layout {
pub mod inline; pub mod inline;
pub mod model; pub mod model;
pub mod text; pub mod text;
pub mod floats; pub mod float_context;
pub mod float;
mod aux; mod aux;
} }