Redo representation of CSS values, and many other related things.

This commit is contained in:
Brian J. Burg 2012-09-07 17:00:09 -07:00
parent cdd85d8d65
commit fd3ade2a3c
11 changed files with 413 additions and 236 deletions

View file

@ -3,8 +3,7 @@
// TODO: fail according to the css spec instead of failing when things // TODO: fail according to the css spec instead of failing when things
// are not as expected // are not as expected
use css::values::{TextColor, BackgroundColor, FontSize, Height, Width, use css::values::*;
Display, StyleDeclaration};
// Disambiguate parsed Selector, Rule values from tokens // Disambiguate parsed Selector, Rule values from tokens
use css = css::values; use css = css::values;
use tok = lexer; use tok = lexer;
@ -156,17 +155,20 @@ impl TokenReader : ParserMethods {
match tok { match tok {
tok::EndDescription => { break; } tok::EndDescription => { break; }
tok::Description(prop, val) => { tok::Description(prop, val) => {
let desc = match prop { let desc : Option<StyleDeclaration> = match prop {
// TODO: have color parsing return an option instead of a real value // TODO: have color parsing return a ParseResult instead of a real value
~"background-color" => parse_color(val).map(|res| BackgroundColor(res)), ~"background-color" => parse_color(val).map(|res| BackgroundColor(Specified(BgColor(res)))),
~"color" => parse_color(val).map(|res| TextColor(res)), ~"color" => parse_color(val).map(|res| Color(Specified(TextColor(res)))),
~"display" => parse_display_type(val).map(|res| Display(res)), ~"display" => parse_display_type(val).extract(|res| Display(res)),
~"font-size" => parse_font_size(val).map(|res| FontSize(res)), ~"font-size" => parse_font_size(val).extract(|res| FontSize(res)),
~"height" => parse_size(val).map(|res| Height(res)), ~"height" => parse_box_sizing(val).extract(|res| Height(res)),
~"width" => parse_size(val).map(|res| Width(res)), ~"width" => parse_box_sizing(val).extract(|res| Width(res)),
_ => { #debug["Recieved unknown style property '%s'", val]; None } _ => { #debug["Recieved unknown style property '%s'", val]; None }
}; };
desc.map(|res| push(desc_list, res)); match desc {
Some(d) => push(desc_list, d),
None => { #debug["Couldn't parse value '%s' for property '%s'", val, prop] }
}
} }
tok::Eof => { return None; } tok::Eof => { return None; }
tok::StartDescription | tok::Descendant | tok::Child | tok::Sibling tok::StartDescription | tok::Descendant | tok::Child | tok::Sibling

View file

@ -1,63 +1,76 @@
#[doc = "Helper functions to parse values of specific attributes."] #[doc = "Helper functions to parse values of specific attributes."]
use css::values::{DisplayType, Inline, Block, DisplayNone}; use css::values::*;
use css::values::{Unit, Pt, Mm, Px, Percent, Auto};
use str::{pop_char, from_chars}; use str::{pop_char, from_chars};
use float::from_str; use float::from_str;
use option::map; use option::map;
export parse_font_size; export parse_font_size;
export parse_size; export parse_size;
export parse_box_sizing;
export parse_display_type; export parse_display_type;
fn parse_unit(str : ~str) -> Option<Unit> {
fn parse_length(str : ~str) -> Option<Length> {
// TODO: use these once we stop lexing below
const PTS_PER_INCH: float = 72.0;
const CM_PER_INCH: float = 2.54;
const PX_PER_PT: float = 1.0 / 0.75;
match str { match str {
s if s.ends_with(~"%") => from_str(str.substr(0, str.len() - 1)).map(|f| Percent(f)), s if s.ends_with(~"in") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * 72.0 * f)),
s if s.ends_with(~"in") => from_str(str.substr(0, str.len() - 2)).map(|f| Pt(72.0*f)), s if s.ends_with(~"cm") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(f / 2.54 * 72.0 * 1.0/0.75)),
s if s.ends_with(~"cm") => from_str(str.substr(0, str.len() - 2)).map(|f| Mm(10.0*f)), s if s.ends_with(~"mm") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(f * 0.1 / 2.54 * 72.0 * 1.0/0.75)),
s if s.ends_with(~"mm") => from_str(str.substr(0, str.len() - 2)).map(|f| Mm(f)), s if s.ends_with(~"pt") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * f)),
s if s.ends_with(~"pt") => from_str(str.substr(0, str.len() - 2)).map(|f| Pt(f)), s if s.ends_with(~"pc") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(1.0/0.75 * 12.0 * f)),
s if s.ends_with(~"pc") => from_str(str.substr(0, str.len() - 2)).map(|f| Pt(12.0*f)),
s if s.ends_with(~"px") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(f)), s if s.ends_with(~"px") => from_str(str.substr(0, str.len() - 2)).map(|f| Px(f)),
s if s.ends_with(~"ex") | s.ends_with(~"em") => fail ~"Em and Ex sizes not yet supported", s if s.ends_with(~"em") => from_str(str.substr(0, str.len() - 2)).map(|f| Em(f)),
s if s.ends_with(~"ex") => from_str(str.substr(0, str.len() - 2)).map(|f| Em(0.5*f)),
_ => None, _ => None,
} }
} }
fn parse_font_size(str : ~str) -> Option<Unit> { fn parse_absolute_size(str : ~str) -> ParseResult<AbsoluteSize> {
// The default pixel size, not sure if this is accurate.
let default = 16.0;
match str { match str {
~"xx-small" => Some(Px(0.6*default)), ~"xx-small" => Value(XXSmall),
~"x-small" => Some(Px(0.75*default)), ~"x-small" => Value(XSmall),
~"small" => Some(Px(8.0/9.0*default)), ~"small" => Value(Small),
~"medium" => Some(Px(default)), ~"medium" => Value(Medium),
~"large" => Some(Px(1.2*default)), ~"large" => Value(Large),
~"x-large" => Some(Px(1.5*default)), ~"x-large" => Value(XLarge),
~"xx-large" => Some(Px(2.0*default)), ~"xx-large" => Value(XXLarge),
~"smaller" => Some(Percent(80.0)), _ => Fail
~"larger" => Some(Percent(125.0)),
~"inherit" => Some(Percent(100.0)),
_ => parse_unit(str),
} }
} }
fn parse_relative_size(str: ~str) -> ParseResult<RelativeSize> {
match str {
~"smaller" => Value(Smaller),
~"larger" => Value(Larger),
_ => Fail
}
}
fn parse_font_size(str: ~str) -> ParseResult<CSSFontSize> {
// TODO: complete me
Value(LengthSize(Px(14.0)))
}
// For width / height, and anything else with the same attribute values // For width / height, and anything else with the same attribute values
fn parse_size(str : ~str) -> Option<Unit> { fn parse_box_sizing(str : ~str) -> ParseResult<BoxSizing> {
match str { match str {
~"auto" => Some(Auto), ~"auto" => Value(BoxAuto),
~"inherit" => Some(Percent(100.0)), ~"inherit" => CSSInherit,
_ => parse_unit(str), _ => Fail,
} }
} }
fn parse_display_type(str : ~str) -> Option<DisplayType> { fn parse_display_type(str : ~str) -> ParseResult<CSSDisplay> {
match str { match str {
~"inline" => Some(Inline), ~"inline" => Value(DisplayInline),
~"block" => Some(Block), ~"block" => Value(DisplayBlock),
~"none" => Some(DisplayNone), ~"none" => Value(DisplayNone),
_ => { #debug["Recieved unknown display value '%s'", str]; None } _ => { #debug["Recieved unknown display value '%s'", str]; Fail }
} }
} }

View file

@ -1,6 +1,6 @@
#[doc="Applies the appropriate CSS style to boxes."] #[doc="Applies the appropriate CSS style to boxes."]
import dom::base::{Element, HTMLImageElement, Node}; import dom = dom::base;
import gfx::geometry::au_to_px; import gfx::geometry::au_to_px;
import layout::base::{Box, BTree, NTree, LayoutData, SpecifiedStyle, ImageHolder, import layout::base::{Box, BTree, NTree, LayoutData, SpecifiedStyle, ImageHolder,
BlockBox, InlineBox, IntrinsicBox, TextBox}; BlockBox, InlineBox, IntrinsicBox, TextBox};
@ -8,7 +8,28 @@ import layout::traverse::{top_down_traversal};
import std::net::url::Url; import std::net::url::Url;
import resource::image_cache_task::ImageCacheTask; import resource::image_cache_task::ImageCacheTask;
import css::values::{Percent, Mm, Pt, Px, Auto, PtToPx, MmToPx}; import css::values::*;
trait ResolveMethods<T> {
pure fn initial() -> T;
}
impl CSSValue<CSSBackgroundColor> : ResolveMethods<CSSBackgroundColor> {
pure fn initial() -> CSSBackgroundColor { return BgTransparent; }
}
impl CSSValue<CSSDisplay> : ResolveMethods<CSSDisplay> {
pure fn initial() -> CSSDisplay { return DisplayInline; }
}
impl CSSValue<BoxSizing> : ResolveMethods<BoxSizing> {
pure fn initial() -> BoxSizing { return BoxAuto; }
}
impl CSSValue<CSSFontSize> : ResolveMethods<CSSFontSize> {
pure fn initial() -> CSSFontSize { return AbsoluteSize(Medium); }
}
struct StyleApplicator { struct StyleApplicator {
box: @Box; box: @Box;
@ -17,6 +38,7 @@ struct StyleApplicator {
reflow: fn~(); reflow: fn~();
} }
fn apply_style(box: @Box, doc_url: &Url, image_cache_task: ImageCacheTask, reflow: fn~()) { fn apply_style(box: @Box, doc_url: &Url, image_cache_task: ImageCacheTask, reflow: fn~()) {
let applicator = StyleApplicator { let applicator = StyleApplicator {
box: box, box: box,
@ -28,6 +50,8 @@ fn apply_style(box: @Box, doc_url: &Url, image_cache_task: ImageCacheTask, reflo
applicator.apply_css_style(); applicator.apply_css_style();
} }
// TODO: this is misleadingly-named. It is actually trying to resolve CSS 'inherit' values.
#[doc="A wrapper around a set of functions that can be applied as a top-down traversal of layout #[doc="A wrapper around a set of functions that can be applied as a top-down traversal of layout
boxes."] boxes."]
fn inheritance_wrapper(box : @Box, doc_url: &Url, image_cache_task: ImageCacheTask, reflow: fn~()) { fn inheritance_wrapper(box : @Box, doc_url: &Url, image_cache_task: ImageCacheTask, reflow: fn~()) {
@ -38,66 +62,51 @@ fn inheritance_wrapper(box : @Box, doc_url: &Url, image_cache_task: ImageCacheTa
reflow: reflow reflow: reflow
}; };
applicator.apply_style(); applicator.apply_style();
inhereit_height(box); inherit_fontsize(box);
inhereit_width(box); inherit_height(box);
inherit_width(box);
} }
/* Turns symbolic (abs, rel) and relative font sizes into absolute lengths */
fn inherit_fontsize(box : @Box) {
// TODO: complete this
return
}
#[doc="Compute the specified height of a layout box based on it's css specification and its #[doc="Compute the specified height of a layout box based on it's css specification and its
parent's height."] parent's height."]
fn inhereit_height(box : @Box) { fn inherit_height(box : @Box) {
let style = box.node.get_specified_style(); let style = box.node.get_specified_style();
let inherit_val = match box.tree.parent {
None => style.height.initial(),
Some(node) => node.appearance.height
};
box.appearance.height = match style.height { box.appearance.height = match style.height {
None => Auto, Initial => style.height.initial(),
Some(h) => match h { Inherit => inherit_val,
Auto | Px(*) => h, Specified(val) => match val { // BoxSizing
Pt(*) => PtToPx(h), BoxPercent(*) | BoxAuto | BoxLength(Px(_)) => val,
Mm(*) => MmToPx(h), BoxLength(Em(n)) => BoxLength(Px(n * box.appearance.font_size.abs()))
Percent(em) => {
match box.tree.parent {
None => Auto,
Some(parent) => {
match parent.appearance.height {
//This is a poorly constrained case, so we ignore the percentage
Auto => Auto,
Px(f) => Px(em*f/100.0),
Percent(*) | Mm(*) | Pt(*) => {
fail ~"failed inheriting heights, parent should only be Px or Auto"
}
}
}
}
}
} }
} }
} }
#[doc="Compute the specified width of a layout box based on it's css specification and its #[doc="Compute the specified width of a layout box based on it's css specification and its
parent's width."] parent's width."]
fn inhereit_width(box : @Box) { fn inherit_width(box : @Box) {
let style = box.node.get_specified_style(); let style = box.node.get_specified_style();
let inherit_val = match box.tree.parent {
None => style.height.initial(),
Some(node) => node.appearance.width
};
box.appearance.width = match style.width { box.appearance.width = match style.width {
None => Auto, Initial => style.width.initial(),
Some(h) => match h { Inherit => inherit_val,
Auto | Px(*) => h, Specified(val) => match val { // BoxSizing
Pt(*) => PtToPx(h), BoxPercent(*) | BoxAuto | BoxLength(Px(_)) => val,
Mm(*) => MmToPx(h), BoxLength(Em(n)) => BoxLength(Px(n * box.appearance.font_size.abs()))
Percent(em) => {
match box.tree.parent {
None => Auto,
Some(parent) => {
match parent.appearance.width {
//This is a poorly constrained case, so we ignore the percentage
Auto => Auto,
Px(f) => Px(em*f/100.0),
Percent(*) | Mm(*) | Pt(*) => {
fail ~"failed inheriting widths, parent should only be Px or Auto"
}
}
}
}
}
} }
} }
} }
@ -124,16 +133,9 @@ impl StyleApplicator {
// Right now, we only handle images. // Right now, we only handle images.
do self.box.node.read |node| { do self.box.node.read |node| {
match node.kind { match node.kind {
~Element(element) => { ~dom::Element(element) => {
let style = self.box.node.get_specified_style();
self.box.appearance.background_color = match style.background_color {
Some(col) => col,
None => node.kind.default_color()
};
match element.kind { match element.kind {
~HTMLImageElement(*) => { ~dom::HTMLImageElement(*) => {
let url = element.get_attr(~"src"); let url = element.get_attr(~"src");
if url.is_some() { if url.is_some() {
@ -192,7 +194,7 @@ mod test {
let g1_box = child_box.tree.first_child.get(); let g1_box = child_box.tree.first_child.get();
let g2_box = child_box.tree.last_child.get(); let g2_box = child_box.tree.last_child.get();
top_down_traversal(parent_box.get(), inhereit_height); top_down_traversal(parent_box.get(), inherit_height);
assert parent_box.get().appearance.height == Px(100.0); assert parent_box.get().appearance.height == Px(100.0);
assert child_box.appearance.height == Auto; assert child_box.appearance.height == Auto;

View file

@ -4,9 +4,7 @@ import dom::base::{LayoutData};
import dom::base; import dom::base;
import base::{ElementData, Node, Text}; import base::{ElementData, Node, Text};
import values::{Selector, StyleDeclaration, FontSize, Display, TextColor, BackgroundColor, import values::*;
Stylesheet, Element, Child, Descendant, Sibling, Attr, Exact, Exists, Includes,
StartsWith, Width, Height};
import styles::{SpecifiedStyle}; import styles::{SpecifiedStyle};
#[doc="Check if a CSS attribute matches the attribute of an HTML element."] #[doc="Check if a CSS attribute matches the attribute of an HTML element."]
@ -169,12 +167,12 @@ impl Node : PrivStyleMethods {
fn update_style(decl : StyleDeclaration) { fn update_style(decl : StyleDeclaration) {
self.aux(|layout| { self.aux(|layout| {
match decl { match decl {
BackgroundColor(col) => layout.specified_style.background_color = Some(col), BackgroundColor(col) => layout.specified_style.background_color = col,
Display(dis) => layout.specified_style.display_type = Some(dis), Display(dis) => layout.specified_style.display_type = dis,
FontSize(size) => layout.specified_style.font_size = Some(size), FontSize(size) => layout.specified_style.font_size = size,
Height(size) => layout.specified_style.height = Some(size), Height(size) => layout.specified_style.height = size,
TextColor(col) => layout.specified_style.text_color = Some(col), Color(col) => layout.specified_style.text_color = col,
Width(size) => layout.specified_style.width = Some(size) Width(size) => layout.specified_style.width = size
}; };
}) })
} }

View file

@ -2,7 +2,7 @@
import std::arc::{ARC, get, clone}; import std::arc::{ARC, get, clone};
import css::values::{DisplayType, DisplayNone, Inline, Block, Unit, Auto}; import css::values::*;
import css::values::Stylesheet; import css::values::Stylesheet;
import dom::base::{HTMLDivElement, HTMLHeadElement, HTMLImageElement, UnknownElement, HTMLScriptElement}; import dom::base::{HTMLDivElement, HTMLHeadElement, HTMLImageElement, UnknownElement, HTMLScriptElement};
import dom::base::{Comment, Doctype, Element, Node, NodeKind, Text}; import dom::base::{Comment, Doctype, Element, Node, NodeKind, Text};
@ -10,19 +10,19 @@ import util::color::{Color, rgb};
import util::color::css_colors::{white, black}; import util::color::css_colors::{white, black};
import layout::base::{LayoutData, NTree}; import layout::base::{LayoutData, NTree};
type SpecifiedStyle = {mut background_color : Option<Color>, type SpecifiedStyle = {mut background_color : CSSValue<CSSBackgroundColor>,
mut display_type : Option<DisplayType>, mut display_type : CSSValue<CSSDisplay>,
mut font_size : Option<Unit>, mut font_size : CSSValue<CSSFontSize>,
mut height : Option<Unit>, mut height : CSSValue<BoxSizing>,
mut text_color : Option<Color>, mut text_color : CSSValue<CSSColor>,
mut width : Option<Unit> mut width : CSSValue<BoxSizing>
}; };
trait DefaultStyleMethods { trait DefaultStyleMethods {
fn default_color() -> Color; fn default_color() -> Color;
fn default_display_type() -> DisplayType; fn default_display_type() -> CSSDisplay;
fn default_width() -> Unit; fn default_width() -> BoxSizing;
fn default_height() -> Unit; fn default_height() -> BoxSizing;
} }
/// Default styles for various attributes in case they don't get initialized from CSS selectors. /// Default styles for various attributes in case they don't get initialized from CSS selectors.
@ -35,28 +35,28 @@ impl NodeKind : DefaultStyleMethods {
} }
} }
fn default_display_type() -> DisplayType { fn default_display_type() -> CSSDisplay {
match self { match self {
Text(*) => { Inline } Text(*) => { DisplayInline }
Element(element) => { Element(element) => {
match *element.kind { match *element.kind {
HTMLDivElement => Block, HTMLDivElement => DisplayBlock,
HTMLHeadElement => DisplayNone, HTMLHeadElement => DisplayNone,
HTMLImageElement(*) => Inline, HTMLImageElement(*) => DisplayInline,
HTMLScriptElement => DisplayNone, HTMLScriptElement => DisplayNone,
UnknownElement => Inline, UnknownElement => DisplayInline,
} }
}, },
Comment(*) | Doctype(*) => DisplayNone Comment(*) | Doctype(*) => DisplayNone
} }
} }
fn default_width() -> Unit { fn default_width() -> BoxSizing {
Auto BoxAuto
} }
fn default_height() -> Unit { fn default_height() -> BoxSizing {
Auto BoxAuto
} }
} }
@ -70,12 +70,12 @@ impl NodeKind : DefaultStyleMethods {
fn empty_style_for_node_kind(kind: NodeKind) -> SpecifiedStyle { fn empty_style_for_node_kind(kind: NodeKind) -> SpecifiedStyle {
let display_type = kind.default_display_type(); let display_type = kind.default_display_type();
{mut background_color : None, {mut background_color : Initial,
mut display_type : Some(display_type), mut display_type : Specified(display_type),
mut font_size : None, mut font_size : Initial,
mut height : None, mut height : Initial,
mut text_color : None, mut text_color : Initial,
mut width : None} mut width : Initial}
} }
trait StylePriv { trait StylePriv {

View file

@ -1,4 +1,5 @@
import util::color::Color; use SharedColor = util::color::Color;
use cmp::Eq;
#[doc = " #[doc = "
Defines how css rules, both selectors and style specifications, are Defines how css rules, both selectors and style specifications, are
@ -6,39 +7,156 @@ import util::color::Color;
http://www.w3.org/TR/CSS2/selector.html are represented by nested types. http://www.w3.org/TR/CSS2/selector.html are represented by nested types.
"] "]
enum DisplayType { // CSS Units
Inline,
Block, enum ParseResult<T> {
ListItem, Value(T),
InlineBlock, CSSInitial,
Table, CSSInherit,
InlineTable, Fail
TableRowGroup, }
TableHeaderGroup,
TableFooterGroup, enum CSSValue<T : copy> {
TableRow, Specified(T),
TableColumnGroup, Initial,
TableColumn, Inherit
TableCell, }
TableCaption,
impl<T : copy> ParseResult<T> {
pure fn extract<U>(f: fn(CSSValue<T>) -> U) -> Option<U> { extract(self, f) }
}
pure fn extract<T : copy, U>(res: ParseResult<T>, f: fn(CSSValue<T>) -> U) -> Option<U> {
match res {
Fail => None,
CSSInitial => Some(f(Initial)),
CSSInherit => Some(f(Inherit)),
Value(x) => Some(f(Specified(x)))
}
}
impl<T: Eq copy> CSSValue<T> : Eq {
pure fn eq(&&other: CSSValue<T>) -> bool {
match (self, other) {
(Initial, Initial) => true,
(Inherit, Inherit) => true,
(Specified(a), Specified(b)) => a == b,
_ => false
}
}
}
enum Auto = ();
enum Length {
Em(float), // normalized to 'em'
Px(float) // normalized to 'px'
}
impl Length {
pure fn rel() -> float {
match self {
Em(x) => x,
_ => fail ~"attempted to access relative unit of an absolute length"
}
}
pure fn abs() -> float {
match self {
Em(x) => x,
_ => fail ~"attempted to access relative unit of an absolute length"
}
}
}
enum BoxSizing { // used by width, height, top, left, etc
BoxLength(Length),
BoxPercent(float),
BoxAuto
}
enum AbsoluteSize {
XXSmall,
XSmall,
Small,
Medium,
Large,
XLarge,
XXLarge
}
enum RelativeSize {
Larger,
Smaller
}
// CSS property values
enum CSSBackgroundAttachment {
BgAttachScroll,
BgAttachFixed
}
enum CSSBackgroundColor {
BgColor(SharedColor),
BgTransparent
}
enum CSSBackgroundRepeat {
BgRepeat,
BgRepeatX,
BgRepeatY,
BgNoRepeat
}
enum CSSColor {
TextColor(SharedColor)
}
enum CSSDirection {
DirectionLtr,
DirectionRtl
}
enum CSSDisplay {
DisplayInline,
DisplayBlock,
DisplayListItem,
DisplayInlineBlock,
DisplayTable,
DisplayInlineTable,
DisplayTableRowGroup,
DisplayTableHeaderGroup,
DisplayTableFooterGroup,
DisplayTableRow,
DisplayTableColumnGroup,
DisplayTableColumn,
DisplayTableCell,
DisplayTableCaption,
DisplayNone DisplayNone
} }
enum Unit { enum CSSFloat {
Auto, FloatLeft,
Percent(float), FloatRight,
Mm(float), FloatNone
Pt(float),
Px(float)
} }
enum CSSFontSize {
AbsoluteSize(AbsoluteSize),
RelativeSize(RelativeSize),
LengthSize(Length),
PercentSize(float)
}
// Stylesheet parts
enum StyleDeclaration { enum StyleDeclaration {
BackgroundColor(Color), BackgroundColor(CSSValue<CSSBackgroundColor>),
Display(DisplayType), Display(CSSValue<CSSDisplay>),
FontSize(Unit), FontSize(CSSValue<CSSFontSize>),
Height(Unit), Height(CSSValue<BoxSizing>),
TextColor(Color), Color(CSSValue<CSSColor>),
Width(Unit) Width(CSSValue<BoxSizing>)
} }
enum Attr{ enum Attr{
@ -59,45 +177,80 @@ type Rule = (~[~Selector], ~[StyleDeclaration]);
type Stylesheet = ~[~Rule]; type Stylesheet = ~[~Rule];
#[doc="Convert between units measured in millimeteres and pixels"]
pure fn MmToPx(u : Unit) -> Unit {
match u {
Mm(m) => Px(m * 3.7795),
_ => fail ~"Calling MmToPx on a unit that is not a Mm"
}
}
#[doc="Convert between units measured in points and pixels"] impl Length: cmp::Eq {
pure fn PtToPx(u : Unit) -> Unit { pure fn eq(&&other: Length) -> bool {
match u {
Pt(m) => Px(m * 1.3333),
_ => fail ~"Calling PtToPx on a unit that is not a Pt"
}
}
impl DisplayType: cmp::Eq {
pure fn eq(&&other: DisplayType) -> bool {
self as uint == other as uint
}
}
impl Unit: cmp::Eq {
pure fn eq(&&other: Unit) -> bool {
match (self, other) { match (self, other) {
(Auto, Auto) => true, (Em(a), Em(b)) => a == b,
(Auto, _) => false,
(Percent(a), Percent(b)) => a == b,
(Percent(*), _) => false,
(Mm(a), Mm(b)) => a == b,
(Mm(*), _) => false,
(Pt(a), Pt(b)) => a == b,
(Pt(*), _) => false,
(Px(a), Px(b)) => a == b, (Px(a), Px(b)) => a == b,
(Px(*), _) => false (_, _) => false
} }
} }
} }
impl BoxSizing: cmp::Eq {
pure fn eq(&&other: BoxSizing) -> bool {
match (self, other) {
(BoxLength(a), BoxLength(b)) => a == b,
(BoxPercent(a), BoxPercent(b)) => a == b,
(BoxAuto, BoxAuto) => true,
(_, _) => false
}
}
}
impl AbsoluteSize: cmp::Eq {
pure fn eq(&&other: AbsoluteSize) -> bool {
self as uint == other as uint
}
}
impl RelativeSize: cmp::Eq {
pure fn eq(&&other: RelativeSize) -> bool {
self as uint == other as uint
}
}
impl CSSBackgroundColor: cmp::Eq {
pure fn eq(&&other: CSSBackgroundColor) -> bool {
match (self, other) {
(BgColor(a), BgColor(b)) => a == b,
(BgTransparent, BgTransparent) => true,
(_, _) => false
}
}
}
impl CSSColor: cmp::Eq {
pure fn eq(&&other: CSSColor) -> bool {
match (self, other) {
(TextColor(a), TextColor(b)) => a == b
}
}
}
impl CSSDisplay: cmp::Eq {
pure fn eq(&&other: CSSDisplay) -> bool {
self as uint == other as uint
}
}
impl CSSFontSize: cmp::Eq {
pure fn eq(&&other: CSSFontSize) -> bool {
match (self, other) {
(AbsoluteSize(a), AbsoluteSize(b)) => a == b,
(RelativeSize(a), RelativeSize(b)) => a == b,
(LengthSize(a), LengthSize(b)) => a == b,
(PercentSize(a), PercentSize(b)) => a == b,
(_, _) => false
}
}
}
/*
impl StyleDeclaration: cmp::Eq { impl StyleDeclaration: cmp::Eq {
pure fn eq(&&other: StyleDeclaration) -> bool { pure fn eq(&&other: StyleDeclaration) -> bool {
match (self, other) { match (self, other) {
@ -105,18 +258,18 @@ impl StyleDeclaration: cmp::Eq {
(Display(a), Display(b)) => a == b, (Display(a), Display(b)) => a == b,
(FontSize(a), FontSize(b)) => a == b, (FontSize(a), FontSize(b)) => a == b,
(Height(a), Height(b)) => a == b, (Height(a), Height(b)) => a == b,
(TextColor(a), TextColor(b)) => a == b, (Color(a), Color(b)) => a == b,
(Width(a), Width(b)) => a == b, (Width(a), Width(b)) => a == b,
(BackgroundColor(*), _) (BackgroundColor(*), _)
| (Display(*), _) | (Display(*), _)
| (FontSize(*), _) | (FontSize(*), _)
| (Height(*), _) | (Height(*), _)
| (TextColor(*), _) | (Color(*), _)
| (Width(*), _) => false | (Width(*), _) => false
} }
} }
} }*/
impl Attr: cmp::Eq { impl Attr: cmp::Eq {
pure fn eq(&&other: Attr) -> bool { pure fn eq(&&other: Attr) -> bool {

View file

@ -1,7 +1,7 @@
#[doc="Fundamental layout structures and algorithms."] #[doc="Fundamental layout structures and algorithms."]
import css::values::Unit;
import css::styles::SpecifiedStyle; import css::styles::SpecifiedStyle;
import css::values::{BoxSizing, Length, Px};
import dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement, Node, NodeData}; import dom::base::{Element, ElementKind, HTMLDivElement, HTMLImageElement, Node, NodeData};
import dom::base::{NodeKind}; import dom::base::{NodeKind};
import dom::rcu; import dom::rcu;
@ -42,13 +42,16 @@ impl BoxKind : cmp::Eq {
struct Appearance { struct Appearance {
let mut background_image: Option<ImageHolder>; let mut background_image: Option<ImageHolder>;
let mut background_color: Color; // TODO: create some sort of layout-specific enum to differentiate between
let mut width: Unit; // relative and resolved values.
let mut height: Unit; let mut width: BoxSizing;
let mut height: BoxSizing;
let mut font_size: Length;
new(kind: NodeKind) { new(kind: NodeKind) {
// TODO: these should come from initial() or elsewhere
self.font_size = Px(14.0);
self.background_image = None; self.background_image = None;
self.background_color = kind.default_color();
self.width = kind.default_width(); self.width = kind.default_width();
self.height = kind.default_height(); self.height = kind.default_height();
} }

View file

@ -1,6 +1,6 @@
#[doc="Block layout."] #[doc="Block layout."]
import css::values::{Px, Mm, Pt, Auto, Percent, Unit}; import css::values::*;
import geom::point::Point2D; import geom::point::Point2D;
import geom::size::Size2D; import geom::size::Size2D;
import gfx::geometry::{px_to_au, au}; import gfx::geometry::{px_to_au, au};
@ -37,15 +37,15 @@ impl @Box : BlockLayoutMethods {
} }
let height = match self.appearance.height { let height = match self.appearance.height {
Px(p) => px_to_au(p.to_int()), BoxLength(Px(p)) => px_to_au(p.to_int()),
Auto => au(current_height), BoxAuto => au(current_height),
_ => fail ~"inhereit_height failed, height is neither a Px or auto" _ => fail ~"inhereit_height failed, height is neither a Px or auto"
}; };
// FIXME: Width is wrong in the calculation below. // FIXME: Width is wrong in the calculation below.
let width = match self.appearance.width { let width = match self.appearance.width {
Px(p) => px_to_au(p.to_int()), BoxLength(Px(p)) => px_to_au(p.to_int()),
Auto => self.bounds.size.width, // Do nothing here, width was set by top-down pass BoxAuto => self.bounds.size.width, // Do nothing here, width was set by top-down pass
_ => fail ~"inhereit_width failed, width is neither a Px or auto" _ => fail ~"inhereit_width failed, width is neither a Px or auto"
}; };

View file

@ -1,6 +1,6 @@
#[doc="Creates CSS boxes from a DOM."] #[doc="Creates CSS boxes from a DOM."]
import css::values::{DisplayType, Block, Inline, DisplayNone}; import css::values::{CSSDisplay, DisplayBlock, DisplayInline, DisplayNone, Specified};
import dom::base::{ElementData, HTMLDivElement, HTMLImageElement, Element, Text, Node, Doctype, Comment}; import dom::base::{ElementData, HTMLDivElement, HTMLImageElement, Element, Text, Node, Doctype, Comment};
import gfx::geometry::zero_size_au; import gfx::geometry::zero_size_au;
import layout::base::{Appearance, BTree, BlockBox, Box, BoxKind, InlineBox, IntrinsicBox, NTree}; import layout::base::{Appearance, BTree, BlockBox, Box, BoxKind, InlineBox, IntrinsicBox, NTree};
@ -49,14 +49,14 @@ impl ctxt {
// Determine the child's display. // Determine the child's display.
let disp = kid.get_specified_style().display_type; let disp = kid.get_specified_style().display_type;
if disp != Some(Inline) { if disp != Specified(DisplayInline) {
self.finish_anonymous_box_if_necessary(); self.finish_anonymous_box_if_necessary();
} }
// Add the child's box to the current enclosing box or the current anonymous box. // Add the child's box to the current enclosing box or the current anonymous box.
match kid.get_specified_style().display_type { match kid.get_specified_style().display_type {
Some(Block) => BTree.add_child(self.parent_box, kid_box.get()), Specified(DisplayBlock) => BTree.add_child(self.parent_box, kid_box.get()),
Some(Inline) => { Specified(DisplayInline) => {
let anon_box = match self.anon_box { let anon_box = match self.anon_box {
None => { None => {
// //
@ -75,7 +75,7 @@ impl ctxt {
}; };
BTree.add_child(anon_box, kid_box.get()); BTree.add_child(anon_box, kid_box.get());
} }
Some(DisplayNone) => { Specified(DisplayNone) => {
// Nothing to do. // Nothing to do.
} }
_ => { //hack for now _ => { //hack for now
@ -96,21 +96,21 @@ impl ctxt {
// Determine the child's display. // Determine the child's display.
let disp = kid.get_specified_style().display_type; let disp = kid.get_specified_style().display_type;
if disp != Some(Inline) { if disp != Specified(DisplayInline) {
// TODO // TODO
} }
// Add the child's box to the current enclosing box. // Add the child's box to the current enclosing box.
match kid.get_specified_style().display_type { match kid.get_specified_style().display_type {
Some(Block) => { Specified(DisplayBlock) => {
// TODO // TODO
#warn("TODO: non-inline display found inside inline box"); #warn("TODO: non-inline display found inside inline box");
BTree.add_child(self.parent_box, kid_box.get()); BTree.add_child(self.parent_box, kid_box.get());
} }
Some(Inline) => { Specified(DisplayInline) => {
BTree.add_child(self.parent_box, kid_box.get()); BTree.add_child(self.parent_box, kid_box.get());
} }
Some(DisplayNone) => { Specified(DisplayNone) => {
// Nothing to do. // Nothing to do.
} }
_ => { //hack for now _ => { //hack for now
@ -125,9 +125,9 @@ impl ctxt {
self.parent_node.dump(); self.parent_node.dump();
match self.parent_node.get_specified_style().display_type { match self.parent_node.get_specified_style().display_type {
Some(Block) => self.construct_boxes_for_block_children(), Specified(DisplayBlock) => self.construct_boxes_for_block_children(),
Some(Inline) => self.construct_boxes_for_inline_children(), Specified(DisplayInline) => self.construct_boxes_for_inline_children(),
Some(DisplayNone) => { /* Nothing to do. */ } Specified(DisplayNone) => { /* Nothing to do. */ }
_ => { //hack for now _ => { //hack for now
} }
} }
@ -164,11 +164,11 @@ impl Node : PrivBoxBuilder {
~Element(element) => { ~Element(element) => {
match (copy *element.kind, self.get_specified_style().display_type) { match (copy *element.kind, self.get_specified_style().display_type) {
(HTMLImageElement({size}), _) => Some(IntrinsicBox(@size)), (HTMLImageElement({size}), _) => Some(IntrinsicBox(@size)),
(_, Some(Block)) => Some(BlockBox), (_, Specified(DisplayBlock)) => Some(BlockBox),
(_, Some(Inline)) => Some(InlineBox), (_, Specified(DisplayInline)) => Some(InlineBox),
(_, Some(DisplayNone)) => None, (_, Specified(DisplayNone)) => None,
(_, Some(_)) => Some(InlineBox), (_, Specified(_)) => Some(InlineBox),
(_, None) => { (_, _) => {
fail ~"The specified display style should be a default instead of none" fail ~"The specified display style should be a default instead of none"
} }
} }

View file

@ -1,5 +1,6 @@
export build_display_list; export build_display_list;
import css::values::{BgColor, BgTransparent, Specified};
import base::{Box, BTree, ImageHolder, TextBoxKind}; import base::{Box, BTree, ImageHolder, TextBoxKind};
import dl = display_list; import dl = display_list;
import dom::base::{Text, NodeScope}; import dom::base::{Text, NodeScope};
@ -65,7 +66,6 @@ fn box_to_display_items(list: dl::display_list, box: @Box, origin: Point2D<au>)
#debug("request to display a box from origin %?", origin); #debug("request to display a box from origin %?", origin);
let bounds = Rect(origin, copy box.bounds.size); let bounds = Rect(origin, copy box.bounds.size);
let col = box.appearance.background_color;
match box.kind { match box.kind {
TextBoxKind(subbox) => { TextBoxKind(subbox) => {
@ -96,10 +96,16 @@ fn box_to_display_items(list: dl::display_list, box: @Box, origin: Point2D<au>)
}); });
list.push(display_item); list.push(display_item);
} else { } else {
#debug("Assigning color %? to box with bounds %?", col, bounds); // DAC
let col = box.appearance.background_color; // TODO: shouldn't need to unbox CSSValue by now
let boxed_color = box.node.get_specified_style().background_color;
let color = match boxed_color {
Specified(BgColor(c)) => c,
Specified(BgTransparent) | _ => util::color::rgba(0,0,0,0.0)
};
#debug("Assigning color %? to box with bounds %?", color, bounds);
list.push(dl::display_item({ list.push(dl::display_item({
item_type: dl::display_item_solid_color(col.red, col.green, col.blue), item_type: dl::display_item_solid_color(color.red, color.green, color.blue),
bounds: bounds bounds: bounds
})); }));
} }

View file

@ -1,7 +1,7 @@
#[doc="Inline layout."] #[doc="Inline layout."]
import base::{Box, InlineBox, BTree}; import base::{Box, InlineBox, BTree};
import css::values::{Auto, Px}; import css::values::{BoxAuto, BoxLength, Px};
import dom::rcu; import dom::rcu;
import geom::point::Point2D; import geom::point::Point2D;
import geom::size::Size2D; import geom::size::Size2D;
@ -34,14 +34,14 @@ impl @Box : InlineLayout {
} }
let height = match self.appearance.height { let height = match self.appearance.height {
Px(p) => px_to_au(p.to_int()), BoxLength(Px(p)) => px_to_au(p.to_int()),
Auto => au(current_height), BoxAuto => au(current_height),
_ => fail ~"inhereit_height failed, height is neither a Px or auto" _ => fail ~"inhereit_height failed, height is neither a Px or auto"
}; };
let width = match self.appearance.width { let width = match self.appearance.width {
Px(p) => px_to_au(p.to_int()), BoxLength(Px(p)) => px_to_au(p.to_int()),
Auto => au(i32::max(x, *self.bounds.size.width)), BoxAuto => au(i32::max(x, *self.bounds.size.width)),
_ => fail ~"inhereit_width failed, width is neither a Px or auto" _ => fail ~"inhereit_width failed, width is neither a Px or auto"
}; };