tawasuyu

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 1077HTML 1008Markdown 12TOML 83209 Definiciones

⎇ c9c40576df24056c2f0f8b421d8046d33bb1cc2d src/style/mod.rs Rust · 528 líneas · 21.4 KB Historia Quién tocó qué crudo

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