Add comments on overall design; move display list building to Flows and Boxes; create a DisplayListBuilder context; rename Box to RenderBox

This commit is contained in:
Brian J. Burg 2012-09-19 17:45:22 -07:00
parent 4e9e679547
commit da7ae8a280
14 changed files with 362 additions and 195 deletions

@ -1 +1 @@
Subproject commit e1ec99049c193aaedbb603e4ceb675251e5814b3 Subproject commit 01de30b15bfb3ecdfb921b7571ff98e63c0d4483

View file

@ -1,7 +1,7 @@
#[doc="Applies the appropriate CSS style to boxes."] #[doc="Applies the appropriate CSS style to boxes."]
use au = gfx::geometry; use au = gfx::geometry;
use layout::base::{Box, SpecifiedStyle, BoxTree}; use layout::base::{RenderBox, SpecifiedStyle, RenderBoxTree};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::traverse_parallel::top_down_traversal; use layout::traverse_parallel::top_down_traversal;
use image::ImageHolder; use image::ImageHolder;
@ -32,12 +32,12 @@ impl CSSValue<CSSFontSize> : ResolveMethods<CSSFontSize> {
struct StyleApplicator { struct StyleApplicator {
box: @Box, box: @RenderBox,
reflow: fn~(), reflow: fn~(),
} }
// TODO: normalize this into a normal preorder tree traversal function // TODO: normalize this into a normal preorder tree traversal function
fn apply_style(layout_ctx: &LayoutContext, box: @Box, reflow: fn~()) { fn apply_style(layout_ctx: &LayoutContext, box: @RenderBox, reflow: fn~()) {
let applicator = StyleApplicator { let applicator = StyleApplicator {
box: box, box: box,
reflow: reflow reflow: reflow
@ -50,7 +50,7 @@ fn apply_style(layout_ctx: &LayoutContext, box: @Box, reflow: fn~()) {
#[doc="A wrapper around a set of functions that can be applied as a top-down traversal of layout #[doc="A wrapper around a set of functions that can be applied as a top-down traversal of layout
boxes."] boxes."]
fn inheritance_wrapper(layout_ctx: &LayoutContext, box : @Box, reflow: fn~()) { fn inheritance_wrapper(layout_ctx: &LayoutContext, box : @RenderBox, reflow: fn~()) {
let applicator = StyleApplicator { let applicator = StyleApplicator {
box: box, box: box,
reflow: reflow reflow: reflow
@ -59,12 +59,12 @@ fn inheritance_wrapper(layout_ctx: &LayoutContext, box : @Box, reflow: fn~()) {
} }
/* /*
fn resolve_fontsize(box : @Box) { fn resolve_fontsize(box : @RenderBox) {
// TODO: complete this // TODO: complete this
return return
} }
fn resolve_height(box : @Box) -> au { fn resolve_height(box : @RenderBox) -> au {
let style = box.node.get_style(); let style = box.node.get_style();
let inherit_val = match box.tree.parent { let inherit_val = match box.tree.parent {
None => au(0), None => au(0),
@ -81,7 +81,7 @@ fn resolve_height(box : @Box) -> au {
} }
} }
fn resolve_width(box : @Box) { fn resolve_width(box : @RenderBox) {
let style = box.node.get_specified_style(); let style = box.node.get_specified_style();
let inherit_val = match box.tree.parent { let inherit_val = match box.tree.parent {
None => style.height.initial(), None => style.height.initial(),
@ -102,7 +102,7 @@ impl StyleApplicator {
fn apply_css_style(layout_ctx: &LayoutContext) { fn apply_css_style(layout_ctx: &LayoutContext) {
let reflow = copy self.reflow; let reflow = copy self.reflow;
do BoxTree.each_child(self.box) |child| { do RenderBoxTree.each_child(self.box) |child| {
inheritance_wrapper(layout_ctx, child, reflow); true inheritance_wrapper(layout_ctx, child, reflow); true
} }
} }

View file

@ -13,7 +13,7 @@ use js::glue::bindgen::RUST_OBJECT_TO_JSVAL;
use js::jsapi::{JSClass, JSObject, JSPropertySpec, JSContext, jsid, jsval, JSBool}; use js::jsapi::{JSClass, JSObject, JSPropertySpec, JSContext, jsid, jsval, JSBool};
use js::rust::{bare_compartment, compartment, methods}; use js::rust::{bare_compartment, compartment, methods};
use js::{JSPROP_ENUMERATE, JSPROP_SHARED}; use js::{JSPROP_ENUMERATE, JSPROP_SHARED};
use layout::base::Box; use layout::base::RenderBox;
use layout::debug::DebugMethods; use layout::debug::DebugMethods;
use ptr::null; use ptr::null;
use std::arc::ARC; use std::arc::ARC;
@ -193,12 +193,12 @@ enum ElementKind {
/** The RCU rd_aux data is a (weak) pointer to the layout data, /** The RCU rd_aux data is a (weak) pointer to the layout data,
defined by this `LayoutData` enum. It contains the CSS style object defined by this `LayoutData` enum. It contains the CSS style object
as well as the primary `Box`. as well as the primary `RenderBox`.
Note that there may be multiple boxes per DOM node. */ Note that there may be multiple boxes per DOM node. */
enum LayoutData = { enum LayoutData = {
mut style: ~SpecifiedStyle, mut style: ~SpecifiedStyle,
mut box: Option<@Box> mut box: Option<@RenderBox>
}; };
type Node = rcu::Handle<NodeData, LayoutData>; type Node = rcu::Handle<NodeData, LayoutData>;

View file

@ -9,6 +9,8 @@ use std::arc::{ARC, clone};
use dvec::DVec; use dvec::DVec;
use text::glyph::Glyph; use text::glyph::Glyph;
pub use layout::display_list_builder::DisplayListBuilder;
struct DisplayItem { struct DisplayItem {
draw: ~fn((&DisplayItem), (&RenderContext)), draw: ~fn((&DisplayItem), (&RenderContext)),
bounds : Rect<au>, // TODO: whose coordinate system should this use? bounds : Rect<au>, // TODO: whose coordinate system should this use?
@ -88,7 +90,7 @@ trait DisplayListMethods {
impl DisplayList : DisplayListMethods { impl DisplayList : DisplayListMethods {
fn draw(ctx: &RenderContext) { fn draw(ctx: &RenderContext) {
for self.each |item| { for self.each |item| {
debug!["drawing %?", item]; debug!("drawing %?", item);
item.draw(item, ctx); item.draw(item, ctx);
} }
} }

View file

@ -1,17 +1,21 @@
/* Fundamental layout structures and algorithms. */ /* Fundamental layout structures and algorithms. */
use arc = std::arc;
use arc::ARC;
use au = gfx::geometry; use au = gfx::geometry;
use au::au;
use core::dvec::DVec; use core::dvec::DVec;
use core::to_str::ToStr; use core::to_str::ToStr;
use core::rand; use core::rand;
use css::styles::SpecifiedStyle; use css::styles::SpecifiedStyle;
use css::values::{BoxSizing, Length, Px, CSSDisplay}; use css::values::{BoxSizing, Length, Px, CSSDisplay, Specified, BgColor, BgTransparent};
use dl = gfx::display_list;
use dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement}; use dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement};
use dom::base::{Node, NodeData, NodeKind, NodeTree}; use dom::base::{Node, NodeData, NodeKind, NodeTree};
use dom::rcu; use dom::rcu;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::geometry::au; use geom::point::Point2D;
use image::{Image, ImageHolder}; use image::{Image, ImageHolder};
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::context::LayoutContext; use layout::context::LayoutContext;
@ -20,14 +24,73 @@ use layout::inline::InlineFlowData;
use layout::root::RootFlowData; use layout::root::RootFlowData;
use layout::text::TextBoxData; use layout::text::TextBoxData;
use servo_text::text_run::TextRun; use servo_text::text_run::TextRun;
use std::arc::{ARC, clone};
use std::net::url::Url; use std::net::url::Url;
use task::spawn; use task::spawn;
use util::color::Color; use util::color::Color;
use util::tree; use util::tree;
use vec::{push, push_all}; use vec::{push, push_all};
/** Servo's experimental layout system builds a tree of FlowContexts
and RenderBoxes, and figures out positions and display attributes of
tree nodes. Positions are computed in several tree traversals driven
by fundamental data dependencies of inline and block layout.
Render boxes (`struct RenderBox`) are the leafs of the layout
tree. They cannot position themselves. In general, render boxes do not
have a simple correspondence with CSS boxes as in the specification:
* Several render boxes may correspond to the same CSS box or DOM
node. For example, a CSS text box broken across two lines is
represented by two render boxes.
* Some CSS boxes are not created at all, such as some anonymous block
boxes induced by inline boxes with block-level sibling boxes. In
that case, Servo uses an InlineFlow with BlockFlow siblings; the
InlineFlow is block-level, but not a block container. It is
positioned as if it were a block box, but its children are
positioned according to inline flow.
Fundamental box types include:
* GenericBox: an empty box that contributes only borders, margins,
padding, backgrounds. It is analogous to a CSS nonreplaced content box.
* ImageBox: a box that represents a (replaced content) image and its
accompanying borders, shadows, etc.
* TextBox: a box representing a single run of text with a distinct
style. A TextBox may be split into two or more render boxes across
line breaks. Several TextBoxes may correspond to a single DOM text
node. Split text boxes are implemented by referring to subsets of a
master TextRun object.
Flows (`struct FlowContext`) 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
auxilliary 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 (as if child flows 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.)
* 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 line breaks and mapping
to CSS boxes, for the purpose of handling `getClientRects()`.
*/
struct FlowLayoutData { struct FlowLayoutData {
// TODO: min/pref and position are used during disjoint phases of
// layout; maybe combine into a single enum to save space.
mut min_width: au, mut min_width: au,
mut pref_width: au, mut pref_width: au,
mut position: Rect<au>, mut position: Rect<au>,
@ -42,7 +105,7 @@ fn FlowLayoutData() -> FlowLayoutData {
} }
/* The type of the formatting context, and data specific to each /* The type of the formatting context, and data specific to each
context, such as lineboxes or float lists */ context, such as linebox structures or float lists */
enum FlowContextData { enum FlowContextData {
AbsoluteFlow, AbsoluteFlow,
BlockFlow(BlockFlowData), BlockFlow(BlockFlowData),
@ -54,14 +117,7 @@ enum FlowContextData {
} }
/* A particular kind of layout context. It manages the positioning of /* A particular kind of layout context. It manages the positioning of
layout boxes within the context. render boxes within the context. */
Flow contexts form a tree that is induced by the structure of the
box tree. Each context is responsible for laying out one or more
boxes, according to the flow type. The number of flow contexts should
be much fewer than the number of boxes. The context maintains a vector
of its constituent boxes in their document order.
*/
struct FlowContext { struct FlowContext {
kind: FlowContextData, kind: FlowContextData,
data: FlowLayoutData, data: FlowLayoutData,
@ -85,6 +141,8 @@ impl @FlowContext : cmp::Eq {
pure fn ne(&&other: @FlowContext) -> bool { !box::ptr_eq(self, other) } pure fn ne(&&other: @FlowContext) -> bool { !box::ptr_eq(self, other) }
} }
/* Flow context disambiguation methods: the verbose alternative to virtual methods */
impl @FlowContext { impl @FlowContext {
fn bubble_widths(ctx: &LayoutContext) { fn bubble_widths(ctx: &LayoutContext) {
match self.kind { match self.kind {
@ -112,6 +170,16 @@ impl @FlowContext {
_ => fail fmt!("Tried to assign_height of flow: %?", self.kind) _ => fail fmt!("Tried to assign_height of flow: %?", self.kind)
} }
} }
fn build_display_list_recurse(builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) {
match self.kind {
RootFlow(*) => self.build_display_list_root(builder, dirty, offset, list),
BlockFlow(*) => self.build_display_list_block(builder, dirty, offset, list),
InlineFlow(*) => self.build_display_list_inline(builder, dirty, offset, list),
_ => fail fmt!("Tried to build_display_list_recurse of flow: %?", self.kind)
}
}
} }
/* The tree holding FlowContexts */ /* The tree holding FlowContexts */
@ -166,9 +234,9 @@ enum BoxData {
TextBox(TextBoxData) TextBox(TextBoxData)
} }
struct Box { struct RenderBox {
/* references to children, parent */ /* references to children, parent */
tree : tree::Tree<@Box>, tree : tree::Tree<@RenderBox>,
/* originating DOM node */ /* originating DOM node */
node : Node, node : Node,
/* reference to containing flow context, which this box /* reference to containing flow context, which this box
@ -182,8 +250,8 @@ struct Box {
mut id: int mut id: int
} }
fn Box(id: int, node: Node, ctx: @FlowContext, kind: BoxData) -> Box { fn RenderBox(id: int, node: Node, ctx: @FlowContext, kind: BoxData) -> RenderBox {
Box { RenderBox {
/* will be set when box is parented */ /* will be set when box is parented */
tree : tree::empty(), tree : tree::empty(),
node : node, node : node,
@ -194,7 +262,7 @@ fn Box(id: int, node: Node, ctx: @FlowContext, kind: BoxData) -> Box {
} }
} }
impl @Box { impl @RenderBox {
pure fn is_replaced() -> bool { pure fn is_replaced() -> bool {
match self.kind { match self.kind {
ImageBox(*) => true, // TODO: form elements, etc ImageBox(*) => true, // TODO: form elements, etc
@ -210,14 +278,12 @@ impl @Box {
// FlowContext will combine the width of this element and // FlowContext will combine the width of this element and
// that of its children to arrive at the context width. // that of its children to arrive at the context width.
GenericBox => au(0), GenericBox => au(0),
// TODO: consult CSS 'width', margin, border.
// TODO: If image isn't available, consult Node // TODO: If image isn't available, consult Node
// attrs, etc. to determine intrinsic dimensions. These // attrs, etc. to determine intrinsic dimensions. These
// dimensions are not defined by CSS 2.1, but are defined // dimensions are not defined by CSS 2.1, but are defined
// by the HTML5 spec in Section 4.8.1 // by the HTML5 spec in Section 4.8.1
ImageBox(size) => size.width, ImageBox(size) => size.width,
// TODO: account for line breaks, etc. The run should know
// how to compute its own min and pref widths, and should
// probably cache them.
TextBox(d) => d.runs.foldl(au(0), |sum, run| { TextBox(d) => d.runs.foldl(au(0), |sum, run| {
au::max(sum, run.min_break_width()) au::max(sum, run.min_break_width())
}) })
@ -282,30 +348,99 @@ impl @Box {
image image
} }
// TODO: to implement stacking contexts correctly, we need to
// create a set of display lists, one per each layer of a stacking
// context. (CSS 2.1, Section 9.9.1). Each box is passed the list
// set representing the box's stacking context. When asked to
// construct its constituent display items, each box puts its
// DisplayItems into the correct stack layer (according to CSS 2.1
// Appendix E). and then builder flattens the list at the end.
/* Methods for building a display list. This is a good candidate
for a function pointer as the number of boxes explodes.
# Arguments
* `builder` - the display list builder which manages the coordinate system and options.
* `dirty` - Dirty rectangle, in the coordinate system of the owning flow (self.ctx)
* `origin` - Total offset from display list root flow to this box's owning flow
* `list` - List to which items should be appended
*/
fn build_display_list(builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) {
if !self.data.position.intersects(dirty) {
return;
}
let bounds : Rect<au> = Rect(self.data.position.origin.add(offset),
copy self.data.position.size);
match self.kind {
TextBox(d) => {
let mut runs = d.runs;
// TODO: don't paint background for text boxes
list.push(~dl::SolidColor(bounds, 255u8, 255u8, 255u8));
let mut bounds = bounds;
for uint::range(0, runs.len()) |i| {
bounds.size.height = runs[i].size().height;
let glyph_run = make_glyph_run(&runs[i]);
list.push(~dl::Glyphs(bounds, glyph_run));
bounds.origin.y += bounds.size.height;
}
return;
pure fn make_glyph_run(text_run: &TextRun) -> dl::GlyphRun {
dl::GlyphRun {
glyphs: copy text_run.glyphs
}
}
},
// TODO: items for background, border, outline
GenericBox(*) => { },
ImageBox(*) => {
match self.get_image() {
Some(image) => list.push(~dl::Image(bounds, image)),
/* No image data at all? Okay, add some fallback content instead. */
None => {
// TODO: shouldn't need to unbox CSSValue by now
let boxed_color = self.node.style().background_color;
let color = match boxed_color {
Specified(BgColor(c)) => c,
Specified(BgTransparent) | _ => util::color::rgba(0,0,0,0.0)
};
list.push(~dl::SolidColor(bounds, color.red, color.green, color.blue));
}
}
}
}
}
} }
// FIXME: Why do these have to be redefined for each node type? // FIXME: Why do these have to be redefined for each node type?
/* The tree holding boxes */ /* The tree holding render box relations. (This should only be used
enum BoxTree { BoxTree } for painting nested inlines, AFAIK-- everything else depends on Flow tree) */
enum RenderBoxTree { RenderBoxTree }
impl BoxTree : tree::ReadMethods<@Box> { impl RenderBoxTree : tree::ReadMethods<@RenderBox> {
fn each_child(node: @Box, f: fn(&&@Box) -> bool) { fn each_child(node: @RenderBox, f: fn(&&@RenderBox) -> bool) {
tree::each_child(self, node, f) tree::each_child(self, node, f)
} }
fn with_tree_fields<R>(&&b: @Box, f: fn(tree::Tree<@Box>) -> R) -> R { fn with_tree_fields<R>(&&b: @RenderBox, f: fn(tree::Tree<@RenderBox>) -> R) -> R {
f(b.tree) f(b.tree)
} }
} }
impl BoxTree : tree::WriteMethods<@Box> { impl RenderBoxTree : tree::WriteMethods<@RenderBox> {
fn add_child(parent: @Box, child: @Box) { fn add_child(parent: @RenderBox, child: @RenderBox) {
assert !box::ptr_eq(parent, child); assert !box::ptr_eq(parent, child);
tree::add_child(self, parent, child) tree::add_child(self, parent, child)
} }
fn with_tree_fields<R>(&&b: @Box, f: fn(tree::Tree<@Box>) -> R) -> R { fn with_tree_fields<R>(&&b: @RenderBox, f: fn(tree::Tree<@RenderBox>) -> R) -> R {
f(b.tree) f(b.tree)
} }
} }
@ -354,7 +489,7 @@ impl @FlowContext : DebugMethods {
} }
} }
impl @Box : DebugMethods { impl @RenderBox : DebugMethods {
fn dump() { fn dump() {
self.dump_indent(0u); self.dump_indent(0u);
} }
@ -369,7 +504,7 @@ impl @Box : DebugMethods {
s += self.debug_str(); s += self.debug_str();
debug!("%s", s); debug!("%s", s);
for BoxTree.each_child(self) |kid| { for RenderBoxTree.each_child(self) |kid| {
kid.dump_indent(indent + 1u) kid.dump_indent(indent + 1u)
} }
} }
@ -412,9 +547,9 @@ mod test {
} }
*/ */
fn flat_bounds(root: @Box) -> ~[Rect<au>] { fn flat_bounds(root: @RenderBox) -> ~[Rect<au>] {
let mut r = ~[]; let mut r = ~[];
for tree::each_child(BoxTree, root) |c| { for tree::each_child(RenderBoxTree, root) |c| {
push_all(r, flat_bounds(c)); push_all(r, flat_bounds(c));
} }

View file

@ -1,14 +1,16 @@
use au = gfx::geometry; use au = gfx::geometry;
use css::values::*; use css::values::*;
use dl = gfx::display_list;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::geometry::au; use gfx::geometry::au;
use layout::base::{Box, FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow}; use layout::base::{RenderBox, FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use util::tree; use util::tree;
struct BlockFlowData { struct BlockFlowData {
mut box: Option<@Box> mut box: Option<@RenderBox>
} }
fn BlockFlowData() -> BlockFlowData { fn BlockFlowData() -> BlockFlowData {
@ -20,11 +22,14 @@ fn BlockFlowData() -> BlockFlowData {
trait BlockLayout { trait BlockLayout {
pure fn starts_block_flow() -> bool; pure fn starts_block_flow() -> bool;
pure fn access_block<T>(fn(&&BlockFlowData) -> T) -> T; pure fn access_block<T>(fn(&&BlockFlowData) -> T) -> T;
pure fn with_block_box(fn(&&@Box) -> ()) -> (); pure fn with_block_box(fn(&&@RenderBox) -> ()) -> ();
fn bubble_widths_block(ctx: &LayoutContext); fn bubble_widths_block(ctx: &LayoutContext);
fn assign_widths_block(ctx: &LayoutContext); fn assign_widths_block(ctx: &LayoutContext);
fn assign_height_block(ctx: &LayoutContext); fn assign_height_block(ctx: &LayoutContext);
fn build_display_list_block(a: &dl::DisplayListBuilder, b: &Rect<au>,
c: &Point2D<au>, d: &dl::DisplayList);
} }
impl @FlowContext : BlockLayout { impl @FlowContext : BlockLayout {
@ -45,7 +50,7 @@ impl @FlowContext : BlockLayout {
/* Get the current flow's corresponding block box, if it exists, and do something with it. /* Get the current flow's corresponding block box, if it exists, and do something with it.
This works on both BlockFlow and RootFlow, since they are mostly the same. */ This works on both BlockFlow and RootFlow, since they are mostly the same. */
pure fn with_block_box(cb:fn(&&@Box) -> ()) -> () { pure fn with_block_box(cb:fn(&&@RenderBox) -> ()) -> () {
match self.kind { match self.kind {
BlockFlow(*) => { BlockFlow(*) => {
do self.access_block |d| { do self.access_block |d| {
@ -147,4 +152,22 @@ impl @FlowContext : BlockLayout {
let (used_top, used_bot) = box.get_used_height(); let (used_top, used_bot) = box.get_used_height();
} }
} }
fn build_display_list_block(builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_block_flow();
// add box that starts block context
do self.with_block_box |box| {
box.build_display_list(builder, dirty, offset, list)
}
// TODO: handle any out-of-flow elements
// go deeper into the flow tree
for FlowTree.each_child(self) |child| {
self.build_display_list_for_child(builder, child, dirty, offset, list)
}
}
} }

View file

@ -5,7 +5,7 @@ use css::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayInlineBlock, D
use css::values::{Inherit, Initial, Specified}; use css::values::{Inherit, Initial, Specified};
use dom::base::{ElementData, HTMLDivElement, HTMLImageElement}; use dom::base::{ElementData, HTMLDivElement, HTMLImageElement};
use dom::base::{Element, Text, Node, Doctype, Comment, NodeTree}; use dom::base::{Element, Text, Node, Doctype, Comment, NodeTree};
use layout::base::{Box, BoxData, GenericBox, ImageBox, TextBox, BoxTree}; use layout::base::{RenderBox, BoxData, GenericBox, ImageBox, TextBox, RenderBoxTree};
use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree}; use layout::base::{FlowContext, FlowContextData, BlockFlow, InlineFlow, InlineBlockFlow, RootFlow, FlowTree};
use layout::block::BlockFlowData; use layout::block::BlockFlowData;
use layout::context::LayoutContext; use layout::context::LayoutContext;
@ -20,7 +20,7 @@ use servo_text::font_cache::FontCache;
export LayoutTreeBuilder; export LayoutTreeBuilder;
struct LayoutTreeBuilder { struct LayoutTreeBuilder {
mut root_box: Option<@Box>, mut root_box: Option<@RenderBox>,
mut root_ctx: Option<@FlowContext>, mut root_ctx: Option<@FlowContext>,
mut next_bid: int, mut next_bid: int,
mut next_cid: int mut next_cid: int
@ -42,7 +42,7 @@ impl LayoutTreeBuilder {
/** Creates necessary box(es) and flow context(s) for the current DOM node, /** Creates necessary box(es) and flow context(s) for the current DOM node,
and recurses on its children. */ and recurses on its children. */
fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: @FlowContext, parent_box: @Box) { fn construct_recursively(layout_ctx: &LayoutContext, cur_node: Node, parent_ctx: @FlowContext, parent_box: @RenderBox) {
let style = cur_node.style(); let style = cur_node.style();
// DEBUG // DEBUG
@ -109,7 +109,7 @@ impl LayoutTreeBuilder {
// connect the box to its parent box // connect the box to its parent box
debug!("Adding child box b%? of b%?", parent_box.id, new_box.id); debug!("Adding child box b%? of b%?", parent_box.id, new_box.id);
BoxTree.add_child(parent_box, new_box); RenderBoxTree.add_child(parent_box, new_box);
if (!next_ctx.eq(parent_ctx)) { if (!next_ctx.eq(parent_ctx)) {
debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id); debug!("Adding child flow f%? of f%?", parent_ctx.id, next_ctx.id);
@ -146,7 +146,7 @@ impl LayoutTreeBuilder {
/** entry point for box creation. Should only be /** entry point for box creation. Should only be
called on root DOM element. */ called on root DOM element. */
fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@Box, ()> { fn construct_trees(layout_ctx: &LayoutContext, root: Node) -> Result<@RenderBox, ()> {
self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty())); self.root_ctx = Some(self.make_ctx(RootFlow(RootFlowData()), tree::empty()));
self.root_box = Some(self.make_box(root, self.root_ctx.get(), GenericBox)); self.root_box = Some(self.make_box(root, self.root_ctx.get(), GenericBox));
@ -160,8 +160,8 @@ impl LayoutTreeBuilder {
ret ret
} }
fn make_box(node : Node, ctx: @FlowContext, data: BoxData) -> @Box { fn make_box(node : Node, ctx: @FlowContext, data: BoxData) -> @RenderBox {
let ret = @Box(self.next_box_id(), node, ctx, data); let ret = @RenderBox(self.next_box_id(), node, ctx, data);
debug!("Created box: %s", ret.debug_str()); debug!("Created box: %s", ret.debug_str());
ret ret
} }

View file

@ -1,7 +1,7 @@
export build_display_list; export DisplayListBuilder;
use au = gfx::geometry; use au = gfx::geometry;
use base::Box; use base::{RenderBox, RenderBoxTree};
use css::values::{BgColor, BgTransparent, Specified}; use css::values::{BgColor, BgTransparent, Specified};
use dl = gfx::display_list; use dl = gfx::display_list;
use dom::base::{Text, NodeScope}; use dom::base::{Text, NodeScope};
@ -13,118 +13,51 @@ use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::geometry::au; use gfx::geometry::au;
use layout::text::TextBoxData; use layout::text::TextBoxData;
use layout::base::{TextBox, BoxTree}; use layout::base::{LayoutContext, FlowContext, TextBox};
use servo_text::text_run::TextRun; use servo_text::text_run::TextRun;
use util::tree; use util::tree;
use vec::push; use vec::push;
/** /** A builder object that manages display list builder should mainly
Builds a display list for a box and all its children hold information about the initial request and desired result---for
*/ example, whether the DisplayList to be used for painting or hit
fn build_display_list(box : @Box) -> dl::DisplayList { testing. This can affect which boxes are created.
let list = DVec();
build_display_list_from_origin(list, box, Point2D(au(0), au(0))); Right now, the builder isn't used for much, but it establishes the
return list; pattern we'll need once we support DL-based hit testing &c. */
struct DisplayListBuilder {
ctx: &LayoutContext,
} }
/**
Builds a display list for a box and all its children.
# Arguments trait FlowDisplayListBuilderMethods {
fn build_display_list(a: &DisplayListBuilder, b: &Rect<au>, c: &dl::DisplayList);
* `box` - The box to build the display list for. fn build_display_list_for_child(a: &DisplayListBuilder, b: @FlowContext,
* `origin` - The coordinates of upper-left corner of the box containing the c: &Rect<au>, d: &Point2D<au>, e: &dl::DisplayList);
passed-in box. }
*/ impl @FlowContext: FlowDisplayListBuilderMethods {
fn build_display_list_from_origin(list: dl::DisplayList, box: @Box, origin: Point2D<au>) {
let box_origin = Point2D(
au::from_px(au::to_px(origin.x) + au::to_px(box.data.position.origin.x)),
au::from_px(au::to_px(origin.y) + au::to_px(box.data.position.origin.y)));
debug!("Handed origin %?, box has bounds %?, starting with origin %?", origin, box.data.position.size, box_origin);
box_to_display_items(list, box, box_origin); fn build_display_list(builder: &DisplayListBuilder, dirty: &Rect<au>, list: &dl::DisplayList) {
let zero = au::zero_point();
self.build_display_list_recurse(builder, dirty, &zero, list);
}
for BoxTree.each_child(box) |c| { fn build_display_list_for_child(builder: &DisplayListBuilder, child: @FlowContext,
debug!("Recursively building display list with origin %?", box_origin); dirty: &Rect<au>, offset: &Point2D<au>, list: &dl::DisplayList) {
build_display_list_from_origin(list, c, box_origin);
} // adjust the dirty rect to child flow context coordinates
} let adj_dirty = dirty.translate(&child.data.position.origin);
let adj_offset = offset.add(&child.data.position.origin);
/**
Creates a display list item for a single block. if (adj_dirty.intersects(&child.data.position)) {
child.build_display_list_recurse(builder, &adj_dirty, &adj_offset, list);
# Arguments }
* `box` - The box to build the display list for
* `origin` - The coordinates of upper-left corner of the passed in box.
*/
#[allow(non_implicitly_copyable_typarams)]
fn box_to_display_items(list: dl::DisplayList, box: @Box, origin: Point2D<au>) {
// TODO: each box should know how to make its own display items.
// The display list builder should mainly hold information about
// the initial request and desired result---for example, is the
// DisplayList to be used for painting or hit testing. This can
// influence which boxes are created.
// TODO: to implement stacking contexts correctly, we need to
// create a set of display lists, one per each layer of a stacking
// context. (CSS 2.1, Section 9.9.1). Each box is passed the list
// set representing the box's stacking context. When asked to
// construct its constituent display items, each box puts its
// DisplayItems into the correct stack layer (according to CSS 2.1
// Appendix E). and then builder flattens the list at the end.
debug!("request to display a box from origin %?", origin);
let bounds : Rect<au> = Rect(origin, copy box.data.position.size);
match box.kind {
TextBox(d) => {
let mut runs = d.runs;
list.push(~dl::SolidColor(bounds, 255u8, 255u8, 255u8));
let mut bounds = bounds;
for uint::range(0, runs.len()) |i| {
bounds.size.height = runs[i].size().height;
let glyph_run = text_run_to_dl_glyph_run(& runs[i]);
list.push(~dl::Glyphs(bounds, glyph_run));
bounds.origin.y += bounds.size.height;
}
return;
pure fn text_run_to_dl_glyph_run(text_run: &TextRun) ->
dl::GlyphRun {
dl::GlyphRun {
glyphs: copy text_run.glyphs
}
}
}
_ => {
// Fall through
}
};
// Check if there is a background image, if not set the background color.
let image = box.get_image();
if image.is_some() {
list.push(~dl::Image(bounds, option::unwrap(image)))
} else {
// DAC
// TODO: shouldn't need to unbox CSSValue by now
let boxed_color = box.node.style().background_color;
let color = match boxed_color {
Specified(BgColor(c)) => c,
Specified(BgTransparent) | _ => util::color::rgba(0,0,0,0.0)
};
debug!("Assigning color %? to box with bounds %?", color, bounds);
list.push(~dl::SolidColor(bounds, color.red, color.green, color.blue));
} }
} }
/* TODO: redo unit tests, if possible?gn
fn should_convert_text_boxes_to_solid_color_background_items() { fn should_convert_text_boxes_to_solid_color_background_items() {
#[test]; #[test];
@ -212,3 +145,4 @@ fn should_calculate_the_bounds_of_the_text_items() {
do list.borrow |l| { assert l[1].bounds == expected; } do list.borrow |l| { assert l[1].bounds == expected; }
} }
*/

View file

@ -1,18 +1,44 @@
use au = gfx::geometry; use au = gfx::geometry;
use base::Box; use base::RenderBox;
use core::dvec::DVec; use core::dvec::DVec;
use css::values::{BoxAuto, BoxLength, Px}; use css::values::{BoxAuto, BoxLength, Px};
use dl = gfx::display_list;
use dom::rcu; use dom::rcu;
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::geometry::au; use gfx::geometry::au;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use layout::base::{FlowContext, InlineFlow, BoxTree, ImageBox, TextBox, GenericBox}; use layout::base::{FlowContext, InlineFlow, RenderBoxTree, ImageBox, TextBox, GenericBox};
use num::Num; use num::Num;
use util::tree; use util::tree;
/*
Tentative design: (may not line up with reality)
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, lender 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.
*/
struct InlineFlowData { struct InlineFlowData {
boxes: ~DVec<@Box> boxes: ~DVec<@RenderBox>
} }
fn InlineFlowData() -> InlineFlowData { fn InlineFlowData() -> InlineFlowData {
@ -28,6 +54,7 @@ trait InlineLayout {
fn bubble_widths_inline(ctx: &LayoutContext); fn bubble_widths_inline(ctx: &LayoutContext);
fn assign_widths_inline(ctx: &LayoutContext); fn assign_widths_inline(ctx: &LayoutContext);
fn assign_height_inline(ctx: &LayoutContext); fn assign_height_inline(ctx: &LayoutContext);
fn build_display_list_inline(a: &dl::DisplayListBuilder, b: &Rect<au>, c: &Point2D<au>, d: &dl::DisplayList);
} }
impl @FlowContext : InlineLayout { impl @FlowContext : InlineLayout {
@ -116,4 +143,27 @@ impl @FlowContext : InlineLayout {
// during inline flowing. // during inline flowing.
} }
fn build_display_list_inline(builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_inline_flow();
// TODO: if the CSS box introducing this inline context is *not* anonymous,
// we need to draw it too, in a way similar to BlowFlowContext
// TODO: once we form line boxes and have their cached bounds, we can be
// smarter and not recurse on a line if nothing in it can intersect dirty
do self.access_inline |d| {
for d.boxes.each |box| {
box.build_display_list(builder, dirty, offset, list)
}
}
// TODO: should inline-block elements have flows as children
// of the inline flow, or should the flow be nested inside the
// box somehow? Maybe it's best to unify flows and boxes into
// the same enum, so inline-block flows are normal
// (indivisible) children in the inline flow child list.
}
} // @FlowContext : InlineLayout } // @FlowContext : InlineLayout

View file

@ -6,16 +6,17 @@
use au = gfx::geometry; use au = gfx::geometry;
use au::au; use au::au;
use content::content_task; use content::content_task;
use core::dvec::DVec;
use css::resolve::apply::apply_style; use css::resolve::apply::apply_style;
use css::values::Stylesheet; use css::values::Stylesheet;
use display_list_builder::build_display_list; use dl = gfx::display_list;
use dom::base::Node; use dom::base::Node;
use dom::event::{Event, ReflowEvent}; use dom::event::{Event, ReflowEvent};
use geom::point::Point2D; use geom::point::Point2D;
use geom::rect::Rect; use geom::rect::Rect;
use geom::size::Size2D; use geom::size::Size2D;
use gfx::render_task; use gfx::render_task;
use layout::base::Box; use layout::base::RenderBox;
use layout::box_builder::LayoutTreeBuilder; use layout::box_builder::LayoutTreeBuilder;
use layout::context::LayoutContext; use layout::context::LayoutContext;
use render_task::RenderTask; use render_task::RenderTask;
@ -67,14 +68,15 @@ fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> Layo
layout_data_refs += node.initialize_style_for_subtree(); layout_data_refs += node.initialize_style_for_subtree();
node.recompute_style_for_subtree(&layout_ctx, styles); node.recompute_style_for_subtree(&layout_ctx, styles);
let root_box: @Box; // TODO: this should care about root flow, not root box.
let root_box: @RenderBox;
let builder = LayoutTreeBuilder(); let builder = LayoutTreeBuilder();
match builder.construct_trees(&layout_ctx, node) { match builder.construct_trees(&layout_ctx, node) {
Ok(root) => root_box = root, Ok(root) => root_box = root,
Err(*) => fail ~"Root node should always exist" Err(*) => fail ~"Root node should always exist"
} }
debug!("layout: constructed Box tree"); debug!("layout: constructed RenderBox tree");
root_box.dump(); root_box.dump();
debug!("layout: constructed Flow tree"); debug!("layout: constructed Flow tree");
@ -90,7 +92,13 @@ fn LayoutTask(render_task: RenderTask, image_cache_task: ImageCacheTask) -> Layo
do root_flow.traverse_preorder |f| { f.assign_widths(&layout_ctx) } do root_flow.traverse_preorder |f| { f.assign_widths(&layout_ctx) }
do root_flow.traverse_postorder |f| { f.assign_height(&layout_ctx) } do root_flow.traverse_postorder |f| { f.assign_height(&layout_ctx) }
let dlist = build_display_list(root_box); let dlist = DVec();
let builder = dl::DisplayListBuilder {
ctx: &layout_ctx,
};
// TODO: set options on the builder before building
// TODO: be smarter about what needs painting
root_flow.build_display_list(&builder, &copy root_flow.data.position, &dlist);
render_task.send(render_task::RenderMsg(dlist)); render_task.send(render_task::RenderMsg(dlist));
} }
} }

View file

@ -1,12 +1,15 @@
use au = gfx::geometry; use au = gfx::geometry;
use css::values::*; use css::values::*;
use dl = gfx::display_list;
use geom::point::Point2D;
use geom::rect::Rect;
use gfx::geometry::au; use gfx::geometry::au;
use layout::base::{Box, FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow}; use layout::base::{RenderBox, FlowContext, FlowTree, InlineBlockFlow, BlockFlow, RootFlow};
use layout::context::LayoutContext; use layout::context::LayoutContext;
use util::tree; use util::tree;
struct RootFlowData { struct RootFlowData {
mut box: Option<@Box> mut box: Option<@RenderBox>
} }
fn RootFlowData() -> RootFlowData { fn RootFlowData() -> RootFlowData {
@ -22,6 +25,8 @@ trait RootLayout {
fn bubble_widths_root(ctx: &LayoutContext); fn bubble_widths_root(ctx: &LayoutContext);
fn assign_widths_root(ctx: &LayoutContext); fn assign_widths_root(ctx: &LayoutContext);
fn assign_height_root(ctx: &LayoutContext); fn assign_height_root(ctx: &LayoutContext);
fn build_display_list_root(a: &dl::DisplayListBuilder, b: &Rect<au>, c: &Point2D<au>, d: &dl::DisplayList);
} }
impl @FlowContext : RootLayout { impl @FlowContext : RootLayout {
@ -58,4 +63,11 @@ impl @FlowContext : RootLayout {
self.assign_height_block(ctx); self.assign_height_block(ctx);
} }
fn build_display_list_root(builder: &dl::DisplayListBuilder, dirty: &Rect<au>,
offset: &Point2D<au>, list: &dl::DisplayList) {
assert self.starts_root_flow();
self.build_display_list_block(builder, dirty, offset, list);
}
} }

View file

@ -5,7 +5,7 @@ use geom::size::Size2D;
use gfx::geometry::au; use gfx::geometry::au;
use servo_text::text_run::TextRun; use servo_text::text_run::TextRun;
use servo_text::font_cache::FontCache; use servo_text::font_cache::FontCache;
use layout::base::{TextBox, Box}; use layout::base::{TextBox, RenderBox};
use layout::context::LayoutContext; use layout::context::LayoutContext;
struct TextBoxData { struct TextBoxData {
@ -25,7 +25,7 @@ trait TextLayout {
} }
#[doc="The main reflow routine for text layout."] #[doc="The main reflow routine for text layout."]
impl @Box : TextLayout { impl @RenderBox : TextLayout {
fn reflow_text(ctx: &LayoutContext) { fn reflow_text(ctx: &LayoutContext) {
let d = match self.kind { let d = match self.kind {
TextBox(d) => { d } TextBox(d) => { d }
@ -78,6 +78,7 @@ impl @Box : TextLayout {
} }
} }
/* TODO: new unit tests for TextBox splitting, etc
fn should_calculate_the_size_of_the_text_box() { fn should_calculate_the_size_of_the_text_box() {
#[test]; #[test];
#[ignore(cfg(target_os = "macos"))]; #[ignore(cfg(target_os = "macos"))];
@ -97,3 +98,4 @@ fn should_calculate_the_size_of_the_text_box() {
let expected = Size2D(au::from_px(84), au::from_px(20)); let expected = Size2D(au::from_px(84), au::from_px(20));
assert b.data.position.size == expected; assert b.data.position.size == expected;
} }
*/

View file

@ -1,16 +1,17 @@
/** Interface for running tree-based traversals over layout boxes and contextsg */ /** Interface for running tree-based traversals over layout boxes and contextsg */
use layout::base::{Box, BoxTree}; use layout::base::{RenderBox, RenderBoxTree};
use layout::base::{FlowContext, FlowTree}; use layout::base::{FlowContext, FlowTree};
trait BoxTraversals { /* TODO: we shouldn't need render box traversals */
fn traverse_preorder(preorder_cb: &fn(@Box)); trait RenderBoxTraversals {
fn traverse_preorder(preorder_cb: &fn(@RenderBox));
} }
impl @Box : BoxTraversals { impl @RenderBox : RenderBoxTraversals {
fn traverse_preorder(preorder_cb: &fn(@Box)) { fn traverse_preorder(preorder_cb: &fn(@RenderBox)) {
preorder_cb(self); preorder_cb(self);
do BoxTree.each_child(self) |child| { child.traverse_preorder(preorder_cb); true } do RenderBoxTree.each_child(self) |child| { child.traverse_preorder(preorder_cb); true }
} }
} }

View file

@ -1,6 +1,6 @@
#[doc = "Interface for running tree-based traversals over layout boxes"] #[doc = "Interface for running tree-based traversals over layout boxes"]
use base::{Box, BoxTree}; use base::{RenderBox, RenderBoxTree};
use intrinsic::TyDesc; use intrinsic::TyDesc;
export full_traversal; export full_traversal;
@ -22,14 +22,14 @@ type shared_box<T> = {
}; };
#[doc="Transform and @ into its underlying representation. The reference count stays constant."] #[doc="Transform and @ into its underlying representation. The reference count stays constant."]
fn unwrap_box(-b : @Box) -> *shared_box<Box> unsafe { fn unwrap_box(-b : @RenderBox) -> *shared_box<RenderBox> unsafe {
let new_box : *shared_box<Box> = unsafe::transmute(b); let new_box : *shared_box<RenderBox> = unsafe::transmute(b);
return new_box; return new_box;
} }
#[doc="Transform an underlying representation back to an @. The reference count stays constant."] #[doc="Transform an underlying representation back to an @. The reference count stays constant."]
fn rewrap_box(-b : *shared_box<Box>) -> @Box unsafe { fn rewrap_box(-b : *shared_box<RenderBox>) -> @RenderBox unsafe {
let new_box : @Box = unsafe::transmute(b); let new_box : @RenderBox = unsafe::transmute(b);
return new_box; return new_box;
} }
@ -53,8 +53,8 @@ finish, and then applies the second function to the current box.
applied to that node's children applied to that node's children
"] "]
fn traverse_helper<T : Copy Send>(-root : @Box, returned : T, -top_down : fn~(+T, @Box) -> T, fn traverse_helper<T : Copy Send>(-root : @RenderBox, returned : T, -top_down : fn~(+T, @RenderBox) -> T,
-bottom_up : fn~(@Box)) { -bottom_up : fn~(@RenderBox)) {
let returned = top_down(returned, root); let returned = top_down(returned, root);
do listen |ack_chan| { do listen |ack_chan| {
@ -67,21 +67,21 @@ fn traverse_helper<T : Copy Send>(-root : @Box, returned : T, -top_down : fn~(+T
// current task will block until all of it's children return, // current task will block until all of it's children return,
// so the original owner of the @-box will not exit while the // so the original owner of the @-box will not exit while the
// children are still live. // children are still live.
for BoxTree.each_child(root) |kid| { for RenderBoxTree.each_child(root) |kid| {
count += 1; count += 1;
// Unwrap the box so we can send it out of this task // Unwrap the box so we can send it out of this task
let unwrapped = unwrap_box(copy kid); let unwrapped = unwrap_box(copy kid);
// Hide the box in an option so we can get it across the // Hide the box in an option so we can get it across the
// task boundary without copying it // task boundary without copying it
let swappable : ~mut Option<*shared_box<Box>> = ~mut Some(unwrapped); let swappable : ~mut Option<*shared_box<RenderBox>> = ~mut Some(unwrapped);
do task::spawn |copy top_down, copy bottom_up| { do task::spawn |copy top_down, copy bottom_up| {
// Get the box out of the option and into the new task // Get the box out of the option and into the new task
let mut swapped_in = None; let mut swapped_in = None;
swapped_in <-> *swappable; swapped_in <-> *swappable;
// Retrieve the original @Box and recurse // Retrieve the original @RenderBox and recurse
let new_kid = rewrap_box(option::unwrap(swapped_in)); let new_kid = rewrap_box(option::unwrap(swapped_in));
traverse_helper(new_kid, copy returned, copy top_down, copy bottom_up); traverse_helper(new_kid, copy returned, copy top_down, copy bottom_up);
@ -97,7 +97,7 @@ fn traverse_helper<T : Copy Send>(-root : @Box, returned : T, -top_down : fn~(+T
} }
#[doc="A noneffectful function to be used if only one pass is required."] #[doc="A noneffectful function to be used if only one pass is required."]
fn nop(_box : @Box) { fn nop(_box : @RenderBox) {
return; return;
} }
@ -105,15 +105,15 @@ fn nop(_box : @Box) {
A wrapper to change a function that only acts on a box to one that A wrapper to change a function that only acts on a box to one that
threasds a unit through to match travserse_helper threasds a unit through to match travserse_helper
"] "]
fn unit_wrapper(-fun : fn~(@Box)) -> fn~(+(), @Box) { fn unit_wrapper(-fun : fn~(@RenderBox)) -> fn~(+(), @RenderBox) {
fn~(+_u : (), box : @Box) { fun(box); } fn~(+_u : (), box : @RenderBox) { fun(box); }
} }
#[doc=" #[doc="
Iterate in parallel over the boxes in a tree, applying one function Iterate in parallel over the boxes in a tree, applying one function
to a parent before recursing on its children and one after. to a parent before recursing on its children and one after.
"] "]
fn full_traversal(+root : @Box, -top_down : fn~(@Box), -bottom_up : fn~(@Box)) { fn full_traversal(+root : @RenderBox, -top_down : fn~(@RenderBox), -bottom_up : fn~(@RenderBox)) {
traverse_helper(root, (), unit_wrapper(top_down), bottom_up); traverse_helper(root, (), unit_wrapper(top_down), bottom_up);
} }
@ -121,7 +121,7 @@ fn full_traversal(+root : @Box, -top_down : fn~(@Box), -bottom_up : fn~(@Box)) {
Iterate in parallel over the boxes in a tree, applying the given Iterate in parallel over the boxes in a tree, applying the given
function to a parent before its children. function to a parent before its children.
"] "]
fn top_down_traversal(+root : @Box, -top_down : fn~(@Box)) { fn top_down_traversal(+root : @RenderBox, -top_down : fn~(@RenderBox)) {
traverse_helper(root, (), unit_wrapper(top_down), nop); traverse_helper(root, (), unit_wrapper(top_down), nop);
} }
@ -129,7 +129,7 @@ fn top_down_traversal(+root : @Box, -top_down : fn~(@Box)) {
Iterate in parallel over the boxes in a tree, applying the given Iterate in parallel over the boxes in a tree, applying the given
function to a parent after its children. function to a parent after its children.
"] "]
fn bottom_up_traversal(+root : @Box, -bottom_up : fn~(@Box)) { fn bottom_up_traversal(+root : @RenderBox, -bottom_up : fn~(@RenderBox)) {
traverse_helper(root, (), unit_wrapper(nop), bottom_up); traverse_helper(root, (), unit_wrapper(nop), bottom_up);
} }
@ -140,9 +140,9 @@ fn bottom_up_traversal(+root : @Box, -bottom_up : fn~(@Box)) {
the recursion unwinds, the second function is applied to first the the recursion unwinds, the second function is applied to first the
children in parallel, and then the parent. children in parallel, and then the parent.
"] "]
fn extended_full_traversal<T : Copy Send>(+root : @Box, first_val : T, fn extended_full_traversal<T : Copy Send>(+root : @RenderBox, first_val : T,
-top_down : fn~(+T, @Box) -> T, -top_down : fn~(+T, @RenderBox) -> T,
-bottom_up : fn~(@Box)) { -bottom_up : fn~(@RenderBox)) {
traverse_helper(root, first_val, top_down, bottom_up); traverse_helper(root, first_val, top_down, bottom_up);
} }
@ -151,7 +151,7 @@ fn extended_full_traversal<T : Copy Send>(+root : @Box, first_val : T,
function to a parent before its children, the value returned by the function to a parent before its children, the value returned by the
function is passed to each child when they are recursed upon. function is passed to each child when they are recursed upon.
"] "]
fn extended_top_down_traversal<T : Copy Send>(+root : @Box, first_val : T, fn extended_top_down_traversal<T : Copy Send>(+root : @RenderBox, first_val : T,
-top_down : fn~(+T, @Box) -> T) { -top_down : fn~(+T, @RenderBox) -> T) {
traverse_helper(root, first_val, top_down, nop); traverse_helper(root, first_val, top_down, nop);
} }