added layout
This commit is contained in:
10
src/css.rs
10
src/css.rs
@@ -81,6 +81,16 @@ pub enum Value {
|
||||
ColorValue(Color),
|
||||
}
|
||||
|
||||
impl Value {
|
||||
pub fn to_px(&self) -> f32 {
|
||||
match *self {
|
||||
Value::Length(f, Unit::Px) => f,
|
||||
// TODO: other lengths
|
||||
_ => 0.0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::fmt::Display for Value {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
match *self {
|
||||
|
215
src/layout.rs
Normal file
215
src/layout.rs
Normal file
@@ -0,0 +1,215 @@
|
||||
struct Rect {
|
||||
x: f32,
|
||||
y: f32,
|
||||
width: f32,
|
||||
height: f32,
|
||||
}
|
||||
|
||||
impl Rect {
|
||||
fn expanded_by(self, edge: Edge) -> Rect {
|
||||
Rect {
|
||||
x: self.x - edge.left,
|
||||
y: self.y - edge.top,
|
||||
width: self.width + edge.left + edge.right,
|
||||
height: self.height + edge.top + edge.bottom,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct Edge {
|
||||
left: f32,
|
||||
right: f32,
|
||||
top: f32,
|
||||
bottom: f32,
|
||||
}
|
||||
|
||||
impl Dimensions {
|
||||
fn padding_box(self) -> Rect {
|
||||
self.content.expanded_by(self.padding)
|
||||
}
|
||||
fn border_box(self) -> Rect {
|
||||
self.padding_box().expanded_by(self.border)
|
||||
}
|
||||
fn margin_box(self) -> Rect {
|
||||
self.border_box().expanded_by(self.margin)
|
||||
}
|
||||
}
|
||||
|
||||
struct Dim {
|
||||
content: Rect,
|
||||
|
||||
padding: Edge,
|
||||
border: Edge,
|
||||
margin: Edge,
|
||||
}
|
||||
|
||||
enum BoxType<'a> {
|
||||
Block(&'a StyledNode<'a>),
|
||||
Inline(&'a StyledNode<'a>),
|
||||
Anonymous
|
||||
}
|
||||
|
||||
struct LayoutBox<'a> {
|
||||
dimensions: Dim,
|
||||
btype: BoxType<'a>,
|
||||
children: Vec<LayoutBox<'a>>,
|
||||
}
|
||||
|
||||
impl LayoutBox {
|
||||
fn new(btype: BoxType) -> LayoutBox {
|
||||
LayoutBox {
|
||||
btype: btype,
|
||||
dimensions: Default::default(),
|
||||
children: Vec::new(),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_inline_container(&mut self) -> &mut LayoutBox {
|
||||
match self.btype {
|
||||
Inline(_) | Anonymous => self,
|
||||
Block(_) => {
|
||||
match self.children.last() {
|
||||
Some(&LayoutBox { btype: Anonymous,..}) => {}
|
||||
_ => self.children.push(LayoutBox::new(Anonymous))
|
||||
}
|
||||
self.children.last_mut().unwrap()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn layout(&mut self, containing: Dim) {
|
||||
match self.btype {
|
||||
Block(_) => self.layout_block(containing),
|
||||
Inline(_) => {} // TODO
|
||||
Anonymous => {} // TODO
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_block(&mut self, containing: Dim) {
|
||||
self.calculate_block_width(containing);
|
||||
|
||||
self.calculate_block_position(containing);
|
||||
|
||||
self.layout_block_children();
|
||||
|
||||
self.calculate_block_height();
|
||||
}
|
||||
|
||||
fn calculate_block_width(&mut self, containing: Dim) {
|
||||
let style = self.get_style_node();
|
||||
|
||||
let auto = Keyword("auto".to_string());
|
||||
let mut width = style.value("width").unwrap_or(auto.clone());
|
||||
|
||||
let zero = Length(0.0, Px);
|
||||
|
||||
let mut margin_left = style.lookup("margin-left", "margin", &zero);
|
||||
let mut margin_right = style.lookup("margin-right", "margin", &zero);
|
||||
|
||||
let border_left = style.lookup("border-left-width", "border-width", &zero);
|
||||
let border_right = style.lookup("border-right-width", "border-width", &zero);
|
||||
|
||||
let padding_left = style.lookup("padding-left", "padding", &zero);
|
||||
let padding_right = style.lookup("padding-right", "padding", &zero);
|
||||
|
||||
let total = [&margin_left, &margin_right, &border_left, &border_right,
|
||||
&padding_left, &padding_right, &width
|
||||
].iter().map(|v| v.to_px()).sum();
|
||||
|
||||
if width != auto && total > containing.content.width {
|
||||
if margin_left == auto {
|
||||
margin_left = Length(0.0, Px);
|
||||
}
|
||||
if margin_right == auto {
|
||||
margin_right = Length(0.0, Px);
|
||||
}
|
||||
}
|
||||
|
||||
let underflow = containing.content.width - total;
|
||||
|
||||
match (width == auto, margin_left == auto, margin_right == auto) {
|
||||
(false, false, false) => {
|
||||
margin_right = Length(margin_right.to_px() + underflow, Px);
|
||||
}
|
||||
|
||||
(false, false, true) => { margin_right = Length(underflow, Px); }
|
||||
(false, true, false) => { margin_left = Length(underflow, Px); }
|
||||
|
||||
(true, _, _) => {
|
||||
if margin_left == auto { margin_left = Length(0.0, Px); }
|
||||
if margin_right == auto { margin_right = Length(0.0, Px); }
|
||||
|
||||
if underflow >= 0.0 {
|
||||
width = Length(underflow, Px);
|
||||
} else {
|
||||
width = Length(0.0, Px);
|
||||
margin_right = Length(margin_right.to_px() + underflow, Px);
|
||||
}
|
||||
}
|
||||
|
||||
(false, true, true) => {
|
||||
margin_left = Length(underflow / 2.0, Px);
|
||||
margin_right = Length(underflow / 2.0, Px);
|
||||
}
|
||||
}
|
||||
|
||||
let d = &mut self.dimensions;
|
||||
d.content.width = width.to_px();
|
||||
|
||||
d.padding.left = padding_left.to_px();
|
||||
d.padding.right = padding_right.to_px();
|
||||
|
||||
d.border.left = border_left.to_px();
|
||||
d.border.right = border_right.to_px();
|
||||
|
||||
d.margin.left = margin_left.to_px();
|
||||
d.margin.right = margin_right.to_px();
|
||||
}
|
||||
|
||||
fn calculate_block_position(&mut self, containing: Dim) {
|
||||
let style = self.get_style_node();
|
||||
let d = &mut self.dimensions;
|
||||
|
||||
let zero = Length(0.0, Px);
|
||||
|
||||
d.margin.top = style.lookup("margin-top", "margin", &zero).to_px();
|
||||
d.margin.bottom = style.lookup("margin-bottom", "margin", &zero).to_px();
|
||||
|
||||
d.border.top = style.lookup("border-top-width", "border-width", &zero).to_px();
|
||||
d.border.bottom = style.lookup("border-bottom-width", "border-width", &zero).to_px();
|
||||
|
||||
d.padding.top = style.lookup("padding-top", "padding", &zero).to_px();
|
||||
d.padding.bottom = style.lookup("padding-bottom", "padding", &zero).to_px();
|
||||
|
||||
d.content.x = containing.content.x +
|
||||
d.margin.left + d.border.left + d.padding.left;
|
||||
|
||||
d.content.y = containing.content.height + containing.content.y +
|
||||
d.margin.top + d.border.top + d.padding.top;
|
||||
}
|
||||
|
||||
fn layout_block_children(&mut self) {
|
||||
let d = &mut self.dimensions;
|
||||
for child in &mut self.children {
|
||||
child.layout(*d);
|
||||
d.content.height = d.content.height + child.dimensions.margin_box().height;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn build_layout_tree<'a>(style_node: &'a StyledNode<'a>) -> LayoutBox<'a> {
|
||||
let mut root = LayoutBox::new(match style_node.display() {
|
||||
css::Display::Block => Block(style_node),
|
||||
css::Display::Inline => Inline(style_node),
|
||||
css::Display::DisplayNone => panic!("Root node has display: none.")
|
||||
});
|
||||
|
||||
for child in &style_node.children {
|
||||
match child.display() {
|
||||
css::Display::Block => root.children.push(build_layout_tree(child)),
|
||||
css::Display::Inline => root.get_inline_container().children.push(build_layout_tree(child)),
|
||||
css::Display::DisplayNone => {}
|
||||
}
|
||||
}
|
||||
return root;
|
||||
}
|
@@ -11,6 +11,29 @@ pub struct Node<'a> {
|
||||
children: Vec<Node<'a>>,
|
||||
}
|
||||
|
||||
enum Display {
|
||||
Inline,
|
||||
Block,
|
||||
None,
|
||||
}
|
||||
|
||||
impl<'a> Node<'a> {
|
||||
fn value(&self, name: &str) -> Option<css::Value> {
|
||||
self.values.get(name).map(|v| v.clone())
|
||||
}
|
||||
|
||||
fn display(&self) -> Display {
|
||||
match self.value("display") {
|
||||
Some(css::Value::Keyword(s)) => match &*s {
|
||||
"block" => Display::Block,
|
||||
"none" => Display::None,
|
||||
_ => Display::Inline
|
||||
},
|
||||
_ => Display::Inline
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn matches(elem: &dom::EData, selector: &css::Selector) -> bool {
|
||||
match *selector {
|
||||
css::Selector::Simple(ref s) => matches_simple(elem, s)
|
||||
|
Reference in New Issue
Block a user