mirror of
https://github.com/servo/servo.git
synced 2025-08-09 07:25:35 +01:00
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:
parent
4e9e679547
commit
da7ae8a280
14 changed files with 362 additions and 195 deletions
|
@ -1 +1 @@
|
||||||
Subproject commit e1ec99049c193aaedbb603e4ceb675251e5814b3
|
Subproject commit 01de30b15bfb3ecdfb921b7571ff98e63c0d4483
|
|
@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
|
@ -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; }
|
||||||
}
|
}
|
||||||
|
*/
|
|
@ -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
|
||||||
|
|
|
@ -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, © root_flow.data.position, &dlist);
|
||||||
render_task.send(render_task::RenderMsg(dlist));
|
render_task.send(render_task::RenderMsg(dlist));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
*/
|
|
@ -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 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue