mirror of
https://github.com/servo/servo.git
synced 2025-08-04 13:10:20 +01:00
Implement the beginnings of the box model for render boxes
This commit is contained in:
parent
c14a137233
commit
233a2040da
4 changed files with 155 additions and 73 deletions
|
@ -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 } }
|
||||
|
||||
|
|
|
@ -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 {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
125
src/components/main/layout/model.rs
Normal file
125
src/components/main/layout/model.rs
Normal 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");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue