accessibility_rs/engine/styles/
layout.rs

1use accessibility_scraper::ElementRef;
2use accessibility_scraper::Html;
3use accessibility_tree::style::values::LengthOrPercentageOrAuto;
4use accessibility_tree::style::ComputedValues;
5use accessibility_tree::style::StyleSet;
6use ego_tree::NodeRef;
7use std::collections::HashSet;
8use std::sync::Arc;
9use taffy::prelude::*;
10use taffy::style::Dimension;
11
12lazy_static! {
13    static ref NODE_IGNORE: HashSet<&'static str> =
14        HashSet::from(["meta", "style", "link", "script", "head", "html", "body"]);
15}
16
17/// length to taffy dimensions
18pub fn length_dimensions(v: &LengthOrPercentageOrAuto) -> Dimension {
19    match v {
20        LengthOrPercentageOrAuto::Length(l) => Dimension::Length(l.px),
21        LengthOrPercentageOrAuto::Percentage(l) => Dimension::Percent(l.unit_value),
22        LengthOrPercentageOrAuto::Auto => Dimension::Auto,
23    }
24}
25
26/// layout style
27pub fn node_layout_style(style: Arc<ComputedValues>, element: &ElementRef) -> Style {
28    let physical_size = style.box_size().size_to_physical(style.writing_mode());
29    let mut size = Size {
30        width: length_dimensions(&physical_size.x),
31        height: length_dimensions(&physical_size.y),
32    };
33
34    // get the img raw height/width
35    if element.value().name() == "img" {
36        let width = element.attr("width");
37        let height = element.attr("height");
38        if physical_size.x.inner_px() == 0.0 {
39            match width {
40                Some(w) => {
41                    let w = w.parse::<f32>();
42                    match w {
43                        Ok(w) => {
44                            size.width = length(w);
45                        }
46                        _ => (),
47                    }
48                }
49                _ => (),
50            }
51        }
52        if physical_size.y.inner_px() == 0.0 {
53            match height {
54                Some(h) => {
55                    let h = h.parse::<f32>();
56
57                    match h {
58                        Ok(h) => {
59                            size.height = length(h);
60                        }
61                        _ => (),
62                    }
63                }
64                _ => (),
65            }
66        }
67    }
68
69    // todo: determine if all children at the top level have floats set to use flex-row
70    Style {
71        size,
72        border: length(style.border_width().inner_px()),
73        padding: length(style.padding().inner_px()),
74        margin: length(style.margin().inner_px()),
75        ..Default::default()
76    }
77}
78
79/// push leaf
80pub fn push_leaf<'a, 'b, 'c>(
81    node: &NodeRef<'_, accessibility_scraper::Node>,
82    author: &StyleSet,
83    document: &'a Html,
84    taffy: &mut TaffyTree,
85    l_leafs: &mut Vec<NodeId>,
86) {
87    match ElementRef::wrap(*node) {
88        Some(element) => {
89            let name = element.value().name();
90            if !NODE_IGNORE.contains(name) {
91                let style = accessibility_tree::style::cascade::style_for_element_ref(
92                    &element, &author, &document,
93                );
94
95                if node.has_children() {
96                    let children = node.children();
97                    let mut child_leafs: Vec<NodeId> = vec![];
98
99                    // iterate all children and push into one leaf
100                    for child in children {
101                        push_leaf(&child, author, document, taffy, &mut child_leafs);
102                    }
103
104                    l_leafs.push(
105                        taffy
106                            .new_with_children(node_layout_style(style, &element), &child_leafs)
107                            .unwrap(),
108                    );
109                } else {
110                    l_leafs.push(taffy.new_leaf(node_layout_style(style, &element)).unwrap());
111                }
112            }
113        }
114        _ => (),
115    }
116}
117
118/// get a layout leaf a new leaf
119pub fn leaf<'a, 'b, 'c>(
120    element: &ElementRef,
121    author: &StyleSet,
122    document: &'a Html,
123    taffy: &mut TaffyTree,
124) -> NodeId {
125    let mut l_leafs: Vec<NodeId> = vec![];
126    let mut children = element.children();
127
128    while let Some(child) = children.next() {
129        push_leaf(&child, author, document, taffy, &mut l_leafs);
130    }
131
132    let style =
133        accessibility_tree::style::cascade::style_for_element_ref(&element, &author, &document);
134
135    let leaf_style = node_layout_style(style, &element);
136
137    // build leaf with children
138    if l_leafs.len() > 0 {
139        taffy.new_with_children(leaf_style, &l_leafs)
140    } else {
141        taffy.new_leaf(leaf_style)
142    }
143    .unwrap()
144}