gioser

tawasuyu/taffy

A high performance rust-powered UI layout library

Rust 492HTML 245JavaScript 7CSS 51137 Definitions

⎇ 936ce7c79678915766c6bd374401807909026bf9 src/style/mod.rs Rust · 518 lines · 20.9 KB History Who touched what raw

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