Implement the beginnings of the box model for render boxes

This commit is contained in:
Patrick Walton 2013-05-28 22:17:41 -07:00
parent c14a137233
commit 233a2040da
4 changed files with 155 additions and 73 deletions

View file

@ -6,7 +6,7 @@ use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use core::num::NumCast;
use core::num::{NumCast, One, Zero};
pub struct Au(i32);
@ -46,6 +46,17 @@ impl cmp::Eq for Au {
fn ne(&self, other: &Au) -> bool { **self != **other }
}
impl One for Au {
fn one() -> Au { Au(1) }
}
impl Zero for Au {
fn zero() -> Au { Au(0) }
fn is_zero(&self) -> bool { **self == 0 }
}
impl Num for Au {}
pub fn min(x: Au, y: Au) -> Au { if x < y { x } else { y } }
pub fn max(x: Au, y: Au) -> Au { if x > y { x } else { y } }

View file

@ -8,11 +8,13 @@ use css::node_style::StyledNode;
use layout::context::LayoutContext;
use layout::display_list_builder::{DisplayListBuilder, ToGfxColor};
use layout::flow::FlowContext;
use layout::model::BoxModel;
use layout::text;
use core::cell::Cell;
use core::cmp::ApproxEq;
use core::managed;
use core::num::Zero;
use geom::{Point2D, Rect, Size2D};
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass};
use gfx::display_list::{DisplayList, ImageDisplayItem, ImageDisplayItemClass};
@ -24,7 +26,6 @@ use gfx::text::text_run::TextRun;
use newcss::color::rgb;
use newcss::complete::CompleteStyle;
use newcss::units::{Cursive, Em, Fantasy, Monospace, Pt, Px, SansSerif, Serif};
use newcss::values::{CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSFontFamilyFamilyName, CSSFontFamilyGenericFamily};
use newcss::values::{CSSFontSizeLength, CSSFontStyleItalic, CSSFontStyleNormal};
use newcss::values::{CSSFontStyleOblique, CSSTextAlign, CSSTextDecoration};
@ -154,6 +155,9 @@ pub struct RenderBoxBase {
/// The position of this box relative to its owning flow.
position: Rect<Au>,
/// The core parameters (border, padding, margin) used by the box model.
model: BoxModel,
/// A debug ID.
///
/// TODO(#87) Make this only present in debug builds.
@ -168,6 +172,7 @@ impl RenderBoxBase {
node: node,
ctx: flow_context,
position: Au::zero_rect(),
model: Zero::zero(),
id: id,
}
}
@ -367,6 +372,16 @@ pub impl RenderBox {
/// Returns the *minimum width* of this render box as defined by the CSS specification.
fn get_min_width(&self, _: &LayoutContext) -> Au {
// FIXME(pcwalton): I think we only need to calculate this if the damage says that CSS
// needs to be restyled.
do self.with_mut_base |base| {
// TODO(pcwalton): Hmm, it seems wasteful to have the box model stuff inside every
// render box if they can only be nonzero if the box is an element.
if base.node.is_element() {
base.model.populate(base.node.style())
}
}
match *self {
// TODO: This should account for the minimum width of the box element in isolation.
// That includes borders, margins, and padding, but not child widths. The block
@ -489,13 +504,6 @@ pub impl RenderBox {
self.content_box()
}
/// A convenience function to determine whether this render box represents a DOM element.
fn is_element(&self) -> bool {
do self.with_imm_base |base| {
base.node.is_element()
}
}
/// A convenience function to access the computed style of the DOM node that this render box
/// represents.
fn style(&self) -> CompleteStyle {
@ -684,70 +692,6 @@ pub impl RenderBox {
}
}
/// Adds the display items necessary to paint the borders of this render box to the display
/// list if necessary.
fn paint_borders_if_applicable(&self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
if !self.is_element() {
return
}
let style = self.style();
let (top_width, right_width) = (style.border_top_width(), style.border_right_width());
let (bottom_width, left_width) = (style.border_bottom_width(), style.border_left_width());
match (top_width, right_width, bottom_width, left_width) {
(CSSBorderWidthLength(Px(top)),
CSSBorderWidthLength(Px(right)),
CSSBorderWidthLength(Px(bottom)),
CSSBorderWidthLength(Px(left))) => {
let top_au = Au::from_frac_px(top);
let right_au = Au::from_frac_px(right);
let bottom_au = Au::from_frac_px(bottom);
let left_au = Au::from_frac_px(left);
// Are all the widths equal?
if [ top_au, right_au, bottom_au ].all(|a| *a == left_au) {
let border_width = top_au;
let bounds = Rect {
origin: Point2D {
x: abs_bounds.origin.x - border_width / Au(2),
y: abs_bounds.origin.y - border_width / Au(2),
},
size: Size2D {
width: abs_bounds.size.width + border_width,
height: abs_bounds.size.height + border_width
}
};
let top_color = self.style().border_top_color();
let color = top_color.to_gfx_color(); // FIXME
// Append the border to the display list.
do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
},
width: border_width,
color: color,
};
list.append_item(BorderDisplayItemClass(border_display_item))
}
} else {
warn!("ignoring unimplemented border widths");
}
}
(CSSBorderWidthMedium,
CSSBorderWidthMedium,
CSSBorderWidthMedium,
CSSBorderWidthMedium) => {
// FIXME: This seems to be the default for non-root nodes. For now we'll ignore it.
warn!("ignoring medium border widths");
}
_ => warn!("ignoring unimplemented border widths")
}
}
/// Converts this node's computed style to a font style used for rendering.
fn font_style(&self) -> FontStyle {
let my_style = self.nearest_ancestor_element().style();
@ -870,3 +814,4 @@ pub impl RenderBox {
}
}

View file

@ -0,0 +1,125 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
//! Borders, padding, and margins.
use layout::display_list_builder::ToGfxColor;
use layout::box::RenderBox;
use core::cell::Cell;
use core::num::Zero;
use geom::point::Point2D;
use geom::rect::Rect;
use geom::size::Size2D;
use geom::side_offsets::SideOffsets2D;
use gfx::display_list::{BaseDisplayItem, BorderDisplayItem, BorderDisplayItemClass, DisplayList};
use gfx::geometry::Au;
use newcss::complete::CompleteStyle;
use newcss::units::{Em, Pt, Px};
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
/// Encapsulates the borders, padding, and margins, which we collectively call the "box model".
pub struct BoxModel {
border: SideOffsets2D<Au>,
padding: SideOffsets2D<Au>,
margin: SideOffsets2D<Au>,
}
impl Zero for BoxModel {
fn zero() -> BoxModel {
BoxModel {
border: Zero::zero(),
padding: Zero::zero(),
margin: Zero::zero(),
}
}
fn is_zero(&self) -> bool {
self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
}
}
impl BoxModel {
/// Populates the box model parameters from the given computed style.
pub fn populate(&mut self, style: CompleteStyle) {
// Populate the borders.
self.border.top = self.compute_border_width(style.border_top_width());
self.border.right = self.compute_border_width(style.border_right_width());
self.border.bottom = self.compute_border_width(style.border_bottom_width());
self.border.left = self.compute_border_width(style.border_left_width());
// TODO(pcwalton): Padding, margins.
}
/// Helper function to compute the border width in app units from the CSS border width.
fn compute_border_width(&self, width: CSSBorderWidth) -> Au {
match width {
CSSBorderWidthLength(Px(v)) |
CSSBorderWidthLength(Em(v)) |
CSSBorderWidthLength(Pt(v)) => {
// FIXME(pcwalton): Handle `em` and `pt` correctly.
Au::from_frac_px(v)
}
CSSBorderWidthThin => Au::from_px(1),
CSSBorderWidthMedium => Au::from_px(5),
CSSBorderWidthThick => Au::from_px(10),
}
}
}
//
// Painting
//
impl RenderBox {
/// Adds the display items necessary to paint the borders of this render box to a display list
/// if necessary.
pub fn paint_borders_if_applicable(&self, list: &Cell<DisplayList>, abs_bounds: &Rect<Au>) {
// Fast path.
let border = do self.with_imm_base |base| {
base.model.border
};
if border.is_zero() {
return
}
// Are all the widths equal?
//
// FIXME(pcwalton): Obviously this is wrong.
if [ border.top, border.right, border.bottom ].all(|a| *a == border.left) {
let border_width = border.top;
let bounds = Rect {
origin: Point2D {
x: abs_bounds.origin.x - border_width / Au(2),
y: abs_bounds.origin.y - border_width / Au(2),
},
size: Size2D {
width: abs_bounds.size.width + border_width,
height: abs_bounds.size.height + border_width
}
};
let top_color = self.style().border_top_color();
let color = top_color.to_gfx_color(); // FIXME
// Append the border to the display list.
do list.with_mut_ref |list| {
let border_display_item = ~BorderDisplayItem {
base: BaseDisplayItem {
bounds: bounds,
},
width: border_width,
color: color,
};
list.append_item(BorderDisplayItemClass(border_display_item))
}
} else {
warn!("ignoring unimplemented border widths");
}
}
}

View file

@ -68,6 +68,7 @@ pub mod layout {
pub mod flow;
pub mod layout_task;
pub mod inline;
pub mod model;
pub mod text;
mod aux;
}