gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definitions

⎇ d4374b93f0ec2c0ace42d3feafec22d3af1940d8 src/compute/leaf.rs Rust · 166 lines · 7.3 KB History Who touched what raw

Refactor `LayoutTree` + add examples of custom trees (#564) d4374b93f · Nico Burns ·

1//! Computes size using styles and measure functions
2
3use crate::geometry::{Point, Size};
4use crate::style::{AvailableSpace, Display, Overflow, Position, Style};
5use crate::tree::CollapsibleMarginSet;
6use crate::tree::{LayoutInput, LayoutOutput, SizingMode};
7use crate::util::debug::debug_log;
8use crate::util::sys::f32_max;
9use crate::util::MaybeMath;
10use crate::util::{MaybeResolve, ResolveOrZero};
11
12/// Compute the size of a leaf node (node with no children)
13pub fn compute_leaf_layout<MeasureFunction>(
14    inputs: LayoutInput,
15    style: &Style,
16    measure_function: Option<MeasureFunction>,
17) -> LayoutOutput
18where
19    MeasureFunction: FnOnce(Size<Option<f32>>, Size<AvailableSpace>) -> Size<f32>,
20{
21    let LayoutInput { known_dimensions, parent_size, available_space, sizing_mode, .. } = inputs;
22
23    // Resolve node's preferred/min/max sizes (width/heights) against the available space (percentages resolve to pixel values)
24    // For ContentSize mode, we pretend that the node has no size styles as these should be ignored.
25    let (node_size, node_min_size, node_max_size, aspect_ratio) = match sizing_mode {
26        SizingMode::ContentSize => {
27            let node_size = known_dimensions;
28            let node_min_size = Size::NONE;
29            let node_max_size = Size::NONE;
30            (node_size, node_min_size, node_max_size, None)
31        }
32        SizingMode::InherentSize => {
33            let aspect_ratio = style.aspect_ratio;
34            let style_size = style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
35            let style_min_size = style.min_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
36            let style_max_size = style.max_size.maybe_resolve(parent_size);
37
38            let node_size = known_dimensions.or(style_size);
39            (node_size, style_min_size, style_max_size, aspect_ratio)
40        }
41    };
42
43    // Note: both horizontal and vertical percentage padding/borders are resolved against the container's inline size (i.e. width).
44    // This is not a bug, but is how CSS is specified (see: https://developer.mozilla.org/en-US/docs/Web/CSS/padding#values)
45    let margin = style.margin.resolve_or_zero(parent_size.width);
46    let padding = style.padding.resolve_or_zero(parent_size.width);
47    let border = style.border.resolve_or_zero(parent_size.width);
48    let padding_border = padding + border;
49
50    // Scrollbar gutters are reserved when the `overflow` property is set to `Overflow::Scroll`.
51    // However, the axis are switched (transposed) because a node that scrolls vertically needs
52    // *horizontal* space to be reserved for a scrollbar
53    let scrollbar_gutter = style.overflow.transpose().map(|overflow| match overflow {
54        Overflow::Scroll => style.scrollbar_width,
55        _ => 0.0,
56    });
57    // TODO: make side configurable based on the `direction` property
58    let mut content_box_inset = padding_border;
59    content_box_inset.right += scrollbar_gutter.x;
60    content_box_inset.bottom += scrollbar_gutter.y;
61
62    #[cfg(feature = "block_layout")]
63    let is_block = style.display == Display::Block;
64    #[cfg(not(feature = "block_layout"))]
65    let is_block = false;
66
67    let has_styles_preventing_being_collapsed_through = !is_block
68        || style.overflow.x.is_scroll_container()
69        || style.overflow.y.is_scroll_container()
70        || style.position == Position::Absolute
71        || padding.top > 0.0
72        || padding.bottom > 0.0
73        || border.top > 0.0
74        || border.bottom > 0.0;
75
76    debug_log!("LEAF");
77    debug_log!("node_size", dbg:node_size);
78    debug_log!("min_size ", dbg:node_min_size);
79    debug_log!("max_size ", dbg:node_max_size);
80
81    // Return early if both width and height are known
82    if let Size { width: Some(width), height: Some(height) } = node_size {
83        let size = Size { width, height }
84            .maybe_clamp(node_min_size, node_max_size)
85            .maybe_max(padding_border.sum_axes().map(Some));
86        return LayoutOutput {
87            size,
88            first_baselines: Point::NONE,
89            top_margin: CollapsibleMarginSet::ZERO,
90            bottom_margin: CollapsibleMarginSet::ZERO,
91            margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
92                && size.height == 0.0
93                && measure_function.is_none(),
94        };
95    };
96
97    if let Some(measure_function) = measure_function {
98        // Compute available space
99        let available_space = Size {
100            width: available_space
101                .width
102                .maybe_sub(margin.horizontal_axis_sum())
103                .maybe_set(node_size.width)
104                .maybe_set(node_max_size.width)
105                .map_definite_value(|size| {
106                    size.maybe_clamp(node_min_size.width, node_max_size.width) - content_box_inset.horizontal_axis_sum()
107                }),
108            height: available_space
109                .height
110                .maybe_sub(margin.vertical_axis_sum())
111                .maybe_set(node_size.height)
112                .maybe_set(node_max_size.height)
113                .map_definite_value(|size| {
114                    size.maybe_clamp(node_min_size.height, node_max_size.height) - content_box_inset.vertical_axis_sum()
115                }),
116        };
117
118        // Measure node
119        let measured_size = measure_function(known_dimensions, available_space);
120        let clamped_size =
121            node_size.unwrap_or(measured_size + content_box_inset.sum_axes()).maybe_clamp(node_min_size, node_max_size);
122        let size = Size {
123            width: clamped_size.width,
124            height: f32_max(clamped_size.height, aspect_ratio.map(|ratio| clamped_size.width / ratio).unwrap_or(0.0)),
125        };
126        let size = size.maybe_max(padding_border.sum_axes().map(Some));
127
128        return LayoutOutput {
129            size,
130            first_baselines: Point::NONE,
131            top_margin: CollapsibleMarginSet::ZERO,
132            bottom_margin: CollapsibleMarginSet::ZERO,
133            margins_can_collapse_through: !has_styles_preventing_being_collapsed_through
134                && size.height == 0.0
135                && measured_size.height == 0.0,
136        };
137    }
138
139    let size = Size {
140        width: node_size
141            .width
142            // .unwrap_or(0.0) + padding.horizontal_axis_sum() + border.horizontal_axis_sum(), // content-box
143            .unwrap_or(content_box_inset.horizontal_axis_sum()) // border-box
144            .maybe_clamp(node_min_size.width, node_max_size.width)
145            .maybe_max(padding_border.horizontal_axis_sum().into()),
146        height: node_size
147            .height
148            // .unwrap_or(0.0) + padding.vertical_axis_sum() + border.vertical_axis_sum(), // content-box
149            .unwrap_or(content_box_inset.vertical_axis_sum()) // border-box
150            .maybe_clamp(node_min_size.height, node_max_size.height)
151            .maybe_max(padding_border.vertical_axis_sum().into()),
152    };
153
154    let size = Size {
155        width: f32_max(size.width, aspect_ratio.map(|ratio| size.height * ratio).unwrap_or(0.0)),
156        height: f32_max(size.height, aspect_ratio.map(|ratio| size.width / ratio).unwrap_or(0.0)),
157    };
158
159    LayoutOutput {
160        size,
161        first_baselines: Point::NONE,
162        top_margin: CollapsibleMarginSet::ZERO,
163        bottom_margin: CollapsibleMarginSet::ZERO,
164        margins_can_collapse_through: !has_styles_preventing_being_collapsed_through && size.height == 0.0,
165    }
166}