Fix flexbox baseline alignment (#325)
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: &mut 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 #[cfg(feature = "debug")]
76 NODE_LOGGER.log("LEAF");
77 #[cfg(feature = "debug")]
78 NODE_LOGGER.labelled_debug_log("node_size", node_size);
79 #[cfg(feature = "debug")]
80 NODE_LOGGER.labelled_debug_log("min_size ", node_min_size);
81 #[cfg(feature = "debug")]
82 NODE_LOGGER.labelled_debug_log("max_size ", node_max_size);
83
84 // Return early if both width and height are known
85 if let Size { width: Some(width), height: Some(height) } = node_size {
86 let size = Size { width, height }.maybe_clamp(node_min_size, node_max_size);
87 return SizeAndBaselines { size, first_baselines: Point::NONE };
88 };
89
90 if tree.needs_measure(node) {
91 // Compute available space
92 let available_space = Size {
93 width: available_space
94 .width
95 .maybe_set(node_size.width)
96 .maybe_set(node_max_size.width)
97 .map_definite_value(|size| size.maybe_clamp(node_min_size.width, node_max_size.width)),
98 height: available_space
99 .height
100 .maybe_set(node_size.height)
101 .maybe_set(node_max_size.height)
102 .map_definite_value(|size| size.maybe_clamp(node_min_size.height, node_max_size.height)),
103 };
104
105 // Measure node
106 let measured_size = tree.measure_node(node, known_dimensions, available_space);
107
108 let measured_size = Size {
109 width: measured_size.width,
110 height: f32_max(measured_size.height, aspect_ratio.map(|ratio| measured_size.width / ratio).unwrap_or(0.0)),
111 };
112
113 let size = node_size.unwrap_or(measured_size).maybe_clamp(node_min_size, node_max_size);
114 return SizeAndBaselines { size, first_baselines: Point::NONE };
115 }
116
117 // Note: both horizontal and vertical percentage padding/borders are resolved against the container's inline size (i.e. width).
118 // This is not a bug, but is how CSS is specified (see: https://developer.mozilla.org/en-US/docs/Web/CSS/padding#values)
119 let padding = style.padding.resolve_or_zero(parent_size.width);
120 let border = style.border.resolve_or_zero(parent_size.width);
121
122 let size = Size {
123 width: node_size
124 .width
125 // .unwrap_or(0.0) + padding.horizontal_axis_sum() + border.horizontal_axis_sum(), // content-box
126 .unwrap_or(0.0 + padding.horizontal_axis_sum() + border.horizontal_axis_sum()) // border-box
127 .maybe_clamp(node_min_size.width, node_max_size.width),
128 height: node_size
129 .height
130 // .unwrap_or(0.0) + padding.vertical_axis_sum() + border.vertical_axis_sum(), // content-box
131 .unwrap_or(0.0 + padding.vertical_axis_sum() + border.vertical_axis_sum()) // border-box
132 .maybe_clamp(node_min_size.height, node_max_size.height),
133 };
134 SizeAndBaselines { size, first_baselines: Point::NONE }
135}