gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

⎇ 67f286f3666b621ef5ee2e1a1fbfb40a1084349e src/compute/leaf.rs Rust · 207 líneas · 8.8 KB Historia Quién tocó qué crudo

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