mirror of
https://github.com/servo/servo.git
synced 2025-08-05 21:50:18 +01:00
auto merge of #542 : eric93/servo/floats, r=pcwalton
I added the minimal amount of code needed to place left-floats on the screen (right floats should also be possible soon). Text does not wrap around floats yet. One thing I'm curious about is whether some existing abstractions (like Cell) can be used instead of this weird overwriting thing done in float_context.rs.
This commit is contained in:
commit
a01f6b97f2
10 changed files with 694 additions and 36 deletions
|
@ -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,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 cur_y = Au(0);
|
||||||
let mut top_offset = Au(0);
|
let mut top_offset = Au(0);
|
||||||
|
let mut left_offset = Au(0);
|
||||||
|
|
||||||
for self.box.each |&box| {
|
for self.box.each |&box| {
|
||||||
do box.with_model |model| {
|
do box.with_model |model| {
|
||||||
top_offset = model.margin.top + model.border.top + model.padding.top;
|
top_offset = model.margin.top + model.border.top + model.padding.top;
|
||||||
cur_y += top_offset;
|
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| {
|
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 +311,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(left_offset, 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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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};
|
||||||
|
@ -159,6 +161,18 @@ impl BoxGenerator {
|
||||||
assert!(block.box.is_none());
|
assert!(block.box.is_none());
|
||||||
block.box = Some(new_box);
|
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()),
|
_ => warn!("push_node() not implemented for flow f%d", self.flow.id()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -349,8 +363,24 @@ pub impl LayoutTreeBuilder {
|
||||||
None => None,
|
None => None,
|
||||||
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.
|
||||||
|
@ -360,6 +390,11 @@ pub impl LayoutTreeBuilder {
|
||||||
self.create_child_generator(node, parent_generator, Flow_Block)
|
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
|
// Inlines that are children of inlines are part of the same flow
|
||||||
(CSSDisplayInline, InlineFlow(*), _) => parent_generator,
|
(CSSDisplayInline, InlineFlow(*), _) => parent_generator,
|
||||||
(CSSDisplayInlineBlock, InlineFlow(*), _) => parent_generator,
|
(CSSDisplayInlineBlock, InlineFlow(*), _) => parent_generator,
|
||||||
|
@ -371,6 +406,15 @@ pub impl LayoutTreeBuilder {
|
||||||
self.create_child_generator(node, parent_generator, Flow_Inline)
|
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
|
// Inlines whose previous sibling was not a block try to use their
|
||||||
// sibling's flow context.
|
// sibling's flow context.
|
||||||
(CSSDisplayInline, BlockFlow(*), _) |
|
(CSSDisplayInline, BlockFlow(*), _) |
|
||||||
|
@ -383,8 +427,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 +562,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)),
|
||||||
|
|
241
src/components/main/layout/float.rs
Normal file
241
src/components/main/layout/float.rs
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
258
src/components/main/layout/float_context.rs
Normal file
258
src/components/main/layout/float_context.rs
Normal file
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -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))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -408,11 +420,17 @@ impl<'self> FlowContext {
|
||||||
None => ~"BlockFlow",
|
None => ~"BlockFlow",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
FloatFlow(float) => {
|
||||||
|
match float.box {
|
||||||
|
Some(box) => fmt!("FloatFlow(box=b%d)", box.id()),
|
||||||
|
None => ~"FloatFlow",
|
||||||
|
}
|
||||||
|
},
|
||||||
_ => ~"(Unknown flow)"
|
_ => ~"(Unknown flow)"
|
||||||
};
|
};
|
||||||
|
|
||||||
do self.with_base |base| {
|
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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -20,6 +20,7 @@ use newcss::units::{Em, Pt, Px};
|
||||||
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
|
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
|
||||||
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
|
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
|
||||||
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
|
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
|
||||||
|
use newcss::values::{CSSHeight, CSSHeightLength, CSSHeightPercentage, CSSHeightAuto};
|
||||||
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
|
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
|
||||||
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
|
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
|
||||||
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
|
/// 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{
|
pub fn spec_or_default(&self, default: Au) -> Au{
|
||||||
match *self{
|
match *self{
|
||||||
Auto => default,
|
Auto => default,
|
||||||
|
@ -112,7 +124,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 +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 {
|
match padding {
|
||||||
CSSPaddingLength(Px(v)) |
|
CSSPaddingLength(Px(v)) |
|
||||||
CSSPaddingLength(Pt(v)) |
|
CSSPaddingLength(Pt(v)) |
|
||||||
|
|
|
@ -73,6 +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 float_context;
|
||||||
|
pub mod float;
|
||||||
mod aux;
|
mod aux;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue