gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 5364Rust 86Markdown 12TOML 91841 Definiciones

⎇ 7b6efafe42c49ebdf1d84a7afe8ff4e62fda94ed src/compute/leaf.rs Rust · 152 líneas · 6.5 KB Historia Quién tocó qué crudo

Convert algorithms to traitified styles 7b6efafe4 · Nico Burns ·

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