From 233a2040daf94ed60177cc72fecd0627debce0af Mon Sep 17 00:00:00 2001 From: Patrick Walton Date: Tue, 28 May 2013 22:17:41 -0700 Subject: [PATCH] Implement the beginnings of the box model for render boxes --- src/components/gfx/geometry.rs | 13 ++- src/components/main/layout/box.rs | 89 ++++---------------- src/components/main/layout/model.rs | 125 ++++++++++++++++++++++++++++ src/components/main/servo.rc | 1 + 4 files changed, 155 insertions(+), 73 deletions(-) create mode 100644 src/components/main/layout/model.rs diff --git a/src/components/gfx/geometry.rs b/src/components/gfx/geometry.rs index d3b1f019df9..01cee75193a 100644 --- a/src/components/gfx/geometry.rs +++ b/src/components/gfx/geometry.rs @@ -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 } } diff --git a/src/components/main/layout/box.rs b/src/components/main/layout/box.rs index a9f834f4d9c..f5cac35f568 100644 --- a/src/components/main/layout/box.rs +++ b/src/components/main/layout/box.rs @@ -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, + /// 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, abs_bounds: &Rect) { - 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 { } } + diff --git a/src/components/main/layout/model.rs b/src/components/main/layout/model.rs new file mode 100644 index 00000000000..35b545baebf --- /dev/null +++ b/src/components/main/layout/model.rs @@ -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, + padding: SideOffsets2D, + margin: SideOffsets2D, +} + +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, abs_bounds: &Rect) { + // 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"); + } + } + +} + diff --git a/src/components/main/servo.rc b/src/components/main/servo.rc index d2c29354bdd..50a599a0634 100755 --- a/src/components/main/servo.rc +++ b/src/components/main/servo.rc @@ -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; }