mirror of
https://github.com/servo/servo.git
synced 2025-08-04 21:20:23 +01:00
layout: Refactor the RenderBox type to save memory and improve the coding style of box.rs
This commit is contained in:
parent
49edf85f5d
commit
b17e68ee04
9 changed files with 792 additions and 569 deletions
|
@ -10,11 +10,11 @@ use newcss::complete::CompleteStyle;
|
||||||
|
|
||||||
/// Node mixin providing `style` method that returns a `NodeStyle`
|
/// Node mixin providing `style` method that returns a `NodeStyle`
|
||||||
pub trait StyledNode {
|
pub trait StyledNode {
|
||||||
fn style<'a>(&'a self) -> CompleteStyle<'a>;
|
fn style(&self) -> CompleteStyle;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StyledNode for AbstractNode {
|
impl StyledNode for AbstractNode {
|
||||||
fn style<'a>(&'a self) -> CompleteStyle<'a> {
|
fn style(&self) -> CompleteStyle {
|
||||||
assert!(self.is_element()); // Only elements can have styles
|
assert!(self.is_element()); // Only elements can have styles
|
||||||
let results = self.get_css_select_results();
|
let results = self.get_css_select_results();
|
||||||
results.computed_style()
|
results.computed_style()
|
||||||
|
|
|
@ -23,7 +23,7 @@ pub struct BlockFlowData {
|
||||||
common: FlowData,
|
common: FlowData,
|
||||||
|
|
||||||
/// The associated render box.
|
/// The associated render box.
|
||||||
box: Option<@mut RenderBox>
|
box: Option<RenderBox>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl BlockFlowData {
|
impl BlockFlowData {
|
||||||
|
@ -40,7 +40,7 @@ impl BlockFlowData {
|
||||||
/// merged into this.
|
/// merged into this.
|
||||||
pub trait BlockLayout {
|
pub trait BlockLayout {
|
||||||
fn starts_block_flow(&self) -> bool;
|
fn starts_block_flow(&self) -> bool;
|
||||||
fn with_block_box(&self, &fn(box: &@mut RenderBox) -> ()) -> ();
|
fn with_block_box(&self, &fn(box: RenderBox) -> ()) -> ();
|
||||||
|
|
||||||
fn bubble_widths_block(&self, ctx: &LayoutContext);
|
fn bubble_widths_block(&self, ctx: &LayoutContext);
|
||||||
fn assign_widths_block(&self, ctx: &LayoutContext);
|
fn assign_widths_block(&self, ctx: &LayoutContext);
|
||||||
|
@ -62,17 +62,17 @@ impl BlockLayout for FlowContext {
|
||||||
|
|
||||||
/// 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.
|
||||||
fn with_block_box(&self, callback: &fn(box: &@mut RenderBox) -> ()) -> () {
|
fn with_block_box(&self, callback: &fn(box: RenderBox) -> ()) -> () {
|
||||||
match *self {
|
match *self {
|
||||||
BlockFlow(*) => {
|
BlockFlow(*) => {
|
||||||
let box = self.block().box;
|
let box = self.block().box;
|
||||||
for box.each |b| {
|
for box.each |&b| {
|
||||||
callback(b);
|
callback(b);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RootFlow(*) => {
|
RootFlow(*) => {
|
||||||
let mut box = self.root().box;
|
let mut box = self.root().box;
|
||||||
for box.each |b| {
|
for box.each |&b| {
|
||||||
callback(b);
|
callback(b);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -132,10 +132,13 @@ impl BlockLayout for FlowContext {
|
||||||
|
|
||||||
// Let the box consume some width. It will return the amount remaining for its children.
|
// Let the box consume some width. It will return the amount remaining for its children.
|
||||||
do self.with_block_box |box| {
|
do self.with_block_box |box| {
|
||||||
box.d().position.size.width = remaining_width;
|
do box.with_mut_base |base| {
|
||||||
|
base.position.size.width = remaining_width;
|
||||||
|
|
||||||
let (left_used, right_used) = box.get_used_width();
|
let (left_used, right_used) = box.get_used_width();
|
||||||
remaining_width -= left_used.add(&right_used);
|
remaining_width -= left_used.add(&right_used);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for self.each_child |kid| {
|
for self.each_child |kid| {
|
||||||
assert!(kid.starts_block_flow() || kid.starts_inline_flow());
|
assert!(kid.starts_block_flow() || kid.starts_inline_flow());
|
||||||
|
@ -167,11 +170,13 @@ impl BlockLayout for FlowContext {
|
||||||
let _used_bot = Au(0);
|
let _used_bot = Au(0);
|
||||||
|
|
||||||
do self.with_block_box |box| {
|
do self.with_block_box |box| {
|
||||||
box.d().position.origin.y = Au(0);
|
do box.with_mut_base |base| {
|
||||||
box.d().position.size.height = cur_y;
|
base.position.origin.y = Au(0);
|
||||||
|
base.position.size.height = cur_y;
|
||||||
let (_used_top, _used_bot) = box.get_used_height();
|
let (_used_top, _used_bot) = box.get_used_height();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn build_display_list_block(&self,
|
fn build_display_list_block(&self,
|
||||||
builder: &DisplayListBuilder,
|
builder: &DisplayListBuilder,
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -8,10 +8,15 @@ use dom::element::*;
|
||||||
use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
|
use dom::node::{AbstractNode, CommentNodeTypeId, DoctypeNodeTypeId};
|
||||||
use dom::node::{ElementNodeTypeId, TextNodeTypeId};
|
use dom::node::{ElementNodeTypeId, TextNodeTypeId};
|
||||||
use layout::block::BlockFlowData;
|
use layout::block::BlockFlowData;
|
||||||
use layout::box::*;
|
use layout::box::{GenericRenderBoxClass, ImageRenderBox, ImageRenderBoxClass, RenderBox};
|
||||||
|
use layout::box::{RenderBoxBase, RenderBoxType, RenderBox_Generic, RenderBox_Image};
|
||||||
|
use layout::box::{RenderBox_Text, TextRenderBox, UnscannedTextRenderBox};
|
||||||
|
use layout::box::{UnscannedTextRenderBoxClass};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::debug::{BoxedMutDebugMethods, DebugMethods};
|
use layout::debug::{BoxedMutDebugMethods, DebugMethods};
|
||||||
use layout::flow::*;
|
use layout::flow::{AbsoluteFlow, BlockFlow, FloatFlow, Flow_Absolute, Flow_Block, Flow_Float};
|
||||||
|
use layout::flow::{Flow_Inline, Flow_InlineBlock, Flow_Root, Flow_Table, FlowContext};
|
||||||
|
use layout::flow::{FlowContextType, FlowData, InlineBlockFlow, InlineFlow, RootFlow, TableFlow};
|
||||||
use layout::inline::{InlineFlowData, InlineLayout};
|
use layout::inline::{InlineFlowData, InlineLayout};
|
||||||
use layout::root::RootFlowData;
|
use layout::root::RootFlowData;
|
||||||
|
|
||||||
|
@ -102,7 +107,7 @@ impl BoxGenerator {
|
||||||
_: &LayoutContext,
|
_: &LayoutContext,
|
||||||
_: AbstractNode,
|
_: AbstractNode,
|
||||||
_: InlineSpacerSide)
|
_: InlineSpacerSide)
|
||||||
-> Option<@mut RenderBox> {
|
-> Option<RenderBox> {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +138,7 @@ impl BoxGenerator {
|
||||||
} else if self.inline_spacers_needed_for_node(node) {
|
} else if self.inline_spacers_needed_for_node(node) {
|
||||||
// else, maybe make a spacer for "left" margin, border, padding
|
// else, maybe make a spacer for "left" margin, border, padding
|
||||||
for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each
|
for self.make_inline_spacer_for_node_side(ctx, node, LogicalBefore).each
|
||||||
|spacer: &@mut RenderBox| {
|
|spacer: &RenderBox| {
|
||||||
inline.boxes.push(*spacer);
|
inline.boxes.push(*spacer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -145,7 +150,7 @@ impl BoxGenerator {
|
||||||
|
|
||||||
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
|
debug!("BoxGenerator[f%d]: attaching box[b%d] to block flow (node: %s)",
|
||||||
block.common.id,
|
block.common.id,
|
||||||
new_box.d().id,
|
new_box.id(),
|
||||||
node.debug_str());
|
node.debug_str());
|
||||||
|
|
||||||
assert!(block.box.is_none());
|
assert!(block.box.is_none());
|
||||||
|
@ -157,7 +162,7 @@ impl BoxGenerator {
|
||||||
debug!("BoxGenerator[f%d]: (node is: %s)", root.common.id, node.debug_str());
|
debug!("BoxGenerator[f%d]: (node is: %s)", root.common.id, node.debug_str());
|
||||||
debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)",
|
debug!("BoxGenerator[f%d]: attaching box[b%d] to root flow (node: %s)",
|
||||||
root.common.id,
|
root.common.id,
|
||||||
new_box.d().id,
|
new_box.id(),
|
||||||
node.debug_str());
|
node.debug_str());
|
||||||
|
|
||||||
assert!(root.box.is_none());
|
assert!(root.box.is_none());
|
||||||
|
@ -450,55 +455,40 @@ pub impl LayoutTreeBuilder {
|
||||||
layout_ctx: &LayoutContext,
|
layout_ctx: &LayoutContext,
|
||||||
ty: RenderBoxType,
|
ty: RenderBoxType,
|
||||||
node: AbstractNode,
|
node: AbstractNode,
|
||||||
ctx: FlowContext)
|
flow_context: FlowContext)
|
||||||
-> @mut RenderBox {
|
-> RenderBox {
|
||||||
|
let base = RenderBoxBase::new(node, flow_context, self.next_box_id());
|
||||||
let result = match ty {
|
let result = match ty {
|
||||||
RenderBox_Generic => self.make_generic_box(layout_ctx, node, ctx),
|
RenderBox_Generic => GenericRenderBoxClass(@mut base),
|
||||||
RenderBox_Text => self.make_text_box(layout_ctx, node, ctx),
|
RenderBox_Text => UnscannedTextRenderBoxClass(@mut UnscannedTextRenderBox::new(base)),
|
||||||
RenderBox_Image => self.make_image_box(layout_ctx, node, ctx),
|
RenderBox_Image => self.make_image_box(layout_ctx, node, flow_context, base),
|
||||||
};
|
};
|
||||||
debug!("LayoutTreeBuilder: created box: %s", result.debug_str());
|
debug!("LayoutTreeBuilder: created box: %s", result.debug_str());
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_generic_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
|
fn make_image_box(&mut self,
|
||||||
-> @mut RenderBox {
|
layout_ctx: &LayoutContext,
|
||||||
@mut GenericBox(RenderBoxData(copy node, ctx, self.next_box_id()))
|
node: AbstractNode,
|
||||||
}
|
flow_context: FlowContext,
|
||||||
|
base: RenderBoxBase)
|
||||||
fn make_image_box(&mut self, layout_ctx: &LayoutContext, node: AbstractNode, ctx: FlowContext)
|
-> RenderBox {
|
||||||
-> @mut RenderBox {
|
assert!(node.is_image_element());
|
||||||
if !node.is_image_element() {
|
|
||||||
fail!(~"WAT error: why couldn't we make an image box?");
|
|
||||||
}
|
|
||||||
|
|
||||||
do node.with_imm_image_element |image_element| {
|
do node.with_imm_image_element |image_element| {
|
||||||
if image_element.image.is_some() {
|
if image_element.image.is_some() {
|
||||||
let holder = ImageHolder::new(copy *image_element.image.get_ref(),
|
// FIXME(pcwalton): Don't copy URLs.
|
||||||
layout_ctx.image_cache);
|
let url = copy *image_element.image.get_ref();
|
||||||
@mut ImageBox(RenderBoxData(node, ctx, self.next_box_id()), holder)
|
ImageRenderBoxClass(@mut ImageRenderBox::new(base, url, layout_ctx.image_cache))
|
||||||
} else {
|
} else {
|
||||||
info!("Tried to make image box, but couldn't find image. Made generic box \
|
info!("Tried to make image box, but couldn't find image. Made generic box \
|
||||||
instead.");
|
instead.");
|
||||||
self.make_generic_box(layout_ctx, node, ctx)
|
GenericRenderBoxClass(@mut base)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_text_box(&mut self, _: &LayoutContext, node: AbstractNode, ctx: FlowContext)
|
fn decide_box_type(&self, node: AbstractNode, _: CSSDisplay) -> RenderBoxType {
|
||||||
-> @mut RenderBox {
|
|
||||||
if !node.is_text() {
|
|
||||||
fail!(~"WAT error: why couldn't we make a text box?");
|
|
||||||
}
|
|
||||||
|
|
||||||
// FIXME: Don't copy text. I guess it should be atomically reference counted?
|
|
||||||
do node.with_imm_text |text_node| {
|
|
||||||
let string = text_node.parent.data.to_str();
|
|
||||||
@mut UnscannedTextBox(RenderBoxData(node, ctx, self.next_box_id()), string)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decide_box_type(&self, node: AbstractNode, _display: CSSDisplay) -> RenderBoxType {
|
|
||||||
if node.is_text() {
|
if node.is_text() {
|
||||||
RenderBox_Text
|
RenderBox_Text
|
||||||
} else if node.is_image_element() {
|
} else if node.is_image_element() {
|
||||||
|
|
|
@ -16,6 +16,7 @@ use geom::rect::Rect;
|
||||||
use gfx::display_list::DisplayList;
|
use gfx::display_list::DisplayList;
|
||||||
use gfx::geometry::Au;
|
use gfx::geometry::Au;
|
||||||
use gfx;
|
use gfx;
|
||||||
|
use newcss;
|
||||||
use servo_util::tree::TreeNodeRef;
|
use servo_util::tree::TreeNodeRef;
|
||||||
|
|
||||||
/// A builder object that manages display list builder should mainly hold information about the
|
/// A builder object that manages display list builder should mainly hold information about the
|
||||||
|
@ -73,3 +74,19 @@ impl FlowDisplayListBuilderMethods for FlowContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Miscellaneous useful routines
|
||||||
|
//
|
||||||
|
|
||||||
|
/// Allows a CSS color to be converted into a graphics color.
|
||||||
|
pub trait ToGfxColor {
|
||||||
|
/// Converts a CSS color to a graphics color.
|
||||||
|
fn to_gfx_color(&self) -> gfx::color::Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ToGfxColor for newcss::color::Color {
|
||||||
|
fn to_gfx_color(&self) -> gfx::color::Color {
|
||||||
|
gfx::color::rgba(self.red, self.green, self.blue, self.alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
|
@ -285,31 +285,37 @@ impl<'self> FlowContext {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actual methods that do not require much flow-specific logic
|
// Actual methods that do not require much flow-specific logic
|
||||||
pub fn foldl_all_boxes<B:Copy>(&self, seed: B, cb: &fn(a: B, b: @mut RenderBox) -> B) -> B {
|
pub fn foldl_all_boxes<B:Copy>(&self, seed: B, cb: &fn(a: B, b: RenderBox) -> B) -> B {
|
||||||
match *self {
|
match *self {
|
||||||
RootFlow(root) => {
|
RootFlow(root) => {
|
||||||
let root = &mut *root;
|
let root = &mut *root;
|
||||||
root.box.map_default(seed, |box| { cb(seed, *box) })
|
do root.box.map_default(seed) |box| {
|
||||||
|
cb(seed, *box)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
BlockFlow(block) => {
|
BlockFlow(block) => {
|
||||||
let block = &mut *block;
|
let block = &mut *block;
|
||||||
block.box.map_default(seed, |box| { cb(seed, *box) })
|
do block.box.map_default(seed) |box| {
|
||||||
|
cb(seed, *box)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
InlineFlow(inline) => {
|
InlineFlow(inline) => {
|
||||||
let inline = &mut *inline;
|
let inline = &mut *inline;
|
||||||
inline.boxes.foldl(seed, |acc, box| { cb(*acc, *box) })
|
do inline.boxes.foldl(seed) |acc, box| {
|
||||||
|
cb(*acc, *box)
|
||||||
}
|
}
|
||||||
_ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self))
|
}
|
||||||
|
_ => fail!(fmt!("Don't know how to iterate node's RenderBoxes for %?", self)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn foldl_boxes_for_node<B:Copy>(&self,
|
pub fn foldl_boxes_for_node<B:Copy>(&self,
|
||||||
node: AbstractNode,
|
node: AbstractNode,
|
||||||
seed: B,
|
seed: B,
|
||||||
callback: &fn(a: B, @mut RenderBox) -> B)
|
callback: &fn(a: B, RenderBox) -> B)
|
||||||
-> B {
|
-> B {
|
||||||
do self.foldl_all_boxes(seed) |acc, box| {
|
do self.foldl_all_boxes(seed) |acc, box| {
|
||||||
if box.d().node == node {
|
if box.node() == node {
|
||||||
callback(acc, box)
|
callback(acc, box)
|
||||||
} else {
|
} else {
|
||||||
acc
|
acc
|
||||||
|
@ -317,7 +323,7 @@ impl<'self> FlowContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_all_boxes(&self, cb: &fn(@mut RenderBox) -> bool) {
|
pub fn iter_all_boxes(&self, cb: &fn(RenderBox) -> bool) {
|
||||||
match *self {
|
match *self {
|
||||||
RootFlow(root) => {
|
RootFlow(root) => {
|
||||||
let root = &mut *root;
|
let root = &mut *root;
|
||||||
|
@ -347,9 +353,9 @@ impl<'self> FlowContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(@mut RenderBox) -> bool) {
|
pub fn iter_boxes_for_node(&self, node: AbstractNode, callback: &fn(RenderBox) -> bool) {
|
||||||
for self.iter_all_boxes |box| {
|
for self.iter_all_boxes |box| {
|
||||||
if box.d().node == node {
|
if box.node() == node {
|
||||||
if !callback(box) {
|
if !callback(box) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -384,7 +390,7 @@ impl DebugMethods for FlowContext {
|
||||||
InlineFlow(inline) => {
|
InlineFlow(inline) => {
|
||||||
let inline = &mut *inline;
|
let inline = &mut *inline;
|
||||||
let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| {
|
let mut s = inline.boxes.foldl(~"InlineFlow(children=", |s, box| {
|
||||||
fmt!("%s b%d", *s, box.d().id)
|
fmt!("%s b%d", *s, box.id())
|
||||||
});
|
});
|
||||||
s += ~")";
|
s += ~")";
|
||||||
s
|
s
|
||||||
|
@ -392,14 +398,14 @@ impl DebugMethods for FlowContext {
|
||||||
BlockFlow(block) => {
|
BlockFlow(block) => {
|
||||||
let block = &mut *block;
|
let block = &mut *block;
|
||||||
match block.box {
|
match block.box {
|
||||||
Some(box) => fmt!("BlockFlow(box=b%d)", box.d().id),
|
Some(box) => fmt!("BlockFlow(box=b%d)", box.id()),
|
||||||
None => ~"BlockFlow",
|
None => ~"BlockFlow",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
RootFlow(root) => {
|
RootFlow(root) => {
|
||||||
let root = &mut *root;
|
let root = &mut *root;
|
||||||
match root.box {
|
match root.box {
|
||||||
Some(box) => fmt!("RootFlo(box=b%d)", box.d().id),
|
Some(box) => fmt!("RootFlow(box=b%d)", box.id()),
|
||||||
None => ~"RootFlow",
|
None => ~"RootFlow",
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -5,7 +5,8 @@
|
||||||
use core::cell::Cell;
|
use core::cell::Cell;
|
||||||
use core;
|
use core;
|
||||||
use dom::node::AbstractNode;
|
use dom::node::AbstractNode;
|
||||||
use layout::box::*;
|
use layout::box::{CannotSplit, GenericRenderBoxClass, ImageRenderBoxClass, RenderBox};
|
||||||
|
use layout::box::{SplitDidFit, SplitDidNotFit, TextRenderBoxClass, UnscannedTextRenderBoxClass};
|
||||||
use layout::context::LayoutContext;
|
use layout::context::LayoutContext;
|
||||||
use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods};
|
use layout::debug::{BoxedDebugMethods, BoxedMutDebugMethods, DebugMethods};
|
||||||
use layout::display_list_builder::DisplayListBuilder;
|
use layout::display_list_builder::DisplayListBuilder;
|
||||||
|
@ -82,9 +83,7 @@ impl ElementMapping {
|
||||||
do self.entries.eachi |i, nr| { cb(i, nr) }
|
do self.entries.eachi |i, nr| { cb(i, nr) }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn repair_for_box_changes(&mut self,
|
pub fn repair_for_box_changes(&mut self, old_boxes: &[RenderBox], new_boxes: &[RenderBox]) {
|
||||||
old_boxes: &[@mut RenderBox],
|
|
||||||
new_boxes: &[@mut RenderBox]) {
|
|
||||||
let entries = &mut self.entries;
|
let entries = &mut self.entries;
|
||||||
|
|
||||||
debug!("--- Old boxes: ---");
|
debug!("--- Old boxes: ---");
|
||||||
|
@ -129,9 +128,15 @@ impl ElementMapping {
|
||||||
// XXX: the following loop form causes segfaults; assigning to locals doesn't.
|
// XXX: the following loop form causes segfaults; assigning to locals doesn't.
|
||||||
// while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node {
|
// while new_j < new_boxes.len() && old_boxes[old_i].d().node != new_boxes[new_j].d().node {
|
||||||
while new_j < new_boxes.len() {
|
while new_j < new_boxes.len() {
|
||||||
let o = old_boxes[old_i];
|
let should_leave = do old_boxes[old_i].with_imm_base |old_box_base| {
|
||||||
let n = new_boxes[new_j];
|
do new_boxes[new_j].with_imm_base |new_box_base| {
|
||||||
if o.d().node != n.d().node { break }
|
old_box_base.node != new_box_base.node
|
||||||
|
}
|
||||||
|
};
|
||||||
|
if should_leave {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
debug!("repair_for_box_changes: Slide through new box %u", new_j);
|
debug!("repair_for_box_changes: Slide through new box %u", new_j);
|
||||||
new_j += 1;
|
new_j += 1;
|
||||||
}
|
}
|
||||||
|
@ -192,14 +197,16 @@ impl TextRunScanner {
|
||||||
inline.boxes = out_boxes;
|
inline.boxes = out_boxes;
|
||||||
|
|
||||||
// A helper function.
|
// A helper function.
|
||||||
fn can_coalesce_text_nodes(boxes: &[@mut RenderBox], left_i: uint, right_i: uint) -> bool {
|
fn can_coalesce_text_nodes(boxes: &[RenderBox], left_i: uint, right_i: uint) -> bool {
|
||||||
assert!(left_i < boxes.len());
|
assert!(left_i < boxes.len());
|
||||||
assert!(right_i > 0 && right_i < boxes.len());
|
assert!(right_i > 0 && right_i < boxes.len());
|
||||||
assert!(left_i != right_i);
|
assert!(left_i != right_i);
|
||||||
|
|
||||||
let (left, right) = (boxes[left_i], boxes[right_i]);
|
let (left, right) = (boxes[left_i], boxes[right_i]);
|
||||||
match (left, right) {
|
match (left, right) {
|
||||||
(@UnscannedTextBox(*), @UnscannedTextBox(*)) => left.can_merge_with_box(right),
|
(UnscannedTextRenderBoxClass(*), UnscannedTextRenderBoxClass(*)) => {
|
||||||
|
left.can_merge_with_box(right)
|
||||||
|
}
|
||||||
(_, _) => false
|
(_, _) => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -218,14 +225,14 @@ impl TextRunScanner {
|
||||||
fn flush_clump_to_list(&mut self,
|
fn flush_clump_to_list(&mut self,
|
||||||
ctx: &mut LayoutContext,
|
ctx: &mut LayoutContext,
|
||||||
flow: FlowContext,
|
flow: FlowContext,
|
||||||
in_boxes: &[@mut RenderBox],
|
in_boxes: &[RenderBox],
|
||||||
out_boxes: &mut ~[@mut RenderBox]) {
|
out_boxes: &mut ~[RenderBox]) {
|
||||||
assert!(self.clump.length() > 0);
|
assert!(self.clump.length() > 0);
|
||||||
|
|
||||||
debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
|
debug!("TextRunScanner: flushing boxes in range=%?", self.clump);
|
||||||
let is_singleton = self.clump.length() == 1;
|
let is_singleton = self.clump.length() == 1;
|
||||||
let is_text_clump = match *in_boxes[self.clump.begin()] {
|
let is_text_clump = match in_boxes[self.clump.begin()] {
|
||||||
UnscannedTextBox(*) => true,
|
UnscannedTextRenderBoxClass(*) => true,
|
||||||
_ => false
|
_ => false
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -254,10 +261,12 @@ impl TextRunScanner {
|
||||||
let run = @fontgroup.create_textrun(transformed_text);
|
let run = @fontgroup.create_textrun(transformed_text);
|
||||||
|
|
||||||
debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
|
debug!("TextRunScanner: pushing single text box in range: %?", self.clump);
|
||||||
let new_box = adapt_textbox_with_range(old_box.d(),
|
let new_box = do old_box.with_imm_base |old_box_base| {
|
||||||
run,
|
let range = Range::new(0, run.char_len());
|
||||||
&Range::new(0, run.char_len()));
|
@mut adapt_textbox_with_range(*old_box_base, run, range)
|
||||||
out_boxes.push(new_box);
|
};
|
||||||
|
|
||||||
|
out_boxes.push(TextRenderBoxClass(new_box));
|
||||||
},
|
},
|
||||||
(false, true) => {
|
(false, true) => {
|
||||||
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
// TODO(#115): Use the actual CSS `white-space` property of the relevant style.
|
||||||
|
@ -297,7 +306,7 @@ impl TextRunScanner {
|
||||||
debug!("TextRunScanner: pushing box(es) in range: %?", self.clump);
|
debug!("TextRunScanner: pushing box(es) in range: %?", self.clump);
|
||||||
let clump = self.clump;
|
let clump = self.clump;
|
||||||
for clump.eachi |i| {
|
for clump.eachi |i| {
|
||||||
let range = &new_ranges[i - self.clump.begin()];
|
let range = new_ranges[i - self.clump.begin()];
|
||||||
if range.length() == 0 {
|
if range.length() == 0 {
|
||||||
error!("Elided an `UnscannedTextbox` because it was zero-length after \
|
error!("Elided an `UnscannedTextbox` because it was zero-length after \
|
||||||
compression; %s",
|
compression; %s",
|
||||||
|
@ -305,8 +314,10 @@ impl TextRunScanner {
|
||||||
loop
|
loop
|
||||||
}
|
}
|
||||||
|
|
||||||
let new_box = adapt_textbox_with_range(in_boxes[i].d(), run, range);
|
do in_boxes[i].with_imm_base |base| {
|
||||||
out_boxes.push(new_box);
|
let new_box = @mut adapt_textbox_with_range(*base, run, range);
|
||||||
|
out_boxes.push(TextRenderBoxClass(new_box));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} // End of match.
|
} // End of match.
|
||||||
|
@ -342,14 +353,14 @@ struct PendingLine {
|
||||||
|
|
||||||
struct LineboxScanner {
|
struct LineboxScanner {
|
||||||
flow: FlowContext,
|
flow: FlowContext,
|
||||||
new_boxes: ~[@mut RenderBox],
|
new_boxes: ~[RenderBox],
|
||||||
work_list: @mut Deque<@mut RenderBox>,
|
work_list: @mut Deque<RenderBox>,
|
||||||
pending_line: PendingLine,
|
pending_line: PendingLine,
|
||||||
line_spans: ~[Range],
|
line_spans: ~[Range],
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LineboxScanner {
|
impl LineboxScanner {
|
||||||
fn new(inline: FlowContext) -> LineboxScanner {
|
pub fn new(inline: FlowContext) -> LineboxScanner {
|
||||||
assert!(inline.starts_inline_flow());
|
assert!(inline.starts_inline_flow());
|
||||||
|
|
||||||
LineboxScanner {
|
LineboxScanner {
|
||||||
|
@ -361,14 +372,14 @@ impl LineboxScanner {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
priv fn reset_scanner(&mut self) {
|
fn reset_scanner(&mut self) {
|
||||||
debug!("Resetting line box scanner's state for flow f%d.", self.flow.id());
|
debug!("Resetting line box scanner's state for flow f%d.", self.flow.id());
|
||||||
self.line_spans = ~[];
|
self.line_spans = ~[];
|
||||||
self.new_boxes = ~[];
|
self.new_boxes = ~[];
|
||||||
self.reset_linebox();
|
self.reset_linebox();
|
||||||
}
|
}
|
||||||
|
|
||||||
priv fn reset_linebox(&mut self) {
|
fn reset_linebox(&mut self) {
|
||||||
self.pending_line.range.reset(0,0);
|
self.pending_line.range.reset(0,0);
|
||||||
self.pending_line.width = Au(0);
|
self.pending_line.width = Au(0);
|
||||||
}
|
}
|
||||||
|
@ -382,13 +393,15 @@ impl LineboxScanner {
|
||||||
loop {
|
loop {
|
||||||
// acquire the next box to lay out from work list or box list
|
// acquire the next box to lay out from work list or box list
|
||||||
let cur_box = if self.work_list.is_empty() {
|
let cur_box = if self.work_list.is_empty() {
|
||||||
if i == boxes.len() { break; }
|
if i == boxes.len() {
|
||||||
|
break
|
||||||
|
}
|
||||||
let box = boxes[i]; i += 1;
|
let box = boxes[i]; i += 1;
|
||||||
debug!("LineboxScanner: Working with box from box list: b%d", box.d().id);
|
debug!("LineboxScanner: Working with box from box list: b%d", box.id());
|
||||||
box
|
box
|
||||||
} else {
|
} else {
|
||||||
let box = self.work_list.pop_front();
|
let box = self.work_list.pop_front();
|
||||||
debug!("LineboxScanner: Working with box from work list: b%d", box.d().id);
|
debug!("LineboxScanner: Working with box from work list: b%d", box.id());
|
||||||
box
|
box
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -414,7 +427,7 @@ impl LineboxScanner {
|
||||||
self.swap_out_results();
|
self.swap_out_results();
|
||||||
}
|
}
|
||||||
|
|
||||||
priv fn swap_out_results(&mut self) {
|
fn swap_out_results(&mut self) {
|
||||||
debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d",
|
debug!("LineboxScanner: Propagating scanned lines[n=%u] to inline flow f%d",
|
||||||
self.line_spans.len(),
|
self.line_spans.len(),
|
||||||
self.flow.id());
|
self.flow.id());
|
||||||
|
@ -425,7 +438,7 @@ impl LineboxScanner {
|
||||||
util::swap(lines, &mut self.line_spans);
|
util::swap(lines, &mut self.line_spans);
|
||||||
}
|
}
|
||||||
|
|
||||||
priv fn flush_current_line(&mut self) {
|
fn flush_current_line(&mut self) {
|
||||||
debug!("LineboxScanner: Flushing line %u: %?",
|
debug!("LineboxScanner: Flushing line %u: %?",
|
||||||
self.line_spans.len(), self.pending_line);
|
self.line_spans.len(), self.pending_line);
|
||||||
// set box horizontal offsets
|
// set box horizontal offsets
|
||||||
|
@ -453,25 +466,28 @@ impl LineboxScanner {
|
||||||
// TODO(Issue #213): implement `text-align: justify`
|
// TODO(Issue #213): implement `text-align: justify`
|
||||||
CSSTextAlignLeft | CSSTextAlignJustify => {
|
CSSTextAlignLeft | CSSTextAlignJustify => {
|
||||||
for line_range.eachi |i| {
|
for line_range.eachi |i| {
|
||||||
let box_data = &self.new_boxes[i].d();
|
do self.new_boxes[i].with_mut_base |base| {
|
||||||
box_data.position.origin.x = offset_x;
|
base.position.origin.x = offset_x;
|
||||||
offset_x += box_data.position.size.width;
|
offset_x += base.position.size.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CSSTextAlignCenter => {
|
CSSTextAlignCenter => {
|
||||||
offset_x = slack_width.scale_by(0.5f);
|
offset_x = slack_width.scale_by(0.5f);
|
||||||
for line_range.eachi |i| {
|
for line_range.eachi |i| {
|
||||||
let box_data = &self.new_boxes[i].d();
|
do self.new_boxes[i].with_mut_base |base| {
|
||||||
box_data.position.origin.x = offset_x;
|
base.position.origin.x = offset_x;
|
||||||
offset_x += box_data.position.size.width;
|
offset_x += base.position.size.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
CSSTextAlignRight => {
|
CSSTextAlignRight => {
|
||||||
offset_x = slack_width;
|
offset_x = slack_width;
|
||||||
for line_range.eachi |i| {
|
for line_range.eachi |i| {
|
||||||
let box_data = &self.new_boxes[i].d();
|
do self.new_boxes[i].with_mut_base |base| {
|
||||||
box_data.position.origin.x = offset_x;
|
base.position.origin.x = offset_x;
|
||||||
offset_x += box_data.position.size.width;
|
offset_x += base.position.size.width;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -483,13 +499,17 @@ impl LineboxScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// return value: whether any box was appended.
|
// return value: whether any box was appended.
|
||||||
priv fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: @mut RenderBox) -> bool {
|
fn try_append_to_line(&mut self, ctx: &LayoutContext, in_box: RenderBox) -> bool {
|
||||||
let remaining_width = self.flow.position().size.width - self.pending_line.width;
|
let remaining_width = self.flow.position().size.width - self.pending_line.width;
|
||||||
let in_box_width = in_box.d().position.size.width;
|
let in_box_width = in_box.position().size.width;
|
||||||
let line_is_empty: bool = self.pending_line.range.length() == 0;
|
let line_is_empty: bool = self.pending_line.range.length() == 0;
|
||||||
|
|
||||||
debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: %?): %s",
|
debug!("LineboxScanner: Trying to append box to line %u (box width: %?, remaining width: \
|
||||||
self.line_spans.len(), in_box_width, remaining_width, in_box.debug_str());
|
%?): %s",
|
||||||
|
self.line_spans.len(),
|
||||||
|
in_box_width,
|
||||||
|
remaining_width,
|
||||||
|
in_box.debug_str());
|
||||||
|
|
||||||
if in_box_width <= remaining_width {
|
if in_box_width <= remaining_width {
|
||||||
debug!("LineboxScanner: case=box fits without splitting");
|
debug!("LineboxScanner: case=box fits without splitting");
|
||||||
|
@ -501,7 +521,8 @@ impl LineboxScanner {
|
||||||
// force it onto the line anyway, if its otherwise empty
|
// force it onto the line anyway, if its otherwise empty
|
||||||
// TODO(Issue #224): signal that horizontal overflow happened?
|
// TODO(Issue #224): signal that horizontal overflow happened?
|
||||||
if line_is_empty {
|
if line_is_empty {
|
||||||
debug!("LineboxScanner: case=box can't split and line %u is empty, so overflowing.",
|
debug!("LineboxScanner: case=box can't split and line %u is empty, so \
|
||||||
|
overflowing.",
|
||||||
self.line_spans.len());
|
self.line_spans.len());
|
||||||
self.push_box_to_line(in_box);
|
self.push_box_to_line(in_box);
|
||||||
return true;
|
return true;
|
||||||
|
@ -514,7 +535,8 @@ impl LineboxScanner {
|
||||||
// not enough width; try splitting?
|
// not enough width; try splitting?
|
||||||
match in_box.split_to_width(ctx, remaining_width, line_is_empty) {
|
match in_box.split_to_width(ctx, remaining_width, line_is_empty) {
|
||||||
CannotSplit(_) => {
|
CannotSplit(_) => {
|
||||||
error!("LineboxScanner: Tried to split unsplittable render box! %s", in_box.debug_str());
|
error!("LineboxScanner: Tried to split unsplittable render box! %s",
|
||||||
|
in_box.debug_str());
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
SplitDidFit(left, right) => {
|
SplitDidFit(left, right) => {
|
||||||
|
@ -524,11 +546,9 @@ impl LineboxScanner {
|
||||||
self.push_box_to_line(left_box);
|
self.push_box_to_line(left_box);
|
||||||
self.work_list.add_front(right_box);
|
self.work_list.add_front(right_box);
|
||||||
},
|
},
|
||||||
(Some(left_box), None) => { self.push_box_to_line(left_box); }
|
(Some(left_box), None) => self.push_box_to_line(left_box),
|
||||||
(None, Some(right_box)) => { self.push_box_to_line(right_box); }
|
(None, Some(right_box)) => self.push_box_to_line(right_box),
|
||||||
(None, None) => {
|
(None, None) => error!("LineboxScanner: This split case makes no sense!"),
|
||||||
error!("LineboxScanner: This split case makes no sense!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
},
|
},
|
||||||
|
@ -563,15 +583,15 @@ impl LineboxScanner {
|
||||||
}
|
}
|
||||||
|
|
||||||
// unconditional push
|
// unconditional push
|
||||||
priv fn push_box_to_line(&mut self, box: @mut RenderBox) {
|
fn push_box_to_line(&mut self, box: RenderBox) {
|
||||||
debug!("LineboxScanner: Pushing box b%d to line %u", box.d().id, self.line_spans.len());
|
debug!("LineboxScanner: Pushing box b%d to line %u", box.id(), self.line_spans.len());
|
||||||
|
|
||||||
if self.pending_line.range.length() == 0 {
|
if self.pending_line.range.length() == 0 {
|
||||||
assert!(self.new_boxes.len() <= (core::u16::max_value as uint));
|
assert!(self.new_boxes.len() <= (core::u16::max_value as uint));
|
||||||
self.pending_line.range.reset(self.new_boxes.len(), 0);
|
self.pending_line.range.reset(self.new_boxes.len(), 0);
|
||||||
}
|
}
|
||||||
self.pending_line.range.extend_by(1);
|
self.pending_line.range.extend_by(1);
|
||||||
self.pending_line.width += box.d().position.size.width;
|
self.pending_line.width += box.position().size.width;
|
||||||
self.new_boxes.push(box);
|
self.new_boxes.push(box);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -582,7 +602,7 @@ pub struct InlineFlowData {
|
||||||
|
|
||||||
// A vec of all inline render boxes. Several boxes may
|
// A vec of all inline render boxes. Several boxes may
|
||||||
// correspond to one Node/Element.
|
// correspond to one Node/Element.
|
||||||
boxes: ~[@mut RenderBox],
|
boxes: ~[RenderBox],
|
||||||
// vec of ranges into boxes that represents line positions.
|
// vec of ranges into boxes that represents line positions.
|
||||||
// these ranges are disjoint, and are the result of inline layout.
|
// these ranges are disjoint, and are the result of inline layout.
|
||||||
lines: ~[Range],
|
lines: ~[Range],
|
||||||
|
@ -648,21 +668,24 @@ impl InlineFlowData {
|
||||||
{
|
{
|
||||||
let this = &mut *self;
|
let this = &mut *self;
|
||||||
for this.boxes.each |&box| {
|
for this.boxes.each |&box| {
|
||||||
let box2 = &mut *box;
|
match box {
|
||||||
box.d().position.size.width = match *box2 {
|
ImageRenderBoxClass(image_box) => {
|
||||||
ImageBox(_, ref img) => {
|
let size = image_box.image.get_size();
|
||||||
let img2: &mut holder::ImageHolder = unsafe { cast::transmute(img) };
|
let width = Au::from_px(size.get_or_default(Size2D(0, 0)).width);
|
||||||
Au::from_px(img2.get_size().get_or_default(Size2D(0,0)).width)
|
image_box.base.position.size.width = width;
|
||||||
|
}
|
||||||
|
TextRenderBoxClass(text_box) => {
|
||||||
|
// Text boxes are preinitialized.
|
||||||
|
}
|
||||||
|
GenericRenderBoxClass(generic_box) => {
|
||||||
|
// TODO(#225): There will be different cases here for `inline-block` and
|
||||||
|
// other replaced content.
|
||||||
|
// FIXME(pcwalton): This seems clownshoes; can we remove?
|
||||||
|
generic_box.position.size.width = Au::from_px(45);
|
||||||
|
}
|
||||||
|
// FIXME(pcwalton): This isn't very type safe!
|
||||||
|
_ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box)),
|
||||||
}
|
}
|
||||||
TextBox(*) => {
|
|
||||||
// Text boxes are initialized with dimensions.
|
|
||||||
box.d().position.size.width
|
|
||||||
},
|
|
||||||
// TODO(#225): There will be different cases here for `inline-block` and other
|
|
||||||
// replaced content.
|
|
||||||
GenericBox(*) => Au::from_px(45),
|
|
||||||
_ => fail!(fmt!("Tried to assign width to unknown Box variant: %?", box))
|
|
||||||
};
|
|
||||||
} // End of for loop.
|
} // End of for loop.
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -698,59 +721,64 @@ impl InlineFlowData {
|
||||||
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
|
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
|
||||||
|
|
||||||
// Compute the height of each box.
|
// Compute the height of each box.
|
||||||
let d = cur_box.d(); // FIXME: borrow checker workaround
|
match cur_box {
|
||||||
let cur_box = &mut *cur_box; // FIXME: borrow checker workaround
|
ImageRenderBoxClass(image_box) => {
|
||||||
d.position.size.height = match *cur_box {
|
let size = image_box.image.get_size();
|
||||||
ImageBox(_, ref img) => {
|
let height = Au::from_px(size.get_or_default(Size2D(0, 0)).height);
|
||||||
Au::from_px(img.size().height)
|
image_box.base.position.size.height = height;
|
||||||
}
|
}
|
||||||
TextBox(*) => {
|
TextRenderBoxClass(*) => {
|
||||||
// Text boxes are initialized with dimensions.
|
// Text boxes are preinitialized.
|
||||||
d.position.size.height
|
}
|
||||||
},
|
GenericRenderBoxClass(generic_box) => {
|
||||||
// TODO(Issue #225): different cases for 'inline-block', other replaced content
|
// TODO(Issue #225): There will be different cases here for `inline-block`
|
||||||
GenericBox(*) => Au::from_px(30),
|
// and other replaced content.
|
||||||
|
// FIXME(pcwalton): This seems clownshoes; can we remove?
|
||||||
|
generic_box.position.size.height = Au::from_px(30);
|
||||||
|
}
|
||||||
|
// FIXME(pcwalton): This isn't very type safe!
|
||||||
_ => {
|
_ => {
|
||||||
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
|
|
||||||
fail!(fmt!("Tried to assign height to unknown Box variant: %s",
|
fail!(fmt!("Tried to assign height to unknown Box variant: %s",
|
||||||
cur_box.debug_str()))
|
cur_box.debug_str()))
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// Compute the bounding rect with the left baseline as origin. Determining line box
|
// Compute the bounding rect with the left baseline as origin. Determining line box
|
||||||
// height is a matter of lining up ideal baselines and then taking the union of all
|
// height is a matter of lining up ideal baselines and then taking the union of all
|
||||||
// these rects.
|
// these rects.
|
||||||
let bounding_box = match *cur_box {
|
let bounding_box = match cur_box {
|
||||||
// Adjust to baseline coordinates.
|
// Adjust to baseline coordinates.
|
||||||
//
|
//
|
||||||
// TODO(#227): Use left/right margins, border, padding for nonreplaced content,
|
// TODO(#227): Use left/right margins, border, padding for nonreplaced content,
|
||||||
// and also use top/bottom margins, border, padding for replaced or
|
// and also use top/bottom margins, border, padding for replaced or
|
||||||
// inline-block content.
|
// inline-block content.
|
||||||
//
|
//
|
||||||
// TODO(#225): Use height, width for 'inline-block' and other replaced content.
|
// TODO(#225): Use height, width for `inline-block` and other replaced content.
|
||||||
ImageBox(*) | GenericBox(*) => {
|
ImageRenderBoxClass(*) | GenericRenderBoxClass(*) => {
|
||||||
let box_bounds = d.position;
|
let height = cur_box.position().size.height;
|
||||||
box_bounds.translate(&Point2D(Au(0), -d.position.size.height))
|
cur_box.position().translate(&Point2D(Au(0), -height))
|
||||||
},
|
},
|
||||||
|
|
||||||
// Adjust the bounding box metric to the box's horizontal offset.
|
// Adjust the bounding box metric to the box's horizontal offset.
|
||||||
//
|
//
|
||||||
// TODO: We can use font metrics directly instead of re-measuring for the
|
// TODO: We can use font metrics directly instead of re-measuring for the
|
||||||
// bounding box.
|
// bounding box.
|
||||||
TextBox(_, data) => {
|
TextRenderBoxClass(text_box) => {
|
||||||
let text_bounds = data.run.metrics_for_range(&data.range).bounding_box;
|
let text_box = &mut *text_box; // FIXME: borrow check workaround
|
||||||
text_bounds.translate(&Point2D(d.position.origin.x, Au(0)))
|
let range = &text_box.text_data.range;
|
||||||
|
let run = &text_box.text_data.run;
|
||||||
|
let text_bounds = run.metrics_for_range(range).bounding_box;
|
||||||
|
text_bounds.translate(&Point2D(text_box.base.position.origin.x, Au(0)))
|
||||||
},
|
},
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
let cur_box = boxes[box_i]; // FIXME: borrow checker workaround
|
|
||||||
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
|
fail!(fmt!("Tried to compute bounding box of unknown Box variant: %s",
|
||||||
cur_box.debug_str()))
|
cur_box.debug_str()))
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
debug!("assign_height_inline: bounding box for box b%d = %?",
|
debug!("assign_height_inline: bounding box for box b%d = %?",
|
||||||
cur_box.d().id,
|
cur_box.id(),
|
||||||
bounding_box);
|
bounding_box);
|
||||||
|
|
||||||
linebox_bounding_box = linebox_bounding_box.union(&bounding_box);
|
linebox_bounding_box = linebox_bounding_box.union(&bounding_box);
|
||||||
|
@ -768,15 +796,17 @@ impl InlineFlowData {
|
||||||
// TODO(#226): This is completely wrong. We need to use the element's `line-height`
|
// TODO(#226): This is completely wrong. We need to use the element's `line-height`
|
||||||
// when calculating line box height. Then we should go back over and set Y offsets
|
// when calculating line box height. Then we should go back over and set Y offsets
|
||||||
// according to the `vertical-align` property of the containing block.
|
// according to the `vertical-align` property of the containing block.
|
||||||
let halfleading = match *cur_box {
|
let halfleading = match cur_box {
|
||||||
TextBox(_, data) => {
|
TextRenderBoxClass(text_box) => {
|
||||||
(data.run.font.metrics.em_size - line_height).scale_by(0.5f)
|
(text_box.text_data.run.font.metrics.em_size - line_height).scale_by(0.5)
|
||||||
},
|
},
|
||||||
_ => Au(0),
|
_ => Au(0),
|
||||||
};
|
};
|
||||||
|
|
||||||
cur_box.d().position.origin.y =
|
do cur_box.with_mut_base |base| {
|
||||||
cur_y + halfleading + (baseline_offset - cur_box.d().position.size.height);
|
let height = base.position.size.height;
|
||||||
|
base.position.origin.y = cur_y + halfleading + baseline_offset - height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cur_y += Au::max(line_height, linebox_height);
|
cur_y += Au::max(line_height, linebox_height);
|
||||||
|
|
|
@ -20,7 +20,7 @@ pub struct RootFlowData {
|
||||||
common: FlowData,
|
common: FlowData,
|
||||||
|
|
||||||
/// The render box at the root of the tree.
|
/// The render box at the root of the tree.
|
||||||
box: Option<@mut RenderBox>
|
box: Option<RenderBox>
|
||||||
}
|
}
|
||||||
|
|
||||||
impl RootFlowData {
|
impl RootFlowData {
|
||||||
|
@ -73,11 +73,13 @@ impl RootFlowData {
|
||||||
self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
|
self.common.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
|
||||||
|
|
||||||
do RootFlow(self).with_block_box |box| {
|
do RootFlow(self).with_block_box |box| {
|
||||||
box.d().position.origin.y = Au(0);
|
do box.with_mut_base |base| {
|
||||||
box.d().position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
|
base.position.origin.y = Au(0);
|
||||||
|
base.position.size.height = Au::max(ctx.screen_size.size.height, cur_y);
|
||||||
let (_used_top, _used_bot) = box.get_used_height();
|
let (_used_top, _used_bot) = box.get_used_height();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn build_display_list_root(@mut self,
|
pub fn build_display_list_root(@mut self,
|
||||||
builder: &DisplayListBuilder,
|
builder: &DisplayListBuilder,
|
||||||
|
|
|
@ -2,9 +2,9 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
/** Text layout. */
|
//! Text layout.
|
||||||
|
|
||||||
use layout::box::{TextBox, RenderBox, RenderBoxData, UnscannedTextBox};
|
use layout::box::{RenderBox, RenderBoxBase, TextRenderBox, UnscannedTextRenderBoxClass};
|
||||||
|
|
||||||
use gfx::text::text_run::TextRun;
|
use gfx::text::text_run::TextRun;
|
||||||
use servo_util::range::Range;
|
use servo_util::range::Range;
|
||||||
|
@ -14,37 +14,47 @@ pub struct TextBoxData {
|
||||||
range: Range,
|
range: Range,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn TextBoxData(run: @TextRun, range: &Range) -> TextBoxData {
|
impl TextBoxData {
|
||||||
|
pub fn new(run: @TextRun, range: Range) -> TextBoxData {
|
||||||
TextBoxData {
|
TextBoxData {
|
||||||
run: run,
|
run: run,
|
||||||
range: copy *range,
|
range: range,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn adapt_textbox_with_range(box_data: &mut RenderBoxData, run: @TextRun,
|
pub fn adapt_textbox_with_range(mut base: RenderBoxBase, run: @TextRun, range: Range)
|
||||||
range: &Range) -> @mut RenderBox {
|
-> TextRenderBox {
|
||||||
assert!(range.begin() < run.char_len());
|
assert!(range.begin() < run.char_len());
|
||||||
assert!(range.end() <= run.char_len());
|
assert!(range.end() <= run.char_len());
|
||||||
assert!(range.length() > 0);
|
assert!(range.length() > 0);
|
||||||
|
|
||||||
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
|
debug!("Creating textbox with span: (strlen=%u, off=%u, len=%u) of textrun: %s",
|
||||||
run.char_len(), range.begin(), range.length(), run.text);
|
run.char_len(),
|
||||||
let mut new_box_data = copy *box_data;
|
range.begin(),
|
||||||
let new_text_data = TextBoxData(run, range);
|
range.length(),
|
||||||
let metrics = run.metrics_for_range(range);
|
run.text);
|
||||||
new_box_data.position.size = metrics.bounding_box.size;
|
let new_text_data = TextBoxData::new(run, range);
|
||||||
@mut TextBox(new_box_data, new_text_data)
|
let metrics = run.metrics_for_range(&range);
|
||||||
|
|
||||||
|
base.position.size = metrics.bounding_box.size;
|
||||||
|
|
||||||
|
TextRenderBox {
|
||||||
|
base: base,
|
||||||
|
text_data: new_text_data,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait UnscannedMethods {
|
pub trait UnscannedMethods {
|
||||||
fn raw_text(&mut self) -> ~str;
|
/// Copies out the text from an unscanned text box. Fails if this is not an unscanned text box.
|
||||||
|
fn raw_text(&self) -> ~str;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnscannedMethods for RenderBox {
|
impl UnscannedMethods for RenderBox {
|
||||||
fn raw_text(&mut self) -> ~str {
|
fn raw_text(&self) -> ~str {
|
||||||
match self {
|
match *self {
|
||||||
&UnscannedTextBox(_, ref s) => copy *s,
|
UnscannedTextRenderBoxClass(text_box) => copy text_box.text,
|
||||||
_ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box.")
|
_ => fail!(~"unsupported operation: box.raw_text() on non-unscanned text box."),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue