azul_layout/
lib.rs

1// MIT License
2//
3// Copyright (c) 2018 Visly Inc.
4//
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11//
12// The above copyright notice and this permission notice shall be included in all
13// copies or substantial portions of the Software.
14//
15// THE SOFTWARE IS PROVIDED "anon_nodes_direct_childrenAS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23extern crate azul_core;
24extern crate azul_css;
25#[cfg(feature = "text_layout")]
26pub extern crate azul_text_layout as text_layout;
27
28use std::collections::BTreeMap;
29use azul_css::LayoutRect;
30use azul_core::{
31    ui_solver::PositionedRectangle,
32    id_tree::{NodeHierarchy, NodeDepths, NodeDataContainer},
33    dom::NodeId,
34    display_list::DisplayRectangle,
35    traits::GetTextLayout,
36};
37
38mod anon;
39mod block;
40// mod flex;
41mod number;
42mod geometry;
43
44pub mod style;
45#[cfg(feature = "text_layout")]
46pub mod ui_solver;
47pub use crate::geometry::{Size, Offsets};
48pub use crate::number::Number;
49pub use crate::style::Style;
50
51pub trait GetStyle {
52    fn get_style(&self) -> Style;
53}
54
55#[derive(Debug, Clone, PartialEq, PartialOrd)]
56pub struct SolvedUi {
57    pub solved_rects: NodeDataContainer<PositionedRectangle>,
58}
59
60#[derive(Debug, Clone, PartialEq)]
61pub enum RectContent<T: GetTextLayout> {
62    // Returns the original (width, height) of the image
63    Image(usize, usize),
64    /// Gives access an anonymous struct which, given the text bounds,
65    /// can be used to calculate the text dimensions
66    Text(T),
67}
68
69impl<T: GetTextLayout> RectContent<T> {
70
71    pub fn is_text(&self) -> bool {
72        use self::RectContent::*;
73        match self {
74            Image(_, _) => false,
75            Text(_) => true,
76        }
77    }
78
79    pub fn is_image(&self) -> bool {
80        use self::RectContent::*;
81        match self {
82            Image(_, _) => true,
83            Text(_) => false,
84        }
85    }
86}
87
88impl SolvedUi {
89    pub fn new<T: GetStyle, U: GetTextLayout>(
90        bounds: LayoutRect,
91        node_hierarchy: &NodeHierarchy,
92        display_rects: &NodeDataContainer<T>,
93        rect_contents: &mut BTreeMap<NodeId, RectContent<U>>,
94        node_depths: &NodeDepths,
95    ) -> Self {
96
97        use crate::anon::AnonDom;
98
99        let styles = display_rects.transform(|node, node_id| Style {
100            aspect_ratio: match rect_contents.get(&node_id) {
101                Some(RectContent::Image(w, h)) => Number::Defined(*w as f32 / *h as f32),
102                _ => Number::Undefined,
103            },
104            .. node.get_style()
105        });
106
107        let anon_dom = AnonDom::new(
108            node_hierarchy,
109            &styles,
110            node_depths,
111            rect_contents,
112        );
113
114        // let solved_rects = flex::compute(NodeId::ZERO, node_hierarchy, &styles, rect_contents, bounds.size, node_depths);
115        let solved_rects = block::compute(
116            bounds.size,
117            &anon_dom,
118            rect_contents,
119        );
120
121        SolvedUi { solved_rects }
122    }
123}
124
125impl GetStyle for DisplayRectangle {
126
127    fn get_style(&self) -> Style {
128
129        use crate::style::*;
130        use azul_css::{
131            PixelValue, LayoutDisplay, LayoutDirection, LayoutWrap, LayoutPosition,
132            LayoutAlignItems, LayoutAlignContent, LayoutJustifyContent,
133            LayoutBoxSizing, Overflow as LayoutOverflow, CssPropertyValue,
134        };
135        use azul_core::ui_solver::DEFAULT_FONT_SIZE;
136
137        let rect_layout = &self.layout;
138        let rect_style = &self.style;
139
140        #[inline]
141        fn translate_dimension(input: Option<CssPropertyValue<PixelValue>>) -> Dimension {
142            use azul_css::{SizeMetric, EM_HEIGHT, PT_TO_PX};
143            match input {
144                None => Dimension::Undefined,
145                Some(CssPropertyValue::Auto) => Dimension::Auto,
146                Some(CssPropertyValue::None) => Dimension::Pixels(0.0),
147                Some(CssPropertyValue::Initial) => Dimension::Undefined,
148                Some(CssPropertyValue::Inherit) => Dimension::Undefined,
149                Some(CssPropertyValue::Exact(pixel_value)) => match pixel_value.metric {
150                    SizeMetric::Px => Dimension::Pixels(pixel_value.number.get()),
151                    SizeMetric::Percent => Dimension::Percent(pixel_value.number.get()),
152                    SizeMetric::Pt => Dimension::Pixels(pixel_value.number.get() * PT_TO_PX),
153                    SizeMetric::Em => Dimension::Pixels(pixel_value.number.get() * EM_HEIGHT),
154                }
155            }
156        }
157
158        Style {
159            display: match rect_layout.display {
160                None => Display::Block,
161                Some(CssPropertyValue::None) => Display::None,
162                Some(CssPropertyValue::Auto) => Display::Block,
163                Some(CssPropertyValue::Initial) => Display::Block,
164                Some(CssPropertyValue::Inherit) => Display::Block,
165                Some(CssPropertyValue::Exact(LayoutDisplay::Block)) => Display::Block,
166                Some(CssPropertyValue::Exact(LayoutDisplay::Flex)) => Display::Flex,
167                Some(CssPropertyValue::Exact(LayoutDisplay::InlineBlock)) => Display::InlineBlock,
168            },
169            box_sizing: match rect_layout.box_sizing.unwrap_or_default().get_property_or_default() {
170                None => BoxSizing::ContentBox,
171                Some(LayoutBoxSizing::ContentBox) => BoxSizing::ContentBox,
172                Some(LayoutBoxSizing::BorderBox) => BoxSizing::BorderBox,
173            },
174            position_type: match rect_layout.position.unwrap_or_default().get_property_or_default() {
175                Some(LayoutPosition::Static) => PositionType::Static,
176                Some(LayoutPosition::Relative) => PositionType::Relative,
177                Some(LayoutPosition::Absolute) => PositionType::Absolute,
178                Some(LayoutPosition::Fixed) => PositionType::Fixed,
179                None => PositionType::Static,
180            },
181            direction: Direction::LTR,
182            flex_direction: match rect_layout.direction.unwrap_or_default().get_property_or_default() {
183                Some(LayoutDirection::Row) => FlexDirection::Row,
184                Some(LayoutDirection::RowReverse) => FlexDirection::RowReverse,
185                Some(LayoutDirection::Column) => FlexDirection::Column,
186                Some(LayoutDirection::ColumnReverse) => FlexDirection::ColumnReverse,
187                None => FlexDirection::Row,
188            },
189            flex_wrap: match rect_layout.wrap.unwrap_or_default().get_property_or_default() {
190                Some(LayoutWrap::Wrap) => FlexWrap::Wrap,
191                Some(LayoutWrap::NoWrap) => FlexWrap::NoWrap,
192                None => FlexWrap::Wrap,
193            },
194            overflow: match rect_layout.overflow_x.unwrap_or_default().get_property_or_default() {
195                Some(LayoutOverflow::Scroll) => Overflow::Scroll,
196                Some(LayoutOverflow::Auto) => Overflow::Scroll,
197                Some(LayoutOverflow::Hidden) => Overflow::Hidden,
198                Some(LayoutOverflow::Visible) => Overflow::Visible,
199                None => Overflow::Scroll,
200            },
201            align_items: match rect_layout.align_items.unwrap_or_default().get_property_or_default() {
202                Some(LayoutAlignItems::Stretch) => AlignItems::Stretch,
203                Some(LayoutAlignItems::Center) => AlignItems::Center,
204                Some(LayoutAlignItems::Start) => AlignItems::FlexStart,
205                Some(LayoutAlignItems::End) => AlignItems::FlexEnd,
206                None => AlignItems::FlexStart,
207            },
208            align_content: match rect_layout.align_content.unwrap_or_default().get_property_or_default() {
209                Some(LayoutAlignContent::Stretch) => AlignContent::Stretch,
210                Some(LayoutAlignContent::Center) => AlignContent::Center,
211                Some(LayoutAlignContent::Start) => AlignContent::FlexStart,
212                Some(LayoutAlignContent::End) => AlignContent::FlexEnd,
213                Some(LayoutAlignContent::SpaceBetween) => AlignContent::SpaceBetween,
214                Some(LayoutAlignContent::SpaceAround) => AlignContent::SpaceAround,
215                None => AlignContent::Stretch,
216            },
217            justify_content: match rect_layout.justify_content.unwrap_or_default().get_property_or_default() {
218                Some(LayoutJustifyContent::Center) => JustifyContent::Center,
219                Some(LayoutJustifyContent::Start) => JustifyContent::FlexStart,
220                Some(LayoutJustifyContent::End) => JustifyContent::FlexEnd,
221                Some(LayoutJustifyContent::SpaceBetween) => JustifyContent::SpaceBetween,
222                Some(LayoutJustifyContent::SpaceAround) => JustifyContent::SpaceAround,
223                Some(LayoutJustifyContent::SpaceEvenly) => JustifyContent::SpaceEvenly,
224                None => JustifyContent::FlexStart,
225            },
226            position: Offsets {
227                left: translate_dimension(rect_layout.left.map(|prop| prop.map_property(|l| l.0))),
228                right: translate_dimension(rect_layout.right.map(|prop| prop.map_property(|r| r.0))),
229                top: translate_dimension(rect_layout.top.map(|prop| prop.map_property(|t| t.0))),
230                bottom: translate_dimension(rect_layout.bottom.map(|prop| prop.map_property(|b| b.0))),
231            },
232            margin: Offsets {
233                left: translate_dimension(rect_layout.margin_left.map(|prop| prop.map_property(|l| l.0))),
234                right: translate_dimension(rect_layout.margin_right.map(|prop| prop.map_property(|r| r.0))),
235                top: translate_dimension(rect_layout.margin_top.map(|prop| prop.map_property(|t| t.0))),
236                bottom: translate_dimension(rect_layout.margin_bottom.map(|prop| prop.map_property(|b| b.0))),
237            },
238            padding: Offsets {
239                left: translate_dimension(rect_layout.padding_left.map(|prop| prop.map_property(|l| l.0))),
240                right: translate_dimension(rect_layout.padding_right.map(|prop| prop.map_property(|r| r.0))),
241                top: translate_dimension(rect_layout.padding_top.map(|prop| prop.map_property(|t| t.0))),
242                bottom: translate_dimension(rect_layout.padding_bottom.map(|prop| prop.map_property(|b| b.0))),
243            },
244            border: Offsets {
245                left: translate_dimension(rect_layout.border_left_width.map(|prop| prop.map_property(|l| l.0))),
246                right: translate_dimension(rect_layout.border_right_width.map(|prop| prop.map_property(|r| r.0))),
247                top: translate_dimension(rect_layout.border_top_width.map(|prop| prop.map_property(|t| t.0))),
248                bottom: translate_dimension(rect_layout.border_bottom_width.map(|prop| prop.map_property(|b| b.0))),
249            },
250            flex_grow: rect_layout.flex_grow.unwrap_or_default().get_property_or_default().unwrap_or_default().0.get(),
251            flex_shrink: rect_layout.flex_shrink.unwrap_or_default().get_property_or_default().unwrap_or_default().0.get(),
252            size: Size {
253                width: translate_dimension(rect_layout.width.map(|prop| prop.map_property(|l| l.0))),
254                height: translate_dimension(rect_layout.height.map(|prop| prop.map_property(|l| l.0))),
255            },
256            min_size: Size {
257                width: translate_dimension(rect_layout.min_width.map(|prop| prop.map_property(|l| l.0))),
258                height: translate_dimension(rect_layout.min_height.map(|prop| prop.map_property(|l| l.0))),
259            },
260            max_size: Size {
261                width: translate_dimension(rect_layout.max_width.map(|prop| prop.map_property(|l| l.0))),
262                height: translate_dimension(rect_layout.max_height.map(|prop| prop.map_property(|l| l.0))),
263            },
264            align_self: AlignSelf::default(), // todo!
265            flex_basis: Dimension::default(), // todo!
266            aspect_ratio: Number::Undefined,
267            font_size_px: rect_style.font_size.and_then(|fs| fs.get_property_owned()).unwrap_or(DEFAULT_FONT_SIZE).0,
268            line_height: rect_style.line_height.and_then(|lh| lh.map_property(|lh| lh.0).get_property_owned()).map(|lh| lh.get()),
269            letter_spacing: rect_style.letter_spacing.and_then(|ls| ls.map_property(|ls| ls.0).get_property_owned()),
270            word_spacing: rect_style.word_spacing.and_then(|ws| ws.map_property(|ws| ws.0).get_property_owned()),
271            tab_width: rect_style.tab_width.and_then(|tw| tw.map_property(|tw| tw.0).get_property_owned()).map(|tw| tw.get()),
272        }
273    }
274}