mirror of
https://github.com/servo/servo.git
synced 2025-06-20 07:08:59 +01:00
layout: Change RenderBox
to an enum and shorten its name in
preparation for removing its `@`-ness. Also removes a few text copies that were taking place. This sure does remove a lot of code!
This commit is contained in:
parent
2c4714e803
commit
4fda26f76e
8 changed files with 679 additions and 957 deletions
|
@ -2,9 +2,9 @@
|
|||
* 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/. */
|
||||
|
||||
//! CSS block layout.
|
||||
//! CSS block formatting contexts.
|
||||
|
||||
use layout::box::{RenderBox, RenderBoxUtils};
|
||||
use layout::box::Box;
|
||||
use layout::context::LayoutContext;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||
use layout::flow::{BlockFlowClass, FlowClass, Flow, FlowData, ImmutableFlowUtils};
|
||||
|
@ -13,13 +13,12 @@ use layout::model::{MaybeAuto, Specified, Auto, specified_or_none, specified};
|
|||
use layout::float_context::{FloatContext, PlacementInfo, Invalid, FloatType};
|
||||
|
||||
use std::cell::Cell;
|
||||
use geom::point::Point2D;
|
||||
use geom::size::Size2D;
|
||||
use geom::rect::Rect;
|
||||
use geom::{Point2D, Rect, SideOffsets2D, Size2D};
|
||||
use gfx::display_list::DisplayList;
|
||||
use servo_util::geometry::{Au, to_frac_px};
|
||||
use servo_util::geometry;
|
||||
|
||||
/// Information specific to floated blocks.
|
||||
pub struct FloatedBlockInfo {
|
||||
containing_width: Au,
|
||||
|
||||
|
@ -48,12 +47,13 @@ impl FloatedBlockInfo {
|
|||
}
|
||||
}
|
||||
|
||||
/// A block formatting context.
|
||||
pub struct BlockFlow {
|
||||
/// Data common to all flows.
|
||||
base: FlowData,
|
||||
|
||||
/// The associated render box.
|
||||
box: Option<@RenderBox>,
|
||||
/// The associated box.
|
||||
box: Option<@Box>,
|
||||
|
||||
/// Whether this block flow is the root flow.
|
||||
is_root: bool,
|
||||
|
@ -72,7 +72,7 @@ impl BlockFlow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_box(base: FlowData, box: @RenderBox) -> BlockFlow {
|
||||
pub fn from_box(base: FlowData, box: @Box) -> BlockFlow {
|
||||
BlockFlow {
|
||||
base: base,
|
||||
box: Some(box),
|
||||
|
@ -81,7 +81,7 @@ impl BlockFlow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn float_from_box(base: FlowData, float_type: FloatType, box: @RenderBox) -> BlockFlow {
|
||||
pub fn float_from_box(base: FlowData, float_type: FloatType, box: @Box) -> BlockFlow {
|
||||
BlockFlow {
|
||||
base: base,
|
||||
box: Some(box),
|
||||
|
@ -172,9 +172,9 @@ 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();
|
||||
fn compute_block_margins(&self, box: @Box, remaining_width: Au, available_width: Au)
|
||||
-> (Au, Au, Au) {
|
||||
let style = box.style();
|
||||
|
||||
let (width, maybe_margin_left, maybe_margin_right) =
|
||||
(MaybeAuto::from_style(style.Box.width, remaining_width),
|
||||
|
@ -215,8 +215,8 @@ impl BlockFlow {
|
|||
return (width, margin_left, margin_right);
|
||||
}
|
||||
|
||||
fn compute_float_margins(&self, box: @RenderBox, remaining_width: Au) -> (Au, Au, Au) {
|
||||
let style = box.base().style();
|
||||
fn compute_float_margins(&self, box: @Box, remaining_width: Au) -> (Au, Au, Au) {
|
||||
let style = box.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,
|
||||
|
@ -241,18 +241,19 @@ impl BlockFlow {
|
|||
let mut float_ctx = Invalid;
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
clearance = match base.clear() {
|
||||
clearance = match box.clear() {
|
||||
None => Au::new(0),
|
||||
Some(clear) => {
|
||||
self.base.floats_in.clearance(clear)
|
||||
}
|
||||
};
|
||||
|
||||
top_offset = clearance + base.margin.top + base.border.top + base.padding.top;
|
||||
top_offset = clearance + box.margin.get().top + box.border.get().top +
|
||||
box.padding.get().top;
|
||||
cur_y = cur_y + top_offset;
|
||||
bottom_offset = base.margin.bottom + base.border.bottom + base.padding.bottom;
|
||||
left_offset = base.offset();
|
||||
bottom_offset = box.margin.get().bottom + box.border.get().bottom +
|
||||
box.padding.get().bottom;
|
||||
left_offset = box.offset();
|
||||
}
|
||||
|
||||
if inorder {
|
||||
|
@ -279,16 +280,16 @@ impl BlockFlow {
|
|||
let mut bottom_margin_collapsible = false;
|
||||
let mut first_in_flow = true;
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
if !self.is_root && base.border.top == Au::new(0) && base.padding.top == Au::new(0) {
|
||||
collapsible = base.margin.top;
|
||||
if !self.is_root && box.border.get().top == Au(0) && box.padding.get().top == Au(0) {
|
||||
collapsible = box.margin.get().top;
|
||||
top_margin_collapsible = true;
|
||||
}
|
||||
if !self.is_root && base.border.bottom == Au::new(0) && base.padding.bottom == Au::new(0) {
|
||||
if !self.is_root && box.border.get().bottom == Au(0) &&
|
||||
box.padding.get().bottom == Au(0) {
|
||||
bottom_margin_collapsible = true;
|
||||
}
|
||||
margin_top = base.margin.top;
|
||||
margin_bottom = base.margin.bottom;
|
||||
margin_top = box.margin.get().top;
|
||||
margin_bottom = box.margin.get().bottom;
|
||||
}
|
||||
|
||||
for kid in self.base.child_iter() {
|
||||
|
@ -328,8 +329,7 @@ impl BlockFlow {
|
|||
};
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
let style = base.style();
|
||||
let style = box.style();
|
||||
height = match MaybeAuto::from_style(style.Box.height, Au::new(0)) {
|
||||
Auto => height,
|
||||
Specified(value) => value
|
||||
|
@ -338,22 +338,23 @@ impl BlockFlow {
|
|||
|
||||
let mut noncontent_height = Au::new(0);
|
||||
for box in self.box.iter() {
|
||||
let base = box.mut_base();
|
||||
let mut position_ref = base.position.mutate();
|
||||
let position = &mut position_ref.ptr;
|
||||
let mut position = box.position.get();
|
||||
let mut margin = box.margin.get();
|
||||
|
||||
// The associated box is the border box of this flow.
|
||||
base.margin.top = margin_top;
|
||||
base.margin.bottom = margin_bottom;
|
||||
margin.top = margin_top;
|
||||
margin.bottom = margin_bottom;
|
||||
|
||||
position.origin.y = clearance + base.margin.top;
|
||||
position.origin.y = clearance + margin.top;
|
||||
|
||||
noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
|
||||
base.border.bottom;
|
||||
noncontent_height = box.padding.get().top + box.padding.get().bottom +
|
||||
box.border.get().top + box.border.get().bottom;
|
||||
position.size.height = height + noncontent_height;
|
||||
|
||||
noncontent_height = noncontent_height + clearance + base.margin.top +
|
||||
base.margin.bottom;
|
||||
noncontent_height = noncontent_height + clearance + margin.top + margin.bottom;
|
||||
|
||||
box.position.set(position);
|
||||
box.margin.set(margin);
|
||||
}
|
||||
|
||||
self.base.position.size.height = height + noncontent_height;
|
||||
|
@ -376,18 +377,18 @@ impl BlockFlow {
|
|||
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() {
|
||||
height = box.position.get().size.height;
|
||||
clearance = match box.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;
|
||||
let noncontent_width = box.padding.get().left + box.padding.get().right +
|
||||
box.border.get().left + box.border.get().right;
|
||||
|
||||
full_noncontent_width = noncontent_width + base.margin.left + base.margin.right;
|
||||
margin_height = base.margin.top + base.margin.bottom;
|
||||
full_noncontent_width = noncontent_width + box.margin.get().left +
|
||||
box.margin.get().right;
|
||||
margin_height = box.margin.get().top + box.margin.get().bottom;
|
||||
}
|
||||
|
||||
let info = PlacementInfo {
|
||||
|
@ -419,8 +420,7 @@ impl BlockFlow {
|
|||
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;
|
||||
top_offset = box.margin.get().top + box.border.get().top + box.padding.get().top;
|
||||
cur_y = cur_y + top_offset;
|
||||
}
|
||||
|
||||
|
@ -434,24 +434,23 @@ impl BlockFlow {
|
|||
|
||||
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;
|
||||
let mut position = box.position.get();
|
||||
|
||||
// The associated box is the border box of this flow.
|
||||
position.origin.y = base.margin.top;
|
||||
position.origin.y = box.margin.get().top;
|
||||
|
||||
noncontent_height = base.padding.top + base.padding.bottom + base.border.top +
|
||||
base.border.bottom;
|
||||
noncontent_height = box.padding.get().top + box.padding.get().bottom +
|
||||
box.border.get().top + box.border.get().bottom;
|
||||
|
||||
//TODO(eatkinson): compute heights properly using the 'height' property.
|
||||
let height_prop = MaybeAuto::from_style(base.style().Box.height,
|
||||
let height_prop = MaybeAuto::from_style(box.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;
|
||||
box.position.set(position);
|
||||
}
|
||||
|
||||
pub fn build_display_list_block<E:ExtraDisplayListData>(
|
||||
|
@ -466,18 +465,16 @@ impl BlockFlow {
|
|||
|
||||
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();
|
||||
base.margin.left + base.border.left + base.padding.left
|
||||
box.margin.get().left + box.border.get().left + box.padding.get().left
|
||||
};
|
||||
let y = self.base.abs_position.y + do self.box.map_default(Au::new(0)) |box| {
|
||||
let base = box.base();
|
||||
base.margin.top + base.border.top + base.padding.top
|
||||
box.margin.get().top + box.border.get().top + box.padding.get().top
|
||||
};
|
||||
let w = self.base.position.size.width - do self.box.map_default(Au::new(0)) |box| {
|
||||
box.base().noncontent_width()
|
||||
box.noncontent_width()
|
||||
};
|
||||
let h = self.base.position.size.height - do self.box.map_default(Au::new(0)) |box| {
|
||||
box.base().noncontent_height()
|
||||
box.noncontent_height()
|
||||
};
|
||||
do self.base.node.with_mut_iframe_element |iframe_element| {
|
||||
iframe_element.size.get_mut_ref().set_rect(Rect(Point2D(to_frac_px(x) as f32,
|
||||
|
@ -587,7 +584,7 @@ impl Flow for BlockFlow {
|
|||
for box in self.box.iter() {
|
||||
{
|
||||
// Can compute border width here since it doesn't depend on anything.
|
||||
box.mut_base().compute_borders(box.base().style());
|
||||
box.compute_borders(box.style())
|
||||
}
|
||||
|
||||
let (this_minimum_width, this_preferred_width) = box.minimum_and_preferred_widths();
|
||||
|
@ -631,16 +628,13 @@ impl Flow for BlockFlow {
|
|||
}
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
let mut_base = box.mut_base();
|
||||
let style = base.style();
|
||||
let mut_position = &mut base.position.mutate().ptr;
|
||||
let style = box.style();
|
||||
|
||||
// Can compute padding here since we know containing block width.
|
||||
mut_base.compute_padding(style, remaining_width);
|
||||
box.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();
|
||||
let available_width = remaining_width - box.noncontent_width();
|
||||
|
||||
// Top and bottom margins for blocks are 0 if auto.
|
||||
let margin_top = MaybeAuto::from_style(style.Margin.margin_top,
|
||||
|
@ -654,20 +648,20 @@ impl Flow for BlockFlow {
|
|||
self.compute_block_margins(box, remaining_width, available_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;
|
||||
box.margin.set(SideOffsets2D::new(margin_top,
|
||||
margin_right,
|
||||
margin_bottom,
|
||||
margin_left));
|
||||
|
||||
x_offset = base.offset();
|
||||
x_offset = box.offset();
|
||||
remaining_width = width;
|
||||
|
||||
//The associated box is the border box of this flow
|
||||
mut_position.origin.x = base.margin.left;
|
||||
|
||||
let padding_and_borders = base.padding.left + base.padding.right +
|
||||
base.border.left + base.border.right;
|
||||
mut_position.size.width = remaining_width + padding_and_borders;
|
||||
// The associated box is the border box of this flow.
|
||||
let position_ref = box.position.mutate();
|
||||
position_ref.ptr.origin.x = box.margin.get().left;
|
||||
let padding_and_borders = box.padding.get().left + box.padding.get().right +
|
||||
box.border.get().left + box.border.get().right;
|
||||
position_ref.ptr.size.width = remaining_width + padding_and_borders;
|
||||
}
|
||||
|
||||
if self.is_float() {
|
||||
|
@ -734,25 +728,23 @@ impl Flow for BlockFlow {
|
|||
}
|
||||
|
||||
for &box in self.box.iter() {
|
||||
let base = box.base();
|
||||
|
||||
// The top margin collapses with its first in-flow block-level child's
|
||||
// top margin if the parent has no top border, no top padding.
|
||||
if *first_in_flow && top_margin_collapsible {
|
||||
// If top-margin of parent is less than top-margin of its first child,
|
||||
// the parent box goes down until its top is aligned with the child.
|
||||
if *margin_top < base.margin.top {
|
||||
if *margin_top < box.margin.get().top {
|
||||
// TODO: The position of child floats should be updated and this
|
||||
// would influence clearance as well. See #725
|
||||
let extra_margin = base.margin.top - *margin_top;
|
||||
let extra_margin = box.margin.get().top - *margin_top;
|
||||
*top_offset = *top_offset + extra_margin;
|
||||
*margin_top = base.margin.top;
|
||||
*margin_top = box.margin.get().top;
|
||||
}
|
||||
}
|
||||
// The bottom margin of an in-flow block-level element collapses
|
||||
// with the top margin of its next in-flow block-level sibling.
|
||||
*collapsing = geometry::min(base.margin.top, *collapsible);
|
||||
*collapsible = base.margin.bottom;
|
||||
*collapsing = geometry::min(box.margin.get().top, *collapsible);
|
||||
*collapsible = box.margin.get().bottom;
|
||||
}
|
||||
|
||||
*first_in_flow = false;
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,8 +7,8 @@
|
|||
//! Each step of the traversal considers the node and existing flow, if there is one. If a node is
|
||||
//! not dirty and an existing flow exists, then the traversal reuses that flow. Otherwise, it
|
||||
//! proceeds to construct either a flow or a `ConstructionItem`. A construction item is a piece of
|
||||
//! intermediate data that goes with a DOM node and hasn't found its "home" yet—maybe it's a render
|
||||
//! box, maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
|
||||
//! intermediate data that goes with a DOM node and hasn't found its "home" yet—maybe it's a box,
|
||||
//! maybe it's an absolute or fixed position thing that hasn't found its containing block yet.
|
||||
//! Construction items bubble up the tree from children to parents until they find their homes.
|
||||
//!
|
||||
//! TODO(pcwalton): There is no incremental reflow yet. This scheme requires that nodes either have
|
||||
|
@ -22,8 +22,7 @@
|
|||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::block::BlockFlow;
|
||||
use layout::box::{GenericRenderBox, ImageRenderBox, RenderBox, RenderBoxBase};
|
||||
use layout::box::{UnscannedTextRenderBox};
|
||||
use layout::box::{Box, GenericBox, ImageBox, ImageBoxInfo, UnscannedTextBox, UnscannedTextBoxInfo};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::float_context::FloatType;
|
||||
use layout::flow::{Flow, FlowData, MutableFlowUtils};
|
||||
|
@ -70,8 +69,8 @@ struct InlineBoxesConstructionResult {
|
|||
/// TODO(pcwalton): Small vector optimization.
|
||||
splits: Option<~[InlineBlockSplit]>,
|
||||
|
||||
/// Any render boxes that succeed the {ib} splits.
|
||||
boxes: ~[@RenderBox],
|
||||
/// Any boxes that succeed the {ib} splits.
|
||||
boxes: ~[@Box],
|
||||
}
|
||||
|
||||
/// Represents an {ib} split that has not yet found the containing block that it belongs to. This
|
||||
|
@ -97,10 +96,10 @@ struct InlineBoxesConstructionResult {
|
|||
/// C
|
||||
/// ])
|
||||
struct InlineBlockSplit {
|
||||
/// The inline render boxes that precede the flow.
|
||||
/// The inline boxes that precede the flow.
|
||||
///
|
||||
/// TODO(pcwalton): Small vector optimization.
|
||||
predecessor_boxes: ~[@RenderBox],
|
||||
predecessor_boxes: ~[@Box],
|
||||
|
||||
/// The flow that caused this {ib} split.
|
||||
flow: ~Flow:,
|
||||
|
@ -180,11 +179,6 @@ pub struct FlowConstructor<'self> {
|
|||
///
|
||||
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
|
||||
next_flow_id: Slot<int>,
|
||||
|
||||
/// The next box ID to assign.
|
||||
///
|
||||
/// FIXME(pcwalton): This is going to have to be atomic; can't we do something better?
|
||||
next_box_id: Slot<int>,
|
||||
}
|
||||
|
||||
impl<'self> FlowConstructor<'self> {
|
||||
|
@ -193,7 +187,6 @@ impl<'self> FlowConstructor<'self> {
|
|||
FlowConstructor {
|
||||
layout_context: layout_context,
|
||||
next_flow_id: Slot::init(0),
|
||||
next_box_id: Slot::init(0),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -204,40 +197,37 @@ impl<'self> FlowConstructor<'self> {
|
|||
id
|
||||
}
|
||||
|
||||
/// Returns the next render box ID and bumps the internal counter.
|
||||
fn next_box_id(&self) -> int {
|
||||
let id = self.next_box_id.get();
|
||||
self.next_box_id.set(id + 1);
|
||||
id
|
||||
}
|
||||
|
||||
/// Builds a `RenderBox` for the given image. This is out of line to guide inlining.
|
||||
fn build_box_for_image(&self, base: RenderBoxBase, node: AbstractNode<LayoutView>)
|
||||
-> @RenderBox {
|
||||
/// Builds the `ImageBoxInfo` for the given image. This is out of line to guide inlining.
|
||||
fn build_box_info_for_image(&self, node: AbstractNode<LayoutView>) -> Option<ImageBoxInfo> {
|
||||
// FIXME(pcwalton): Don't copy URLs.
|
||||
let url = node.with_imm_image_element(|image_element| {
|
||||
image_element.image.as_ref().map(|url| (*url).clone())
|
||||
});
|
||||
|
||||
match url {
|
||||
None => @GenericRenderBox::new(base) as @RenderBox,
|
||||
None => None,
|
||||
Some(url) => {
|
||||
// FIXME(pcwalton): The fact that image render boxes store the cache in the
|
||||
// box makes little sense to me.
|
||||
@ImageRenderBox::new(base, url, self.layout_context.image_cache) as @RenderBox
|
||||
// FIXME(pcwalton): The fact that image boxes store the cache within them makes
|
||||
// little sense to me.
|
||||
Some(ImageBoxInfo::new(url, self.layout_context.image_cache))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds a `RenderBox` for the given node.
|
||||
fn build_box_for_node(&self, node: AbstractNode<LayoutView>) -> @RenderBox {
|
||||
let base = RenderBoxBase::new(node, self.next_box_id());
|
||||
match node.type_id() {
|
||||
ElementNodeTypeId(HTMLImageElementTypeId) => self.build_box_for_image(base, node),
|
||||
TextNodeTypeId => @UnscannedTextRenderBox::new(base) as @RenderBox,
|
||||
_ => @GenericRenderBox::new(base) as @RenderBox,
|
||||
/// Builds a `Box` for the given node.
|
||||
fn build_box_for_node(&self, node: AbstractNode<LayoutView>) -> @Box {
|
||||
let specific = match node.type_id() {
|
||||
ElementNodeTypeId(HTMLImageElementTypeId) => {
|
||||
match self.build_box_info_for_image(node) {
|
||||
None => GenericBox,
|
||||
Some(image_box_info) => ImageBox(image_box_info),
|
||||
}
|
||||
}
|
||||
TextNodeTypeId => UnscannedTextBox(UnscannedTextBoxInfo::new(&node)),
|
||||
_ => GenericBox,
|
||||
};
|
||||
@Box::new(node, specific)
|
||||
}
|
||||
|
||||
/// Creates an inline flow from a set of inline boxes and adds it as a child of the given flow.
|
||||
///
|
||||
|
@ -245,7 +235,7 @@ impl<'self> FlowConstructor<'self> {
|
|||
/// otherwise.
|
||||
#[inline(always)]
|
||||
fn flush_inline_boxes_to_flow(&self,
|
||||
boxes: ~[@RenderBox],
|
||||
boxes: ~[@Box],
|
||||
flow: &mut ~Flow:,
|
||||
node: AbstractNode<LayoutView>) {
|
||||
if boxes.len() > 0 {
|
||||
|
@ -259,7 +249,7 @@ impl<'self> FlowConstructor<'self> {
|
|||
/// Creates an inline flow from a set of inline boxes, if present, and adds it as a child of
|
||||
/// the given flow.
|
||||
fn flush_inline_boxes_to_flow_if_necessary(&self,
|
||||
opt_boxes: &mut Option<~[@RenderBox]>,
|
||||
opt_boxes: &mut Option<~[@Box]>,
|
||||
flow: &mut ~Flow:,
|
||||
node: AbstractNode<LayoutView>) {
|
||||
let opt_boxes = util::replace(opt_boxes, None);
|
||||
|
@ -378,13 +368,13 @@ impl<'self> FlowConstructor<'self> {
|
|||
let mut opt_inline_block_splits = None;
|
||||
let mut opt_box_accumulator = None;
|
||||
|
||||
// Concatenate all the render boxes of our kids, creating {ib} splits as necessary.
|
||||
// Concatenate all the boxes of our kids, creating {ib} splits as necessary.
|
||||
for kid in node.children() {
|
||||
match kid.swap_out_construction_result() {
|
||||
NoConstructionResult => {}
|
||||
FlowConstructionResult(flow) => {
|
||||
// {ib} split. Flush the accumulator to our new split and make a new
|
||||
// accumulator to hold any subsequent `RenderBox`es we come across.
|
||||
// accumulator to hold any subsequent boxes we come across.
|
||||
let split = InlineBlockSplit {
|
||||
predecessor_boxes: util::replace(&mut opt_box_accumulator, None).to_vec(),
|
||||
flow: flow,
|
||||
|
@ -438,7 +428,7 @@ impl<'self> FlowConstructor<'self> {
|
|||
}
|
||||
|
||||
/// Creates an `InlineBoxesConstructionResult` for replaced content. Replaced content doesn't
|
||||
/// render its children, so this just nukes a child's boxes and creates a `RenderBox`.
|
||||
/// render its children, so this just nukes a child's boxes and creates a `Box`.
|
||||
fn build_boxes_for_replaced_inline_content(&self, node: AbstractNode<LayoutView>)
|
||||
-> ConstructionResult {
|
||||
for kid in node.children() {
|
||||
|
@ -454,7 +444,7 @@ impl<'self> FlowConstructor<'self> {
|
|||
ConstructionItemConstructionResult(construction_item)
|
||||
}
|
||||
|
||||
/// Builds one or more render boxes for a node with `display: inline`. This yields an
|
||||
/// Builds one or more boxes for a node with `display: inline`. This yields an
|
||||
/// `InlineBoxesConstructionResult`.
|
||||
fn build_boxes_for_inline(&self, node: AbstractNode<LayoutView>) -> ConstructionResult {
|
||||
// Is this node replaced content?
|
||||
|
@ -462,8 +452,7 @@ impl<'self> FlowConstructor<'self> {
|
|||
// Go to a path that concatenates our kids' boxes.
|
||||
self.build_boxes_for_nonreplaced_inline_content(node)
|
||||
} else {
|
||||
// Otherwise, just nuke our kids' boxes, create our `RenderBox` if any, and be done
|
||||
// with it.
|
||||
// Otherwise, just nuke our kids' boxes, create our box if any, and be done with it.
|
||||
self.build_boxes_for_replaced_inline_content(node)
|
||||
}
|
||||
}
|
||||
|
@ -494,7 +483,7 @@ impl<'self> PostorderNodeTraversal for FlowConstructor<'self> {
|
|||
}
|
||||
}
|
||||
|
||||
// Inline items contribute inline render box construction results.
|
||||
// Inline items contribute inline box construction results.
|
||||
(display::inline, float::none) => {
|
||||
let construction_result = self.build_boxes_for_inline(node);
|
||||
node.set_flow_construction_result(construction_result)
|
||||
|
@ -575,7 +564,7 @@ impl NodeUtils for AbstractNode<LayoutView> {
|
|||
}
|
||||
|
||||
/// Strips ignorable whitespace from the start of a list of boxes.
|
||||
fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@RenderBox]>) {
|
||||
fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@Box]>) {
|
||||
match util::replace(opt_boxes, None) {
|
||||
None => return,
|
||||
Some(boxes) => {
|
||||
|
@ -597,7 +586,7 @@ fn strip_ignorable_whitespace_from_start(opt_boxes: &mut Option<~[@RenderBox]>)
|
|||
}
|
||||
|
||||
/// Strips ignorable whitespace from the end of a list of boxes.
|
||||
fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[@RenderBox]>) {
|
||||
fn strip_ignorable_whitespace_from_end(opt_boxes: &mut Option<~[@Box]>) {
|
||||
match *opt_boxes {
|
||||
None => {}
|
||||
Some(ref mut boxes) => {
|
||||
|
|
|
@ -2,9 +2,9 @@
|
|||
* 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/. */
|
||||
|
||||
//! Constructs display lists from render boxes.
|
||||
//! Constructs display lists from boxes.
|
||||
|
||||
use layout::box::{RenderBox, RenderBoxUtils};
|
||||
use layout::box::Box;
|
||||
use layout::context::LayoutContext;
|
||||
use std::cast::transmute;
|
||||
use script::dom::node::AbstractNode;
|
||||
|
@ -16,27 +16,27 @@ use style;
|
|||
/// that nodes in this view shoud not really be touched. The idea is to
|
||||
/// store the nodes in the display list and have layout transmute them.
|
||||
pub trait ExtraDisplayListData {
|
||||
fn new(box: &@RenderBox) -> Self;
|
||||
fn new(box: &@Box) -> Self;
|
||||
}
|
||||
|
||||
pub type Nothing = ();
|
||||
|
||||
impl ExtraDisplayListData for AbstractNode<()> {
|
||||
fn new(box: &@RenderBox) -> AbstractNode<()> {
|
||||
fn new(box: &@Box) -> AbstractNode<()> {
|
||||
unsafe {
|
||||
transmute(box.base().node)
|
||||
transmute(box.node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraDisplayListData for Nothing {
|
||||
fn new(_: &@RenderBox) -> Nothing {
|
||||
fn new(_: &@Box) -> Nothing {
|
||||
()
|
||||
}
|
||||
}
|
||||
|
||||
impl ExtraDisplayListData for @RenderBox {
|
||||
fn new(box: &@RenderBox) -> @RenderBox {
|
||||
impl ExtraDisplayListData for @Box {
|
||||
fn new(box: &@Box) -> @Box {
|
||||
*box
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,23 +2,23 @@
|
|||
* 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/. */
|
||||
|
||||
//! Servo's experimental layout system builds a tree of `Flow` and `RenderBox` objects and
|
||||
/// solves layout constraints to obtain positions and display attributes of tree nodes. Positions
|
||||
/// are computed in several tree traversals driven by the fundamental data dependencies required by
|
||||
//! Servo's experimental layout system builds a tree of `Flow` and `Box` objects and solves
|
||||
//! layout constraints to obtain positions and display attributes of tree nodes. Positions are
|
||||
//! computed in several tree traversals driven by the fundamental data dependencies required by
|
||||
/// inline and block layout.
|
||||
///
|
||||
/// Flows are interior nodes in the layout tree and correspond closely to *flow contexts* in the
|
||||
/// CSS specification. Flows are responsible for positioning their child flow contexts and render
|
||||
/// boxes. Flows have purpose-specific fields, such as auxiliary line box structs, out-of-flow
|
||||
/// child lists, and so on.
|
||||
/// CSS specification. Flows are responsible for positioning their child flow contexts and boxes.
|
||||
/// Flows have purpose-specific fields, such as auxiliary line box structs, out-of-flow child
|
||||
/// lists, and so on.
|
||||
///
|
||||
/// Currently, the important types of flows are:
|
||||
///
|
||||
/// * `BlockFlow`: A flow that establishes a block context. It has several child flows, each of
|
||||
/// which are positioned according to block formatting context rules (CSS block boxes). Block
|
||||
/// flows also contain a single `GenericBox` to represent their rendered borders, padding, etc.
|
||||
/// (In the future, this render box may be folded into `BlockFlow` to save space.) The BlockFlow
|
||||
/// at the root of the tree has special behavior: it stretches to the boundaries of the viewport.
|
||||
/// (In the future, this box may be folded into `BlockFlow` to save space.) The BlockFlow at the
|
||||
/// root of the tree has special behavior: it stretches to the boundaries of the viewport.
|
||||
///
|
||||
/// * `InlineFlow`: A flow that establishes an inline context. It has a flat list of child
|
||||
/// boxes/flows that are subject to inline layout and line breaking and structs to represent
|
||||
|
@ -27,7 +27,7 @@
|
|||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::block::BlockFlow;
|
||||
use layout::box::RenderBox;
|
||||
use layout::box::Box;
|
||||
use layout::context::LayoutContext;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||
use layout::float_context::{FloatContext, Invalid};
|
||||
|
@ -340,12 +340,12 @@ pub struct FlowData {
|
|||
}
|
||||
|
||||
pub struct BoxIterator {
|
||||
priv boxes: ~[@RenderBox],
|
||||
priv boxes: ~[@Box],
|
||||
priv index: uint,
|
||||
}
|
||||
|
||||
impl Iterator<@RenderBox> for BoxIterator {
|
||||
fn next(&mut self) -> Option<@RenderBox> {
|
||||
impl Iterator<@Box> for BoxIterator {
|
||||
fn next(&mut self) -> Option<@Box> {
|
||||
if self.index >= self.boxes.len() {
|
||||
None
|
||||
} else {
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
use css::node_style::StyledNode;
|
||||
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
|
||||
use layout::box::{RenderBoxUtils, SplitDidFit, SplitDidNotFit, TextRenderBoxClass};
|
||||
use layout::box::{Box, CannotSplit, GenericBox, ImageBox, ScannedTextBox, SplitDidFit};
|
||||
use layout::box::{SplitDidNotFit, UnscannedTextBox};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
||||
use layout::flow::{FlowClass, Flow, FlowData, InlineFlowClass};
|
||||
|
@ -25,34 +25,31 @@ use std::cell::Cell;
|
|||
use std::u16;
|
||||
use std::util;
|
||||
|
||||
/*
|
||||
Lineboxes are represented as offsets into the child list, rather than
|
||||
as an object that "owns" boxes. Choosing a different set of line
|
||||
breaks requires a new list of offsets, and possibly some splitting and
|
||||
merging of TextBoxes.
|
||||
|
||||
A similar list will keep track of the mapping between CSS boxes and
|
||||
the corresponding render boxes in the inline flow.
|
||||
|
||||
After line breaks are determined, render boxes in the inline flow may
|
||||
overlap visually. For example, in the case of nested inline CSS boxes,
|
||||
outer inlines must be at least as large as the inner inlines, for
|
||||
purposes of drawing noninherited things like backgrounds, borders,
|
||||
outlines.
|
||||
|
||||
N.B. roc has an alternative design where the list instead consists of
|
||||
things like "start outer box, text, start inner box, text, end inner
|
||||
box, text, end outer box, text". This seems a little complicated to
|
||||
serve as the starting point, but the current design doesn't make it
|
||||
hard to try out that alternative.
|
||||
|
||||
Line boxes also contain some metadata used during line breaking. The
|
||||
green zone is the area that the line can expand to before it collides
|
||||
with a float or a horizontal wall of the containing block. The top
|
||||
left corner of the green zone is the same as that of the line, but
|
||||
the green zone can be taller and wider than the line itself.
|
||||
*/
|
||||
|
||||
/// Lineboxes are represented as offsets into the child list, rather than
|
||||
/// as an object that "owns" boxes. Choosing a different set of line
|
||||
/// breaks requires a new list of offsets, and possibly some splitting and
|
||||
/// merging of TextBoxes.
|
||||
///
|
||||
/// A similar list will keep track of the mapping between CSS boxes and
|
||||
/// the corresponding boxes in the inline flow.
|
||||
///
|
||||
/// After line breaks are determined, render boxes in the inline flow may
|
||||
/// overlap visually. For example, in the case of nested inline CSS boxes,
|
||||
/// outer inlines must be at least as large as the inner inlines, for
|
||||
/// purposes of drawing noninherited things like backgrounds, borders,
|
||||
/// outlines.
|
||||
///
|
||||
/// N.B. roc has an alternative design where the list instead consists of
|
||||
/// things like "start outer box, text, start inner box, text, end inner
|
||||
/// box, text, end outer box, text". This seems a little complicated to
|
||||
/// serve as the starting point, but the current design doesn't make it
|
||||
/// hard to try out that alternative.
|
||||
///
|
||||
/// Line boxes also contain some metadata used during line breaking. The
|
||||
/// green zone is the area that the line can expand to before it collides
|
||||
/// with a float or a horizontal wall of the containing block. The top
|
||||
/// left corner of the green zone is the same as that of the line, but
|
||||
/// the green zone can be taller and wider than the line itself.
|
||||
struct LineBox {
|
||||
range: Range,
|
||||
bounds: Rect<Au>,
|
||||
|
@ -61,8 +58,9 @@ struct LineBox {
|
|||
|
||||
struct LineboxScanner {
|
||||
floats: FloatContext,
|
||||
new_boxes: ~[@RenderBox],
|
||||
work_list: @mut RingBuf<@RenderBox>,
|
||||
new_boxes: ~[@Box],
|
||||
/// FIXME(pcwalton): `@mut`? :(
|
||||
work_list: @mut RingBuf<@Box>,
|
||||
pending_line: LineBox,
|
||||
lines: ~[LineBox],
|
||||
cur_y: Au,
|
||||
|
@ -128,11 +126,11 @@ impl LineboxScanner {
|
|||
break
|
||||
}
|
||||
let box = flow.boxes[i]; i += 1;
|
||||
debug!("LineboxScanner: Working with box from box list: b{:d}", box.base().id());
|
||||
debug!("LineboxScanner: Working with box from box list: b{}", box.debug_id());
|
||||
box
|
||||
} else {
|
||||
let box = self.work_list.pop_front().unwrap();
|
||||
debug!("LineboxScanner: Working with box from work list: b{:d}", box.base().id());
|
||||
debug!("LineboxScanner: Working with box from work list: b{}", box.debug_id());
|
||||
box
|
||||
};
|
||||
|
||||
|
@ -179,7 +177,7 @@ impl LineboxScanner {
|
|||
|
||||
// FIXME(eatkinson): this assumes that the tallest box in the line determines the line height
|
||||
// This might not be the case with some weird text fonts.
|
||||
fn new_height_for_line(&self, new_box: &RenderBox) -> Au {
|
||||
fn new_height_for_line(&self, new_box: &Box) -> Au {
|
||||
let box_height = new_box.box_height();
|
||||
if box_height > self.pending_line.bounds.size.height {
|
||||
box_height
|
||||
|
@ -188,14 +186,14 @@ impl LineboxScanner {
|
|||
}
|
||||
}
|
||||
|
||||
/// Computes the position of a line that has only the provided RenderBox.
|
||||
/// Returns: the bounding rect of the line's green zone (whose origin coincides
|
||||
/// with the line's origin) and the actual width of the first box after splitting.
|
||||
fn initial_line_placement(&self, first_box: @RenderBox, ceiling: Au, flow: &mut InlineFlow)
|
||||
/// Computes the position of a line that has only the provided box. Returns the bounding rect
|
||||
/// of the line's green zone (whose origin coincides with the line's origin) and the actual
|
||||
/// width of the first box after splitting.
|
||||
fn initial_line_placement(&self, first_box: @Box, ceiling: Au, flow: &mut InlineFlow)
|
||||
-> (Rect<Au>, Au) {
|
||||
debug!("LineboxScanner: Trying to place first box of line {}", self.lines.len());
|
||||
|
||||
let first_box_size = first_box.base().position.get().size;
|
||||
let first_box_size = first_box.position.get().size;
|
||||
let splittable = first_box.can_split();
|
||||
debug!("LineboxScanner: box size: {}, splittable: {}", first_box_size, splittable);
|
||||
let line_is_empty: bool = self.pending_line.range.length() == 0;
|
||||
|
@ -250,9 +248,9 @@ impl LineboxScanner {
|
|||
|
||||
debug!("LineboxScanner: case=box split and fit");
|
||||
let actual_box_width = match (left, right) {
|
||||
(Some(l_box), Some(_)) => l_box.base().position.get().size.width,
|
||||
(Some(l_box), None) => l_box.base().position.get().size.width,
|
||||
(None, Some(r_box)) => r_box.base().position.get().size.width,
|
||||
(Some(l_box), Some(_)) => l_box.position.get().size.width,
|
||||
(Some(l_box), None) => l_box.position.get().size.width,
|
||||
(None, Some(r_box)) => r_box.position.get().size.width,
|
||||
(None, None) => fail!("This case makes no sense.")
|
||||
};
|
||||
return (line_bounds, actual_box_width);
|
||||
|
@ -264,9 +262,9 @@ impl LineboxScanner {
|
|||
|
||||
debug!("LineboxScanner: case=box split and fit didn't fit; trying to push it down");
|
||||
let actual_box_width = match (left, right) {
|
||||
(Some(l_box), Some(_)) => l_box.base().position.get().size.width,
|
||||
(Some(l_box), None) => l_box.base().position.get().size.width,
|
||||
(None, Some(r_box)) => r_box.base().position.get().size.width,
|
||||
(Some(l_box), Some(_)) => l_box.position.get().size.width,
|
||||
(Some(l_box), None) => l_box.position.get().size.width,
|
||||
(None, Some(r_box)) => r_box.position.get().size.width,
|
||||
(None, None) => fail!("This case makes no sense.")
|
||||
};
|
||||
|
||||
|
@ -281,7 +279,7 @@ impl LineboxScanner {
|
|||
}
|
||||
|
||||
/// Returns false only if we should break the line.
|
||||
fn try_append_to_line(&mut self, in_box: @RenderBox, flow: &mut InlineFlow) -> bool {
|
||||
fn try_append_to_line(&mut self, in_box: @Box, flow: &mut InlineFlow) -> bool {
|
||||
let line_is_empty: bool = self.pending_line.range.length() == 0;
|
||||
|
||||
if line_is_empty {
|
||||
|
@ -293,7 +291,7 @@ impl LineboxScanner {
|
|||
debug!("LineboxScanner: Trying to append box to line {:u} (box size: {}, green zone: \
|
||||
{}): {:s}",
|
||||
self.lines.len(),
|
||||
in_box.base().position.get().size,
|
||||
in_box.position.get().size,
|
||||
self.pending_line.green_zone,
|
||||
in_box.debug_str());
|
||||
|
||||
|
@ -349,8 +347,7 @@ impl LineboxScanner {
|
|||
// horizontally. We'll try to place the whole box on this line and break somewhere
|
||||
// if it doesn't fit.
|
||||
|
||||
let new_width = self.pending_line.bounds.size.width +
|
||||
in_box.base().position.get().size.width;
|
||||
let new_width = self.pending_line.bounds.size.width + in_box.position.get().size.width;
|
||||
|
||||
if new_width <= green_zone.width {
|
||||
debug!("LineboxScanner: case=box fits without splitting");
|
||||
|
@ -424,8 +421,8 @@ impl LineboxScanner {
|
|||
}
|
||||
|
||||
// unconditional push
|
||||
fn push_box_to_line(&mut self, box: @RenderBox) {
|
||||
debug!("LineboxScanner: Pushing box b{:d} to line {:u}", box.base().id(), self.lines.len());
|
||||
fn push_box_to_line(&mut self, box: @Box) {
|
||||
debug!("LineboxScanner: Pushing box {} to line {:u}", box.debug_id(), self.lines.len());
|
||||
|
||||
if self.pending_line.range.length() == 0 {
|
||||
assert!(self.new_boxes.len() <= (u16::max_value as uint));
|
||||
|
@ -433,9 +430,9 @@ impl LineboxScanner {
|
|||
}
|
||||
self.pending_line.range.extend_by(1);
|
||||
self.pending_line.bounds.size.width = self.pending_line.bounds.size.width +
|
||||
box.base().position.get().size.width;
|
||||
box.position.get().size.width;
|
||||
self.pending_line.bounds.size.height = Au::max(self.pending_line.bounds.size.height,
|
||||
box.base().position.get().size.height);
|
||||
box.position.get().size.height);
|
||||
self.new_boxes.push(box);
|
||||
}
|
||||
}
|
||||
|
@ -444,9 +441,9 @@ pub struct InlineFlow {
|
|||
/// Data common to all flows.
|
||||
base: FlowData,
|
||||
|
||||
// A vec of all inline render boxes. Several boxes may
|
||||
// correspond to one Node/Element.
|
||||
boxes: ~[@RenderBox],
|
||||
/// A vector of all inline render boxes. Several boxes may correspond to one node/element.
|
||||
boxes: ~[@Box],
|
||||
|
||||
// vec of ranges into boxes that represents line positions.
|
||||
// these ranges are disjoint, and are the result of inline layout.
|
||||
// also some metadata used for positioning lines
|
||||
|
@ -467,7 +464,7 @@ impl InlineFlow {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn from_boxes(base: FlowData, boxes: ~[@RenderBox]) -> InlineFlow {
|
||||
pub fn from_boxes(base: FlowData, boxes: ~[@Box]) -> InlineFlow {
|
||||
InlineFlow {
|
||||
base: base,
|
||||
boxes: boxes,
|
||||
|
@ -560,8 +557,7 @@ impl Flow for InlineFlow {
|
|||
fn assign_widths(&mut self, _: &mut LayoutContext) {
|
||||
// Initialize content box widths if they haven't been initialized already.
|
||||
//
|
||||
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into
|
||||
// `RenderBox`.
|
||||
// TODO: Combine this with `LineboxScanner`'s walk in the box list, or put this into `Box`.
|
||||
|
||||
debug!("assign_widths_inline: floats_in: {:?}", self.base.floats_in);
|
||||
{
|
||||
|
@ -620,14 +616,13 @@ impl Flow for InlineFlow {
|
|||
if slack_width < Au::new(0) {
|
||||
slack_width = Au::new(0);
|
||||
}
|
||||
//assert!(slack_width >= Au::new(0), "Too many boxes on line");
|
||||
|
||||
// Get the text alignment.
|
||||
// TODO(Issue #222): use 'text-align' property from InlineFlow's
|
||||
// block container, not from the style of the first box child.
|
||||
let linebox_align = if line.range.begin() < self.boxes.len() {
|
||||
let first_box = self.boxes[line.range.begin()];
|
||||
first_box.base().nearest_ancestor_element().style().Text.text_align
|
||||
first_box.nearest_ancestor_element().style().Text.text_align
|
||||
} else {
|
||||
// Nothing to lay out, so assume left alignment.
|
||||
text_align::left
|
||||
|
@ -640,7 +635,7 @@ impl Flow for InlineFlow {
|
|||
// TODO(Issue #213): implement `text-align: justify`
|
||||
text_align::left | text_align::justify => {
|
||||
for i in line.range.eachi() {
|
||||
let box = self.boxes[i].base();
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
|
@ -648,7 +643,7 @@ impl Flow for InlineFlow {
|
|||
text_align::center => {
|
||||
offset_x = offset_x + slack_width.scale_by(0.5);
|
||||
for i in line.range.eachi() {
|
||||
let box = self.boxes[i].base();
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
|
@ -656,7 +651,7 @@ impl Flow for InlineFlow {
|
|||
text_align::right => {
|
||||
offset_x = offset_x + slack_width;
|
||||
for i in line.range.eachi() {
|
||||
let box = self.boxes[i].base();
|
||||
let box = &self.boxes[i];
|
||||
box.position.mutate().ptr.origin.x = offset_x;
|
||||
offset_x = offset_x + box.position.get().size.width;
|
||||
}
|
||||
|
@ -680,41 +675,40 @@ impl Flow for InlineFlow {
|
|||
for box_i in line.range.eachi() {
|
||||
let cur_box = self.boxes[box_i];
|
||||
|
||||
let (top_from_base, bottom_from_base, ascent) = match cur_box.class() {
|
||||
ImageRenderBoxClass => {
|
||||
let image_box = cur_box.as_image_render_box();
|
||||
let mut height = image_box.image_height();
|
||||
// FIXME(pcwalton): Move into `box.rs` like the rest of box-specific layout code?
|
||||
let (top_from_base, bottom_from_base, ascent) = match cur_box.specific {
|
||||
ImageBox(ref image_box) => {
|
||||
let mut height = image_box.image_height(cur_box);
|
||||
|
||||
// TODO: margin, border, padding's top and bottom should be calculated in advance,
|
||||
// since baseline of image is bottom margin edge.
|
||||
let mut top;
|
||||
let mut bottom;
|
||||
{
|
||||
let base = &image_box.base;
|
||||
top = base.border.top + base.padding.top + base.margin.top;
|
||||
bottom = base.border.bottom + base.padding.bottom +
|
||||
base.margin.bottom;
|
||||
top = cur_box.border.get().top + cur_box.padding.get().top +
|
||||
cur_box.margin.get().top;
|
||||
bottom = cur_box.border.get().bottom + cur_box.padding.get().bottom +
|
||||
cur_box.margin.get().bottom;
|
||||
}
|
||||
|
||||
let noncontent_height = top + bottom;
|
||||
height = height + noncontent_height;
|
||||
|
||||
let position_ref = image_box.base.position.mutate();
|
||||
let position_ref = cur_box.position.mutate();
|
||||
position_ref.ptr.size.height = height;
|
||||
position_ref.ptr.translate(&Point2D(Au::new(0), -height));
|
||||
|
||||
let ascent = height + bottom;
|
||||
(height, Au::new(0), ascent)
|
||||
},
|
||||
TextRenderBoxClass => {
|
||||
let text_box = cur_box.as_text_render_box();
|
||||
ScannedTextBox(ref text_box) => {
|
||||
let range = &text_box.range;
|
||||
let run = &text_box.run;
|
||||
|
||||
// Compute the height based on the line-height and font size
|
||||
let text_bounds = run.metrics_for_range(range).bounding_box;
|
||||
let em_size = text_bounds.size.height;
|
||||
let line_height = text_box.base.calculate_line_height(em_size);
|
||||
let line_height = cur_box.calculate_line_height(em_size);
|
||||
|
||||
// Find the top and bottom of the content area.
|
||||
// Those are used in text-top and text-bottom value of 'vertical-align'
|
||||
|
@ -722,20 +716,16 @@ impl Flow for InlineFlow {
|
|||
|
||||
// Offset from the top of the box is 1/2 of the leading + ascent
|
||||
let text_offset = text_ascent + (line_height - em_size).scale_by(0.5);
|
||||
text_bounds.translate(&Point2D(text_box.base.position.get().origin.x,
|
||||
Au::new(0)));
|
||||
text_bounds.translate(&Point2D(cur_box.position.get().origin.x, Au(0)));
|
||||
|
||||
(text_offset, line_height - text_offset, text_ascent)
|
||||
},
|
||||
GenericRenderBoxClass => {
|
||||
let base = cur_box.base();
|
||||
let height = base.position.get().size.height;
|
||||
GenericBox => {
|
||||
let height = cur_box.position.get().size.height;
|
||||
(height, Au::new(0), height)
|
||||
},
|
||||
// FIXME(pcwalton): This isn't very type safe!
|
||||
_ => {
|
||||
fail!("Tried to assign height to unknown Box variant: {:s}",
|
||||
cur_box.debug_str())
|
||||
UnscannedTextBox(_) => {
|
||||
fail!("Unscanned text boxes should have been scanned by now.")
|
||||
}
|
||||
};
|
||||
let mut top_from_base = top_from_base;
|
||||
|
@ -753,9 +743,8 @@ impl Flow for InlineFlow {
|
|||
// It should calculate the distance from baseline to the bottom of parent's content area.
|
||||
// But, it is assumed now as 0.
|
||||
let parent_text_bottom = Au::new(0);
|
||||
let cur_box_base = cur_box.base();
|
||||
// Get parent node
|
||||
let parent = cur_box_base.node.parent_node();
|
||||
let parent = cur_box.node.parent_node();
|
||||
|
||||
let font_size = parent.unwrap().style().Font.font_size;
|
||||
parent_text_top = font_size;
|
||||
|
@ -764,7 +753,7 @@ impl Flow for InlineFlow {
|
|||
// That is, if the box has top or bottom value, no_update_flag becomes true.
|
||||
let mut no_update_flag = false;
|
||||
// Calculate a relative offset from baseline.
|
||||
let offset = match cur_box_base.vertical_align() {
|
||||
let offset = match cur_box.vertical_align() {
|
||||
vertical_align::baseline => {
|
||||
-ascent
|
||||
},
|
||||
|
@ -819,9 +808,8 @@ impl Flow for InlineFlow {
|
|||
-(length + ascent)
|
||||
},
|
||||
vertical_align::Percentage(p) => {
|
||||
let pt_size = cur_box.base().font_style().pt_size;
|
||||
let line_height = cur_box.base()
|
||||
.calculate_line_height(Au::from_pt(pt_size));
|
||||
let pt_size = cur_box.font_style().pt_size;
|
||||
let line_height = cur_box.calculate_line_height(Au::from_pt(pt_size));
|
||||
let percent_offset = line_height.scale_by(p);
|
||||
-(percent_offset + ascent)
|
||||
}
|
||||
|
@ -836,7 +824,7 @@ impl Flow for InlineFlow {
|
|||
bottommost = bottom_from_base;
|
||||
}
|
||||
|
||||
cur_box.base().position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
|
||||
cur_box.position.mutate().ptr.origin.y = line.bounds.origin.y + offset;
|
||||
}
|
||||
|
||||
// Calculate the distance from baseline to the top of the biggest box with 'bottom' value.
|
||||
|
@ -859,15 +847,14 @@ impl Flow for InlineFlow {
|
|||
// All boxes' y position is updated following the new baseline offset.
|
||||
for box_i in line.range.eachi() {
|
||||
let cur_box = self.boxes[box_i];
|
||||
let cur_box_base = cur_box.base();
|
||||
let adjust_offset = match cur_box_base.vertical_align() {
|
||||
let adjust_offset = match cur_box.vertical_align() {
|
||||
vertical_align::top => Au::new(0),
|
||||
vertical_align::bottom => baseline_offset + bottommost,
|
||||
_ => baseline_offset,
|
||||
};
|
||||
|
||||
cur_box_base.position.mutate().ptr.origin.y =
|
||||
cur_box_base.position.get().origin.y + adjust_offset;
|
||||
cur_box.position.mutate().ptr.origin.y = cur_box.position.get().origin.y +
|
||||
adjust_offset;
|
||||
}
|
||||
|
||||
// This is used to set the top y position of the next linebox in the next loop.
|
||||
|
|
|
@ -4,13 +4,13 @@
|
|||
|
||||
//! Text layout.
|
||||
|
||||
use std::vec;
|
||||
use layout::box::{Box, ScannedTextBox, ScannedTextBoxInfo, UnscannedTextBox};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::flow::Flow;
|
||||
|
||||
use gfx::text::text_run::TextRun;
|
||||
use gfx::text::util::{CompressWhitespaceNewline, transform_text};
|
||||
use layout::box::{RenderBox, RenderBoxUtils, TextRenderBox, UnscannedTextRenderBoxClass};
|
||||
use layout::context::LayoutContext;
|
||||
use layout::flow::Flow;
|
||||
use std::vec;
|
||||
use servo_util::range::Range;
|
||||
|
||||
/// A stack-allocated object for scanning an inline flow into `TextRun`-containing `TextBox`es.
|
||||
|
@ -56,27 +56,20 @@ impl TextRunScanner {
|
|||
flow.as_inline().boxes = out_boxes;
|
||||
|
||||
// A helper function.
|
||||
fn can_coalesce_text_nodes(boxes: &[@RenderBox], left_i: uint, right_i: uint) -> bool {
|
||||
fn can_coalesce_text_nodes(boxes: &[@Box], left_i: uint, right_i: uint) -> bool {
|
||||
assert!(left_i < boxes.len());
|
||||
assert!(right_i > 0 && right_i < boxes.len());
|
||||
assert!(left_i != right_i);
|
||||
|
||||
let (left, right) = (boxes[left_i], boxes[right_i]);
|
||||
match (left.class(), right.class()) {
|
||||
(UnscannedTextRenderBoxClass, UnscannedTextRenderBoxClass) => {
|
||||
left.can_merge_with_box(right)
|
||||
}
|
||||
(_, _) => false
|
||||
}
|
||||
boxes[left_i].can_merge_with_box(boxes[right_i])
|
||||
}
|
||||
}
|
||||
|
||||
/// A "clump" is a range of inline flow leaves that can be merged together into a single
|
||||
/// `RenderBox`. Adjacent text with the same style can be merged, and nothing else can.
|
||||
/// A "clump" is a range of inline flow leaves that can be merged together into a single box.
|
||||
/// Adjacent text with the same style can be merged, and nothing else can.
|
||||
///
|
||||
/// The flow keeps track of the `RenderBox`es contained by all non-leaf DOM nodes. This is
|
||||
/// necessary for correct painting order. Since we compress several leaf `RenderBox`es here,
|
||||
/// the mapping must be adjusted.
|
||||
/// The flow keeps track of the boxes contained by all non-leaf DOM nodes. This is necessary
|
||||
/// for correct painting order. Since we compress several leaf boxes here, the mapping must be
|
||||
/// adjusted.
|
||||
///
|
||||
/// N.B. `in_boxes` is passed by reference, since the old code used a `DVec`. The caller is
|
||||
/// responsible for swapping out the list. It is not clear to me (pcwalton) that this is still
|
||||
|
@ -85,7 +78,7 @@ impl TextRunScanner {
|
|||
ctx: &LayoutContext,
|
||||
flow: &mut Flow,
|
||||
last_whitespace: bool,
|
||||
out_boxes: &mut ~[@RenderBox])
|
||||
out_boxes: &mut ~[@Box])
|
||||
-> bool {
|
||||
let inline = flow.as_inline();
|
||||
let in_boxes = &inline.boxes;
|
||||
|
@ -95,10 +88,12 @@ impl TextRunScanner {
|
|||
debug!("TextRunScanner: flushing boxes in range={}", self.clump);
|
||||
let is_singleton = self.clump.length() == 1;
|
||||
let possible_text_clump = in_boxes[self.clump.begin()]; // FIXME(pcwalton): Rust bug
|
||||
let is_text_clump = possible_text_clump.class() == UnscannedTextRenderBoxClass;
|
||||
let is_text_clump = match possible_text_clump.specific {
|
||||
UnscannedTextBox(_) => true,
|
||||
_ => false,
|
||||
};
|
||||
|
||||
let mut new_whitespace = last_whitespace;
|
||||
|
||||
match (is_singleton, is_text_clump) {
|
||||
(false, false) => {
|
||||
fail!(~"WAT: can't coalesce non-text nodes in flush_clump_to_list()!")
|
||||
|
@ -109,14 +104,20 @@ impl TextRunScanner {
|
|||
},
|
||||
(true, true) => {
|
||||
let old_box = in_boxes[self.clump.begin()];
|
||||
let text = old_box.as_unscanned_text_render_box().raw_text();
|
||||
let font_style = old_box.base().font_style();
|
||||
let decoration = old_box.base().text_decoration();
|
||||
let text = match old_box.specific {
|
||||
UnscannedTextBox(ref text_box_info) => &text_box_info.text,
|
||||
_ => fail!("Expected an unscanned text box!"),
|
||||
};
|
||||
|
||||
let font_style = old_box.font_style();
|
||||
let decoration = old_box.text_decoration();
|
||||
|
||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||
let compression = CompressWhitespaceNewline;
|
||||
|
||||
let (transformed_text, whitespace) = transform_text(text, compression, last_whitespace);
|
||||
let (transformed_text, whitespace) = transform_text(*text,
|
||||
compression,
|
||||
last_whitespace);
|
||||
new_whitespace = whitespace;
|
||||
|
||||
if transformed_text.len() > 0 {
|
||||
|
@ -126,11 +127,15 @@ impl TextRunScanner {
|
|||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||
let run = @fontgroup.create_textrun(transformed_text, decoration);
|
||||
|
||||
debug!("TextRunScanner: pushing single text box in range: {} ({})", self.clump, text);
|
||||
debug!("TextRunScanner: pushing single text box in range: {} ({})",
|
||||
self.clump,
|
||||
*text);
|
||||
let range = Range::new(0, run.char_len());
|
||||
let new_box = @TextRenderBox::new((*old_box.base()).clone(), run, range);
|
||||
|
||||
out_boxes.push(new_box as @RenderBox);
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(run, range);
|
||||
let new_metrics = run.metrics_for_range(&range);
|
||||
let new_box = @old_box.transform(new_metrics.bounding_box.size,
|
||||
ScannedTextBox(new_text_box_info));
|
||||
out_boxes.push(new_box)
|
||||
}
|
||||
},
|
||||
(false, true) => {
|
||||
|
@ -144,8 +149,12 @@ impl TextRunScanner {
|
|||
// `transform_text`, so that boxes starting and/or ending with whitespace can
|
||||
// be compressed correctly with respect to the text run.
|
||||
let idx = i + self.clump.begin();
|
||||
let in_box = in_boxes[idx].as_unscanned_text_render_box().raw_text();
|
||||
let (new_str, new_whitespace) = transform_text(in_box,
|
||||
let in_box = match in_boxes[idx].specific {
|
||||
UnscannedTextBox(ref text_box_info) => &text_box_info.text,
|
||||
_ => fail!("Expected an unscanned text box!"),
|
||||
};
|
||||
|
||||
let (new_str, new_whitespace) = transform_text(*in_box,
|
||||
compression,
|
||||
last_whitespace_in_clump);
|
||||
last_whitespace_in_clump = new_whitespace;
|
||||
|
@ -171,9 +180,9 @@ impl TextRunScanner {
|
|||
// font group fonts. This is probably achieved by creating the font group above
|
||||
// and then letting `FontGroup` decide which `Font` to stick into the text run.
|
||||
let in_box = in_boxes[self.clump.begin()];
|
||||
let font_style = in_box.base().font_style();
|
||||
let font_style = in_box.font_style();
|
||||
let fontgroup = ctx.font_ctx.get_resolved_font_for_style(&font_style);
|
||||
let decoration = in_box.base().text_decoration();
|
||||
let decoration = in_box.text_decoration();
|
||||
|
||||
// TextRuns contain a cycle which is usually resolved by the teardown
|
||||
// sequence. If no clump takes ownership, however, it will leak.
|
||||
|
@ -195,10 +204,11 @@ impl TextRunScanner {
|
|||
continue
|
||||
}
|
||||
|
||||
let new_box = @TextRenderBox::new((*in_boxes[i].base()).clone(),
|
||||
run.unwrap(),
|
||||
range);
|
||||
out_boxes.push(new_box as @RenderBox);
|
||||
let new_text_box_info = ScannedTextBoxInfo::new(run.unwrap(), range);
|
||||
let new_metrics = new_text_box_info.run.metrics_for_range(&range);
|
||||
let new_box = @in_boxes[i].transform(new_metrics.bounding_box.size,
|
||||
ScannedTextBox(new_text_box_info));
|
||||
out_boxes.push(new_box)
|
||||
}
|
||||
}
|
||||
} // End of match.
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* 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::box::Box;
|
||||
use layout::construct::{ConstructionResult, NoConstructionResult};
|
||||
|
||||
use extra::arc::Arc;
|
||||
|
@ -76,7 +76,7 @@ impl ElementMapping {
|
|||
self.entries.iter().enumerate()
|
||||
}
|
||||
|
||||
pub fn repair_for_box_changes(&mut self, old_boxes: &[@RenderBox], new_boxes: &[@RenderBox]) {
|
||||
pub fn repair_for_box_changes(&mut self, old_boxes: &[@Box], new_boxes: &[@Box]) {
|
||||
let entries = &mut self.entries;
|
||||
|
||||
debug!("--- Old boxes: ---");
|
||||
|
@ -118,8 +118,7 @@ impl ElementMapping {
|
|||
repair_stack.push(item);
|
||||
entries_k += 1;
|
||||
}
|
||||
while new_j < new_boxes.len() &&
|
||||
old_boxes[old_i].base().node != new_boxes[new_j].base().node {
|
||||
while new_j < new_boxes.len() && old_boxes[old_i].node != new_boxes[new_j].node {
|
||||
debug!("repair_for_box_changes: Slide through new box {:u}", new_j);
|
||||
new_j += 1;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue