gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 229Rust 8TOML 3CSS 1310 Definitions

⎇ d2b2eb08a93c6088e89b14d630cc962d551b084e src/compute/leaf.rs Rust · 150 lines · 6.2 KB History Who touched what raw

Flexbox: pass correct cross-axis available space when computing an item's intrinsic main size (+misc fixes) (#522) df4873ec3 · Nico Burns ·

1//! Computes size using styles and measure functions
2
3use crate::compute::LayoutAlgorithm;
4use crate::geometry::{Point, Size};
5use crate::layout::{SizeAndBaselines, SizingMode};
6use crate::math::MaybeMath;
7use crate::node::Node;
8use crate::resolve::{MaybeResolve, ResolveOrZero};
9use crate::style::AvailableSpace;
10use crate::sys::f32_max;
11use crate::tree::LayoutTree;
12
13#[cfg(feature = "debug")]
14use crate::debug::NODE_LOGGER;
15
16/// The public interface to Taffy's leaf node algorithm implementation
17pub(crate) struct LeafAlgorithm;
18impl LayoutAlgorithm for LeafAlgorithm {
19    const NAME: &'static str = "LEAF";
20
21    fn perform_layout(
22        tree: &mut impl LayoutTree,
23        node: Node,
24        known_dimensions: Size<Option<f32>>,
25        parent_size: Size<Option<f32>>,
26        available_space: Size<AvailableSpace>,
27        sizing_mode: SizingMode,
28    ) -> SizeAndBaselines {
29        compute(tree, node, known_dimensions, parent_size, available_space, sizing_mode)
30    }
31
32    fn measure_size(
33        tree: &mut impl LayoutTree,
34        node: Node,
35        known_dimensions: Size<Option<f32>>,
36        parent_size: Size<Option<f32>>,
37        available_space: Size<AvailableSpace>,
38        sizing_mode: SizingMode,
39    ) -> Size<f32> {
40        compute(tree, node, known_dimensions, parent_size, available_space, sizing_mode).size
41    }
42}
43
44/// Compute the size of a leaf node (node with no children)
45pub(crate) fn compute(
46    tree: &impl LayoutTree,
47    node: Node,
48    known_dimensions: Size<Option<f32>>,
49    parent_size: Size<Option<f32>>,
50    available_space: Size<AvailableSpace>,
51    sizing_mode: SizingMode,
52) -> SizeAndBaselines {
53    let style = tree.style(node);
54
55    // Resolve node's preferred/min/max sizes (width/heights) against the available space (percentages resolve to pixel values)
56    // For ContentSize mode, we pretend that the node has no size styles as these should be ignored.
57    let (node_size, node_min_size, node_max_size, aspect_ratio) = match sizing_mode {
58        SizingMode::ContentSize => {
59            let node_size = known_dimensions;
60            let node_min_size = Size::NONE;
61            let node_max_size = Size::NONE;
62            (node_size, node_min_size, node_max_size, None)
63        }
64        SizingMode::InherentSize => {
65            let aspect_ratio = style.aspect_ratio;
66            let style_size = style.size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
67            let style_min_size = style.min_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
68            let style_max_size = style.max_size.maybe_resolve(parent_size).maybe_apply_aspect_ratio(aspect_ratio);
69
70            let node_size = known_dimensions.or(style_size);
71            (node_size, style_min_size, style_max_size, aspect_ratio)
72        }
73    };
74
75    // Note: both horizontal and vertical percentage padding/borders are resolved against the container's inline size (i.e. width).
76    // This is not a bug, but is how CSS is specified (see: https://developer.mozilla.org/en-US/docs/Web/CSS/padding#values)
77    let margin = style.margin.resolve_or_zero(parent_size.width);
78    let padding = style.padding.resolve_or_zero(parent_size.width);
79    let border = style.border.resolve_or_zero(parent_size.width);
80    let padding_border = padding + border;
81
82    #[cfg(feature = "debug")]
83    NODE_LOGGER.log("LEAF");
84    #[cfg(feature = "debug")]
85    NODE_LOGGER.labelled_debug_log("node_size", node_size);
86    #[cfg(feature = "debug")]
87    NODE_LOGGER.labelled_debug_log("min_size ", node_min_size);
88    #[cfg(feature = "debug")]
89    NODE_LOGGER.labelled_debug_log("max_size ", node_max_size);
90
91    // Return early if both width and height are known
92    if let Size { width: Some(width), height: Some(height) } = node_size {
93        let size = Size { width, height }
94            .maybe_clamp(node_min_size, node_max_size)
95            .maybe_max(padding_border.sum_axes().map(Some));
96        return SizeAndBaselines { size, first_baselines: Point::NONE };
97    };
98
99    if tree.needs_measure(node) {
100        // Compute available space
101        let available_space = Size {
102            width: available_space
103                .width
104                .maybe_sub(margin.horizontal_axis_sum())
105                .maybe_set(node_size.width)
106                .maybe_set(node_max_size.width)
107                .map_definite_value(|size| size.maybe_clamp(node_min_size.width, node_max_size.width)),
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| size.maybe_clamp(node_min_size.height, node_max_size.height)),
114        };
115
116        // Measure node
117        let measured_size = tree.measure_node(node, known_dimensions, available_space);
118
119        let measured_size = Size {
120            width: measured_size.width,
121            height: f32_max(measured_size.height, aspect_ratio.map(|ratio| measured_size.width / ratio).unwrap_or(0.0)),
122        };
123
124        let size = node_size.unwrap_or(measured_size).maybe_clamp(node_min_size, node_max_size);
125        let size = size.maybe_max(padding_border.sum_axes().map(Some));
126        return SizeAndBaselines { size, first_baselines: Point::NONE };
127    }
128
129    let size = Size {
130        width: node_size
131            .width
132            // .unwrap_or(0.0) + padding.horizontal_axis_sum() + border.horizontal_axis_sum(), // content-box
133            .unwrap_or(0.0) // border-box
134            .maybe_clamp(node_min_size.width, node_max_size.width)
135            .maybe_max(padding_border.horizontal_axis_sum().into()),
136        height: node_size
137            .height
138            // .unwrap_or(0.0) + padding.vertical_axis_sum() + border.vertical_axis_sum(), // content-box
139            .unwrap_or(0.0) // border-box
140            .maybe_clamp(node_min_size.height, node_max_size.height)
141            .maybe_max(padding_border.vertical_axis_sum().into()),
142    };
143
144    let size = Size {
145        width: f32_max(size.width, aspect_ratio.map(|ratio| size.height * ratio).unwrap_or(0.0)),
146        height: f32_max(size.height, aspect_ratio.map(|ratio| size.width / ratio).unwrap_or(0.0)),
147    };
148
149    SizeAndBaselines { size, first_baselines: Point::NONE }
150}