aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/components/main/layout/block.rs40
-rw-r--r--src/components/main/layout/box.rs36
-rw-r--r--src/components/main/layout/box_builder.rs48
-rw-r--r--src/components/main/layout/float.rs241
-rw-r--r--src/components/main/layout/float_context.rs258
-rw-r--r--src/components/main/layout/flow.rs28
-rw-r--r--src/components/main/layout/inline.rs55
-rw-r--r--src/components/main/layout/layout_task.rs6
-rw-r--r--src/components/main/layout/model.rs16
-rwxr-xr-xsrc/components/main/servo.rc2
10 files changed, 694 insertions, 36 deletions
diff --git a/src/components/main/layout/block.rs b/src/components/main/layout/block.rs
index f7f07b14df2..3fa9906423a 100644
--- a/src/components/main/layout/block.rs
+++ b/src/components/main/layout/block.rs
@@ -8,9 +8,10 @@ use layout::box::{RenderBox};
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
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::model::{MaybeAuto, Specified, Auto};
+use layout::float_context::FloatContext;
use core::cell::Cell;
use geom::point::Point2D;
@@ -72,7 +73,7 @@ impl BlockLayout for FlowContext {
fn starts_block_flow(&self) -> bool {
match *self {
- BlockFlow(*) | InlineBlockFlow(*) => true,
+ BlockFlow(*) | InlineBlockFlow(*) | FloatFlow(*) => true,
_ => false
}
}
@@ -91,14 +92,17 @@ impl BlockFlowData {
pub fn bubble_widths_block(@mut self, ctx: &LayoutContext) {
let mut min_width = Au(0);
let mut pref_width = Au(0);
+ let mut num_floats = 0;
/* find max width from child block contexts */
for BlockFlow(self).each_child |child_ctx| {
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);
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.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.
@@ -180,6 +185,7 @@ impl BlockFlowData {
debug!("Setting root position");
self.common.position.origin = Au::zero_point();
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
@@ -240,17 +246,41 @@ 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 top_offset = Au(0);
+ let mut left_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;
+ left_offset = model.offset();
}
}
+ // 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 method)
+ let mut float_ctx = self.common.floats_in.translate(Point2D(-left_offset, -top_offset));
+ 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| {
do kid.with_mut_base |child_node| {
child_node.position.origin.y = cur_y;
@@ -281,6 +311,8 @@ impl BlockFlowData {
//TODO(eatkinson): compute heights using the 'height' property.
self.common.position.size.height = height + noncontent_height;
+
+ self.common.floats_out = float_ctx.translate(Point2D(left_offset, self.common.position.size.height));
}
pub fn build_display_list_block<E:ExtraDisplayListData>(@mut self,
diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs
index 141425c30ed..7d7db5410bc 100644
--- a/src/components/main/layout/box.rs
+++ b/src/components/main/layout/box.rs
@@ -8,7 +8,7 @@ use css::node_style::StyledNode;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
use layout::flow::FlowContext;
-use layout::model::BoxModel;
+use layout::model::{BoxModel, MaybeAuto};
use layout::text;
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.
fn get_min_width(&self, _: &LayoutContext) -> Au {
// FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
// 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.
// 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
@@ -397,7 +427,7 @@ pub impl RenderBox {
/// Returns the *preferred width* of this render box as defined by the CSS specification.
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.
// 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
diff --git a/src/components/main/layout/box_builder.rs b/src/components/main/layout/box_builder.rs
index b446f8a7172..8d5134b7c4e 100644
--- a/src/components/main/layout/box_builder.rs
+++ b/src/components/main/layout/box_builder.rs
@@ -6,6 +6,7 @@
use layout::aux::LayoutAuxMethods;
use layout::block::BlockFlowData;
+use layout::float::FloatFlowData;
use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image};
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::{CSSDisplayTableCell, CSSDisplayTableCaption};
use newcss::values::{CSSDisplayNone};
+use newcss::values::{CSSFloatNone};
use script::dom::element::*;
use script::dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
use script::dom::node::{ElementNodeTypeId, LayoutView, TextNodeTypeId};
@@ -159,6 +161,18 @@ impl BoxGenerator {
assert!(block.box.is_none());
block.box = Some(new_box);
},
+ FloatFlow(float) => {
+ debug!("BoxGenerator[f%d]: point b", float.common.id);
+ let new_box = self.make_box(ctx, box_type, node, self.flow, builder);
+
+ debug!("BoxGenerator[f%d]: attaching box[b%d] to float flow (node: %s)",
+ float.common.id,
+ new_box.id(),
+ node.debug_str());
+
+ assert!(float.box.is_none());
+ float.box = Some(new_box);
+ },
_ => warn!("push_node() not implemented for flow f%d", self.flow.id()),
}
}
@@ -349,8 +363,24 @@ pub impl LayoutTreeBuilder {
None => None,
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) {
+ (CSSDisplayBlock, BlockFlow(_), _) if is_float => {
+ self.create_child_generator(node, parent_generator, Flow_Float)
+ }
+
(CSSDisplayBlock, BlockFlow(info), _) => match (info.is_root, node.parent_node()) {
// If this is the root node, then use the root flow's
// context. Otherwise, make a child block context.
@@ -360,6 +390,11 @@ pub impl LayoutTreeBuilder {
self.create_child_generator(node, parent_generator, Flow_Block)
}
},
+
+ (CSSDisplayBlock, FloatFlow(*), _) => {
+ self.create_child_generator(node, parent_generator, Flow_Block)
+ }
+
// Inlines that are children of inlines are part of the same flow
(CSSDisplayInline, InlineFlow(*), _) => parent_generator,
(CSSDisplayInlineBlock, InlineFlow(*), _) => parent_generator,
@@ -371,6 +406,15 @@ pub impl LayoutTreeBuilder {
self.create_child_generator(node, parent_generator, Flow_Inline)
}
+ // FIXME(eatkinson): this is bogus. Floats should not be able to split
+ // inlines. They should be appended as children of the inline flow.
+ (CSSDisplayInline, _, Some(FloatFlow(*))) |
+ (CSSDisplayInlineBlock, _, Some(FloatFlow(*))) |
+ (CSSDisplayInline, FloatFlow(*), _) |
+ (CSSDisplayInlineBlock, FloatFlow(*), _) => {
+ self.create_child_generator(node, parent_generator, Flow_Inline)
+ }
+
// Inlines whose previous sibling was not a block try to use their
// sibling's flow context.
(CSSDisplayInline, BlockFlow(*), _) |
@@ -383,8 +427,6 @@ pub impl LayoutTreeBuilder {
// TODO(eatkinson): blocks that are children of inlines need
// to split their parent flows.
- //
- // TODO(eatkinson): floats and positioned elements.
_ => parent_generator
};
@@ -520,7 +562,7 @@ pub impl LayoutTreeBuilder {
let result = match ty {
Flow_Absolute => AbsoluteFlow(@mut 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_Inline => InlineFlow(@mut InlineFlowData::new(info)),
Flow_Root => BlockFlow(@mut BlockFlowData::new_root(info)),
diff --git a/src/components/main/layout/float.rs b/src/components/main/layout/float.rs
new file mode 100644
index 00000000000..d1ea3c3fdc8
--- /dev/null
+++ b/src/components/main/layout/float.rs
@@ -0,0 +1,241 @@
+/* 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,
+
+ /// Parent clobbers our position, so store it separately
+ rel_pos: Point2D<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,
+ rel_pos: Point2D(Au(0), Au(0)),
+ }
+ }
+
+ 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_float: 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);
+ debug!("assign_widths_float -- width: %?", width);
+
+ model.margin.top = margin_top;
+ model.margin.right = margin_right;
+ model.margin.bottom = margin_bottom;
+ model.margin.left = margin_left;
+
+ 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;
+ }
+ }
+
+ self.common.position.size.width = remaining_width;
+
+ 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 mut 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 properly using the 'height' property.
+ for self.box.each |&box| {
+
+ let height_prop =
+ MaybeAuto::from_height(box.style().height(), Au(0)).spec_or_default(Au(0));
+
+ height = geometry::max(height, height_prop) + noncontent_height;
+ debug!("assign_height_float -- height: %?", height);
+ do box.with_mut_base |base| {
+ base.position.size.height = height;
+ }
+ }
+
+ let info = PlacementInfo {
+ width: self.common.position.size.width,
+ height: height,
+ ceiling: Au(0),
+ max_width: self.containing_width,
+ f_type: FloatLeft,
+ };
+
+ // Place the float and return the FloatContext back to the parent flow.
+ // After, grab the position and use that to set our position.
+ self.common.floats_out = self.common.floats_in.add_float(&info);
+ self.rel_pos = self.common.floats_out.last_float_pos();
+ }
+
+ pub fn build_display_list_float<E:ExtraDisplayListData>(@mut self,
+ builder: &DisplayListBuilder,
+ dirty: &Rect<Au>,
+ offset: &Point2D<Au>,
+ list: &Cell<DisplayList<E>>) {
+
+ let offset = *offset + self.rel_pos;
+ 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)
+ }
+ }
+}
+
diff --git a/src/components/main/layout/float_context.rs b/src/components/main/layout/float_context.rs
new file mode 100644
index 00000000000..50fbb5a452b
--- /dev/null
+++ b/src/components/main/layout/float_context.rs
@@ -0,0 +1,258 @@
+/* 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::size::Size2D;
+use geom::rect::Rect;
+use gfx::geometry::{Au, max, min};
+use core::util::replace;
+
+pub enum FloatType{
+ FloatLeft,
+ FloatRight
+}
+
+struct FloatContextBase{
+ float_data: ~[Option<FloatData>],
+ floats_used: uint,
+ max_y : Au,
+ offset: Point2D<Au>
+}
+
+struct FloatData{
+ bounds: Rect<Au>,
+ f_type: FloatType
+}
+
+/// All information necessary to place a float
+pub struct PlacementInfo{
+ width: Au, // The dimensions of the float
+ height: Au,
+ ceiling: Au, // The minimum top of the float, as determined by earlier elements
+ max_width: Au, // The maximum right of the float, generally determined by the contining block
+ f_type: FloatType // left or right
+}
+
+/// Wrappers around float methods. To avoid allocating data we'll never use,
+/// destroy the context on modification.
+pub enum FloatContext {
+ Invalid,
+ Valid(FloatContextBase)
+}
+
+impl FloatContext {
+ pub fn new(num_floats: uint) -> FloatContext {
+ Valid(FloatContextBase::new(num_floats))
+ }
+
+ #[inline(always)]
+ 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_mut_base<R>(&mut self, callback: &fn(&mut FloatContextBase) -> R) -> R {
+ match *self {
+ Invalid => fail!("Float context no longer available"),
+ Valid(ref mut base) => callback(base)
+ }
+ }
+
+ #[inline(always)]
+ pub fn with_base<R>(&self, callback: &fn(&FloatContextBase) -> R) -> R {
+ match *self {
+ Invalid => fail!("Float context no longer available"),
+ Valid(ref base) => callback(base)
+ }
+ }
+
+ #[inline(always)]
+ pub fn translate(&mut self, trans: Point2D<Au>) -> FloatContext {
+ do self.with_mut_base |base| {
+ base.translate(trans);
+ }
+ replace(self, Invalid)
+ }
+
+ #[inline(always)]
+ pub fn available_rect(&mut self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> {
+ do self.with_base |base| {
+ base.available_rect(top, height, max_x)
+ }
+ }
+
+ #[inline(always)]
+ pub fn add_float(&mut self, info: &PlacementInfo) -> FloatContext{
+ do self.with_mut_base |base| {
+ base.add_float(info);
+ }
+ replace(self, Invalid)
+ }
+
+ #[inline(always)]
+ pub fn last_float_pos(&mut self) -> Point2D<Au> {
+ do self.with_base |base| {
+ base.last_float_pos()
+ }
+ }
+}
+
+impl FloatContextBase{
+ fn new(num_floats: uint) -> FloatContextBase {
+ debug!("Creating float context of size %?", num_floats);
+ let new_data = vec::from_elem(num_floats, None);
+ FloatContextBase {
+ float_data: new_data,
+ floats_used: 0,
+ max_y: Au(0),
+ offset: Point2D(Au(0), Au(0))
+ }
+ }
+
+ fn translate(&mut self, trans: Point2D<Au>) {
+ self.offset += trans;
+ }
+
+ fn last_float_pos(&self) -> Point2D<Au> {
+ assert!(self.floats_used > 0, "Error: tried to access FloatContext with no floats in it");
+
+ match self.float_data[self.floats_used - 1] {
+ None => fail!("FloatContext error: floats should never be None here"),
+ Some(float) => {
+ debug!("Returning float position: %?", float.bounds.origin + self.offset);
+ float.bounds.origin + self.offset
+ }
+ }
+ }
+
+ /// Returns a rectangle that encloses the region from top to top + height,
+ /// with width small enough that it doesn't collide with any floats. max_x
+ /// is the x-coordinate beyond which floats have no effect (generally
+ /// this is the containing block width).
+ fn available_rect(&self, top: Au, height: Au, max_x: Au) -> Option<Rect<Au>> {
+ fn range_intersect(top_1: Au, bottom_1: Au, top_2: Au, bottom_2: Au) -> (Au, Au) {
+ (max(top_1, top_2), min(bottom_1, bottom_2))
+ }
+
+ debug!("available_rect: trying to find space at %?", top);
+
+ let top = top - self.offset.y;
+
+ // Relevant dimensions for the right-most left float
+ let mut (max_left, l_top, l_bottom) = (Au(0) - self.offset.x, None, None);
+ // Relevant dimensions for the left-most right float
+ let mut (min_right, r_top, r_bottom) = (max_x - self.offset.x, None, None);
+
+ // Find the float collisions for the given vertical range.
+ for self.float_data.each |float| {
+ match *float{
+ None => (),
+ Some(data) => {
+ let float_pos = data.bounds.origin;
+ let float_size = data.bounds.size;
+ match data.f_type {
+ FloatLeft => {
+ if(float_pos.x + float_size.width > max_left &&
+ float_pos.y + float_size.height > top && float_pos.y < top + height) {
+ max_left = float_pos.x + float_size.width;
+
+ l_top = Some(float_pos.y);
+ l_bottom = Some(float_pos.y + float_size.height);
+ }
+ }
+ FloatRight => {
+ if(float_pos.x < min_right &&
+ float_pos.y + float_size.height > top && float_pos.y < top + height) {
+ min_right = float_pos.x;
+
+ r_top = Some(float_pos.y);
+ r_bottom = Some(float_pos.y + float_size.height);
+ }
+ }
+ }
+ }
+ };
+ }
+
+ // Extend the vertical range of the rectangle to the closest floats.
+ // If there are floats on both sides, take the intersection of the
+ // two areas.
+ let (top, bottom) = match (r_top, r_bottom, l_top, l_bottom) {
+ (Some(r_top), Some(r_bottom), Some(l_top), Some(l_bottom)) =>
+ range_intersect(r_top, r_bottom, l_top, l_bottom),
+
+ (None, None, Some(l_top), Some(l_bottom)) => (l_top, l_bottom),
+ (Some(r_top), Some(r_bottom), None, None) => (r_top, r_bottom),
+ (None, None, None, None) => return None,
+ _ => fail!("Reached unreachable state when computing float area")
+ };
+
+ // When the window is smaller than the float, we will return a rect
+ // with negative width.
+ assert!(max_left < min_right
+ || max_left > max_x - self.offset.x
+ || min_right < Au(0) - self.offset.x
+ ,"Float position error");
+
+ //TODO(eatkinson): do we need to do something similar for heights?
+ assert!(top < bottom, "Float position error");
+
+ Some(Rect{
+ origin: Point2D(max_left, top) + self.offset,
+ size: Size2D(min_right - max_left, bottom - top)
+ })
+ }
+
+ 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() &&
+ self.float_data[self.floats_used].is_none());
+
+ let new_float = FloatData {
+ bounds: Rect {
+ origin: self.place_float(info) - self.offset,
+ size: Size2D(info.width, info.height)
+ },
+ f_type: info.f_type
+ };
+ self.float_data[self.floats_used] = Some(new_float);
+ self.floats_used += 1;
+ }
+
+ /// Given necessary info, finds the position of the float in
+ /// LOCAL COORDINATES. i.e. must be translated before placed
+ /// in the float list
+ fn place_float(&self, info: &PlacementInfo) -> Point2D<Au>{
+ debug!("place_float: Placing float with width %? and height %?", info.width, info.height);
+ // Can't go any higher than previous floats or
+ // previous elements in the document.
+ let mut float_y = max(info.ceiling, self.max_y + self.offset.y);
+ loop {
+ let maybe_location = self.available_rect(float_y, info.height, info.max_width);
+ debug!("place_float: Got available rect: %? for y-pos: %?", maybe_location, float_y);
+ match maybe_location {
+ // If there are no floats blocking us, return the current location
+ // TODO(eatknson): integrate with overflow
+ None => return Point2D(Au(0), float_y),
+ Some(rect) => {
+ assert!(rect.origin.y + rect.size.height != float_y,
+ "Non-terminating float placement");
+
+ // Place here if there is enough room
+ if (rect.size.width >= info.width) {
+ return Point2D(rect.origin.x, float_y);
+ }
+
+ // Try to place at the next-lowest location.
+ // Need to be careful of fencepost errors.
+ float_y = rect.origin.y + rect.size.height;
+ }
+ }
+ }
+ }
+}
+
diff --git a/src/components/main/layout/flow.rs b/src/components/main/layout/flow.rs
index 900a7b0a130..e2c14ce22e9 100644
--- a/src/components/main/layout/flow.rs
+++ b/src/components/main/layout/flow.rs
@@ -26,10 +26,12 @@
/// similar methods.
use layout::block::BlockFlowData;
+use layout::float::FloatFlowData;
use layout::box::RenderBox;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::inline::{InlineFlowData};
+use layout::float_context::{FloatContext, Invalid};
use core::cell::Cell;
use geom::point::Point2D;
@@ -44,7 +46,7 @@ use servo_util::tree::{TreeNode, TreeNodeRef, TreeUtils};
pub enum FlowContext {
AbsoluteFlow(@mut FlowData),
BlockFlow(@mut BlockFlowData),
- FloatFlow(@mut FlowData),
+ FloatFlow(@mut FloatFlowData),
InlineBlockFlow(@mut FlowData),
InlineFlow(@mut InlineFlowData),
TableFlow(@mut FlowData),
@@ -70,10 +72,10 @@ impl FlowContext {
pub fn teardown(&self) {
match *self {
AbsoluteFlow(data) |
- FloatFlow(data) |
InlineBlockFlow(data) |
TableFlow(data) => data.teardown(),
BlockFlow(data) => data.teardown(),
+ FloatFlow(data) => data.teardown(),
InlineFlow(data) => data.teardown()
}
}
@@ -110,7 +112,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
BlockFlow(info) => {
callback(&info.common)
}
- FloatFlow(info) => callback(info),
+ FloatFlow(info) => callback(&info.common),
InlineBlockFlow(info) => callback(info),
InlineFlow(info) => {
callback(&info.common)
@@ -124,7 +126,7 @@ impl TreeNodeRef<FlowData> for FlowContext {
BlockFlow(info) => {
callback(&mut info.common)
}
- FloatFlow(info) => callback(info),
+ FloatFlow(info) => callback(&mut info.common),
InlineBlockFlow(info) => callback(info),
InlineFlow(info) => {
callback(&mut info.common)
@@ -156,6 +158,9 @@ pub struct FlowData {
min_width: Au,
pref_width: Au,
position: Rect<Au>,
+ floats_in: FloatContext,
+ floats_out: FloatContext,
+ num_floats: uint,
}
impl TreeNode<FlowContext> for FlowData {
@@ -216,6 +221,9 @@ impl FlowData {
min_width: Au(0),
pref_width: Au(0),
position: Au::zero_rect(),
+ floats_in: Invalid,
+ floats_out: Invalid,
+ num_floats: 0,
}
}
}
@@ -264,6 +272,7 @@ impl<'self> FlowContext {
match *self {
BlockFlow(info) => info.bubble_widths_block(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()))
}
}
@@ -272,6 +281,7 @@ impl<'self> FlowContext {
match *self {
BlockFlow(info) => info.assign_widths_block(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()))
}
}
@@ -280,6 +290,7 @@ impl<'self> FlowContext {
match *self {
BlockFlow(info) => info.assign_height_block(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()))
}
}
@@ -296,6 +307,7 @@ impl<'self> FlowContext {
match *self {
BlockFlow(info) => info.build_display_list_block(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))
}
}
@@ -408,11 +420,17 @@ impl<'self> FlowContext {
None => ~"BlockFlow",
}
},
+ FloatFlow(float) => {
+ match float.box {
+ Some(box) => fmt!("FloatFlow(box=b%d)", box.id()),
+ None => ~"FloatFlow",
+ }
+ },
_ => ~"(Unknown flow)"
};
do self.with_base |base| {
- fmt!("f%? %? size %?", base.id, repr, base.position)
+ fmt!("f%? %? floats %? size %?", base.id, repr, base.num_floats, base.position)
}
}
}
diff --git a/src/components/main/layout/inline.rs b/src/components/main/layout/inline.rs
index 1e14c36dec9..4894da2fb1c 100644
--- a/src/components/main/layout/inline.rs
+++ b/src/components/main/layout/inline.rs
@@ -10,6 +10,7 @@ use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
use layout::flow::{FlowContext, FlowData, InlineFlow};
use layout::text::{UnscannedMethods, adapt_textbox_with_range};
+use layout::float_context::FloatContext;
use core::util;
use geom::{Point2D, Rect, Size2D};
@@ -25,6 +26,7 @@ use newcss::values::{CSSLineHeightNormal, CSSLineHeightNumber, CSSLineHeightLeng
use servo_util::range::Range;
use std::deque::Deque;
+use servo_util::tree::{TreeNodeRef, TreeUtils};
/*
Lineboxes are represented as offsets into the child list, rather than
@@ -394,7 +396,7 @@ impl TextRunScanner {
struct PendingLine {
range: Range,
- width: Au
+ bounds: Rect<Au>
}
struct LineboxScanner {
@@ -413,8 +415,8 @@ impl LineboxScanner {
flow: inline,
new_boxes: ~[],
work_list: @mut Deque::new(),
- pending_line: PendingLine {mut range: Range::empty(), mut width: Au(0)},
- line_spans: ~[]
+ pending_line: PendingLine {mut range: Range::empty(), mut bounds: Rect(Point2D(Au(0), Au(0)), Size2D(Au(0), Au(0)))},
+ line_spans: ~[],
}
}
@@ -427,7 +429,7 @@ impl LineboxScanner {
fn reset_linebox(&mut self) {
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) {
@@ -508,7 +510,7 @@ impl LineboxScanner {
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 {
// So sorry, but justified text is more complicated than shuffling linebox coordinates.
// TODO(Issue #213): implement `text-align: justify`
@@ -548,7 +550,7 @@ impl LineboxScanner {
// return value: whether any box was appended.
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 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.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);
}
}
@@ -696,6 +698,15 @@ impl InlineFlowData {
pub fn bubble_widths_inline(@mut self, ctx: &mut LayoutContext) {
let mut scanner = TextRunScanner::new();
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;
@@ -711,12 +722,13 @@ impl InlineFlowData {
this.common.min_width = min_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
/// 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.
//
// 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.
}
- let mut scanner = LineboxScanner::new(InlineFlow(self));
- scanner.scan_for_lines(ctx);
-
+ for InlineFlow(self).each_child |kid| {
+ do kid.with_mut_base |base| {
+ base.position.size.width = self.common.position.size.width;
+ }
+ }
// There are no child contexts, so stop here.
// 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.
}
- 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
// determine minimum linebox height.
//
@@ -774,10 +799,8 @@ impl InlineFlowData {
let mut linebox_height = Au(0);
let mut baseline_offset = Au(0);
- let boxes = &mut self.boxes;
-
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.
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.
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`
// when calculating line box height. Then we should go back over and set Y offsets
diff --git a/src/components/main/layout/layout_task.rs b/src/components/main/layout/layout_task.rs
index 0389f7135ce..0d683ec25f4 100644
--- a/src/components/main/layout/layout_task.rs
+++ b/src/components/main/layout/layout_task.rs
@@ -225,9 +225,9 @@ impl Layout {
for layout_root.traverse_preorder |flow| {
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.
diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs
index 37195aa757e..bd465db83b9 100644
--- a/src/components/main/layout/model.rs
+++ b/src/components/main/layout/model.rs
@@ -20,6 +20,7 @@ use newcss::units::{Em, Pt, Px};
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
+use newcss::values::{CSSHeight, CSSHeightLength, CSSHeightPercentage, CSSHeightAuto};
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
@@ -61,6 +62,17 @@ impl MaybeAuto{
}
}
+ pub fn from_height(height: CSSHeight, cb_height: Au) -> MaybeAuto{
+ match height {
+ CSSHeightAuto => Auto,
+ CSSHeightPercentage(percent) => Specified(cb_height.scale_by(percent/100.0)),
+ //FIXME(eatkinson): Compute pt and em values properly
+ CSSHeightLength(Px(v)) |
+ CSSHeightLength(Pt(v)) |
+ CSSHeightLength(Em(v)) => Specified(Au::from_frac_px(v)),
+ }
+ }
+
pub fn spec_or_default(&self, default: Au) -> Au{
match *self{
Auto => default,
@@ -112,7 +124,7 @@ impl BoxModel {
}
/// 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 {
CSSBorderWidthLength(Px(v)) |
CSSBorderWidthLength(Em(v)) |
@@ -126,7 +138,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 {
CSSPaddingLength(Px(v)) |
CSSPaddingLength(Pt(v)) |
diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc
index 99073d701a6..4cdfb710ee5 100755
--- a/src/components/main/servo.rc
+++ b/src/components/main/servo.rc
@@ -73,6 +73,8 @@ pub mod layout {
pub mod inline;
pub mod model;
pub mod text;
+ pub mod float_context;
+ pub mod float;
mod aux;
}