mirror of
https://github.com/servo/servo.git
synced 2025-06-19 14:48:59 +01:00
Remove 'FloatFlow'
Removes 'FloatFlow' in favor of FloatBlockFlow, which is cointained inside BlockFlow in a 'has-a' relationship. This avoids a bunch of duplicated code. This patch is for: https://github.com/mozilla/servo/issues/1281
This commit is contained in:
parent
a0c6075b4d
commit
ac45d70a4a
5 changed files with 321 additions and 412 deletions
|
@ -10,7 +10,7 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
|||
use layout::flow::{BlockFlowClass, FlowClass, Flow, FlowData, ImmutableFlowUtils};
|
||||
use layout::flow;
|
||||
use layout::model::{MaybeAuto, Specified, Auto, specified_or_none, specified};
|
||||
use layout::float_context::{FloatContext, Invalid};
|
||||
use layout::float_context::{FloatContext, PlacementInfo, Invalid, FloatType};
|
||||
|
||||
use std::cell::Cell;
|
||||
use geom::point::Point2D;
|
||||
|
@ -20,6 +20,34 @@ use gfx::display_list::DisplayList;
|
|||
use servo_util::geometry::{Au, to_frac_px};
|
||||
use servo_util::geometry;
|
||||
|
||||
pub struct FloatedBlockInfo {
|
||||
containing_width: Au,
|
||||
|
||||
/// Offset relative to where the parent tried to position this flow
|
||||
rel_pos: Point2D<Au>,
|
||||
|
||||
/// Index into the box list for inline floats
|
||||
index: Option<uint>,
|
||||
|
||||
/// Number of floated children
|
||||
floated_children: uint,
|
||||
|
||||
/// Left or right?
|
||||
float_type: FloatType
|
||||
}
|
||||
|
||||
impl FloatedBlockInfo {
|
||||
pub fn new(float_type: FloatType) -> FloatedBlockInfo {
|
||||
FloatedBlockInfo {
|
||||
containing_width: Au(0),
|
||||
rel_pos: Point2D(Au(0), Au(0)),
|
||||
index: None,
|
||||
floated_children: 0,
|
||||
float_type: float_type
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BlockFlow {
|
||||
/// Data common to all flows.
|
||||
base: FlowData,
|
||||
|
@ -28,7 +56,10 @@ pub struct BlockFlow {
|
|||
box: Option<@RenderBox>,
|
||||
|
||||
/// Whether this block flow is the root flow.
|
||||
is_root: bool
|
||||
is_root: bool,
|
||||
|
||||
// Additional floating flow members.
|
||||
float: Option<~FloatedBlockInfo>
|
||||
}
|
||||
|
||||
impl BlockFlow {
|
||||
|
@ -36,7 +67,8 @@ impl BlockFlow {
|
|||
BlockFlow {
|
||||
base: base,
|
||||
box: None,
|
||||
is_root: false
|
||||
is_root: false,
|
||||
float: None
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -44,7 +76,17 @@ impl BlockFlow {
|
|||
BlockFlow {
|
||||
base: base,
|
||||
box: Some(box),
|
||||
is_root: false
|
||||
is_root: false,
|
||||
float: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn float_from_box(base: FlowData, float_type: FloatType, box: @RenderBox) -> BlockFlow {
|
||||
BlockFlow {
|
||||
base: base,
|
||||
box: Some(box),
|
||||
is_root: false,
|
||||
float: Some(~FloatedBlockInfo::new(float_type))
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,15 +94,30 @@ impl BlockFlow {
|
|||
BlockFlow {
|
||||
base: base,
|
||||
box: None,
|
||||
is_root: true
|
||||
is_root: true,
|
||||
float: None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new_float(base: FlowData, float_type: FloatType) -> BlockFlow {
|
||||
BlockFlow {
|
||||
base: base,
|
||||
box: None,
|
||||
is_root: false,
|
||||
float: Some(~FloatedBlockInfo::new(float_type))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_float(&self) -> bool {
|
||||
self.float.is_some()
|
||||
}
|
||||
|
||||
pub fn teardown(&mut self) {
|
||||
for box in self.box.iter() {
|
||||
box.teardown();
|
||||
}
|
||||
self.box = None;
|
||||
self.float = None;
|
||||
}
|
||||
|
||||
/// Computes left and right margins and width based on CSS 2.1 section 10.3.3.
|
||||
|
@ -115,6 +172,63 @@ impl BlockFlow {
|
|||
(width_Au, left_margin_Au, right_margin_Au)
|
||||
}
|
||||
|
||||
fn compute_block_margins(&self, box: @RenderBox, remaining_width: Au, available_width: Au) -> (Au, Au, Au) {
|
||||
let base = box.base();
|
||||
let style = base.style();
|
||||
|
||||
let (width, maybe_margin_left, maybe_margin_right) =
|
||||
(MaybeAuto::from_style(style.Box.width, remaining_width),
|
||||
MaybeAuto::from_style(style.Margin.margin_left, remaining_width),
|
||||
MaybeAuto::from_style(style.Margin.margin_right, remaining_width));
|
||||
|
||||
let (width, margin_left, margin_right) = self.compute_horiz(width,
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width);
|
||||
|
||||
// If the tentative used width is greater than 'max-width', width should be recalculated,
|
||||
// but this time using the computed value of 'max-width' as the computed value for 'width'.
|
||||
let (width, margin_left, margin_right) = {
|
||||
match specified_or_none(style.Box.max_width, remaining_width) {
|
||||
Some(value) if value < width => self.compute_horiz(Specified(value),
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width),
|
||||
_ => (width, margin_left, margin_right)
|
||||
}
|
||||
};
|
||||
|
||||
// If the resulting width is smaller than 'min-width', width should be recalculated,
|
||||
// but this time using the value of 'min-width' as the computed value for 'width'.
|
||||
let (width, margin_left, margin_right) = {
|
||||
let computed_min_width = specified(style.Box.min_width, remaining_width);
|
||||
if computed_min_width > width {
|
||||
self.compute_horiz(Specified(computed_min_width),
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width)
|
||||
} else {
|
||||
(width, margin_left, margin_right)
|
||||
}
|
||||
};
|
||||
|
||||
return (width, margin_left, margin_right);
|
||||
}
|
||||
|
||||
fn compute_float_margins(&self, box: @RenderBox, remaining_width: Au) -> (Au, Au, Au) {
|
||||
let style = box.base().style();
|
||||
let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
|
||||
remaining_width).specified_or_zero();
|
||||
let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
|
||||
remaining_width).specified_or_zero();
|
||||
let shrink_to_fit = geometry::min(self.base.pref_width,
|
||||
geometry::max(self.base.min_width, remaining_width));
|
||||
let width = MaybeAuto::from_style(style.Box.width,
|
||||
remaining_width).specified_or_default(shrink_to_fit);
|
||||
debug!("assign_widths_float -- width: {}", width);
|
||||
return (width, margin_left, margin_right);
|
||||
}
|
||||
|
||||
// inline(always) because this is only ever called by in-order or non-in-order top-level
|
||||
// methods
|
||||
#[inline(always)]
|
||||
|
@ -252,12 +366,104 @@ impl BlockFlow {
|
|||
}
|
||||
}
|
||||
|
||||
fn assign_height_float_inorder(&mut self) {
|
||||
// assign_height_float was already called by the traversal function
|
||||
// so this is well-defined
|
||||
|
||||
let mut height = Au(0);
|
||||
let mut clearance = Au(0);
|
||||
let mut full_noncontent_width = Au(0);
|
||||
let mut margin_height = Au(0);
|
||||
|
||||
for box in self.box.iter() {
|
||||
let base = box.base();
|
||||
height = base.position.borrow().ptr.size.height;
|
||||
clearance = match base.clear() {
|
||||
None => Au(0),
|
||||
Some(clear) => self.base.floats_in.clearance(clear),
|
||||
};
|
||||
|
||||
let noncontent_width = base.padding.left + base.padding.right + base.border.left +
|
||||
base.border.right;
|
||||
|
||||
full_noncontent_width = noncontent_width + base.margin.left + base.margin.right;
|
||||
margin_height = base.margin.top + base.margin.bottom;
|
||||
}
|
||||
|
||||
let info = PlacementInfo {
|
||||
width: self.base.position.size.width + full_noncontent_width,
|
||||
height: height + margin_height,
|
||||
ceiling: clearance,
|
||||
max_width: self.float.get_ref().containing_width,
|
||||
f_type: self.float.get_ref().float_type,
|
||||
};
|
||||
|
||||
// Place the float and return the FloatContext back to the parent flow.
|
||||
// After, grab the position and use that to set our position.
|
||||
self.base.floats_out = self.base.floats_in.add_float(&info);
|
||||
self.float.get_mut_ref().rel_pos = self.base.floats_out.last_float_pos();
|
||||
}
|
||||
|
||||
fn assign_height_float(&mut self, ctx: &mut LayoutContext) {
|
||||
// Now that we've determined our height, propagate that out.
|
||||
let has_inorder_children = self.base.num_floats > 0;
|
||||
if has_inorder_children {
|
||||
let mut float_ctx = FloatContext::new(self.float.get_ref().floated_children);
|
||||
for kid in self.base.child_iter() {
|
||||
flow::mut_base(*kid).floats_in = float_ctx.clone();
|
||||
kid.assign_height_inorder(ctx);
|
||||
float_ctx = flow::mut_base(*kid).floats_out.clone();
|
||||
}
|
||||
}
|
||||
let mut cur_y = Au(0);
|
||||
let mut top_offset = Au(0);
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
top_offset = base.margin.top + base.border.top + base.padding.top;
|
||||
cur_y = cur_y + top_offset;
|
||||
}
|
||||
|
||||
for kid in self.base.child_iter() {
|
||||
let child_base = flow::mut_base(*kid);
|
||||
child_base.position.origin.y = cur_y;
|
||||
cur_y = cur_y + child_base.position.size.height;
|
||||
}
|
||||
|
||||
let mut height = cur_y - top_offset;
|
||||
|
||||
let mut noncontent_height;
|
||||
let box = self.box.as_ref().unwrap();
|
||||
let base = box.base();
|
||||
let mut position_ref = base.position.mutate();
|
||||
let position = &mut position_ref.ptr;
|
||||
|
||||
// The associated box is the border box of this flow.
|
||||
position.origin.y = base.margin.top;
|
||||
|
||||
noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
|
||||
base.border.bottom;
|
||||
|
||||
//TODO(eatkinson): compute heights properly using the 'height' property.
|
||||
let height_prop = MaybeAuto::from_style(base.style().Box.height,
|
||||
Au::new(0)).specified_or_zero();
|
||||
|
||||
height = geometry::max(height, height_prop) + noncontent_height;
|
||||
debug!("assign_height_float -- height: {}", height);
|
||||
|
||||
position.size.height = height;
|
||||
}
|
||||
|
||||
pub fn build_display_list_block<E:ExtraDisplayListData>(
|
||||
&mut self,
|
||||
builder: &DisplayListBuilder,
|
||||
dirty: &Rect<Au>,
|
||||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
if self.is_float() {
|
||||
return self.build_display_list_float(builder, dirty, list);
|
||||
}
|
||||
|
||||
if self.base.node.is_iframe_element() {
|
||||
let x = self.base.abs_position.x + do self.box.map_default(Au::new(0)) |box| {
|
||||
let base = box.base();
|
||||
|
@ -302,6 +508,39 @@ impl BlockFlow {
|
|||
|
||||
false
|
||||
}
|
||||
|
||||
pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self,
|
||||
builder: &DisplayListBuilder,
|
||||
dirty: &Rect<Au>,
|
||||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
//TODO: implement iframe size messaging
|
||||
if self.base.node.is_iframe_element() {
|
||||
error!("float iframe size messaging not implemented yet");
|
||||
}
|
||||
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
||||
if !abs_rect.intersects(dirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
let offset = self.base.abs_position + self.float.get_ref().rel_pos;
|
||||
// add box that starts block context
|
||||
for box in self.box.iter() {
|
||||
box.build_display_list(builder, dirty, &offset, list)
|
||||
}
|
||||
|
||||
|
||||
// TODO: handle any out-of-flow elements
|
||||
|
||||
// go deeper into the flow tree
|
||||
for child in self.base.child_iter() {
|
||||
let child_base = flow::mut_base(*child);
|
||||
child_base.abs_position = offset + child_base.position.origin;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for BlockFlow {
|
||||
|
@ -334,10 +573,16 @@ impl Flow for BlockFlow {
|
|||
let child_base = flow::mut_base(*child_ctx);
|
||||
min_width = geometry::max(min_width, child_base.min_width);
|
||||
pref_width = geometry::max(pref_width, child_base.pref_width);
|
||||
|
||||
num_floats = num_floats + child_base.num_floats;
|
||||
}
|
||||
|
||||
if self.is_float() {
|
||||
self.base.num_floats = 1;
|
||||
self.float.get_mut_ref().floated_children = num_floats;
|
||||
} else {
|
||||
self.base.num_floats = num_floats;
|
||||
}
|
||||
|
||||
/* if not an anonymous block context, add in block box's widths.
|
||||
these widths will not include child elements, just padding etc. */
|
||||
for box in self.box.iter() {
|
||||
|
@ -355,16 +600,20 @@ impl Flow for BlockFlow {
|
|||
|
||||
self.base.min_width = min_width;
|
||||
self.base.pref_width = pref_width;
|
||||
self.base.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.
|
||||
///
|
||||
/// Dual boxes consume some width first, and the remainder is assigned to all child (block)
|
||||
/// contexts.
|
||||
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
|
||||
debug!("assign_widths_block: assigning width for flow {}", self.base.id);
|
||||
fn assign_widths(&mut self, ctx: &mut LayoutContext) {
|
||||
if self.is_float() {
|
||||
debug!("assign_widths_float: assigning width for flow {}", self.base.id);
|
||||
} else {
|
||||
debug!("assign_widths_block: assigning width for flow {}", self.base.id);
|
||||
}
|
||||
|
||||
if self.is_root {
|
||||
debug!("Setting root position");
|
||||
self.base.position.origin = Au::zero_point();
|
||||
|
@ -377,12 +626,19 @@ impl Flow for BlockFlow {
|
|||
let mut remaining_width = self.base.position.size.width;
|
||||
let mut x_offset = Au::new(0);
|
||||
|
||||
if self.is_float() {
|
||||
// Parent usually sets this, but floats are never inorder
|
||||
self.base.is_inorder = false;
|
||||
}
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.mut_base();
|
||||
let base = box.base();
|
||||
let mut_base = box.mut_base();
|
||||
let style = base.style();
|
||||
let mut_position = &mut base.position.mutate().ptr;
|
||||
|
||||
// Can compute padding here since we know containing block width.
|
||||
base.compute_padding(style, remaining_width);
|
||||
mut_base.compute_padding(style, remaining_width);
|
||||
|
||||
// Margins are 0 right now so base.noncontent_width() is just borders + padding.
|
||||
let available_width = remaining_width - base.noncontent_width();
|
||||
|
@ -393,58 +649,38 @@ impl Flow for BlockFlow {
|
|||
let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
|
||||
remaining_width).specified_or_zero();
|
||||
|
||||
let (width, maybe_margin_left, maybe_margin_right) =
|
||||
(MaybeAuto::from_style(style.Box.width, remaining_width),
|
||||
MaybeAuto::from_style(style.Margin.margin_left, remaining_width),
|
||||
MaybeAuto::from_style(style.Margin.margin_right, remaining_width));
|
||||
|
||||
let (width, margin_left, margin_right) = self.compute_horiz(width,
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width);
|
||||
|
||||
// If the tentative used width is greater than 'max-width', width should be recalculated,
|
||||
// but this time using the computed value of 'max-width' as the computed value for 'width'.
|
||||
let (width, margin_left, margin_right) = {
|
||||
match specified_or_none(style.Box.max_width, remaining_width) {
|
||||
Some(value) if value < width => self.compute_horiz(Specified(value),
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width),
|
||||
_ => (width, margin_left, margin_right)
|
||||
}
|
||||
};
|
||||
// If the resulting width is smaller than 'min-width', width should be recalculated,
|
||||
// but this time using the value of 'min-width' as the computed value for 'width'.
|
||||
let (width, margin_left, margin_right) = {
|
||||
let computed_min_width = specified(style.Box.min_width, remaining_width);
|
||||
if computed_min_width > width {
|
||||
self.compute_horiz(Specified(computed_min_width),
|
||||
maybe_margin_left,
|
||||
maybe_margin_right,
|
||||
available_width)
|
||||
} else {
|
||||
(width, margin_left, margin_right)
|
||||
}
|
||||
let (width, margin_left, margin_right) = if self.is_float() {
|
||||
self.compute_float_margins(box, remaining_width)
|
||||
} else {
|
||||
self.compute_block_margins(box, remaining_width, available_width)
|
||||
};
|
||||
|
||||
base.margin.top = margin_top;
|
||||
base.margin.right = margin_right;
|
||||
base.margin.bottom = margin_bottom;
|
||||
base.margin.left = margin_left;
|
||||
mut_base.margin.top = margin_top;
|
||||
mut_base.margin.right = margin_right;
|
||||
mut_base.margin.bottom = margin_bottom;
|
||||
mut_base.margin.left = margin_left;
|
||||
|
||||
x_offset = base.offset();
|
||||
remaining_width = width;
|
||||
|
||||
//The associated box is the border box of this flow
|
||||
let position_ref = base.position.mutate();
|
||||
position_ref.ptr.origin.x = base.margin.left;
|
||||
mut_position.origin.x = base.margin.left;
|
||||
|
||||
let padding_and_borders = base.padding.left + base.padding.right +
|
||||
base.border.left + base.border.right;
|
||||
position_ref.ptr.size.width = remaining_width + padding_and_borders;
|
||||
mut_position.size.width = remaining_width + padding_and_borders;
|
||||
}
|
||||
|
||||
let has_inorder_children = self.base.is_inorder || self.base.num_floats > 0;
|
||||
if self.is_float() {
|
||||
self.base.position.size.width = remaining_width;
|
||||
}
|
||||
|
||||
let has_inorder_children = if self.is_float() {
|
||||
self.base.num_floats > 0
|
||||
} else {
|
||||
self.base.is_inorder || self.base.num_floats > 0
|
||||
};
|
||||
|
||||
for kid in self.base.child_iter() {
|
||||
assert!(kid.starts_block_flow() || kid.starts_inline_flow());
|
||||
|
||||
|
@ -460,19 +696,29 @@ impl Flow for BlockFlow {
|
|||
}
|
||||
|
||||
fn assign_height_inorder(&mut self, ctx: &mut LayoutContext) {
|
||||
debug!("assign_height_inorder: assigning height for block {}", self.base.id);
|
||||
self.assign_height_block_base(ctx, true);
|
||||
if self.is_float() {
|
||||
debug!("assign_height_inorder_float: assigning height for float {}", self.base.id);
|
||||
self.assign_height_float_inorder();
|
||||
} else {
|
||||
debug!("assign_height_inorder: assigning height for block {}", self.base.id);
|
||||
self.assign_height_block_base(ctx, true);
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_height(&mut self, ctx: &mut LayoutContext) {
|
||||
debug!("assign_height: assigning height for block {}", self.base.id);
|
||||
// This is the only case in which a block flow can start an inorder
|
||||
// subtraversal.
|
||||
if self.is_root && self.base.num_floats > 0 {
|
||||
self.assign_height_inorder(ctx);
|
||||
return;
|
||||
if self.is_float() {
|
||||
debug!("assign_height_float: assigning height for float {}", self.base.id);
|
||||
self.assign_height_float(ctx);
|
||||
} else {
|
||||
debug!("assign_height: assigning height for block {}", self.base.id);
|
||||
// This is the only case in which a block flow can start an inorder
|
||||
// subtraversal.
|
||||
if self.is_root && self.base.num_floats > 0 {
|
||||
self.assign_height_inorder(ctx);
|
||||
return;
|
||||
}
|
||||
self.assign_height_block_base(ctx, false);
|
||||
}
|
||||
self.assign_height_block_base(ctx, false);
|
||||
}
|
||||
|
||||
fn collapse_margins(&mut self,
|
||||
|
@ -482,6 +728,12 @@ impl Flow for BlockFlow {
|
|||
top_offset: &mut Au,
|
||||
collapsing: &mut Au,
|
||||
collapsible: &mut Au) {
|
||||
if self.is_float() {
|
||||
// Margins between a floated box and any other box do not collapse.
|
||||
*collapsing = Au::new(0);
|
||||
return;
|
||||
}
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
|
||||
|
@ -515,7 +767,7 @@ impl Flow for BlockFlow {
|
|||
if self.is_root {
|
||||
~"BlockFlow(root)"
|
||||
} else {
|
||||
let txt = ~"BlockFlow: ";
|
||||
let txt = if self.is_float() { ~"FloatFlow: " } else { ~"BlockFlow: " };
|
||||
txt.append(match self.box {
|
||||
Some(rb) => {
|
||||
rb.debug_str()
|
||||
|
|
|
@ -25,7 +25,6 @@ use layout::block::BlockFlow;
|
|||
use layout::box::{GenericRenderBox, ImageRenderBox, RenderBox, RenderBoxBase};
|
||||
use layout::box::{UnscannedTextRenderBox};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::float::FloatFlow;
|
||||
use layout::float_context::FloatType;
|
||||
use layout::flow::{Flow, FlowData, MutableFlowUtils};
|
||||
use layout::inline::InlineFlow;
|
||||
|
@ -360,13 +359,13 @@ impl<'self> FlowConstructor<'self> {
|
|||
flow
|
||||
}
|
||||
|
||||
/// Builds the flow for a node with `float: {left|right}`. This yields a `FloatFlow` with a
|
||||
/// `BlockFlow` underneath it.
|
||||
/// Builds the flow for a node with `float: {left|right}`. This yields a float `BlockFlow` with
|
||||
/// a `BlockFlow` underneath it.
|
||||
fn build_flow_for_floated_block(&self, node: AbstractNode<LayoutView>, float_type: FloatType)
|
||||
-> ~Flow: {
|
||||
let base = FlowData::new(self.next_flow_id(), node);
|
||||
let box = self.build_box_for_node(node);
|
||||
let mut flow = ~FloatFlow::from_box(base, float_type, box) as ~Flow:;
|
||||
let mut flow = ~BlockFlow::float_from_box(base, float_type, box) as ~Flow:;
|
||||
self.build_children_of_block_flow(&mut flow, node);
|
||||
flow
|
||||
}
|
||||
|
|
|
@ -1,333 +0,0 @@
|
|||
/* 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, RenderBoxUtils};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||
use layout::flow::{FloatFlowClass, FlowClass, Flow, FlowData};
|
||||
use layout::flow;
|
||||
use layout::model::{MaybeAuto};
|
||||
use layout::float_context::{FloatContext, PlacementInfo, FloatType};
|
||||
|
||||
use std::cell::Cell;
|
||||
use geom::point::Point2D;
|
||||
use geom::rect::Rect;
|
||||
use gfx::display_list::DisplayList;
|
||||
use servo_util::geometry::Au;
|
||||
use servo_util::geometry;
|
||||
|
||||
pub struct FloatFlow {
|
||||
/// Data common to all flows.
|
||||
base: FlowData,
|
||||
|
||||
/// The associated render box.
|
||||
box: Option<@RenderBox>,
|
||||
|
||||
containing_width: Au,
|
||||
|
||||
/// Offset relative to where the parent tried to position this flow
|
||||
rel_pos: Point2D<Au>,
|
||||
|
||||
/// Left or right?
|
||||
float_type: FloatType,
|
||||
|
||||
/// Index into the box list for inline floats
|
||||
index: Option<uint>,
|
||||
|
||||
/// Number of floated children
|
||||
floated_children: uint,
|
||||
|
||||
}
|
||||
|
||||
impl FloatFlow {
|
||||
pub fn new(base: FlowData, float_type: FloatType) -> FloatFlow {
|
||||
FloatFlow {
|
||||
base: base,
|
||||
containing_width: Au(0),
|
||||
box: None,
|
||||
index: None,
|
||||
float_type: float_type,
|
||||
rel_pos: Point2D(Au(0), Au(0)),
|
||||
floated_children: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_box(base: FlowData, float_type: FloatType, box: @RenderBox) -> FloatFlow {
|
||||
FloatFlow {
|
||||
base: base,
|
||||
containing_width: Au(0),
|
||||
box: Some(box),
|
||||
index: None,
|
||||
float_type: float_type,
|
||||
rel_pos: Point2D(Au(0), Au(0)),
|
||||
floated_children: 0,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn teardown(&mut self) {
|
||||
for box in self.box.iter() {
|
||||
box.teardown();
|
||||
}
|
||||
self.box = None;
|
||||
self.index = None;
|
||||
}
|
||||
|
||||
pub fn build_display_list_float<E:ExtraDisplayListData>(&mut self,
|
||||
builder: &DisplayListBuilder,
|
||||
dirty: &Rect<Au>,
|
||||
list: &Cell<DisplayList<E>>)
|
||||
-> bool {
|
||||
//TODO: implement iframe size messaging
|
||||
if self.base.node.is_iframe_element() {
|
||||
error!("float iframe size messaging not implemented yet");
|
||||
}
|
||||
let abs_rect = Rect(self.base.abs_position, self.base.position.size);
|
||||
if !abs_rect.intersects(dirty) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
let offset = self.base.abs_position + self.rel_pos;
|
||||
// add box that starts block context
|
||||
for box in self.box.iter() {
|
||||
box.build_display_list(builder, dirty, &offset, list)
|
||||
}
|
||||
|
||||
|
||||
// TODO: handle any out-of-flow elements
|
||||
|
||||
// go deeper into the flow tree
|
||||
for child in self.base.child_iter() {
|
||||
let child_base = flow::mut_base(*child);
|
||||
child_base.abs_position = offset + child_base.position.origin;
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn debug_str(&self) -> ~str {
|
||||
~"FloatFlow"
|
||||
}
|
||||
}
|
||||
|
||||
impl Flow for FloatFlow {
|
||||
fn class(&self) -> FlowClass {
|
||||
FloatFlowClass
|
||||
}
|
||||
|
||||
fn as_float<'a>(&'a mut self) -> &'a mut FloatFlow {
|
||||
self
|
||||
}
|
||||
|
||||
fn bubble_widths(&mut self, _: &mut LayoutContext) {
|
||||
let mut min_width = Au(0);
|
||||
let mut pref_width = Au(0);
|
||||
let mut num_floats = 0;
|
||||
|
||||
for child_ctx in self.base.child_iter() {
|
||||
//assert!(child_ctx.starts_block_flow() || child_ctx.starts_inline_flow());
|
||||
|
||||
let child_base = flow::mut_base(*child_ctx);
|
||||
min_width = geometry::max(min_width, child_base.min_width);
|
||||
pref_width = geometry::max(pref_width, child_base.pref_width);
|
||||
num_floats = num_floats + child_base.num_floats;
|
||||
}
|
||||
|
||||
self.base.num_floats = 1;
|
||||
self.floated_children = num_floats;
|
||||
|
||||
for box in self.box.iter() {
|
||||
{
|
||||
box.mut_base().compute_borders(box.base().style());
|
||||
}
|
||||
|
||||
let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
|
||||
min_width = min_width + this_minimum_width;
|
||||
pref_width = pref_width + this_preferred_width;
|
||||
}
|
||||
|
||||
self.base.min_width = min_width;
|
||||
self.base.pref_width = pref_width;
|
||||
}
|
||||
|
||||
fn assign_widths(&mut self, _: &mut LayoutContext) {
|
||||
debug!("assign_widths_float: assigning width for flow {}", self.base.id);
|
||||
// position.size.width is set by parent even though we don't know
|
||||
// position.origin yet.
|
||||
let mut remaining_width = self.base.position.size.width;
|
||||
self.containing_width = remaining_width;
|
||||
let mut x_offset = Au(0);
|
||||
|
||||
// Parent usually sets this, but floats are never inorder
|
||||
self.base.is_inorder = false;
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
let mut_base = box.mut_base();
|
||||
let style = base.style();
|
||||
let mut position_ref = base.position.mutate();
|
||||
let position = &mut position_ref.ptr;
|
||||
|
||||
// Can compute padding here since we know containing block width.
|
||||
mut_base.compute_padding(style, remaining_width);
|
||||
|
||||
// Margins for floats are 0 if auto.
|
||||
let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
|
||||
remaining_width).specified_or_zero();
|
||||
let margin_bottom = MaybeAuto::from_style(style.Margin.margin_bottom,
|
||||
remaining_width).specified_or_zero();
|
||||
let margin_left = MaybeAuto::from_style(style.Margin.margin_left,
|
||||
remaining_width).specified_or_zero();
|
||||
let margin_right = MaybeAuto::from_style(style.Margin.margin_right,
|
||||
remaining_width).specified_or_zero();
|
||||
|
||||
|
||||
let shrink_to_fit = geometry::min(self.base.pref_width,
|
||||
geometry::max(self.base.min_width, remaining_width));
|
||||
|
||||
|
||||
let width = MaybeAuto::from_style(style.Box.width,
|
||||
remaining_width).specified_or_default(shrink_to_fit);
|
||||
debug!("assign_widths_float -- width: {}", width);
|
||||
|
||||
mut_base.margin.top = margin_top;
|
||||
mut_base.margin.right = margin_right;
|
||||
mut_base.margin.bottom = margin_bottom;
|
||||
mut_base.margin.left = margin_left;
|
||||
|
||||
x_offset = base.offset();
|
||||
remaining_width = width;
|
||||
|
||||
// The associated box is the border box of this flow.
|
||||
position.origin.x = base.margin.left;
|
||||
|
||||
let padding_and_borders = base.padding.left + base.padding.right +
|
||||
base.border.left + base.border.right;
|
||||
position.size.width = remaining_width + padding_and_borders;
|
||||
}
|
||||
|
||||
self.base.position.size.width = remaining_width;
|
||||
|
||||
let has_inorder_children = self.base.num_floats > 0;
|
||||
for kid in self.base.child_iter() {
|
||||
//assert!(kid.starts_block_flow() || kid.starts_inline_flow());
|
||||
|
||||
let child_base = flow::mut_base(*kid);
|
||||
child_base.position.origin.x = x_offset;
|
||||
child_base.position.size.width = remaining_width;
|
||||
child_base.is_inorder = has_inorder_children;
|
||||
|
||||
if !child_base.is_inorder {
|
||||
child_base.floats_in = FloatContext::new(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn assign_height_inorder(&mut self, _: &mut LayoutContext) {
|
||||
debug!("assign_height_inorder_float: assigning height for float {}", self.base.id);
|
||||
// assign_height_float was already called by the traversal function
|
||||
// so this is well-defined
|
||||
|
||||
let mut height = Au(0);
|
||||
let mut clearance = Au(0);
|
||||
let mut full_noncontent_width = Au(0);
|
||||
let mut margin_height = Au(0);
|
||||
|
||||
for box in self.box.iter() {
|
||||
let base = box.base();
|
||||
height = base.position.borrow().ptr.size.height;
|
||||
clearance = match base.clear() {
|
||||
None => Au(0),
|
||||
Some(clear) => self.base.floats_in.clearance(clear),
|
||||
};
|
||||
|
||||
let noncontent_width = base.padding.left + base.padding.right + base.border.left +
|
||||
base.border.right;
|
||||
|
||||
full_noncontent_width = noncontent_width + base.margin.left + base.margin.right;
|
||||
margin_height = base.margin.top + base.margin.bottom;
|
||||
}
|
||||
|
||||
let info = PlacementInfo {
|
||||
width: self.base.position.size.width + full_noncontent_width,
|
||||
height: height + margin_height,
|
||||
ceiling: clearance,
|
||||
max_width: self.containing_width,
|
||||
f_type: self.float_type,
|
||||
};
|
||||
|
||||
// Place the float and return the FloatContext back to the parent flow.
|
||||
// After, grab the position and use that to set our position.
|
||||
self.base.floats_out = self.base.floats_in.add_float(&info);
|
||||
self.rel_pos = self.base.floats_out.last_float_pos();
|
||||
}
|
||||
|
||||
fn assign_height(&mut self, ctx: &mut LayoutContext) {
|
||||
// Now that we've determined our height, propagate that out.
|
||||
let has_inorder_children = self.base.num_floats > 0;
|
||||
if has_inorder_children {
|
||||
let mut float_ctx = FloatContext::new(self.floated_children);
|
||||
for kid in self.base.child_iter() {
|
||||
flow::mut_base(*kid).floats_in = float_ctx.clone();
|
||||
kid.assign_height_inorder(ctx);
|
||||
float_ctx = flow::mut_base(*kid).floats_out.clone();
|
||||
}
|
||||
}
|
||||
debug!("assign_height_float: assigning height for float {}", self.base.id);
|
||||
let mut cur_y = Au(0);
|
||||
let mut top_offset = Au(0);
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
top_offset = base.margin.top + base.border.top + base.padding.top;
|
||||
cur_y = cur_y + top_offset;
|
||||
}
|
||||
|
||||
for kid in self.base.child_iter() {
|
||||
let child_base = flow::mut_base(*kid);
|
||||
child_base.position.origin.y = cur_y;
|
||||
cur_y = cur_y + child_base.position.size.height;
|
||||
}
|
||||
|
||||
let mut height = cur_y - top_offset;
|
||||
|
||||
let mut noncontent_height;
|
||||
let box = self.box.as_ref().unwrap();
|
||||
let base = box.base();
|
||||
let mut position_ref = base.position.mutate();
|
||||
let position = &mut position_ref.ptr;
|
||||
|
||||
// The associated box is the border box of this flow.
|
||||
position.origin.y = base.margin.top;
|
||||
|
||||
noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
|
||||
base.border.bottom;
|
||||
|
||||
//TODO(eatkinson): compute heights properly using the 'height' property.
|
||||
let height_prop = MaybeAuto::from_style(base.style().Box.height,
|
||||
Au::new(0)).specified_or_zero();
|
||||
|
||||
height = geometry::max(height, height_prop) + noncontent_height;
|
||||
debug!("assign_height_float -- height: {}", height);
|
||||
|
||||
position.size.height = height;
|
||||
|
||||
}
|
||||
|
||||
fn collapse_margins(&mut self,
|
||||
_: bool,
|
||||
_: &mut bool,
|
||||
_: &mut Au,
|
||||
_: &mut Au,
|
||||
collapsing: &mut Au,
|
||||
_: &mut Au) {
|
||||
// Margins between a floated box and any other box do not collapse.
|
||||
*collapsing = Au::new(0);
|
||||
}
|
||||
|
||||
fn debug_str(&self) -> ~str {
|
||||
~"FloatFlow"
|
||||
}
|
||||
}
|
||||
|
|
@ -29,7 +29,6 @@ use css::node_style::StyledNode;
|
|||
use layout::block::BlockFlow;
|
||||
use layout::box::RenderBox;
|
||||
use layout::context::LayoutContext;
|
||||
use layout::float::FloatFlow;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||
use layout::float_context::{FloatContext, Invalid};
|
||||
use layout::incremental::RestyleDamage;
|
||||
|
@ -74,11 +73,6 @@ pub trait Flow {
|
|||
fail!("called as_inline() on a non-inline flow")
|
||||
}
|
||||
|
||||
/// If this is a float flow, returns the underlying object. Fails otherwise.
|
||||
fn as_float<'a>(&'a mut self) -> &'a mut FloatFlow {
|
||||
fail!("called as_float() on a non-float flow")
|
||||
}
|
||||
|
||||
// Main methods
|
||||
|
||||
/// Pass 1 of reflow: computes minimum and preferred widths.
|
||||
|
@ -217,7 +211,6 @@ pub trait MutableFlowUtils {
|
|||
pub enum FlowClass {
|
||||
AbsoluteFlowClass,
|
||||
BlockFlowClass,
|
||||
FloatFlowClass,
|
||||
InlineBlockFlowClass,
|
||||
InlineFlowClass,
|
||||
TableFlowClass,
|
||||
|
@ -395,7 +388,7 @@ impl<'self> ImmutableFlowUtils for &'self Flow {
|
|||
/// Returns true if this flow is a block or a float flow.
|
||||
fn is_block_like(self) -> bool {
|
||||
match self.class() {
|
||||
BlockFlowClass | FloatFlowClass => true,
|
||||
BlockFlowClass => true,
|
||||
AbsoluteFlowClass | InlineBlockFlowClass | InlineFlowClass | TableFlowClass => false,
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +401,7 @@ impl<'self> ImmutableFlowUtils for &'self Flow {
|
|||
/// Returns true if this flow is a block flow, an inline-block flow, or a float flow.
|
||||
fn starts_block_flow(self) -> bool {
|
||||
match self.class() {
|
||||
BlockFlowClass | InlineBlockFlowClass | FloatFlowClass => true,
|
||||
BlockFlowClass | InlineBlockFlowClass => true,
|
||||
AbsoluteFlowClass | InlineFlowClass | TableFlowClass => false,
|
||||
}
|
||||
}
|
||||
|
@ -417,7 +410,7 @@ impl<'self> ImmutableFlowUtils for &'self Flow {
|
|||
fn starts_inline_flow(self) -> bool {
|
||||
match self.class() {
|
||||
InlineFlowClass => true,
|
||||
AbsoluteFlowClass | BlockFlowClass | FloatFlowClass | InlineBlockFlowClass |
|
||||
AbsoluteFlowClass | BlockFlowClass | InlineBlockFlowClass |
|
||||
TableFlowClass => false,
|
||||
}
|
||||
}
|
||||
|
@ -525,7 +518,6 @@ impl<'self> MutableFlowUtils for &'self mut Flow {
|
|||
match self.class() {
|
||||
BlockFlowClass => self.as_block().build_display_list_block(builder, dirty, list),
|
||||
InlineFlowClass => self.as_inline().build_display_list_inline(builder, dirty, list),
|
||||
FloatFlowClass => self.as_float().build_display_list_float(builder, dirty, list),
|
||||
_ => fail!("Tried to build_display_list_recurse of flow: {:?}", self),
|
||||
};
|
||||
|
||||
|
|
|
@ -81,7 +81,6 @@ pub mod layout {
|
|||
pub mod context;
|
||||
pub mod display_list_builder;
|
||||
pub mod float_context;
|
||||
pub mod float;
|
||||
pub mod flow;
|
||||
pub mod layout_task;
|
||||
pub mod inline;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue