gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

HTML 229Rust 8TOML 3CSS 1310 Definiciones

⎇ daa07e0f4e3e009f5b0c11ada5df9785efd4b2c2 src/style/mod.rs Rust · 493 líneas · 19.8 KB Historia Quién tocó qué crudo

Remove `LayoutAlgorithm` trait (#562) daa07e0f4 · Nico Burns ·

1//! A typed representation of [CSS style properties](https://css-tricks.com/snippets/css/a-guide-to-flexbox/) in Rust. Used as input to layout computation.
2mod alignment;
3mod dimension;
4
5#[cfg(feature = "flexbox")]
6mod flex;
7
8pub use self::alignment::{AlignContent, AlignItems, AlignSelf, JustifyContent, JustifyItems, JustifySelf};
9pub use self::dimension::{AvailableSpace, Dimension, LengthPercentage, LengthPercentageAuto};
10
11#[cfg(feature = "flexbox")]
12pub use self::flex::{FlexDirection, FlexWrap};
13
14#[cfg(feature = "grid")]
15mod grid;
16#[cfg(feature = "grid")]
17pub(crate) use self::grid::{GenericGridPlacement, OriginZeroGridPlacement};
18#[cfg(feature = "grid")]
19pub use self::grid::{
20    GridAutoFlow, GridPlacement, GridTrackRepetition, MaxTrackSizingFunction, MinTrackSizingFunction,
21    NonRepeatedTrackSizingFunction, TrackSizingFunction,
22};
23use crate::geometry::{Point, Rect, Size};
24
25#[cfg(feature = "grid")]
26use crate::geometry::Line;
27#[cfg(feature = "serde")]
28use crate::style_helpers;
29#[cfg(feature = "grid")]
30use crate::util::sys::GridTrackVec;
31
32/// Sets the layout used for the children of this node
33///
34/// The default values depends on on which feature flags are enabled. The order of precedence is: Flex, Grid, Block, None.
35#[derive(Copy, Clone, PartialEq, Eq, Debug)]
36#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
37pub enum Display {
38    /// The children will follow the block layout algorithm
39    #[cfg(feature = "block_layout")]
40    Block,
41    /// The children will follow the flexbox layout algorithm
42    #[cfg(feature = "flexbox")]
43    Flex,
44    /// The children will follow the CSS Grid layout algorithm
45    #[cfg(feature = "grid")]
46    Grid,
47    /// The children will not be laid out, and will follow absolute positioning
48    None,
49}
50
51impl Display {
52    /// The default of Display.
53    #[cfg(feature = "flexbox")]
54    pub const DEFAULT: Display = Display::Flex;
55
56    /// The default of Display.
57    #[cfg(all(feature = "grid", not(feature = "flexbox")))]
58    pub const DEFAULT: Display = Display::Grid;
59
60    /// The default of Display.
61    #[cfg(all(feature = "block_layout", not(feature = "flexbox"), not(feature = "grid")))]
62    pub const DEFAULT: Display = Display::Block;
63
64    /// The default of Display.
65    #[cfg(all(not(feature = "flexbox"), not(feature = "grid"), not(feature = "block_layout")))]
66    pub const DEFAULT: Display = Display::None;
67}
68
69impl core::fmt::Display for Display {
70    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
71        match self {
72            Display::None => write!(f, "NONE"),
73            #[cfg(feature = "block_layout")]
74            Display::Block => write!(f, "BLOCK"),
75            #[cfg(feature = "flexbox")]
76            Display::Flex => write!(f, "FLEX"),
77            #[cfg(feature = "grid")]
78            Display::Grid => write!(f, "GRID"),
79        }
80    }
81}
82
83impl Default for Display {
84    fn default() -> Self {
85        Self::DEFAULT
86    }
87}
88
89/// The positioning strategy for this item.
90///
91/// This controls both how the origin is determined for the [`Style::position`] field,
92/// and whether or not the item will be controlled by flexbox's layout algorithm.
93///
94/// WARNING: this enum follows the behavior of [CSS's `position` property](https://developer.mozilla.org/en-US/docs/Web/CSS/position),
95/// which can be unintuitive.
96///
97/// [`Position::Relative`] is the default value, in contrast to the default behavior in CSS.
98#[derive(Copy, Clone, PartialEq, Eq, Debug)]
99#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
100pub enum Position {
101    /// The offset is computed relative to the final position given by the layout algorithm.
102    /// Offsets do not affect the position of any other items; they are effectively a correction factor applied at the end.
103    Relative,
104    /// The offset is computed relative to this item's closest positioned ancestor, if any.
105    /// Otherwise, it is placed relative to the origin.
106    /// No space is created for the item in the page layout, and its size will not be altered.
107    ///
108    /// WARNING: to opt-out of layouting entirely, you must use [`Display::None`] instead on your [`Style`] object.
109    Absolute,
110}
111
112impl Default for Position {
113    fn default() -> Self {
114        Self::Relative
115    }
116}
117
118/// How children overflowing their container should affect layout
119///
120/// In CSS the primary effect of this property is to control whether contents of a parent container that overflow that container should
121/// be displayed anyway, be clipped, or trigger the container to become a scroll container. However it also has secondary effects on layout,
122/// the main ones being:
123///
124///   - The automatic minimum size Flexbox/CSS Grid items with non-`Visible` overflow is `0` rather than being content based
125///   - `Overflow::Scroll` nodes have space in the layout reserved for a scrollbar (width controlled by the `scrollbar_width` property)
126///
127/// In Taffy, we only implement the layout related secondary effects as we are not concerned with drawing/painting. The amount of space reserved for
128/// a scrollbar is controlled by the `scrollbar_width` property. If this is `0` then `Scroll` behaves identically to `Hidden`.
129///
130/// <https://developer.mozilla.org/en-US/docs/Web/CSS/overflow>
131#[derive(Copy, Clone, PartialEq, Eq, Debug, Default)]
132#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
133pub enum Overflow {
134    /// The automatic minimum size of this node as a flexbox/grid item should be based on the size of it's content.
135    #[default]
136    Visible,
137    /// The automatic minimum size of this node as a flexbox/grid item should be `0`.
138    Hidden,
139    /// The automatic minimum size of this node as a flexbox/grid item should be `0`. Additionally, space should be reserved
140    /// for a scrollbar. The amount of space reserved is controlled by the `scrollbar_width` property.
141    Scroll,
142}
143
144impl Overflow {
145    /// Returns true for overflow modes that contain their contents (`Overflow::Hidden`, `Overflow::Scroll`, `Overflow::Auto`)
146    /// or else false for overflow modes that allow their contains to spill (`Overflow::Visible`).
147    #[inline(always)]
148    pub(crate) fn is_scroll_container(self) -> bool {
149        self != Overflow::Visible
150    }
151
152    /// Returns `Some(0.0)` if the overflow mode would cause the automatic minimum size of a Flexbox or CSS Grid item
153    /// to be `0`. Else returns None.
154    #[inline(always)]
155    pub(crate) fn maybe_into_automatic_min_size(self) -> Option<f32> {
156        match self.is_scroll_container() {
157            true => Some(0.0),
158            false => None,
159        }
160    }
161}
162
163/// The flexbox layout information for a single node.
164///
165/// The most important idea in flexbox is the notion of a "main" and "cross" axis, which are always perpendicular to each other.
166/// The orientation of these axes are controlled via the [`FlexDirection`] field of this struct.
167///
168/// This struct follows the [CSS equivalent](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Flexible_Box_Layout/Basic_Concepts_of_Flexbox) directly;
169/// information about the behavior on the web should transfer directly.
170///
171/// Detailed information about the exact behavior of each of these fields
172/// can be found on [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS) by searching for the field name.
173/// The distinction between margin, padding and border is explained well in
174/// this [introduction to the box model](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Box_Model/Introduction_to_the_CSS_box_model).
175///
176/// If the behavior does not match the flexbox layout algorithm on the web, please file a bug!
177#[derive(Clone, PartialEq, Debug)]
178#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
179#[cfg_attr(feature = "serde", serde(default))]
180pub struct Style {
181    /// What layout strategy should be used?
182    pub display: Display,
183
184    // Overflow properties
185    /// How children overflowing their container should affect layout
186    pub overflow: Point<Overflow>,
187    /// How much space (in points) should be reserved for the scrollbars of `Overflow::Scroll` and `Overflow::Auto` nodes.
188    pub scrollbar_width: f32,
189
190    // Position properties
191    /// What should the `position` value of this struct use as a base offset?
192    pub position: Position,
193    /// How should the position of this element be tweaked relative to the layout defined?
194    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
195    pub inset: Rect<LengthPercentageAuto>,
196
197    // Size properies
198    /// Sets the initial size of the item
199    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
200    pub size: Size<Dimension>,
201    /// Controls the minimum size of the item
202    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
203    pub min_size: Size<Dimension>,
204    /// Controls the maximum size of the item
205    #[cfg_attr(feature = "serde", serde(default = "style_helpers::auto"))]
206    pub max_size: Size<Dimension>,
207    /// Sets the preferred aspect ratio for the item
208    ///
209    /// The ratio is calculated as width divided by height.
210    pub aspect_ratio: Option<f32>,
211
212    // Spacing Properties
213    /// How large should the margin be on each side?
214    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
215    pub margin: Rect<LengthPercentageAuto>,
216    /// How large should the padding be on each side?
217    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
218    pub padding: Rect<LengthPercentage>,
219    /// How large should the border be on each side?
220    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
221    pub border: Rect<LengthPercentage>,
222
223    // Alignment properties
224    /// How this node's children aligned in the cross/block axis?
225    #[cfg(any(feature = "flexbox", feature = "grid"))]
226    pub align_items: Option<AlignItems>,
227    /// How this node should be aligned in the cross/block axis
228    /// Falls back to the parents [`AlignItems`] if not set
229    #[cfg(any(feature = "flexbox", feature = "grid"))]
230    pub align_self: Option<AlignSelf>,
231    /// How this node's children should be aligned in the inline axis
232    #[cfg(feature = "grid")]
233    pub justify_items: Option<AlignItems>,
234    /// How this node should be aligned in the inline axis
235    /// Falls back to the parents [`JustifyItems`] if not set
236    #[cfg(feature = "grid")]
237    pub justify_self: Option<AlignSelf>,
238    /// How should content contained within this item be aligned in the cross/block axis
239    #[cfg(any(feature = "flexbox", feature = "grid"))]
240    pub align_content: Option<AlignContent>,
241    /// How should contained within this item be aligned in the main/inline axis
242    #[cfg(any(feature = "flexbox", feature = "grid"))]
243    pub justify_content: Option<JustifyContent>,
244    /// How large should the gaps between items in a grid or flex container be?
245    #[cfg(any(feature = "flexbox", feature = "grid"))]
246    #[cfg_attr(feature = "serde", serde(default = "style_helpers::zero"))]
247    pub gap: Size<LengthPercentage>,
248
249    // Flexbox properies
250    /// Which direction does the main axis flow in?
251    #[cfg(feature = "flexbox")]
252    pub flex_direction: FlexDirection,
253    /// Should elements wrap, or stay in a single line?
254    #[cfg(feature = "flexbox")]
255    pub flex_wrap: FlexWrap,
256    /// Sets the initial main axis size of the item
257    #[cfg(feature = "flexbox")]
258    pub flex_basis: Dimension,
259    /// The relative rate at which this item grows when it is expanding to fill space
260    ///
261    /// 0.0 is the default value, and this value must be positive.
262    #[cfg(feature = "flexbox")]
263    pub flex_grow: f32,
264    /// The relative rate at which this item shrinks when it is contracting to fit into space
265    ///
266    /// 1.0 is the default value, and this value must be positive.
267    #[cfg(feature = "flexbox")]
268    pub flex_shrink: f32,
269
270    // Grid container properies
271    /// Defines the track sizing functions (widths) of the grid rows
272    #[cfg(feature = "grid")]
273    pub grid_template_rows: GridTrackVec<TrackSizingFunction>,
274    /// Defines the track sizing functions (heights) of the grid columns
275    #[cfg(feature = "grid")]
276    pub grid_template_columns: GridTrackVec<TrackSizingFunction>,
277    /// Defines the size of implicitly created rows
278    #[cfg(feature = "grid")]
279    pub grid_auto_rows: GridTrackVec<NonRepeatedTrackSizingFunction>,
280    /// Defined the size of implicitly created columns
281    #[cfg(feature = "grid")]
282    pub grid_auto_columns: GridTrackVec<NonRepeatedTrackSizingFunction>,
283    /// Controls how items get placed into the grid for auto-placed items
284    #[cfg(feature = "grid")]
285    pub grid_auto_flow: GridAutoFlow,
286
287    // Grid child properties
288    /// Defines which row in the grid the item should start and end at
289    #[cfg(feature = "grid")]
290    pub grid_row: Line<GridPlacement>,
291    /// Defines which column in the grid the item should start and end at
292    #[cfg(feature = "grid")]
293    pub grid_column: Line<GridPlacement>,
294}
295
296impl Style {
297    /// The [`Default`] layout, in a form that can be used in const functions
298    pub const DEFAULT: Style = Style {
299        display: Display::DEFAULT,
300        overflow: Point { x: Overflow::Visible, y: Overflow::Visible },
301        scrollbar_width: 0.0,
302        position: Position::Relative,
303        inset: Rect::auto(),
304        margin: Rect::zero(),
305        padding: Rect::zero(),
306        border: Rect::zero(),
307        size: Size::auto(),
308        min_size: Size::auto(),
309        max_size: Size::auto(),
310        aspect_ratio: None,
311        #[cfg(any(feature = "flexbox", feature = "grid"))]
312        gap: Size::zero(),
313        // Aligment
314        #[cfg(any(feature = "flexbox", feature = "grid"))]
315        align_items: None,
316        #[cfg(any(feature = "flexbox", feature = "grid"))]
317        align_self: None,
318        #[cfg(feature = "grid")]
319        justify_items: None,
320        #[cfg(feature = "grid")]
321        justify_self: None,
322        #[cfg(any(feature = "flexbox", feature = "grid"))]
323        align_content: None,
324        #[cfg(any(feature = "flexbox", feature = "grid"))]
325        justify_content: None,
326        // Flexbox
327        #[cfg(feature = "flexbox")]
328        flex_direction: FlexDirection::Row,
329        #[cfg(feature = "flexbox")]
330        flex_wrap: FlexWrap::NoWrap,
331        #[cfg(feature = "flexbox")]
332        flex_grow: 0.0,
333        #[cfg(feature = "flexbox")]
334        flex_shrink: 1.0,
335        #[cfg(feature = "flexbox")]
336        flex_basis: Dimension::Auto,
337        // Grid
338        #[cfg(feature = "grid")]
339        grid_template_rows: GridTrackVec::new(),
340        #[cfg(feature = "grid")]
341        grid_template_columns: GridTrackVec::new(),
342        #[cfg(feature = "grid")]
343        grid_auto_rows: GridTrackVec::new(),
344        #[cfg(feature = "grid")]
345        grid_auto_columns: GridTrackVec::new(),
346        #[cfg(feature = "grid")]
347        grid_auto_flow: GridAutoFlow::Row,
348        #[cfg(feature = "grid")]
349        grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
350        #[cfg(feature = "grid")]
351        grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
352    };
353}
354
355impl Default for Style {
356    fn default() -> Self {
357        Style::DEFAULT
358    }
359}
360
361#[cfg(test)]
362mod tests {
363    use super::Style;
364    use crate::geometry::*;
365
366    #[test]
367    fn defaults_match() {
368        #[cfg(feature = "grid")]
369        use super::GridPlacement;
370
371        let old_defaults = Style {
372            display: Default::default(),
373            overflow: Default::default(),
374            scrollbar_width: 0.0,
375            position: Default::default(),
376            #[cfg(feature = "flexbox")]
377            flex_direction: Default::default(),
378            #[cfg(feature = "flexbox")]
379            flex_wrap: Default::default(),
380            #[cfg(any(feature = "flexbox", feature = "grid"))]
381            align_items: Default::default(),
382            #[cfg(any(feature = "flexbox", feature = "grid"))]
383            align_self: Default::default(),
384            #[cfg(feature = "grid")]
385            justify_items: Default::default(),
386            #[cfg(feature = "grid")]
387            justify_self: Default::default(),
388            #[cfg(any(feature = "flexbox", feature = "grid"))]
389            align_content: Default::default(),
390            #[cfg(any(feature = "flexbox", feature = "grid"))]
391            justify_content: Default::default(),
392            inset: Rect::auto(),
393            margin: Rect::zero(),
394            padding: Rect::zero(),
395            border: Rect::zero(),
396            gap: Size::zero(),
397            #[cfg(feature = "flexbox")]
398            flex_grow: 0.0,
399            #[cfg(feature = "flexbox")]
400            flex_shrink: 1.0,
401            #[cfg(feature = "flexbox")]
402            flex_basis: super::Dimension::Auto,
403            size: Size::auto(),
404            min_size: Size::auto(),
405            max_size: Size::auto(),
406            aspect_ratio: Default::default(),
407            #[cfg(feature = "grid")]
408            grid_template_rows: Default::default(),
409            #[cfg(feature = "grid")]
410            grid_template_columns: Default::default(),
411            #[cfg(feature = "grid")]
412            grid_auto_rows: Default::default(),
413            #[cfg(feature = "grid")]
414            grid_auto_columns: Default::default(),
415            #[cfg(feature = "grid")]
416            grid_auto_flow: Default::default(),
417            #[cfg(feature = "grid")]
418            grid_row: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
419            #[cfg(feature = "grid")]
420            grid_column: Line { start: GridPlacement::Auto, end: GridPlacement::Auto },
421        };
422
423        assert_eq!(Style::DEFAULT, Style::default());
424        assert_eq!(Style::DEFAULT, old_defaults);
425    }
426
427    // NOTE: Please feel free the update the sizes in this test as required. This test is here to prevent unintentional size changes
428    // and to serve as accurate up-to-date documentation on the sizes.
429    #[test]
430    fn style_sizes() {
431        use super::*;
432
433        fn assert_type_size<T>(expected_size: usize) {
434            let name = ::core::any::type_name::<T>();
435            let name = name.replace("taffy::geometry::", "");
436            let name = name.replace("taffy::style::dimension::", "");
437            let name = name.replace("taffy::style::alignment::", "");
438            let name = name.replace("taffy::style::flex::", "");
439            let name = name.replace("taffy::style::grid::", "");
440
441            assert_eq!(
442                ::core::mem::size_of::<T>(),
443                expected_size,
444                "Expected {} for be {} byte(s) but it was {} byte(s)",
445                name,
446                expected_size,
447                ::core::mem::size_of::<T>(),
448            );
449        }
450
451        // Display and Position
452        assert_type_size::<Display>(1);
453        assert_type_size::<Position>(1);
454        assert_type_size::<Overflow>(1);
455
456        // Dimensions and aggregations of Dimensions
457        assert_type_size::<f32>(4);
458        assert_type_size::<LengthPercentage>(8);
459        assert_type_size::<LengthPercentageAuto>(8);
460        assert_type_size::<Dimension>(8);
461        assert_type_size::<Size<LengthPercentage>>(16);
462        assert_type_size::<Size<LengthPercentageAuto>>(16);
463        assert_type_size::<Size<Dimension>>(16);
464        assert_type_size::<Rect<LengthPercentage>>(32);
465        assert_type_size::<Rect<LengthPercentageAuto>>(32);
466        assert_type_size::<Rect<Dimension>>(32);
467
468        // Alignment
469        assert_type_size::<AlignContent>(1);
470        assert_type_size::<AlignItems>(1);
471        assert_type_size::<Option<AlignItems>>(1);
472
473        // Flexbox Container
474        assert_type_size::<FlexDirection>(1);
475        assert_type_size::<FlexWrap>(1);
476
477        // CSS Grid Container
478        assert_type_size::<GridAutoFlow>(1);
479        assert_type_size::<MinTrackSizingFunction>(8);
480        assert_type_size::<MaxTrackSizingFunction>(12);
481        assert_type_size::<NonRepeatedTrackSizingFunction>(20);
482        assert_type_size::<TrackSizingFunction>(32);
483        assert_type_size::<Vec<NonRepeatedTrackSizingFunction>>(24);
484        assert_type_size::<Vec<TrackSizingFunction>>(24);
485
486        // CSS Grid Item
487        assert_type_size::<GridPlacement>(4);
488        assert_type_size::<Line<GridPlacement>>(8);
489
490        // Overall
491        assert_type_size::<Style>(352);
492    }
493}