mirror of
https://github.com/servo/servo.git
synced 2025-08-05 05:30:08 +01:00
Add horizontal borders, margins and padding. Broken until rust-css supports padding
This commit is contained in:
parent
facb70756c
commit
d5e47933ab
3 changed files with 174 additions and 49 deletions
|
@ -10,6 +10,7 @@ use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData};
|
|||
use layout::display_list_builder::{FlowDisplayListBuilderMethods};
|
||||
use layout::flow::{BlockFlow, FlowContext, FlowData, InlineBlockFlow};
|
||||
use layout::inline::InlineLayout;
|
||||
use layout::model::{MaybeAuto, Specified, Auto};
|
||||
|
||||
use core::cell::Cell;
|
||||
use geom::point::Point2D;
|
||||
|
@ -104,6 +105,8 @@ impl BlockFlowData {
|
|||
/* if not an anonymous block context, add in block box's widths.
|
||||
these widths will not include child elements, just padding etc. */
|
||||
self.box.map(|&box| {
|
||||
//Can compute border width here since it doesn't depend on anything
|
||||
box.compute_borders();
|
||||
min_width = min_width.add(&box.get_min_width(ctx));
|
||||
pref_width = pref_width.add(&box.get_pref_width(ctx));
|
||||
});
|
||||
|
@ -112,6 +115,57 @@ impl BlockFlowData {
|
|||
self.common.pref_width = pref_width;
|
||||
}
|
||||
|
||||
/// Computes left and right margins and width based on CSS 2.1 secion 10.3.3.
|
||||
/// Requires borders and padding to already be computed
|
||||
priv fn compute_horiz( &self,
|
||||
width: MaybeAuto,
|
||||
left_margin: MaybeAuto,
|
||||
right_margin: MaybeAuto,
|
||||
available_width: Au) -> (Au, Au, Au) {
|
||||
|
||||
//If width is not 'auto', and width + margins > available_width, all 'auto' margins are treated as '0'
|
||||
let (left_margin, right_margin) = match width{
|
||||
Auto => (left_margin, right_margin),
|
||||
Specified(width) => {
|
||||
let left = left_margin.spec_or_default(Au(0));
|
||||
let right = right_margin.spec_or_default(Au(0));
|
||||
|
||||
if((left + right + width) > available_width) {
|
||||
(Specified(left), Specified(right))
|
||||
} else {
|
||||
(left_margin, right_margin)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//Invariant: left_margin_Au + width_Au + right_margin_Au == available_width
|
||||
let (left_margin_Au, width_Au, right_margin_Au) = match (left_margin, width, right_margin) {
|
||||
//If all have a computed value other than 'auto', the system is over-constrained and we need to discard a margin.
|
||||
//if direction is ltr, ignore the specified right margin and solve for it. If it is rtl, ignore the specified
|
||||
//left margin. FIXME(eatkinson): this assumes the direction is ltr
|
||||
(Specified(margin_l), Specified(width), Specified(margin_r)) => (margin_l, width, available_width - (margin_l + width )),
|
||||
|
||||
//If exactly one value is 'auto', solve for it
|
||||
(Auto, Specified(width), Specified(margin_r)) => (available_width - (width + margin_r), width, margin_r),
|
||||
(Specified(margin_l), Auto, Specified(margin_r)) => (margin_l, available_width - (margin_l + margin_r), margin_r),
|
||||
(Specified(margin_l), Specified(width), Auto) => (margin_l, width, available_width - (margin_l + width)),
|
||||
|
||||
//If width is set to 'auto', any other 'auto' value becomes '0', and width is solved for
|
||||
(Auto, Auto, Specified(margin_r)) => (Au(0), available_width - margin_r, margin_r),
|
||||
(Specified(margin_l), Auto, Auto) => (margin_l, available_width - margin_l, Au(0)),
|
||||
(Auto, Auto, Auto) => (Au(0), available_width, Au(0)),
|
||||
|
||||
//If left and right margins are auto, they become equal
|
||||
(Auto, Specified(width), Auto) => {
|
||||
let margin = (available_width - width).scale_by(0.5);
|
||||
(margin, width, margin)
|
||||
}
|
||||
|
||||
};
|
||||
//return values in same order as params
|
||||
(width_Au, left_margin_Au, right_margin_Au)
|
||||
}
|
||||
|
||||
/// Recursively (top-down) determines the actual width of child contexts and boxes. When called
|
||||
/// on this context, the context has had its width set by the parent context.
|
||||
///
|
||||
|
@ -123,24 +177,28 @@ impl BlockFlowData {
|
|||
self.common.position.size.width = ctx.screen_size.size.width;
|
||||
}
|
||||
|
||||
//position was set to the containing block by the flow's parent
|
||||
let mut remaining_width = self.common.position.size.width;
|
||||
let left_used = Au(0);
|
||||
let mut x_offset = Au(0);
|
||||
|
||||
// Let the box consume some width. It will return the amount remaining for its children.
|
||||
self.box.map(|&box| {
|
||||
do box.with_mut_base |base| {
|
||||
base.position.size.width = remaining_width;
|
||||
box.compute_padding(remaining_width);
|
||||
let available_width = remaining_width - box.get_noncontent_width();
|
||||
|
||||
let (left_used, right_used) = box.get_used_width();
|
||||
remaining_width -= left_used.add(&right_used);
|
||||
do box.compute_width(remaining_width) |width, left_margin, right_margin| {
|
||||
self.compute_horiz(width, left_margin, right_margin, available_width)
|
||||
}
|
||||
|
||||
let content_box = box.content_box();
|
||||
x_offset = content_box.origin.x;
|
||||
remaining_width = content_box.size.width;
|
||||
});
|
||||
|
||||
for BlockFlow(self).each_child |kid| {
|
||||
assert!(kid.starts_block_flow() || kid.starts_inline_flow());
|
||||
|
||||
do kid.with_mut_base |child_node| {
|
||||
child_node.position.origin.x = left_used;
|
||||
child_node.position.origin.x = x_offset;
|
||||
child_node.position.size.width = remaining_width;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@ use css::node_style::StyledNode;
|
|||
use layout::context::LayoutContext;
|
||||
use layout::display_list_builder::{DisplayListBuilder, ExtraDisplayListData, ToGfxColor};
|
||||
use layout::flow::FlowContext;
|
||||
use layout::model::BoxModel;
|
||||
use layout::model::{BoxModel,MaybeAuto};
|
||||
use layout::text;
|
||||
|
||||
use core::cell::Cell;
|
||||
|
@ -374,14 +374,6 @@ pub impl RenderBox {
|
|||
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
|
||||
|
@ -456,37 +448,51 @@ pub impl RenderBox {
|
|||
(Au(0), Au(0))
|
||||
}
|
||||
|
||||
fn compute_padding(&self, cb_width: Au) {
|
||||
do self.with_imm_base |base| {
|
||||
base.model.compute_padding(self.style(), cb_width);
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_borders(&self){
|
||||
do self.with_imm_base |base| {
|
||||
base.model.compute_borders(self.style());
|
||||
}
|
||||
}
|
||||
|
||||
fn get_noncontent_width(&self) -> Au {
|
||||
do self.with_imm_base |base| {
|
||||
base.model.border.left + base.model.padding.left + base.model.border.right +
|
||||
base.model.padding.right
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_width (&self, cb_width: Au,
|
||||
callback: &fn(MaybeAuto, MaybeAuto, MaybeAuto) -> (Au, Au, Au)) {
|
||||
let computed_width = MaybeAuto::from_width(self.style().width());
|
||||
let computed_margin_left = MaybeAuto::from_margin(self.style().margin_left());
|
||||
let computed_margin_right = MaybeAuto::from_margin(self.style().margin_right());
|
||||
|
||||
let (used_width, used_margin_left, used_margin_right) =
|
||||
callback(computed_width, computed_margin_left, computed_margin_right);
|
||||
|
||||
do self.with_mut_base |base| {
|
||||
base.model.margin.left = used_margin_left;
|
||||
base.model.margin.right = used_margin_right;
|
||||
base.position.size.width = used_width + self.get_noncontent_width();
|
||||
base.position.origin.x = used_margin_left;
|
||||
}
|
||||
}
|
||||
|
||||
/// The box formed by the content edge as defined in CSS 2.1 § 8.1. Coordinates are relative to
|
||||
/// the owning flow.
|
||||
fn content_box(&self) -> Rect<Au> {
|
||||
let origin = self.position().origin;
|
||||
match *self {
|
||||
ImageRenderBoxClass(image_box) => {
|
||||
Rect {
|
||||
origin: origin,
|
||||
size: image_box.base.position.size,
|
||||
}
|
||||
},
|
||||
GenericRenderBoxClass(*) => {
|
||||
self.position()
|
||||
|
||||
// FIXME: The following hits an ICE for whatever reason.
|
||||
|
||||
/*
|
||||
let origin = self.d().position.origin;
|
||||
let size = self.d().position.size;
|
||||
let (offset_left, offset_right) = self.get_used_width();
|
||||
let (offset_top, offset_bottom) = self.get_used_height();
|
||||
|
||||
Rect {
|
||||
origin: Point2D(origin.x + offset_left, origin.y + offset_top),
|
||||
size: Size2D(size.width - (offset_left + offset_right),
|
||||
size.height - (offset_top + offset_bottom))
|
||||
}
|
||||
*/
|
||||
},
|
||||
TextRenderBoxClass(*) => self.position(),
|
||||
UnscannedTextRenderBoxClass(*) => fail!(~"Shouldn't see unscanned boxes here.")
|
||||
do self.with_imm_base |base| {
|
||||
let origin = Point2D(base.position.origin.x + base.model.border.left + base.model.padding.left,
|
||||
base.position.origin.y);
|
||||
let size = Size2D(base.position.size.width - self.get_noncontent_width(),
|
||||
base.position.size.height);
|
||||
Rect(origin, size)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -19,12 +19,54 @@ use newcss::complete::CompleteStyle;
|
|||
use newcss::units::{Em, Pt, Px};
|
||||
use newcss::values::{CSSBorderWidth, CSSBorderWidthLength, CSSBorderWidthMedium};
|
||||
use newcss::values::{CSSBorderWidthThick, CSSBorderWidthThin};
|
||||
|
||||
use newcss::values::{CSSWidth, CSSWidthLength, CSSWidthPercentage, CSSWidthAuto};
|
||||
use newcss::values::{CSSMargin, CSSMarginLength, CSSMarginPercentage, CSSMarginAuto};
|
||||
use newcss::values::{CSSPadding, CSSPaddingLength, CSSPaddingPercentage};
|
||||
/// 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>,
|
||||
content_width: Au,
|
||||
}
|
||||
|
||||
/// Useful helper data type when computing values for blocks and positioned elements.
|
||||
pub enum MaybeAuto{
|
||||
Auto,
|
||||
Specified(Au),
|
||||
}
|
||||
|
||||
impl MaybeAuto{
|
||||
pub fn from_margin(margin: CSSMargin) -> MaybeAuto{
|
||||
match margin {
|
||||
CSSMarginAuto => Auto,
|
||||
//FIXME(eatkinson): Compute percents properly
|
||||
CSSMarginPercentage(_) => Specified(Au(0)),
|
||||
//FIXME(eatkinson): Compute pt and em values properly
|
||||
CSSMarginLength(Px(v)) |
|
||||
CSSMarginLength(Pt(v)) |
|
||||
CSSMarginLength(Em(v)) => Specified(Au::from_frac_px(v)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_width(width: CSSWidth) -> MaybeAuto{
|
||||
match width{
|
||||
CSSWidthAuto => Auto,
|
||||
//FIXME(eatkinson): Compute percents properly
|
||||
CSSWidthPercentage(_) => Specified(Au(0)),
|
||||
//FIXME(eatkinson): Compute pt and em values properly
|
||||
CSSWidthLength(Px(v)) |
|
||||
CSSWidthLength(Pt(v)) |
|
||||
CSSWidthLength(Em(v)) => Specified(Au::from_frac_px(v)),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn spec_or_default(&self, default: Au) -> Au{
|
||||
match *self{
|
||||
Auto => default,
|
||||
Specified(value) => value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Zero for BoxModel {
|
||||
|
@ -33,24 +75,31 @@ impl Zero for BoxModel {
|
|||
border: Zero::zero(),
|
||||
padding: Zero::zero(),
|
||||
margin: Zero::zero(),
|
||||
content_width: Zero::zero(),
|
||||
}
|
||||
}
|
||||
|
||||
fn is_zero(&self) -> bool {
|
||||
self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero()
|
||||
self.padding.is_zero() && self.border.is_zero() && self.margin.is_zero() &&
|
||||
self.content_width.is_zero()
|
||||
}
|
||||
}
|
||||
|
||||
impl BoxModel {
|
||||
/// Populates the box model parameters from the given computed style.
|
||||
pub fn populate(&mut self, style: CompleteStyle) {
|
||||
// Populate the borders.
|
||||
pub fn compute_borders(&mut self, style: CompleteStyle) {
|
||||
// Compute 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.
|
||||
pub fn compute_padding(&mut self, style: CompleteStyle, cb_width: Au){
|
||||
self.padding.top = self.compute_padding(style.padding_top(), cb_width);
|
||||
self.padding.right = self.compute_padding(style.padding_right(), cb_width);
|
||||
self.padding.bottom = self.compute_padding(style.padding_bottom(), cb_width);
|
||||
self.padding.left = self.compute_padding(style.padding_left(), cb_width);
|
||||
}
|
||||
|
||||
/// Helper function to compute the border width in app units from the CSS border width.
|
||||
|
@ -67,6 +116,18 @@ impl BoxModel {
|
|||
CSSBorderWidthThick => Au::from_px(10),
|
||||
}
|
||||
}
|
||||
|
||||
fn compute_padding(&self, padding: CSSPadding, cb_width: Au) -> Au{
|
||||
match padding {
|
||||
CSSPaddingLength(Px(v)) |
|
||||
CSSPaddingLength(Pt(v)) |
|
||||
CSSPaddingLength(Em(v)) => {
|
||||
// FIXME(eatkinson): Handle 'em' and 'pt' correctly
|
||||
Au::from_frac_px(v)
|
||||
}
|
||||
CSSPaddingPercentage(p) => cb_width.scale_by(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue