added layout

This commit is contained in:
2017-08-26 16:50:37 +02:00
parent e4966a6536
commit 3e91f8ed5b
3 changed files with 248 additions and 0 deletions

View File

@@ -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
View 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;
}

View File

@@ -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)